Overall Statistics |
Total Trades 444 Average Win 0.99% Average Loss -1.27% Compounding Annual Return 24.237% Drawdown 11.400% Expectancy 0.166 Net Profit 60.904% Sharpe Ratio 0.884 Loss Rate 34% Win Rate 66% Profit-Loss Ratio 0.78 Alpha 0.529 Beta -19.822 Annual Standard Deviation 0.229 Annual Variance 0.053 Information Ratio 0.812 Tracking Error 0.229 Treynor Ratio -0.01 Total Fees $603.04 |
namespace QuantConnect.Algorithm.CSharp { public class MeowChickyIronmanFramework : QCAlgorithmFramework { public override void Initialize() { SetStartDate(2017, 1, 1); //Set Start Date SetEndDate(DateTime.Now.AddDays(-1)); //Set End Date SetCash(2000); //Set Strategy Cash /// Available Indicators: { "ema","rsi" } string[] indicators = { "rsi" }; UniverseSettings.Resolution = Resolution.Minute; SetWarmup(TimeSpan.FromDays(50)); SetUniverseSelection(new MeowChickyUniverseSelectionModel()); SetAlpha(new MeowChickyAlphaModel(indicators, resolution: UniverseSettings.Resolution)); //SetAlpha(new EmaCrossAlphaModel(10,50,resolution: UniverseSettings.Resolution)); //SetAlpha(new CompositeAlphaModel( //new MeowChickyAlphaModel(indicators,10,50, resolution: UniverseSettings.Resolution), //new EmaCrossAlphaModel(10,50,resolution: UniverseSettings.Resolution) //)); SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel()); SetExecution(new StandardDeviationExecutionModel(60, 2, UniverseSettings.Resolution)); //SetExecution(new ImmediateExecutionModel()); SetRiskManagement(new CompositeRiskManagementModel( //new MaximumSectorExposureRiskManagementModel(.45m), //new MaximumUnrealizedProfitPercentPerSecurity(.15m), new MaximumDrawdownPercentPerSecurity(.01m), new TrailingStopRiskManagementModel(.02m) )); } public override void OnOrderEvent(OrderEvent orderEvent) { if (orderEvent.Status.IsFill()) { // Debug($"Purchased Stock: {orderEvent.Symbol}"); } } } }
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Data.Fundamental; using QuantConnect.Data.UniverseSelection; using QuantConnect.Securities; namespace QuantConnect.Algorithm.Framework.Selection { /// <summary> /// MeowChicky Universe Selection Model /// </summary> public class MeowChickyUniverseSelectionModel : FundamentalUniverseSelectionModel { private const int NumberOfSymbolsCoarse = 500; private const int NumberOfSymbolsFine = 100; // rebalances at the start of each month private int _lastMonth = -1; private readonly Dictionary<Symbol, decimal> _dollarVolumeBySymbol = new Dictionary<Symbol, decimal>(); /// <summary> /// Initializes a new default instance of the <see cref="MeowChickyUniverseSelectionModel"/> /// </summary> public MeowChickyUniverseSelectionModel() : base(true) { } /// <summary> /// Initializes a new instance of the <see cref="MeowChickyUniverseSelectionModel"/> /// </summary> /// <param name="universeSettings">Universe settings defines what subscription properties will be applied to selected securities</param> /// <param name="securityInitializer">Security initializer initializes newly selected securities</param> public MeowChickyUniverseSelectionModel(UniverseSettings universeSettings, ISecurityInitializer securityInitializer) : base(true, universeSettings, securityInitializer) { } /// <summary> /// The stocks must have fundamental data /// The stock must have positive previous-day close price /// The stock must have positive volume on the previous trading day /// </summary> public override IEnumerable<Symbol> SelectCoarse(QCAlgorithmFramework algorithm, IEnumerable<CoarseFundamental> coarse) { // materialize to collection if not already materialized coarse = coarse as ICollection<CoarseFundamental> ?? coarse.ToList(); if (!coarse.Any()) { return Universe.Unchanged; } var month = coarse.First().EndTime.Month; if (month == _lastMonth) { return Universe.Unchanged; } _lastMonth = month; // The stocks must have fundamental data // The stock must have positive previous-day close price // The stock must have positive volume on the previous trading day var sortedByDollarVolume = from x in coarse where x.HasFundamentalData && x.Volume > 0 && x.Price > 1m && x.Price < 20m // Leave these nasdaq test symbols commented out for the time being. //where x.Symbol != "ZWZZT SJURTIR7CJFP" || //x.Symbol != "ZXZZT SJTSDFE19S9X" || x.Symbol != "ZVZZT SJTSDFE19S9X" || x.Symbol != "ZWZZT" || //x.Symbol != "ZXZZT" || x.Symbol != "ZVZZT" || x.Symbol != "ECOM" where x.Symbol != "ECOM" orderby x.DollarVolume descending select x; var top = sortedByDollarVolume.Take(NumberOfSymbolsCoarse).ToList(); foreach (var i in top) { _dollarVolumeBySymbol[i.Symbol] = i.DollarVolume; } return top.Select(x => x.Symbol); } /// <summary> /// The company's headquarter must in the U.S. /// The stock must be traded on either the NYSE or NASDAQ /// At least half a year since its initial public offering /// The stock's market cap must be greater than 500 million /// </summary> public override IEnumerable<Symbol> SelectFine(QCAlgorithmFramework algorithm, IEnumerable<FineFundamental> fine) { // The company's headquarter must in the U.S. // The stock must be traded on either the NYSE or NASDAQ // At least half a year since its initial public offering // The stock's market cap must be greater than 500 million var filteredFine = (from x in fine where x.CompanyReference.CountryId == "USA" && (x.CompanyReference.PrimaryExchangeID == "NYS" || x.CompanyReference.PrimaryExchangeID == "NAS") && (algorithm.Time - x.SecurityReference.IPODate).Days > 180 && x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 500000000m && x.ValuationRatios.PSRatio < 1m && x.ValuationRatios.PricetoCashRatio < 15 && x.ValuationRatios.EVToEBITDA < 10 && x.ValuationRatios.PEGRatio < 1 select x).ToList(); // select stocks with top dollar volume in every single sector //var myDict = new[] { "B", "I", "M", "N", "T", "U" }.ToDictionary(x => x, y => new List<FineFundamental>()); //var percent = (float)NumberOfSymbolsFine / filteredFine.Count; //foreach (var key in myDict.Keys.ToList()) //{ var value = (from x in filteredFine //where x.CompanyReference.IndustryTemplateCode == key orderby _dollarVolumeBySymbol[x.Symbol] descending select x).ToList(); //myDict[key] = value.Take((int)Math.Ceiling(value.Count * percent)).ToList(); //} //var topFine = myDict.Values.ToList().SelectMany(x => x).Take(NumberOfSymbolsFine).ToList(); return value.Select(x => x.Symbol).Take(NumberOfSymbolsFine); } } }
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect.Algorithm.Framework.Alphas { /// <summary> /// Defines a custom alpha model that uses MACD crossovers. The MACD signal line is /// used to generate up/down insights if it's stronger than the bounce threshold. /// If the MACD signal is within the bounce threshold then a flat price insight is returned. /// </summary> public class MeowChickyAdxAlphaModel3 : AlphaModel { private readonly int _period; private readonly Resolution _resolution; //private const decimal BounceThresholdPercent = 0.01m; private readonly Dictionary<Symbol, SymbolData> _symbolData; /// <summary> /// Initializes a new instance of the <see cref="MacdAlphaModel"/> class /// </summary> /// <param name="period">The ADX period</param> /// <param name="resolution">The resolution of data sent into the MACD indicator</param> public MeowChickyAdxAlphaModel3( int period = 14, Resolution resolution = Resolution.Daily ) { _period = period; _resolution = resolution; _symbolData = new Dictionary<Symbol, SymbolData>(); Name = $"{nameof(MeowChickyAdxAlphaModel3)}({period},{resolution})"; } /// <summary> /// Determines an insight for each security based on it's current Adx signal /// </summary> /// <param name="algorithm">The algorithm instance</param> /// <param name="data">The new data available</param> /// <returns>The new insights generated</returns> public override IEnumerable<Insight> Update(QCAlgorithmFramework algorithm, Slice data) { foreach (var sd in _symbolData.Values) { if (sd.Security.Price == 0) continue; var direction = InsightDirection.Flat; decimal magnitude = 0; if (sd.ADX > 25 && sd.ADX != 0) { if (sd.ADX.PositiveDirectionalIndex > sd.ADX && sd.ADX.NegativeDirectionalIndex < sd.ADX) { direction = InsightDirection.Up; } } else if (sd.ADX > 25 && sd.ADX != 0) { if (sd.ADX.NegativeDirectionalIndex > sd.ADX && sd.ADX.PositiveDirectionalIndex < sd.ADX) { direction = InsightDirection.Down; } } // ignore signal for same direction as previous signal if (direction == sd.PreviousDirection) { continue; } if (sd.ADX >25 && direction == InsightDirection.Up) { magnitude = ((sd.ADX.PositiveDirectionalIndex - sd.ADX.NegativeDirectionalIndex)/100)*(sd.ADX.NegativeDirectionalIndex); } else if (sd.ADX > 25 && direction == InsightDirection.Down) { magnitude = ((sd.ADX.NegativeDirectionalIndex - sd.ADX.PositiveDirectionalIndex)/100)*(sd.ADX.PositiveDirectionalIndex); } if (sd.ADX != 0) { var insightPeriod = _resolution.ToTimeSpan().Multiply(_period); var insight = Insight.Price(sd.Security.Symbol, insightPeriod, direction, magnitude: Convert.ToDouble(magnitude)); sd.PreviousDirection = insight.Direction; yield return insight; } } } /// <summary> /// Event fired each time the we add/remove securities from the data feed. /// This initializes the MACD for each added security and cleans up the indicator for each removed security. /// </summary> /// <param name="algorithm">The algorithm instance that experienced the change in securities</param> /// <param name="changes">The security additions and removals from the algorithm</param> public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes) { foreach (var added in changes.AddedSecurities) { if (_symbolData.ContainsKey(added.Symbol)) { continue; } _symbolData.Add(added.Symbol, new SymbolData(algorithm, added, _period, _resolution)); } foreach (var removed in changes.RemovedSecurities) { SymbolData data; if (_symbolData.TryGetValue(removed.Symbol, out data)) { // clean up our consolidator algorithm.SubscriptionManager.RemoveConsolidator(data.Security.Symbol, data.Consolidator); _symbolData.Remove(removed.Symbol); } } } class SymbolData { public InsightDirection? PreviousDirection { get; set; } public readonly Security Security; public readonly IDataConsolidator Consolidator; public readonly AverageDirectionalIndex ADX; public SymbolData(QCAlgorithmFramework algorithm, Security security, int period, Resolution resolution) { Security = security; Consolidator = algorithm.ResolveConsolidator(security.Symbol, resolution); algorithm.SubscriptionManager.AddConsolidator(security.Symbol, Consolidator); ADX = new AverageDirectionalIndex("ADX", period); TimeSpan timePeriod = TimeSpan.FromDays(period); if (resolution == Resolution.Hour) { timePeriod = TimeSpan.FromHours(period); } else if (resolution == Resolution.Minute) { timePeriod = TimeSpan.FromMinutes(period); } else if (resolution == Resolution.Second) { timePeriod = TimeSpan.FromSeconds(period); } var history = algorithm.History(security.Symbol, timePeriod); foreach (var bar in history) { ADX.Update(bar); } algorithm.RegisterIndicator(security.Symbol, ADX, Consolidator); } } } }
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect.Algorithm.Framework.Alphas { /// <summary> /// Alpha model that uses an EMA cross to create insights /// </summary> public class MeowChickyAlphaModel : AlphaModel { // Alpha Model - Ema Properties private readonly int _emaFastPeriod; private readonly int _emaSlowPeriod; private readonly int _emaPredictionInterval; // Alpha Model - Roc Properties private readonly int _rocPeriod; // Alpha Model - Rsi Properties private readonly int _rsiPeriod; // Alpha Model Properties private readonly string[] _indicators; private readonly Resolution _resolution; private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol; private QCAlgorithm _algorithm; /// <summary> /// Initializes a new instance of the <see cref="MeowChickyAlphaModel"/> class /// </summary> /// <param name="indicators">The list of indicators to be used in this alpha model</param> /// <param name="emaFastPeriod">The fast EMA period</param> /// <param name="emaSlowPeriod">The slow EMA period</param> /// <param name="rocPeriod">The Roc period</param> /// <param name="rsiPeriod">The Rsi period</param> /// <param name="resolution">The resolution of data sent into the EMA indicators</param> public MeowChickyAlphaModel( string[] indicators, int emaFastPeriod = 10, int emaSlowPeriod = 50, int rocPeriod = 10, int rsiPeriod = 14, Resolution resolution = Resolution.Minute ) { _indicators = indicators; _emaFastPeriod = emaFastPeriod; _emaSlowPeriod = emaSlowPeriod; _emaPredictionInterval = emaFastPeriod; _rocPeriod = rocPeriod; _rsiPeriod = rsiPeriod; _resolution = resolution; _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>(); Name = $"{nameof(MeowChickyAlphaModel)}({indicators},{emaFastPeriod},{emaSlowPeriod},{rocPeriod},{rsiPeriod},{resolution})"; } /// <summary> /// Updates this alpha model with the latest data from the algorithm. /// This is called each time the algorithm receives data for subscribed securities /// </summary> /// <param name="algorithm">The algorithm instance</param> /// <param name="data">The new data available</param> /// <returns>The new insights generated</returns> public override IEnumerable<Insight> Update(QCAlgorithmFramework algorithm, Slice data) { _algorithm = algorithm; var insights = new List<Insight>(); foreach (var symbolData in _symbolDataBySymbol.Values) { foreach (var indicator in _indicators) { switch (indicator.ToLower()) { case "ema": { insights = EmaAlphaModel(symbolData, insights); break; } case "rsi": { insights = RsiAlphaModel(symbolData, insights); break; } } } } return insights; } private List<Insight> EmaAlphaModel(SymbolData symbolData, List<Insight> insights) { if (symbolData.EmaFast.IsReady && symbolData.EmaSlow.IsReady) { var insightPeriod = _resolution.ToTimeSpan().Multiply(_emaPredictionInterval); if (symbolData.FastIsOverSlow) { if (symbolData.EmaSlow > symbolData.EmaFast) { insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Down)); } } else if (symbolData.SlowIsOverFast) { if (symbolData.EmaFast > symbolData.EmaSlow) { insights.Add(Insight.Price(symbolData.Symbol, insightPeriod, InsightDirection.Up)); } } } symbolData.FastIsOverSlow = symbolData.EmaFast > symbolData.EmaSlow; return insights; } private List<Insight> RsiAlphaModel(SymbolData symbolData, List<Insight> insights) { if (symbolData.Rsi.IsReady && symbolData.Roc.IsReady) { InsightDirection insightDirection = symbolData.RsiPreviousInsightDirection; var insightPeriod = _resolution.ToTimeSpan().Multiply(_rsiPeriod); if (_algorithm.Portfolio[symbolData.Symbol].Invested == false) { if (symbolData.Rsi < symbolData.RsiBuyThreshold) { if (symbolData.RsiPreviousInsightDirection == InsightDirection.Down || symbolData.RsiPreviousInsightDirection == InsightDirection.Flat) { insights.Add(Insight.Price(symbolData.Symbol, _resolution, _rsiPeriod, InsightDirection.Up, magnitude: Convert.ToDouble(symbolData.Roc))); symbolData.RsiPreviousInsightDirection = InsightDirection.Up; } } } if (_algorithm.Portfolio[symbolData.Symbol].Invested == true) { if (symbolData.Rsi > symbolData.RsiSellThreshold) { if (symbolData.RsiPreviousInsightDirection == InsightDirection.Up || symbolData.RsiPreviousInsightDirection == InsightDirection.Flat) { insights.Add(Insight.Price(symbolData.Symbol, _resolution, _rsiPeriod, InsightDirection.Down, magnitude: Convert.ToDouble(symbolData.Roc))); symbolData.RsiPreviousInsightDirection = InsightDirection.Down; } } } } return insights; } /// <summary> /// Event fired each time the we add/remove securities from the data feed /// </summary> /// <param name="algorithm">The algorithm instance that experienced the change in securities</param> /// <param name="changes">The security additions and removals from the algorithm</param> public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes) { foreach (var added in changes.AddedSecurities) { SymbolData symbolData; if (!_symbolDataBySymbol.TryGetValue(added.Symbol, out symbolData)) { // create fast/slow EMAs var emaFast = algorithm.EMA(added.Symbol, _emaFastPeriod, _resolution); var emaSlow = algorithm.EMA(added.Symbol, _emaSlowPeriod, _resolution); // Create Roc indicator var roc = algorithm.ROC(added.Symbol, _rocPeriod, _resolution); // Create RSIs var rsi = algorithm.RSI(added.Symbol, _rsiPeriod, resolution: _resolution); _symbolDataBySymbol[added.Symbol] = new SymbolData { EmaFast = emaFast, EmaSlow = emaSlow, Roc = roc, Rsi = rsi, Security = added }; } else { // a security that was already initialized was re-added, reset the indicators symbolData.EmaFast.Reset(); symbolData.EmaSlow.Reset(); symbolData.Roc.Reset(); symbolData.Rsi.Reset(); } } } /// <summary> /// Contains data specific to a symbol required by this model /// </summary> private class SymbolData { public Security Security { get; set; } public Symbol Symbol => Security.Symbol; public ExponentialMovingAverage EmaFast { get; set; } public ExponentialMovingAverage EmaSlow { get; set; } public RateOfChange Roc { get; set; } public RelativeStrengthIndex Rsi { get; set; } public InsightDirection RsiPreviousInsightDirection { get; set; } = InsightDirection.Flat; public int RsiBuyThreshold { get; set; } = 25; public int RsiSellThreshold { get; set; } = 70; /// <summary> /// True if the fast is above the slow, otherwise false. /// This is used to prevent emitting the same signal repeatedly /// </summary> public bool FastIsOverSlow { get; set; } public bool SlowIsOverFast => !FastIsOverSlow; } } }
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Util; namespace QuantConnect.Algorithm.Framework.Alphas { /// <summary> /// Uses Roc Percentage to create insights. /// </summary> public class MeowChickyRocpAlphaModel : AlphaModel { private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>(); private readonly int _period; private readonly Resolution _resolution; /// <summary> /// Initializes a new instance of the <see cref="MeowChickyRocpAlphaModel"/> class /// </summary> /// <param name="period">The Rocp indicator period</param> /// <param name="resolution">The resolution of data sent into the RSI indicator</param> public MeowChickyRocpAlphaModel( int period = 14, Resolution resolution = Resolution.Daily ) { _period = period; _resolution = resolution; Name = $"{nameof(MeowChickyRocpAlphaModel)}({_period},{_resolution})"; } /// <summary> /// Updates this alpha model with the latest data from the algorithm. /// This is called each time the algorithm receives data for subscribed securities /// </summary> /// <param name="algorithm">The algorithm instance</param> /// <param name="data">The new data available</param> /// <returns>The new insights generated</returns> public override IEnumerable<Insight> Update(QCAlgorithmFramework algorithm, Slice data) { var insights = new List<Insight>(); foreach (var kvp in _symbolDataBySymbol) { var symbol = kvp.Key; var rocp = kvp.Value.ROCP; var previousDirection = kvp.Value.PreviousDirection; if (rocp.IsReady) { var insightPeriod = _resolution.ToTimeSpan().Multiply(_period); var direction = InsightDirection.Flat; if (rocp > 0) { direction = InsightDirection.Up; } else if(rocp < 0) { direction = InsightDirection.Down; } // ignore signal for same direction as previous signal if (direction == previousDirection) { continue; } var insight = Insight.Price(kvp.Value.Symbol, _resolution, _period, direction, magnitude: Convert.ToDouble(rocp)); kvp.Value.PreviousDirection = insight.Direction; yield return insight; } } } /// <summary> /// Cleans out old security data and initializes the Rocp for any newly added securities. /// This functional also seeds any new indicators using a history request. /// </summary> /// <param name="algorithm">The algorithm instance that experienced the change in securities</param> /// <param name="changes">The security additions and removals from the algorithm</param> public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes) { // clean up data for removed securities if (changes.RemovedSecurities.Count > 0) { var removed = changes.RemovedSecurities.ToHashSet(x => x.Symbol); foreach (var subscription in algorithm.SubscriptionManager.Subscriptions) { if (removed.Contains(subscription.Symbol)) { _symbolDataBySymbol.Remove(subscription.Symbol); subscription.Consolidators.Clear(); } } } // initialize data for added securities var addedSymbols = new List<Symbol>(); foreach (var added in changes.AddedSecurities) { if (!_symbolDataBySymbol.ContainsKey(added.Symbol)) { var rocp = algorithm.ROCP(added.Symbol, _period, _resolution); var symbolData = new SymbolData(added.Symbol, rocp); _symbolDataBySymbol[added.Symbol] = symbolData; addedSymbols.Add(symbolData.Symbol); } } if (addedSymbols.Count > 0) { // warmup our indicators by pushing history through the consolidators algorithm.History(addedSymbols, _period, _resolution) .PushThrough(data => { SymbolData symbolData; if (_symbolDataBySymbol.TryGetValue(data.Symbol, out symbolData)) { symbolData.ROCP.Update(data.EndTime, data.Value); } }); } } /// <summary> /// Contains data specific to a symbol required by this model /// </summary> private class SymbolData { public Symbol Symbol { get; } public RateOfChangePercent ROCP { get; } public InsightDirection PreviousDirection { get; set; } public SymbolData(Symbol symbol, RateOfChangePercent rocp) { Symbol = symbol; ROCP = rocp; } } } }
/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Util; namespace QuantConnect.Algorithm.Framework.Alphas { /// <summary> /// Uses ROC to create insights. Crossing from pos to neg will create a direction insight and rate of change is magnitude /// </summary> public class MeowChickyRocAlphaModel : AlphaModel { private readonly Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>(); private readonly int _period; private readonly Resolution _resolution; /// <summary> /// Initializes a new instance of the <see cref="MeowChickyRocAlphaModel"/> class /// </summary> /// <param name="period">The Roc indicator period</param> /// <param name="resolution">The resolution of data sent into the Roc indicator</param> public MeowChickyRocAlphaModel( int period = 14, Resolution resolution = Resolution.Daily ) { _period = period; _resolution = resolution; Name = $"{nameof(MeowChickyRocAlphaModel)}({_period},{_resolution})"; } /// <summary> /// Updates this alpha model with the latest data from the algorithm. /// This is called each time the algorithm receives data for subscribed securities /// </summary> /// <param name="algorithm">The algorithm instance</param> /// <param name="data">The new data available</param> /// <returns>The new insights generated</returns> public override IEnumerable<Insight> Update(QCAlgorithmFramework algorithm, Slice data) { var insights = new List<Insight>(); foreach (var kvp in _symbolDataBySymbol) { var symbol = kvp.Key; var roc = kvp.Value.ROC; var previousDirection = kvp.Value.PreviousDirection; if (roc.IsReady) { var direction = InsightDirection.Flat; if (roc > 0) { direction = InsightDirection.Up; } else if(roc < 0) { direction = InsightDirection.Down; } // ignore signal for same direction as previous signal if (direction == previousDirection) { continue; } double magnitude = 0; if(roc < 1) { magnitude = Convert.ToDouble(roc.Current.Value); } else { magnitude = 1; } var insight = Insight.Price(kvp.Value.Symbol, _resolution, _period, direction, magnitude: magnitude); kvp.Value.PreviousDirection = insight.Direction; yield return insight; } } } /// <summary> /// Cleans out old security data and initializes the Roc for any newly added securities. /// This functional also seeds any new indicators using a history request. /// </summary> /// <param name="algorithm">The algorithm instance that experienced the change in securities</param> /// <param name="changes">The security additions and removals from the algorithm</param> public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes) { // clean up data for removed securities if (changes.RemovedSecurities.Count > 0) { var removed = changes.RemovedSecurities.ToHashSet(x => x.Symbol); foreach (var subscription in algorithm.SubscriptionManager.Subscriptions) { if (removed.Contains(subscription.Symbol)) { _symbolDataBySymbol.Remove(subscription.Symbol); subscription.Consolidators.Clear(); } } } // initialize data for added securities var addedSymbols = new List<Symbol>(); foreach (var added in changes.AddedSecurities) { if (!_symbolDataBySymbol.ContainsKey(added.Symbol)) { var roc = algorithm.ROC(added.Symbol, _period, _resolution); var symbolData = new SymbolData(added.Symbol, roc); _symbolDataBySymbol[added.Symbol] = symbolData; addedSymbols.Add(symbolData.Symbol); } } if (addedSymbols.Count > 0) { // warmup our indicators by pushing history through the consolidators algorithm.History(addedSymbols, _period, _resolution) .PushThrough(data => { SymbolData symbolData; if (_symbolDataBySymbol.TryGetValue(data.Symbol, out symbolData)) { symbolData.ROC.Update(data.EndTime, data.Value); } }); } } /// <summary> /// Contains data specific to a symbol required by this model /// </summary> private class SymbolData { public Symbol Symbol { get; } public RateOfChange ROC { get; } public InsightDirection PreviousDirection { get; set; } public SymbolData(Symbol symbol, RateOfChange roc) { Symbol = symbol; ROC = roc; } } } }