| Overall Statistics |
|
Total Trades 2054 Average Win 0.32% Average Loss -0.55% Compounding Annual Return 17.101% Drawdown 20.100% Expectancy 0.141 Net Profit 120.379% Sharpe Ratio 0.932 Probabilistic Sharpe Ratio 37.357% Loss Rate 28% Win Rate 72% Profit-Loss Ratio 0.58 Alpha 0.038 Beta 0.453 Annual Standard Deviation 0.134 Annual Variance 0.018 Information Ratio -0.468 Tracking Error 0.146 Treynor Ratio 0.277 Total Fees $3094.09 Estimated Strategy Capacity $7100000.00 Lowest Capacity Asset STNE WYZEPGZ8HZ6T |
#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QuantConnect.Data.Custom.AlphaStreams;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
public class CONSTANTS
{
public const int ROLLING_WINDOW_COUNT = 21;
}
public class Template : QCAlgorithm
{
/********** Const ************/
const string BENCHMARK = "QQQ";
/// Used to Enable Ticker List For Specialized Runs
const bool ENABLE_TICKER_LIST = false;
/********** Vars ************/
int _LastMonth = -1;
// Start Times
DateTime startDate = new DateTime(2016, 3, 1);
DateTime endDate = new DateTime(2021, 3, 1);
/// NYS = NYSE
/// NAS = NASDAQ
/// ASE = AMEX
/// See: https://www.quantconnect.com/forum/discussion/12234/exchange-id-mapping/p1
/// See: https://www.quantconnect.com/forum/discussion/11121/how-to-backtest-and-live-trade-on-chinese-stocks/p1
string[] _ValidExchanges = {"NYS", "NAS", "ASE"};
/********** Structures ************/
string[] _TickerList = {};
// Map Symbols to their Rolling Window of Tradebar Data
Dictionary<Symbol, RollingWindow<TradeBar>> _WindowDict = new Dictionary<Symbol, RollingWindow<TradeBar>>();
// Map Symbols to their Indicator Data
Dictionary<Symbol, SymbolData> _SymbolDict = new Dictionary<Symbol, SymbolData>();
// Industry Group Mapping
Dictionary<int, string> _Industries = new Dictionary<int, string>();
// Mapping Stocks To Their Industry Group
Dictionary<int, List<Security>> _IndustryStocks = new Dictionary <int, List<Security>>();
/********** Methods ************/
public override void Initialize()
{
SetStartDate(startDate);
SetEndDate(endDate);
SetCash(100000);
UniverseSettings.Resolution = Resolution.Daily;
UniverseSettings.DataNormalizationMode = DataNormalizationMode.SplitAdjusted;
EnableAutomaticIndicatorWarmUp = true;
AddUniverseSelection(new FineFundamentalUniverseSelectionModel(SelectCoarse, SelectNasdaq100Proxy));
var security = AddEquity(BENCHMARK, Resolution.Daily);
SetBenchmark(BENCHMARK);
/// Setup Industry Mapping
Type type = typeof(MorningstarIndustryCode); // MyClass is static class with static properties
foreach (var p in type.GetFields( System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public
))
{
object v = p.GetValue(null); // static classes cannot be instanced, so use null...=
var industryCode = v.ToString().ToInt32();
_Industries[industryCode] = p.Name;
_IndustryStocks[industryCode] = new List<Security>();
}
Schedule.On(Schedule.DateRules.EveryDay(),
Schedule.TimeRules.BeforeMarketClose(BENCHMARK, 1), OnNewDay);
SetWarmUp(new TimeSpan(200, 0, 0, 0));
}
public void OnNewDay()
{
if(IsWarmingUp) return;
if(ActiveSecurities[BENCHMARK].Close < _SymbolDict[BENCHMARK].TwoHundredSma)
{
Liquidate();
return;
}
foreach(var kvp in ActiveSecurities)
{
bool lContainKey = _SymbolDict.ContainsKey(kvp.Key);
bool lInvested = kvp.Value.Invested;
if(!lContainKey && lInvested)
{
Liquidate(kvp.Key);
continue;
}
else if(lContainKey && lInvested && _SymbolDict[kvp.Key].FiveDayRSI > 65)
{
Liquidate(kvp.Key);
continue;
}
}
var lPotentialTrades = (from kvp in ActiveSecurities
where _SymbolDict.ContainsKey(kvp.Key)
where _WindowDict.ContainsKey(kvp.Key)
where kvp.Key != BENCHMARK
where !kvp.Value.Invested
where kvp.Value.Close > _SymbolDict[kvp.Key].TwoHundredSma
where kvp.Value.Close < _SymbolDict[kvp.Key].TwentySma
where _SymbolDict[kvp.Key].FiveDayRSI < 45
orderby _SymbolDict[kvp.Key].FiveDayRSI descending
select kvp.Key).Take(10).ToList();
foreach(var lPotentialTrade in lPotentialTrades)
{
var lTarget = 0.1;
var lQuantity = CalculateOrderQuantity(lPotentialTrade, lTarget);
if(! (ActiveSecurities[lPotentialTrade].Close * lQuantity > Portfolio.MarginRemaining) )
{
SetHoldings(lPotentialTrade, 0.1);
}
}
}
public void WindowBarHandler(object sender, TradeBar windowBar)
{
if(!_WindowDict.ContainsKey(windowBar.Symbol))
{
_WindowDict[windowBar.Symbol] = new RollingWindow<TradeBar>(CONSTANTS.ROLLING_WINDOW_COUNT);
}
_WindowDict[windowBar.Symbol].Add(windowBar);
}
public void WarmupWindow(Symbol aSymbol)
{
var lHistory = History<TradeBar>(aSymbol, CONSTANTS.ROLLING_WINDOW_COUNT, Resolution.Daily);
if(!_WindowDict.ContainsKey(aSymbol))
{
_WindowDict[aSymbol] = new RollingWindow<TradeBar>(CONSTANTS.ROLLING_WINDOW_COUNT);
}
foreach(var lBar in lHistory)
{
_WindowDict[aSymbol].Add(lBar);
}
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
// if we have no changes, do nothing
if (changes == SecurityChanges.None) return;
foreach (var security in changes.RemovedSecurities)
{
if (_SymbolDict.TryGetValue(security.Symbol, out var lSymbolData))
{
lSymbolData.Dispose();
_SymbolDict.Remove(security.Symbol);
}
if(security.Fundamentals != null)
{
var lCode = security.Fundamentals.AssetClassification.MorningstarIndustryCode;
if(_IndustryStocks.ContainsKey(lCode))
{
_IndustryStocks[lCode].Remove(security);
}
}
_WindowDict.Remove(security.Symbol);
}
foreach (var security in changes.AddedSecurities)
{
if(!_SymbolDict.ContainsKey(security.Symbol) && security.IsTradable)
{
security.SetLeverage(1);
_SymbolDict.Add(security.Symbol, new SymbolData(this, security.Symbol));
var consolidator = new TradeBarConsolidator(TimeSpan.FromDays(1));
consolidator.DataConsolidated += WindowBarHandler;
SubscriptionManager.AddConsolidator(security.Symbol, consolidator);
if(security.Fundamentals != null)
{
var lCode = security.Fundamentals.AssetClassification.MorningstarIndustryCode;
if(_IndustryStocks.ContainsKey(lCode))
{
_IndustryStocks[lCode].Add(security);
}
}
}
}
}
IEnumerable<Symbol> SelectCoarse(IEnumerable<CoarseFundamental> coarse)
{
if (Time.Month == _LastMonth)
{
return Universe.Unchanged;
}
var sortedByDollarVolume =
(from x in coarse
where x.HasFundamentalData && x.Volume > 0 && x.Price > 0
&& DoesTickerExist(x.Symbol.Value)
orderby x.DollarVolume descending
select x.Symbol).ToList();
return sortedByDollarVolume;
}
IEnumerable<Symbol> SelectFine(IEnumerable<FineFundamental> fine)
{
var filteredFine =
(from security in fine
where _ValidExchanges.Contains(security.CompanyReference.PrimaryExchangeID)
orderby security.MarketCap descending
select security.Symbol).ToList();
_LastMonth = Time.Month;
return filteredFine;
}
IEnumerable<Symbol> SelectNasdaq100Proxy(IEnumerable<FineFundamental> fine)
{
var filteredFine =
(from security in fine
where security.CompanyReference.PrimaryExchangeID == "NAS"
where security.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices
orderby security.MarketCap descending
select security.Symbol).Take(100).ToList();
_LastMonth = Time.Month;
return filteredFine;
}
public bool DoesTickerExist(string ticker)
{
if(!ENABLE_TICKER_LIST)
{
return true;
}
return _TickerList.Contains(ticker);
}
}
/// Handles Tracking Indicators and Warming Up the Data For Indicators as well as
/// attaching to the Algorithm
public class SymbolData
{
private Template _algorithm;
private Symbol _symbol;
private TradeBarConsolidator _consolidator;
/// Daily Indicators
public AverageTrueRange ATR;
public RelativeStrengthIndex FiveDayRSI;
public SimpleMovingAverage TwoHundredSma;
public SimpleMovingAverage TwentySma;
public SymbolData(Template algorithm, Symbol symbol)
{
_algorithm = algorithm;
_symbol = symbol;
// Create Indicators
ATR = new AverageTrueRange(10);
FiveDayRSI = new RelativeStrengthIndex(5);
TwoHundredSma = new SimpleMovingAverage(200);
TwentySma = new SimpleMovingAverage(20);
// Create a consolidator to update the indicator
_consolidator = new TradeBarConsolidator(TimeSpan.FromDays(1));
_consolidator.DataConsolidated += DailyDataUpdate;
// Register the consolidator to update the indicator
algorithm.SubscriptionManager.AddConsolidator(symbol, _consolidator);
// Warm Up Indicators
algorithm.WarmUpIndicator(symbol, ATR, Resolution.Daily);
algorithm.WarmUpIndicator(symbol, FiveDayRSI, Resolution.Daily);
algorithm.WarmUpIndicator(symbol, TwoHundredSma, Resolution.Daily);
algorithm.WarmUpIndicator(symbol, TwentySma, Resolution.Daily);
// Prepare Window Of Data
algorithm.WarmupWindow(symbol);
}
/// Updates Indicators that run on specifically at a daily resolution
/// Updates Once a Day
private void DailyDataUpdate(object sender, TradeBar consolidatedBar)
{
ATR.Update(consolidatedBar);
FiveDayRSI.Update(consolidatedBar.EndTime, consolidatedBar.Close);
TwoHundredSma.Update(consolidatedBar.EndTime, consolidatedBar.Close);
TwentySma.Update(consolidatedBar.EndTime, consolidatedBar.Close);
}
// If you have a dynamic universe, remove consolidators for the securities removed from the universe
public void Dispose()
{
_algorithm.SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
}
}
}