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