| 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;
}
}
}