/* * 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 { /// /// Reversal strategy that goes long when price crosses below SMA and Short when price crosses above SMA. /// The trading strategy is implemented only between 10AM - 3PM (NY time). Research suggests this is due to /// institutional trades during market hours which need hedging with the USD. Source paper: /// LeBaron, Zhao: Intraday Foreign Exchange Reversals /// http://people.brandeis.edu/~blebaron/wps/fxnyc.pdf /// http://www.fma.org/Reno/Papers/ForeignExchangeReversalsinNewYorkTime.pdf /// /// 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 IntradayReversalCurrencyMarketsAlpha : QCAlgorithm { public override void Initialize() { SetStartDate(2015, 1, 1); SetCash(100000); // Set zero transaction fees SetSecurityInitializer(security => security.FeeModel = new ConstantFeeModel(0)); // Select resolution var resolution = Resolution.Hour; // Reversion on the USD. var symbols = new[] { QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda) }; // Set requested data resolution UniverseSettings.Resolution = resolution; SetUniverseSelection(new ManualUniverseSelectionModel(symbols)); // Use IntradayReversalAlphaModel to establish insights SetAlpha(new IntradayReversalAlphaModel(5, resolution)); // 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()); } /// /// Alpha model that uses a Price/SMA Crossover to create insights on Hourly Frequency. /// Frequency: Hourly data with 5-hour simple moving average. /// Strategy: /// Reversal strategy that goes Long when price crosses below SMA and Short when price crosses above SMA. /// The trading strategy is implemented only between 10AM - 3PM (NY time) /// private class IntradayReversalAlphaModel : AlphaModel { private readonly int _periodSma; private readonly Resolution _resolution; private readonly Dictionary _cache; public IntradayReversalAlphaModel( int periodSma = 5, Resolution resolution = Resolution.Hour) { _periodSma = periodSma; _resolution = resolution; _cache = new Dictionary(); Name = "IntradayReversalAlphaModel"; } public override IEnumerable Update(QCAlgorithm algorithm, Slice data) { // Set the time to close all positions at 3PM var timeToClose = algorithm.Time.Date.Add(new TimeSpan(0, 15, 1, 0)); var insights = new List(); foreach (var kvp in algorithm.ActiveSecurities) { var symbol = kvp.Key; SymbolData symbolData; if (ShouldEmitInsight(algorithm, symbol) && _cache.TryGetValue(symbol, out symbolData)) { var price = kvp.Value.Price; var direction = symbolData.IsUptrend(price) ? InsightDirection.Up : InsightDirection.Down; // Ignore signal for same direction as previous signal (when no crossover) if (direction == symbolData.PreviousDirection) { continue; } // Save the current Insight Direction to check when the crossover happens symbolData.PreviousDirection = direction; // Generate insight insights.Add(Insight.Price(symbol, timeToClose, direction)); } } return insights; } private bool ShouldEmitInsight(QCAlgorithm algorithm, Symbol symbol) { var timeOfDay = algorithm.Time.TimeOfDay; return algorithm.Securities[symbol].HasData && timeOfDay >= TimeSpan.FromHours(10) && timeOfDay <= TimeSpan.FromHours(15); } public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { foreach (var symbol in changes.AddedSecurities.Select(x => x.Symbol)) { if (_cache.ContainsKey(symbol)) continue; _cache.Add(symbol, new SymbolData(algorithm, symbol, _periodSma, _resolution)); } } /// /// Contains data specific to a symbol required by this model /// private class SymbolData { private readonly SimpleMovingAverage _priceSMA; public InsightDirection PreviousDirection { get; set; } public SymbolData(QCAlgorithm algorithm, Symbol symbol, int periodSma, Resolution resolution) { PreviousDirection = InsightDirection.Flat; _priceSMA = algorithm.SMA(symbol, periodSma, resolution); } public bool IsUptrend(decimal price) { return _priceSMA.IsReady && price < Math.Round(_priceSMA * 1.001m, 6); } } } } }