Hi all, very new to QC, just sharing a example of an Alpha indicator using RollingWindows under QC Framework. Basically using an EMA crossover of EMA values 1 day earlier (e.g. _fastEmaWin[1] instead of _fastEmaWin[0]). Hope for any feedback/comments, on whether there's a better way of doing this. Are many of you also using the QC Framework model, or not?


Something to note is that in the QC example RollingWindowAlgorithm.cs file, the event handler/delegate is set-up before the RollingWindow is declared, however when I ran the program i encountered bugs, and had to swap the order (create RollingWindow before adding event handler. Wonder why is that? 

From QC example:

SMA("SPY", 5).Updated += (sender, updated) => _smaWin.Add(updated); _smaWin = new RollingWindow<IndicatorDataPoint>(5);

From my alpha example:

_fastEmaWin = new RollingWindow<IndicatorDataPoint>(5); algorithm.EMA(added.Symbol, _fastPeriod).Updated += (sender, updated) => _fastEmaWin.Add(updated);


Full alphas code (EMAWindowAlphaModel.cs):

/* * 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.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 EMAWindowAlphaModel : AlphaModel { private readonly int _fastPeriod; private readonly int _slowPeriod; private readonly Resolution _resolution; private readonly int _predictionInterval; private readonly Dictionary<Symbol, SymbolData> _symbolData; /// <summary> /// Initializes a new instance of the <see cref="EmaCrossAlphaModel"/> class /// </summary> /// <param name="fastPeriod">The fast EMA period</param> /// <param name="slowPeriod">The slow EMA period</param> /// <param name="resolution">The resolution of data sent into the EMA indicators</param> public EMAWindowAlphaModel( int fastPeriod = 12, int slowPeriod = 30, Resolution resolution = Resolution.Daily ) { _fastPeriod = fastPeriod; _slowPeriod = slowPeriod; _resolution = resolution; _predictionInterval = fastPeriod; _symbolData = new Dictionary<Symbol, SymbolData>(); Name = $"{nameof(EMAWindowAlphaModel)}({fastPeriod},{slowPeriod},{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 sd in _symbolData.Values) { if (sd._FastEmaWin.IsReady && sd._SlowEmaWin.IsReady) { var direction = InsightDirection.Flat; if (sd._FastEmaWin[1] > sd._SlowEmaWin[1]) { //insights.Add(Insight.Price(sd.Symbol, insightPeriod, InsightDirection.Up)); direction = InsightDirection.Up; } else if (sd._FastEmaWin[1] < sd._SlowEmaWin[1]) { //insights.Add(Insight.Price(sd.Symbol, insightPeriod, InsightDirection.Down)); direction = InsightDirection.Down; } if (direction == sd.PreviousDirection) { continue; } var insightPeriod = _resolution.ToTimeSpan().Multiply(_predictionInterval); var insight = Insight.Price(sd.Symbol, insightPeriod, direction); sd.PreviousDirection = direction; yield return insight; } } // 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 (!_symbolData.TryGetValue(added.Symbol, out symbolData)) { // var myEma = algorithm.EMA(added.Symbol, _emaPeriod, _resolution); RollingWindow<IndicatorDataPoint> _fastEmaWin; RollingWindow<IndicatorDataPoint> _slowEmaWin; _fastEmaWin = new RollingWindow<IndicatorDataPoint>(5); algorithm.EMA(added.Symbol, _fastPeriod).Updated += (sender, updated) => _fastEmaWin.Add(updated); _slowEmaWin = new RollingWindow<IndicatorDataPoint>(5); algorithm.EMA(added.Symbol, _slowPeriod).Updated += (sender, updated) => _slowEmaWin.Add(updated); _symbolData[added.Symbol] = new SymbolData { Security = added, _FastEmaWin = _fastEmaWin, _SlowEmaWin = _slowEmaWin }; } else { // a security that was already initialized was re-added, reset the indicators symbolData._FastEmaWin.Reset(); symbolData._SlowEmaWin.Reset(); } } } /// <summary> /// Contains data specific to a symbol required by this model /// </summary> private class SymbolData { public InsightDirection? PreviousDirection { get; set; } public Security Security { get; set; } public Symbol Symbol => Security.Symbol; //public ExponentialMovingAverage MyEma { get; set; } public RollingWindow<IndicatorDataPoint> _FastEmaWin; public RollingWindow<IndicatorDataPoint> _SlowEmaWin; } } }