/*
* 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);
}
}
}
}
}