| Overall Statistics |
|
Total Trades 23008 Average Win 0.52% Average Loss -0.45% Compounding Annual Return 322.531% Drawdown 70.300% Expectancy 0.157 Net Profit 110450.360% Sharpe Ratio 3.194 Probabilistic Sharpe Ratio 93.394% Loss Rate 47% Win Rate 53% Profit-Loss Ratio 1.16 Alpha 3.043 Beta 0.269 Annual Standard Deviation 0.96 Annual Variance 0.922 Information Ratio 3.079 Tracking Error 0.968 Treynor Ratio 11.38 Total Fees $2756848.24 Estimated Strategy Capacity $8000000.00 Lowest Capacity Asset PINS X3RPXTZRW09X |
#region imports
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Util;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Orders.Fees;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
public class Flock : QCAlgorithm
{
List<Symbol> HighDollarVolumeStocks = new List<Symbol>();
int _coarse_count = 250;
int _stocks_to_hold = 10;
int _period = 5;
decimal Leverage = 0.99m;
decimal Threshold = 0.0m;
private bool _selection_flag = false;
private Dictionary<Symbol, RollingWindow<decimal>> _data = new Dictionary<Symbol, RollingWindow<decimal>>();
private Dictionary<Symbol, float> _weight = new Dictionary<Symbol, float>();
private Dictionary<Symbol, decimal> _price = new Dictionary<Symbol, decimal>();
public override void Initialize()
{
UniverseSettings.Resolution = Resolution.Minute;
SetStartDate(2018, 1, 1);
SetCash(100000);
AddUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));
Symbol symbol = AddEquity("SPY", Resolution.Minute).Symbol;
SetSecurityInitializer((Security security) => security.SetMarketPrice(GetLastKnownPrice(security)));
// selection lambda function
Schedule.On(DateRules.MonthStart(symbol), TimeRules.AfterMarketOpen(symbol), () =>
{
_selection_flag = true;
});
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
foreach (Security security in changes.AddedSecurities){
security.SetFeeModel(new CustomFeeModel());
security.SetLeverage(10);
security.SetMarketPrice(GetLastKnownPrice(security));
}
// liquidate first
Symbol[] invested = (from x in Portfolio where x.Value.Invested select x.Key).ToArray();
foreach (Symbol symbol in invested)
if (!_weight.ContainsKey(symbol))
MarketOnOpenOrder(symbol, -Portfolio[symbol].Quantity);
// Liquidate(symbol);
// trade execution
foreach (KeyValuePair<Symbol, float> item in _weight)
{
// SetHoldings(item.Key, item.Value);
decimal q = (Portfolio.TotalPortfolioValue * (decimal)item.Value) / _price[item.Key]; //#data[item.Key].Value;
MarketOnOpenOrder(item.Key, q);
}
_weight.Clear();
_price.Clear();
}
IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
{
// store daily stock prices
foreach(CoarseFundamental stock in coarse){
Symbol symbol = stock.Symbol;
if (_data.ContainsKey(symbol))
_data[symbol].Add(stock.AdjustedPrice);
}
// if (!_selection_flag)
// return Universe.Unchanged;
Symbol[] selected = coarse.Where(x=> x.Symbol.Value != "AMC" &&
x.Symbol.Value != "GME" && x.Symbol.Value != "UVXY").OrderByDescending(x=>x.DollarVolume).Select(x=>x.Symbol).Take(_coarse_count).ToArray<Symbol>();
foreach (Symbol symbol in selected){
if (_data.ContainsKey(symbol))
continue;
_data[symbol] = new RollingWindow<decimal>((int)_period);
IEnumerable<TradeBar> history = History<TradeBar>(symbol, _period, Resolution.Daily);
if (history.IsNullOrEmpty()){
Log($"Not enough data for {symbol} yet");
continue;
}
foreach (TradeBar bar in history) {
_data[symbol].Add(bar.Close);
}
// TEST
// IEnumerable<TradeBar> m_history = History<TradeBar>(symbol, _period, Resolution.Minute);
}
return selected.Where(x=>_data[x].IsReady).ToList<Symbol>();
}
IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
{
// List<Symbol> fine_symbols = fine.Select(x=>x.Symbol).ToList<Symbol>();
Dictionary<Symbol, decimal> avg_ratio = new Dictionary<Symbol, decimal>();
foreach (FineFundamental stock1 in fine)
{
Symbol symbol1 = stock1.Symbol;
decimal[] prices1 = _data[symbol1].ToArray<decimal>();
decimal averagePrice1 = prices1.Average();
decimal[] normalizedPrices1 = prices1.Select(x => x / averagePrice1).ToArray();
decimal sumRatios = 0;
foreach (FineFundamental stock2 in fine)
{
Symbol symbol2 = stock2.Symbol;
if (symbol1 != symbol2)
{
decimal[] prices2 = _data[symbol2].ToArray<decimal>();
decimal averagePrice2 = prices2.Average();
decimal[] normalizedPrices2 = prices2.Select(x => x / averagePrice2).ToArray();
decimal[] differences = normalizedPrices1.Zip(normalizedPrices2, (x, y) => x - y).ToArray();
decimal maxDifference = differences.Max();
decimal minDifference = differences.Min();
decimal differenceRange = maxDifference - minDifference;
decimal currentDifference = normalizedPrices1.First() - normalizedPrices2.First();
if (differenceRange != 0)
{
decimal ratio = currentDifference / differenceRange;
sumRatios += ratio;
}
}
}
decimal avg_ratio_value = sumRatios / (fine.Count() - 1);
if (avg_ratio_value != 0)
{
avg_ratio[symbol1] = avg_ratio_value;
_price[symbol1] = prices1.First();
}
}
Symbol[] _long = new Symbol[] {};
if (avg_ratio.Count >= _stocks_to_hold)
{
_long = avg_ratio.OrderByDescending(x => Math.Abs(x.Value)).ToArray().Take(_stocks_to_hold).Select(x=>x.Key).ToArray<Symbol>();
foreach(Symbol symbol in _long)
{
if (avg_ratio[symbol] < 0)
_weight[symbol] = 1.0f / (float)(_long.Count());
else if (avg_ratio[symbol] > 0)
_weight[symbol] = -1.0f / (float)(_long.Count());
}
}
return _weight.Keys;
}
public void OnData(TradeBars data)
{
return;
// liquidate first
// Symbol[] invested = (from x in Portfolio where x.Value.Invested select x.Key).ToArray();
// foreach (Symbol symbol in invested)
// if (!_weight.ContainsKey(symbol))
// Liquidate(symbol);
// // trade execution
// foreach (KeyValuePair<Symbol, float> item in _weight)
// if(data.ContainsKey(item.Key))
// {
// // SetHoldings(item.Key, item.Value);
// decimal q = (Portfolio.TotalPortfolioValue * (decimal)item.Value) / data[item.Key].Value;
// MarketOnOpenOrder(item.Key, q);
// }
// _weight.Clear();
}
}
public class CustomFeeModel : FeeModel {
public override OrderFee GetOrderFee(OrderFeeParameters parameters) {
// custom fee math
var fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005m;
return new OrderFee(new CashAmount(fee, "USD"));
}
}
}