Overall Statistics |
Total Trades 25 Average Win 0.92% Average Loss -0.23% Compounding Annual Return 16.924% Drawdown 2.200% Expectancy 1.880 Net Profit 5.318% Sharpe Ratio 2.28 Loss Rate 42% Win Rate 58% Profit-Loss Ratio 3.94 Alpha 0.09 Beta 0.141 Annual Standard Deviation 0.048 Annual Variance 0.002 Information Ratio -0.426 Tracking Error 0.068 Treynor Ratio 0.778 Total Fees $0.00 |
namespace QuantConnect { /// <summary> /// This is a truncated version of the algorithm made for the Trading Strategies Based on Genetic Algorithms project /// for the QuantConnect platform. /// The main difference is that the rules are hard coded in the TradingRule class, instead if being dynamically /// generated as in the original. /// </summary> /// <seealso cref="QuantConnect.Algorithm.QCAlgorithm" /> internal class TradingStrategiesBasedOnGeneticAlgorithmsQCVersion : QCAlgorithm { private readonly int _indicatorSignalCount = 5; private TradingRuleQCVersion _entryradingRule; private TradingRuleQCVersion _exitTradingRule; private Symbol _pair; /// <summary> /// Here are the parameters of the individual with the best in-sample fitness. /// </summary> 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() { SetCash(startingCash: 1e6); var startDate = new DateTime(year: 2017, month: 1, day: 1); SetStartDate(startDate); SetEndDate(startDate.AddMonths(months: 4)); _pair = AddForex("EURUSD", leverage: 10).Symbol; SetParameters(parametersToBacktest); SetUpRules(); } public override void OnData(Slice slice) { if (!_entryradingRule.IsReady) return; if (!Portfolio.Invested) { if (_entryradingRule.TradeRuleSignal) SetHoldings(_pair, percentage: 1m); } else { if (_exitTradingRule.TradeRuleSignal) Liquidate(_pair); } } /// <summary> /// Sets up indicator signal. This method is where the Technical indicator rules are defined. /// </summary> /// <param name="pair">The pair.</param> /// <param name="indicatorN">The number if indicator.</param> /// <param name="ruleAction"> /// The rule action. Should be 'Entry' or 'Exit' and is only used to differentiate the genes for /// entry and exit /// </param> /// <returns></returns> /// <exception cref="System.NotImplementedException">WIP</exception> 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; } /// <summary> /// Gets the gene int from key. /// </summary> /// <param name="key">The key.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">The gene " + key + " is not present either as Config or as Parameter</exception> /// <remarks> /// This method makes the algorithm working with the genes defined from the Config (as in the Lean Optimization) and /// from the Parameters (as the Lean Caller). /// </remarks> private int GetGeneIntFromKey(string key) { // I'll keep this line as in the original code. //var intGene = Config.GetInt(key, int.MinValue); 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}; foreach (var isEntryRule in bools) { var technicalIndicatorSignals = new List<ITechnicalIndicatorSignal>(); var ruleAction = isEntryRule ? "Entry" : "Exit"; for (var i = 1; i <= _indicatorSignalCount; i++) { var indicatorSignal = SetUpIndicatorSignal(_pair, i, ruleAction); technicalIndicatorSignals.Add(indicatorSignal); } if (isEntryRule) { _entryradingRule = new TradingRuleQCVersion(technicalIndicatorSignals.ToArray(), isEntryRule); } else { _exitTradingRule = new TradingRuleQCVersion(technicalIndicatorSignals.ToArray(), isEntryRule); } } } } }
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; } } }