| Overall Statistics |
|
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0.518 Tracking Error 0.174 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset |
#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 TopGainerStudy: QCAlgorithm
{
bool ENABLE_TICKER_LIST = false;
const string BENCHMARK = "QQQ";
const string BORDER = "=====================================================================";
/// Used For Universe Updates
private int _lastMonth = -1;
private const int ROLLING_WINDOW_COUNT = 21;
private const int WARMUP = 252;
/// Minimum To Enter Universe
private const int MINIMUM_ENTRY_CAP = 0;
private const long MAXIMUM_ENTRY_CAP = 6000000000;
private string[] _TickerList = {"BTU", "CEIX"};
/// Exchanges
/// ASE - AMEX
/// NYS - NYSE
/// NAS - NASDAQ
private string[] _ValidExchanges = {"ASE", "NYS", "NAS"};
/// Tracks Symbol Indicators
Dictionary<Symbol, SymbolData> _SymbolDict = new Dictionary<Symbol, SymbolData>();
/// Track Daily Bars Of Data
Dictionary<Symbol, RollingWindow<TradeBar>> _WindowDict = new Dictionary<Symbol, RollingWindow<TradeBar>>();
/// List Storage
Dictionary<Symbol, DateTime> _Leaders = new Dictionary<Symbol, DateTime>();
/// Structures Used to Track MorningStar Industry Group Specifics
Dictionary<int, string> _Industries = new Dictionary<int, string>();
Dictionary<int, List<Security>> _IndustryStocks = new Dictionary <int, List<Security>>();
/// Date Range
private DateTime _StartDate = new DateTime(2021,1,1);
/// Set End Date To Trigger Date
private DateTime _EndDate = new DateTime(2021,1,15);
public override void Initialize()
{
SetStartDate(_StartDate); // Set Start Date, Provide 1 Month Buffer For Warmup
SetEndDate(_EndDate); // Set Start Date
SetCash(5000000); // Set Strategy Cash
UniverseSettings.Resolution = Resolution.Daily;
UniverseSettings.DataNormalizationMode = DataNormalizationMode.SplitAdjusted;
EnableAutomaticIndicatorWarmUp = true;
AddUniverseSelection(new FineFundamentalUniverseSelectionModel(SelectCoarse, SelectFine));
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>();
}
/// Prepare Indicators
SetWarmUp(new TimeSpan(WARMUP,0,0,0));
Log(BORDER);
Schedule.On(Schedule.DateRules.WeekStart(security.Symbol),
Schedule.TimeRules.AfterMarketOpen(security.Symbol, 1), PrintTopGainers);
}
public override void OnEndOfAlgorithm()
{
Log(BORDER);
Log("Number Of Stocks in Universe = " + ActiveSecurities.Keys.Count());
Log("Printing Top Gainer Results For Trading Day Prior To " + Time.Date.ToShortDateString());
Log("Number Of Symbols = " + _Leaders.Count);
Log(BORDER);
var lLeaderList = (from pair in _Leaders
where ActiveSecurities.ContainsKey(pair.Key)
where ActiveSecurities[pair.Key].Fundamentals != null
where ActiveSecurities[pair.Key].Fundamentals.AssetClassification != null
orderby pair.Value ascending
select pair.Key).ToList();
/// Pipe Symbol Info To Log
foreach(var stock in lLeaderList)
{
string lIndustryGroup = "Industry Data Not Available";
/// Label Industry Group if Available
if(_Industries.ContainsKey(ActiveSecurities[stock].Fundamentals.AssetClassification.MorningstarIndustryCode))
{
lIndustryGroup = _Industries[ActiveSecurities[stock].Fundamentals.AssetClassification.MorningstarIndustryCode];
}
string lTickerName = stock.Value;
string lLogString = "Industry - " + lIndustryGroup + " | Stock - " + lTickerName + " | Entry Date - " + _Leaders[stock].ToShortDateString();
Log(lLogString);
}
Log(BORDER);
base.OnEndOfAlgorithm();
}
public void PrintTopGainers()
{
if(IsWarmingUp)
{
return;
}
/// Value Metrics
/// Defining These Locally For Simplicity Purposes, Could Be Global to avoid Redefinitions
decimal lMinimumClosingPrice = 0;
decimal lMinimumAverageVolume = 500000;
decimal lMinimumAverageDollarVolume = 1000000;
decimal lOneWeekGainPercent = 20;
/// Get Top Gainer Scan Today
var lTopGainerStocks = (from symbolSecurityPair in ActiveSecurities
/// *** Initial Validation ***
where !_Leaders.ContainsKey(symbolSecurityPair.Key)
/// Must Be In Universe
where _SymbolDict.ContainsKey(symbolSecurityPair.Key)
/// Must Meet At Least One RateOfChangePercent Metric
&& (_SymbolDict[symbolSecurityPair.Key].OneWeekGain(symbolSecurityPair.Value.High) > lOneWeekGainPercent)
&& symbolSecurityPair.Value.Close < _SymbolDict[symbolSecurityPair.Key].TwoHundredEma
/// Must Have Properties Like Market Cap and Industry Group (Removes ETF's and some Data Issues)
where symbolSecurityPair.Value.Fundamentals != null
where symbolSecurityPair.Value.Fundamentals.AssetClassification != null
/// *** Value Validation **
where symbolSecurityPair.Value.Close > lMinimumClosingPrice
/// where symbolSecurityPair.Value.Fundamentals.MarketCap > lMinimumMarketCap
where _SymbolDict[symbolSecurityPair.Key].AverageVolume > lMinimumAverageVolume
where _SymbolDict[symbolSecurityPair.Key].AverageDollarVolume > lMinimumAverageDollarVolume
orderby symbolSecurityPair.Value.Fundamentals.AssetClassification.MorningstarIndustryCode
/// Get The Symbol's
select symbolSecurityPair.Key).ToList();
foreach(var leader in lTopGainerStocks)
{
_Leaders[leader] = Time;
}
}
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.ContainsKey(security.Symbol))
{
_SymbolDict.Remove(security.Symbol);
}
if(security.Fundamentals != null)
{
var lCode = security.Fundamentals.AssetClassification.MorningstarIndustryCode;
if(_IndustryStocks.ContainsKey(lCode))
{
_IndustryStocks[lCode].Remove(security);
}
}
}
foreach (var security in changes.AddedSecurities)
{
if(!_SymbolDict.ContainsKey(security.Symbol) && security.IsTradable)
{
security.SetLeverage(1);
_SymbolDict.Add(security.Symbol, new SymbolData(security.Symbol, this));
var consolidator = new TradeBarConsolidator(TimeSpan.FromDays(1));
consolidator.DataConsolidated += WindowBarHandler;
_WindowDict[security.Symbol] = new RollingWindow<TradeBar>(ROLLING_WINDOW_COUNT);
var lBarHistory = History<TradeBar>(security.Symbol, WARMUP, Resolution.Daily).Reverse().ToArray();
foreach(var lBar in lBarHistory)
{
_WindowDict[security.Symbol].Add(lBar);
}
SubscriptionManager.AddConsolidator(security.Symbol, consolidator);
if(security.Fundamentals != null)
{
var lCode = security.Fundamentals.AssetClassification.MorningstarIndustryCode;
if(_IndustryStocks.ContainsKey(lCode))
{
_IndustryStocks[lCode].Add(security);
}
}
}
}
}
public void WindowBarHandler(object sender, TradeBar windowBar)
{
if(!_WindowDict.ContainsKey(windowBar.Symbol))
{
_WindowDict[windowBar.Symbol] = new RollingWindow<TradeBar>(ROLLING_WINDOW_COUNT);
}
_WindowDict[windowBar.Symbol].Add(windowBar);
}
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 x in fine
where (_ValidExchanges.Contains(x.CompanyReference.PrimaryExchangeID))
where x.MarketCap < MAXIMUM_ENTRY_CAP
where x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.Healthcare
/// where x.MarketCap > MINIMUM_ENTRY_CAP
orderby x.MarketCap descending
select x.Symbol).ToList();
_lastMonth = Time.Month;
return filteredFine;
}
public bool DoesTickerExist(string ticker)
{
if(!ENABLE_TICKER_LIST)
{
return true;
}
bool tickerExists = false;
foreach(var symbol in _TickerList)
{
if(ticker == symbol)
{
tickerExists = true;
break;
}
}
return tickerExists;
}
class SymbolData
{
public Symbol MySymbol;
QCAlgorithm myAlgo;
/// Indicators
public SimpleMovingAverage AverageVolume;
public SimpleMovingAverage AverageDollarVolume;
/// Special Var for ADR Percent
public SimpleMovingAverage HighLowRatioAverage;
public Minimum OneMonthLow;
public Minimum ThreeMonthLow;
public Minimum SixMonthLow;
public Minimum TwelveMonthLow;
public Minimum OneWeekLow;
public Minimum EightWeekLow;
public ExponentialMovingAverage TwoHundredEma;
public SymbolData(Symbol symbol, QCAlgorithm algorithm)
{
MySymbol = symbol;
myAlgo = algorithm;
/// Indicator Setup
AverageVolume = algorithm.SMA(symbol, 20, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Volume);
AverageDollarVolume = algorithm.SMA(symbol, 20, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Volume * ((TradeBar)tradeBar).Close);
OneMonthLow = algorithm.MIN(symbol, 21, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Low);
ThreeMonthLow = algorithm.MIN(symbol, 63, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Low);
SixMonthLow = algorithm.MIN(symbol, 126, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Low);
TwelveMonthLow = algorithm.MIN(symbol, 252, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Low);
EightWeekLow = algorithm.MIN(symbol, 42, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Low);
OneWeekLow = algorithm.MIN(symbol, 5, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).Low);
HighLowRatioAverage = algorithm.SMA(symbol, 20, Resolution.Daily, tradeBar => ((TradeBar)tradeBar).High / ((TradeBar)tradeBar).Low);
TwoHundredEma = algorithm.EMA(symbol, 200);
}
private decimal GetRateChange(decimal aPeriodLowPrice, decimal aCurrentClosingPrice)
{
if(aPeriodLowPrice < 0.1m || aCurrentClosingPrice < 0.1m)
{
return 0;
}
return Math.Round(100 * ((aCurrentClosingPrice / aPeriodLowPrice) - 1), 2);
}
/// ROCP Method, To-Do Optimize
public decimal OneWeekGain(decimal aCurrentClosingPrice)
{
return GetRateChange(OneWeekLow, aCurrentClosingPrice);
}
public decimal OneMonthROCP(decimal aCurrentClosingPrice)
{
return GetRateChange(OneMonthLow, aCurrentClosingPrice);
}
public decimal EightWeekGain(decimal aCurrentClosingPrice)
{
return GetRateChange(EightWeekLow, aCurrentClosingPrice);
}
public decimal ThreeMonthROCP(decimal aCurrentClosingPrice)
{
return GetRateChange(ThreeMonthLow, aCurrentClosingPrice);
}
public decimal SixMonthROCP(decimal aCurrentClosingPrice)
{
return GetRateChange(SixMonthLow, aCurrentClosingPrice);
}
public decimal TwelveMonthROCP(decimal aCurrentClosingPrice)
{
return GetRateChange(TwelveMonthLow, aCurrentClosingPrice);
}
public decimal ADRPercent
{
get
{
decimal lAdrPercent = 100 * (HighLowRatioAverage - 1);
return Math.Round(lAdrPercent, 2);
}
}
}
}
}