/* * 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 QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; using QuantConnect.Orders.Fees; using System; using System.Collections.Generic; using System.Linq; namespace QuantConnect.Algorithm.CSharp.Alphas { /// /// This alpha aims to capture the mean-reversion effect of ETFs during lunch-break by ranking 20 ETFs /// on their return between the close of the previous day to 12:00 the day after and predicting mean-reversion /// in price during lunch-break. /// /// Source: Lunina, V. (June 2011). The Intraday Dynamics of Stock Returns and Trading Activity: Evidence from OMXS 30 (Master's Essay, Lund University). /// Retrieved from http://lup.lub.lu.se/luur/download?func=downloadFile&recordOId=1973850&fileOId=1973852 /// /// This alpha is part of the Benchmark Alpha Series created by QuantConnect which are open sourced so the community and client funds can see an example of an alpha. /// public class MeanReversionLunchBreakAlpha : QCAlgorithm { public override void Initialize() { SetStartDate(2018, 1, 1); SetCash(100000); // Set zero transaction fees SetSecurityInitializer(security => security.FeeModel = new ConstantFeeModel(0)); // Use Hourly Data For Simplicity UniverseSettings.Resolution = Resolution.Hour; SetUniverseSelection(new CoarseFundamentalUniverseSelectionModel(CoarseSelectionFunction)); // Use MeanReversionLunchBreakAlphaModel to establish insights SetAlpha(new MeanReversionLunchBreakAlphaModel()); // Equally weigh securities in portfolio, based on insights SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel()); // Set Immediate Execution Model SetExecution(new ImmediateExecutionModel()); // Set Null Risk Management Model SetRiskManagement(new NullRiskManagementModel()); } /// /// Sort the data by daily dollar volume and take the top '20' ETFs /// private IEnumerable CoarseSelectionFunction(IEnumerable coarse) { return (from cf in coarse where !cf.HasFundamentalData orderby cf.DollarVolume descending select cf.Symbol).Take(20); } /// /// Uses the price return between the close of previous day to 12:00 the day after to /// predict mean-reversion of stock price during lunch break and creates direction prediction /// for insights accordingly. /// private class MeanReversionLunchBreakAlphaModel : AlphaModel { private const Resolution _resolution = Resolution.Hour; private readonly TimeSpan _predictionInterval; private readonly Dictionary _symbolDataBySymbol; public MeanReversionLunchBreakAlphaModel(int lookback = 1) { _predictionInterval = _resolution.ToTimeSpan().Multiply(lookback); _symbolDataBySymbol = new Dictionary(); } public override IEnumerable Update(QCAlgorithm algorithm, Slice data) { foreach (var kvp in _symbolDataBySymbol) { if (data.Bars.ContainsKey(kvp.Key)) { var bar = data.Bars.GetValue(kvp.Key); kvp.Value.Update(bar.EndTime, bar.Close); } } return algorithm.Time.Hour == 12 ? _symbolDataBySymbol.Select(kvp => kvp.Value.Insight) : Enumerable.Empty(); } public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { foreach (var security in changes.RemovedSecurities) { if (_symbolDataBySymbol.ContainsKey(security.Symbol)) { _symbolDataBySymbol.Remove(security.Symbol); } } // Retrieve price history for all securities in the security universe // and update the indicators in the SymbolData object var symbols = changes.AddedSecurities.Select(x => x.Symbol); var history = algorithm.History(symbols, 1, _resolution); if (symbols.Count() > 0 && history.Count() == 0) { algorithm.Debug($"No data on {algorithm.Time}"); } history.PushThrough(bar => { SymbolData symbolData; if (!_symbolDataBySymbol.TryGetValue(bar.Symbol, out symbolData)) { symbolData = new SymbolData(bar.Symbol, _predictionInterval); } symbolData.Update(bar.EndTime, bar.Price); _symbolDataBySymbol[bar.Symbol] = symbolData; }); } /// /// Contains data specific to a symbol required by this model /// private class SymbolData { // Mean value of returns for magnitude prediction private readonly SimpleMovingAverage _meanOfPriceChange = new RateOfChangePercent(1).SMA(3); // Price change from close price the previous day private readonly RateOfChangePercent _priceChange = new RateOfChangePercent(3); private readonly Symbol _symbol; private readonly TimeSpan _period; public Insight Insight { get { // Emit "down" insight for the securities that increased in value and // emit "up" insight for securities that have decreased in value var direction = _priceChange > 0 ? InsightDirection.Down : InsightDirection.Up; var magnitude = Convert.ToDouble(Math.Abs(_meanOfPriceChange)); return Insight.Price(_symbol, _period, direction, magnitude); } } public SymbolData(Symbol symbol, TimeSpan period) { _symbol = symbol; _period = period; } public bool Update(DateTime time, decimal value) { return _meanOfPriceChange.Update(time, value) & _priceChange.Update(time, value); } } } } }