| Overall Statistics |
|
Total Trades 22767 Average Win 0.44% Average Loss -0.40% Compounding Annual Return -28.040% Drawdown 83.300% Expectancy -0.027 Net Profit -79.704% Sharpe Ratio -0.246 Probabilistic Sharpe Ratio 0.040% Loss Rate 54% Win Rate 46% Profit-Loss Ratio 1.11 Alpha -0.134 Beta 0.271 Annual Standard Deviation 0.459 Annual Variance 0.211 Information Ratio -0.405 Tracking Error 0.475 Treynor Ratio -0.417 Total Fees $6668.67 Estimated Strategy Capacity $6200000.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>();
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;
// 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);
}
}
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);
}
}
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 (Symbol symbol1 in fine_symbols)
{
decimal[] prices1 = _data[symbol1].ToArray<decimal>();
decimal averagePrice1 = prices1.Average();
decimal[] normalizedPrices1 = prices1.Select(x => x / averagePrice1).ToArray();
decimal sumRatios = 0;
foreach (Symbol symbol2 in fine_symbols)
{
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_symbols.Count - 1);
if (avg_ratio_value != 0)
avg_ratio[symbol1] = avg_ratio_value;
}
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)
{
if(!(Time.Hour == 9 && Time.Minute == 31))
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);
// MarketOnOpenOrder(symbol, -Portfolio[symbol].Quantity);
// 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"));
}
}
}