Overall Statistics |
Total Trades 281 Average Win 0.18% Average Loss -0.11% Compounding Annual Return 431.368% Drawdown 18.500% Expectancy 0.579 Net Profit 52.175% Sharpe Ratio 2.407 Loss Rate 39% Win Rate 61% Profit-Loss Ratio 1.58 Alpha 3.301 Beta -110.535 Annual Standard Deviation 0.642 Annual Variance 0.412 Information Ratio 2.382 Tracking Error 0.642 Treynor Ratio -0.014 Total Fees $0.00 |
namespace QuantConnect { using System.Collections.Generic; internal class TradingStrategiesBasedOnGeneticAlgorithmsQCVersion : QCAlgorithm { private readonly int _indicatorSignalCount = 5; private System.Collections.Generic.Dictionary<String, TradingRuleQCVersion> _entryradingRules=new Dictionary<String, TradingRuleQCVersion>(); private Dictionary<String, TradingRuleQCVersion> _exitradingRules=new Dictionary<String, TradingRuleQCVersion>(); private List<Symbol> _pair=new List<Symbol>(); private readonly Dictionary<string, string> parametersToBacktest = new Dictionary<string, string> { {"EntryIndicator1", "0"}, {"EntryIndicator2", "3"}, {"EntryIndicator3", "3"}, {"EntryIndicator4", "7"}, {"EntryIndicator5", "5"}, {"EntryIndicator1Direction", "0"}, {"EntryIndicator2Direction", "0"}, {"EntryIndicator3Direction", "1"}, {"EntryIndicator4Direction", "1"}, {"EntryIndicator5Direction", "0"}, {"ExitIndicator1", "4"}, {"ExitIndicator2", "3"}, {"ExitIndicator3", "1"}, {"ExitIndicator4", "0"}, {"ExitIndicator5", "7"}, {"ExitIndicator1Direction", "1"}, {"ExitIndicator2Direction", "0"}, {"ExitIndicator3Direction", "0"}, {"ExitIndicator4Direction", "0"}, {"ExitIndicator5Direction", "0"} }; public override void Initialize() { SetBrokerageModel(BrokerageName.OandaBrokerage); SetCash(startingCash: 1000); var startDate = new DateTime(year: 2017, month: 9, day: 1); SetStartDate(startDate); SetEndDate(startDate.AddMonths(months: 3)); _pair.Add(AddForex("EURAUD", leverage: 50).Symbol); _pair.Add(AddForex("EURCAD", leverage: 50).Symbol); _pair.Add(AddForex("EURCHF", leverage: 50).Symbol); _pair.Add(AddForex("EURGBP", leverage: 50).Symbol); _pair.Add(AddForex("EURJPY", leverage: 50).Symbol); _pair.Add(AddForex("EURNZD", leverage: 50).Symbol); _pair.Add(AddForex("EURUSD", leverage: 50).Symbol); _pair.Add(AddForex("GBPAUD", leverage: 50).Symbol); _pair.Add(AddForex("GBPCAD", leverage: 50).Symbol); _pair.Add(AddForex("GBPCHF", leverage: 50).Symbol); _pair.Add(AddForex("GBPJPY", leverage: 50).Symbol); _pair.Add(AddForex("GBPNZD", leverage: 50).Symbol); _pair.Add(AddForex("GBPUSD", leverage: 50).Symbol); _pair.Add(AddForex("AUDUSD", leverage: 50).Symbol); _pair.Add(AddForex("NZDUSD", leverage: 50).Symbol); _pair.Add(AddForex("USDCAD", leverage: 50).Symbol); _pair.Add(AddForex("USDCHF", leverage: 50).Symbol); _pair.Add(AddForex("USDJPY", leverage: 50).Symbol); SetParameters(parametersToBacktest); SetUpRules(); } private TradingRuleQCVersion GetValue(String symbol,Dictionary<String,TradingRuleQCVersion> tradingRule){ if(tradingRule.ContainsKey(symbol)) { return tradingRule[symbol]; } return null; } public override void OnData(Slice slice) { foreach(Symbol symbol in _pair) { TradingRuleQCVersion tradingRule=GetValue(symbol,_entryradingRules); if(_entryradingRules.ContainsKey(symbol)) { tradingRule=GetValue(symbol,_entryradingRules); //if (!tradingRule.IsReady) break; //if (!Portfolio.Invested) { if (tradingRule.TradeRuleSignal) SetHoldings(symbol, percentage: 1m); } } else { tradingRule=GetValue(symbol,_exitradingRules); if (tradingRule.TradeRuleSignal) Liquidate(symbol); } //if (!_entryradingRule.IsReady) return; //if (!Portfolio.Invested) //{ // if (_entryradingRule.TradeRuleSignal) SetHoldings(symbol, percentage: 1m); //} //else //{ // if (_exitTradingRule.TradeRuleSignal) Liquidate(symbol); //} } } public ITechnicalIndicatorSignal SetUpIndicatorSignal(Symbol pair, int indicatorN, string ruleAction) { var oscillatorThresholds = new OscillatorThresholds {Lower = 20, Upper = 80}; var key = ruleAction + "Indicator" + indicatorN + "Direction"; var intDirection = GetGeneIntFromKey(key); var direction = intDirection == 0 ? TradeRuleDirection.LongOnly : TradeRuleDirection.ShortOnly; key = ruleAction + "Indicator" + indicatorN; var indicatorId = GetGeneIntFromKey(key); var indicator = (TechicalIndicators) indicatorId; ITechnicalIndicatorSignal technicalIndicator = null; switch (indicator) { case TechicalIndicators.SimpleMovingAverage: // Canonical cross moving average parameters. var fast = SMA(pair, period: 50); var slow = SMA(pair, period: 200); technicalIndicator = new CrossingMovingAverages(fast, slow, direction); break; case TechicalIndicators.MovingAverageConvergenceDivergence: var macd = MACD(pair, fastPeriod: 12, slowPeriod: 26, signalPeriod: 9); technicalIndicator = new CrossingMovingAverages(macd, macd.Signal, direction); break; case TechicalIndicators.Stochastic: var sto = STO(pair, period: 14); technicalIndicator = new OscillatorSignal(sto.StochD, oscillatorThresholds, direction); break; case TechicalIndicators.RelativeStrengthIndex: var rsi = RSI(pair, period: 14); technicalIndicator = new OscillatorSignal(rsi, oscillatorThresholds, direction); break; case TechicalIndicators.CommodityChannelIndex: var cci = CCI(pair, period: 20); oscillatorThresholds.Lower = -100; oscillatorThresholds.Lower = 100; technicalIndicator = new OscillatorSignal(cci, oscillatorThresholds, direction); break; case TechicalIndicators.MomentumPercent: var pm = MOMP(pair, period: 60); oscillatorThresholds.Lower = -5; oscillatorThresholds.Lower = 5; technicalIndicator = new OscillatorSignal(pm, oscillatorThresholds, direction); break; case TechicalIndicators.WilliamsPercentR: var wr = WILR(pair, period: 14); technicalIndicator = new OscillatorSignal(wr, oscillatorThresholds, direction); break; case TechicalIndicators.PercentagePriceOscillator: var ppo = MACD(pair, fastPeriod: 12, slowPeriod: 26, signalPeriod: 9).Over(EMA(pair, period: 26)) .Plus(constant: 100m); var signal = new SimpleMovingAverage(period: 9).Of(ppo); technicalIndicator = new CrossingMovingAverages(ppo, signal, direction); break; case TechicalIndicators.BollingerBands: throw new NotImplementedException("WIP"); break; } return technicalIndicator; } private int GetGeneIntFromKey(string key) { var intGene = int.MinValue; if (intGene == int.MinValue) { try { intGene = int.Parse(GetParameter(key)); Log(string.Format("Parameter {0} set to {1}", key, intGene)); } catch (ArgumentNullException e) { throw new ArgumentNullException(key, "The gene " + key + " is not present either as Config or as Parameter"); } } return intGene; } private void SetUpRules() { bool[] bools = {true, false}; TradingRuleQCVersion _entryradingRule; TradingRuleQCVersion _exitTradingRule; foreach (var isEntryRule in bools) { var technicalIndicatorSignals = new List<ITechnicalIndicatorSignal>(); var ruleAction = isEntryRule ? "Entry" : "Exit"; foreach(Symbol symbol in _pair) { for (var i = 1; i <= _indicatorSignalCount; i++) { var indicatorSignal = SetUpIndicatorSignal(symbol, i, ruleAction); technicalIndicatorSignals.Add(indicatorSignal); } if (isEntryRule) { _entryradingRule = new TradingRuleQCVersion(technicalIndicatorSignals.ToArray(), isEntryRule); _entryradingRules.Add(symbol,_entryradingRule); } else { _exitTradingRule = new TradingRuleQCVersion(technicalIndicatorSignals.ToArray(), isEntryRule); _exitradingRules.Add(symbol,_exitTradingRule); } } } } } }
namespace QuantConnect { /// <summary> /// Interface used by the <see cref="TradingRule" /> class to flag technical indicator signals as crossing moving /// averages or oscillators crossing its thresholds. /// </summary> public interface ITechnicalIndicatorSignal { /// <summary> /// Gets a value indicating whether this instance is ready. /// </summary> /// <value> /// <c>true</c> if this instance is ready; otherwise, <c>false</c>. /// </value> bool IsReady { get; } /// <summary> /// Gets the signal. Only used if the instance will be part of a <see cref="TradingRule" /> class. /// </summary> /// <returns> /// <c>true</c> if the actual <see cref="Signal" /> correspond with the instance <see cref="TradeRuleDirection" />. /// <c>false</c> /// otherwise. /// </returns> bool GetSignal(); } /// <summary> /// The <see cref="TradingStrategiesBasedOnGeneticAlgorithms" /> implementation requires a direction for every /// technical indicator. /// </summary> public enum TradeRuleDirection { LongOnly = 1, ShortOnly = -1 } /// <summary> /// List of the technical indicator implemented... well not really, Bollinger bands wasn't implemented. /// </summary> public enum TechicalIndicators { SimpleMovingAverage = 0, MovingAverageConvergenceDivergence = 1, Stochastic = 2, RelativeStrengthIndex = 3, CommodityChannelIndex = 4, MomentumPercent = 5, WilliamsPercentR = 6, PercentagePriceOscillator = 7, BollingerBands = 8 } }
namespace QuantConnect { /// <summary> /// Possibles states of two moving averages. /// </summary> public enum CrossingMovingAveragesSignals { Bullish = 1, FastCrossSlowFromAbove = -2, Bearish = -1, FastCrossSlowFromBelow = 2 } /// <summary> /// This class keeps track of two crossing moving averages and updates a <see cref="CrossingMovingAveragesSignals" /> /// for each given state. /// </summary> /// <seealso cref="QuantConnect.Algorithm.CSharp.ITechnicalIndicatorSignal" /> public class CrossingMovingAverages : ITechnicalIndicatorSignal { private readonly CompositeIndicator<IndicatorDataPoint> _moving_average_difference; private readonly TradeRuleDirection _tradeRuleDirection; private int _lastSignal; /// <summary> /// Initializes a new instance of the <see cref="CrossingMovingAverages" /> class. /// </summary> /// <param name="fast_moving_average">The fast moving average.</param> /// <param name="slow_moving_average">The slow moving average.</param> /// <param name="tradeRuleDirection"> /// The trade rule direction. Only used if the instance will be part of a /// <see cref="TradingRule" /> class /// </param> /// <remarks> /// Both Moving Averages must be registered BEFORE being used by this constructor. /// </remarks> public CrossingMovingAverages(IndicatorBase<IndicatorDataPoint> fast_moving_average, IndicatorBase<IndicatorDataPoint> slow_moving_average, TradeRuleDirection? tradeRuleDirection = null) { _moving_average_difference = fast_moving_average.Minus(slow_moving_average); _moving_average_difference.Updated += ma_Updated; if (tradeRuleDirection != null) _tradeRuleDirection = (TradeRuleDirection)tradeRuleDirection; } /// <summary> /// Gets a value indicating whether this instance is ready. /// </summary> /// <value> /// <c>true</c> if this instance is ready; otherwise, <c>false</c>. /// </value> public bool IsReady { get { return _moving_average_difference.IsReady; } } /// <summary> /// Gets the actual state of both moving averages. /// </summary> public CrossingMovingAveragesSignals Signal { get; private set; } /// <summary> /// Gets the signal. Only used if the instance will be part of a <see cref="TradingRule" /> class. /// </summary> /// <returns> /// <c>true</c> if the actual <see cref="Signal" /> correspond with the instance <see cref="TradeRuleDirection" />. /// <c>false</c> /// otherwise. /// </returns> public bool GetSignal() { var signal = false; if (IsReady) { switch (_tradeRuleDirection) { case TradeRuleDirection.LongOnly: signal = Signal == CrossingMovingAveragesSignals.FastCrossSlowFromBelow; break; case TradeRuleDirection.ShortOnly: signal = Signal == CrossingMovingAveragesSignals.FastCrossSlowFromAbove; break; } } return signal; } private void ma_Updated(object sender, IndicatorDataPoint updated) { if (!IsReady) { return; } var actualSignal = Math.Sign(_moving_average_difference); if (actualSignal == _lastSignal || _lastSignal == 0) { Signal = (CrossingMovingAveragesSignals)actualSignal; } else if (_lastSignal == -1 && actualSignal == 1) { Signal = CrossingMovingAveragesSignals.FastCrossSlowFromBelow; } else if (_lastSignal == 1 && actualSignal == -1) { Signal = CrossingMovingAveragesSignals.FastCrossSlowFromAbove; } _lastSignal = actualSignal; } } }
namespace QuantConnect { /// <summary> /// Possibles states of an oscillator respect to its thresholds. /// </summary> public enum OscillatorSignals { CrossLowerThresholdFromAbove = -3, BellowLowerThreshold = -2, CrossLowerThresholdFromBelow = -1, BetweenThresholds = 0, CrossUpperThresholdFromBelow = 3, AboveUpperThreshold = 2, CrossUpperThresholdFromAbove = 1 } public struct OscillatorThresholds { public decimal Lower; public decimal Upper; } /// <summary> /// This class keeps track of an oscillator respect to its thresholds and updates an <see cref="OscillatorSignal" /> /// for each given state. /// </summary> /// <seealso cref="QuantConnect.Algorithm.CSharp.ITechnicalIndicatorSignal" /> public class OscillatorSignal : ITechnicalIndicatorSignal { private decimal _previousIndicatorValue; private OscillatorSignals _previousSignal; private OscillatorThresholds _thresholds; private TradeRuleDirection _tradeRuleDirection; /// <summary> /// Initializes a new instance of the <see cref="OscillatorSignal" /> class. /// </summary> /// <param name="indicator">The indicator.</param> /// <param name="thresholds">The thresholds.</param> /// <param name="tradeRuleDirection"> /// The trade rule direction. Only used if the instance will be part of a /// <see cref="TradingRule" /> class /// </param> /// <remarks>The oscillator must be registered BEFORE being used by this constructor.</remarks> public OscillatorSignal(dynamic indicator, OscillatorThresholds thresholds, TradeRuleDirection tradeRuleDirection) { SetUpClass(ref indicator, ref thresholds, tradeRuleDirection); } /// <summary> /// Initializes a new instance of the <see cref="OscillatorSignal" /> class. /// </summary> /// <param name="indicator">The indicator.</param> /// <param name="thresholds">The thresholds.</param> /// <remarks>The oscillator must be registered BEFORE being used by this constructor.</remarks> public OscillatorSignal(dynamic indicator, OscillatorThresholds thresholds) { SetUpClass(ref indicator, ref thresholds); } /// <summary> /// Initializes a new instance of the <see cref="OscillatorSignal" /> class. /// </summary> /// <param name="indicator">The indicator.</param> /// <remarks>The oscillator must be registered BEFORE being used by this constructor.</remarks> public OscillatorSignal(dynamic indicator) { var defaultThresholds = new OscillatorThresholds {Lower = 20, Upper = 80}; SetUpClass(ref indicator, ref defaultThresholds); } /// <summary> /// The underlying indicator, must be an oscillator. /// </summary> public dynamic Indicator { get; private set; } /// <summary> /// Gets the actual state of the oscillator. /// </summary> public OscillatorSignals Signal { get; private set; } /// <summary> /// Gets a value indicating whether this instance is ready. /// </summary> /// <value> /// <c>true</c> if this instance is ready; otherwise, <c>false</c>. /// </value> public bool IsReady { get { return Indicator.IsReady; } } /// <summary> /// Gets the signal. Only used if the instance will be part of a <see cref="TradingRule" /> class. /// </summary> /// <returns> /// <c>true</c> if the actual <see cref="Signal" /> correspond with the instance <see cref="TradeRuleDirection" />. /// <c>false</c> /// otherwise. /// </returns> public bool GetSignal() { var signal = false; if (IsReady) { switch (_tradeRuleDirection) { case TradeRuleDirection.LongOnly: signal = Signal == OscillatorSignals.CrossLowerThresholdFromBelow; break; case TradeRuleDirection.ShortOnly: signal = Signal == OscillatorSignals.CrossUpperThresholdFromAbove; break; } } return signal; } /// <summary> /// Updates the <see cref="Signal" /> status. /// </summary> private void Indicator_Updated(object sender, IndicatorDataPoint updated) { var actualPositionSignal = GetActualPositionSignal(updated); if (!Indicator.IsReady) { _previousIndicatorValue = updated.Value; _previousSignal = actualPositionSignal; Signal = _previousSignal; return; } var actualSignal = GetActualSignal(_previousSignal, actualPositionSignal); Signal = actualSignal; _previousIndicatorValue = updated.Value; _previousSignal = actualSignal; } /// <summary> /// Gets the actual position respect to the thresholds. /// </summary> /// <param name="indicatorCurrentValue">The indicator current value.</param> /// <returns></returns> private OscillatorSignals GetActualPositionSignal(decimal indicatorCurrentValue) { var positionSignal = OscillatorSignals.BetweenThresholds; if (indicatorCurrentValue > _thresholds.Upper) { positionSignal = OscillatorSignals.AboveUpperThreshold; } else if (indicatorCurrentValue < _thresholds.Lower) { positionSignal = OscillatorSignals.BellowLowerThreshold; } return positionSignal; } /// <summary> /// Gets the actual signal from the actual position respect to the thresholds and the last signal. /// </summary> /// <param name="previousSignal">The previous signal.</param> /// <param name="actualPositionSignal">The actual position signal.</param> /// <returns></returns> private OscillatorSignals GetActualSignal(OscillatorSignals previousSignal, OscillatorSignals actualPositionSignal) { OscillatorSignals actualSignal; var previousSignalInt = (int) previousSignal; var actualPositionSignalInt = (int) actualPositionSignal; if (actualPositionSignalInt == 0) { if (Math.Abs(previousSignalInt) > 1) { actualSignal = (OscillatorSignals) Math.Sign(previousSignalInt); } else { actualSignal = OscillatorSignals.BetweenThresholds; } } else { if (previousSignalInt * actualPositionSignalInt <= 0 || Math.Abs(previousSignalInt + actualPositionSignalInt) == 3) { actualSignal = (OscillatorSignals) (Math.Sign(actualPositionSignalInt) * 3); } else { actualSignal = (OscillatorSignals) (Math.Sign(actualPositionSignalInt) * 2); } } return actualSignal; } /// <summary> /// Sets up class. /// </summary> /// <param name="indicator">The indicator.</param> /// <param name="thresholds">The thresholds.</param> /// <param name="tradeRuleDirection">The trade rule direction.</param> private void SetUpClass(ref dynamic indicator, ref OscillatorThresholds thresholds, TradeRuleDirection? tradeRuleDirection = null) { _thresholds = thresholds; Indicator = indicator; indicator.Updated += new IndicatorUpdatedHandler(Indicator_Updated); if (tradeRuleDirection != null) _tradeRuleDirection = (TradeRuleDirection) tradeRuleDirection; } } }
namespace QuantConnect { internal class TradingRuleQCVersion { private readonly ITechnicalIndicatorSignal[] _technicalIndicatorSignals; private readonly bool _isEntryRule; public TradingRuleQCVersion(ITechnicalIndicatorSignal[] technicalIndicatorSignals, bool isEntryRule) { _technicalIndicatorSignals = technicalIndicatorSignals; _isEntryRule = isEntryRule; } public bool IsReady { get { var isReady = true; foreach (var indicator in _technicalIndicatorSignals) { isReady = indicator.IsReady && isReady; } return isReady; } } public bool TradeRuleSignal { get { return GetTradeRuleSignal(); } } /// <summary> /// Gets the trade rule signal for the best in-sample performance individual. /// </summary> /// <returns></returns> private bool GetTradeRuleSignal() { var signal = false; if (_isEntryRule) { signal = _technicalIndicatorSignals[0].GetSignal() && // Long SimpleMovingAverage _technicalIndicatorSignals[1].GetSignal() || // Long Stochastic _technicalIndicatorSignals[2].GetSignal() && // Long RelativeStrengthIndex _technicalIndicatorSignals[3].GetSignal() || // Short MovingAverageConvergenceDivergence _technicalIndicatorSignals[4].GetSignal(); // Short MomentumPercent } else { signal = _technicalIndicatorSignals[0].GetSignal() || // Short CommodityChannelIndex _technicalIndicatorSignals[1].GetSignal() && // Long RelativeStrengthIndex _technicalIndicatorSignals[2].GetSignal() || // Short Stochastic _technicalIndicatorSignals[3].GetSignal() && // Long MovingAverageConvergenceDivergence _technicalIndicatorSignals[4].GetSignal(); // Long Stochastic } return signal; } } }