Overall Statistics |
Total Trades 106 Average Win 3.70% Average Loss -0.90% Compounding Annual Return 100.687% Drawdown 23.300% Expectancy 1.552 Net Profit 71.602% Sharpe Ratio 1.642 Loss Rate 50% Win Rate 50% Profit-Loss Ratio 4.10 Alpha 0.545 Beta -0.096 Annual Standard Deviation 0.325 Annual Variance 0.106 Information Ratio 1.256 Tracking Error 0.331 Treynor Ratio -5.542 Total Fees $0.00 |
using Newtonsoft.Json; namespace QuantConnect.Algorithm.CSharp.Algo { /// <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" /> public partial class TradingStrategiesBasedOnGeneticAlgorithmsQCVersion : QCAlgorithm { private int _entryIndicatorSignalCount = 6; private int _exitIndicatorSignalCount = 6; private List<TradingRuleQCVersion> _entryradingRule; private List<TradingRuleQCVersion> _exitTradingRule; private double accountsize = 10000; public string[] Symbols; FxRiskManagment RiskManager; //[Parameter] public string accessToken; //[Parameter] public string timeZone = "America/New_York"; //[Parameter] private const decimal _leverage = 50m; // How much of the total strategy equity can be at risk as maximum in all trades. private const decimal _maxExposure = 0.8m; // How much of the total strategy equity can be at risk in a single trade. private const decimal _maxExposurePerTrade = 0.25m; // The max strategy equity proportion to put at risk in a single operation. private decimal _riskPerTrade = 0.01m; private decimal takeProfit = 0.10m; private Resolution _resolution = Resolution.Minute; // Smallest lot private LotSize _lotSize = LotSize.Nano; private int ExitSMAFastPeriod = 50; private int ExitSMASlowPeriod = 200; private int EntrySMAFastPeriod = 50; private int EntrySMASlowPeriod = 200; private int EntryEMAFastPeriod = 50; private int EntryEMASlowPeriod = 200; private int EntryMACDFastPeriod = 12; private int EntryMACDSlowPeriod = 26; private int EntryMACDSignalPeriod = 9; private int EntryStochasticPeriod = 14; private int EntryRSIPeriod = 14; private int EntryCCIPeriod = 14; private int EntryMomentumPeriod = 60; private int EntryWILRPeriod = 14; private int EntryBBPeriod = 20; //[Parameter] public bool isLive = false; public bool verbose = false; public string[] OandaFXSymbols = { "AUDCAD","AUDCHF","AUDHKD","AUDJPY","AUDNZD","AUDSGD","AUDUSD","CADCHF","CADHKD","CADJPY", "CADSGD","CHFHKD","CHFJPY","CHFZAR","EURAUD","EURCAD","EURCHF","EURCZK","EURDKK","EURGBP", "EURHKD","EURHUF","EURJPY","EURNOK","EURNZD","EURPLN","EURSEK","EURSGD","EURTRY","EURUSD", "EURZAR","GBPAUD","GBPCAD","GBPCHF","GBPHKD","GBPJPY","GBPNZD","GBPPLN","GBPSGD","GBPUSD", "GBPZAR","HKDJPY","NZDCAD","NZDCHF","NZDHKD","NZDJPY","NZDSGD","NZDUSD","SGDCHF","SGDHKD", "SGDJPY","TRYJPY","USDCAD","USDCHF","USDCNH","USDCZK","USDDKK","USDHKD","USDHUF","USDINR", "USDJPY","USDMXN","USDNOK","USDPLN","USDSAR","USDSEK","USDSGD","USDTHB","USDTRY","USDZAR", "ZARJPY" }; public string[] OandaFXMajors2 = { "AUDJPY","AUDUSD","EURAUD","EURCHF","EURGBP","EURJPY","EURUSD","GBPCHF","GBPJPY","GBPUSD", "NZDUSD","USDCAD","USDCHF","USDJPY" }; public string[] OandaFXMajors1 = { "EURCHF","EURGBP","EURJPY","EURUSD","GBPCHF","GBPJPY","GBPUSD","USDCHF","USDJPY" }; public string[] OandaFXMajors = { "EURCHF","EURGBP","EURJPY","EURUSD" }; public string[] OandaFXMajors0 = { "EURUSD" }; /// <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", "8"}, {"EntryIndicator2", "2"}, {"EntryIndicator3", "3"}, {"EntryIndicator4", "7"}, {"EntryIndicator5", "5"}, {"EntryIndicator6", "0"}, {"EntryIndicator1Direction", "1"},//0 {"EntryIndicator2Direction", "1"},//0 {"EntryIndicator3Direction", "1"}, //1 {"EntryIndicator4Direction", "1"}, //1 {"EntryIndicator5Direction", "1"},//0 {"EntryIndicator6Direction", "1"},//0 {"EntryIndicator1Operator", "0"},//0//0 {"EntryIndicator2Operator", "0"},//0//1 {"EntryIndicator3Operator", "0"},//1 {"EntryIndicator4Operator", "0"},//1 {"EntryIndicator5Operator", "0"},//0 {"ExitIndicator1", "4"}, {"ExitIndicator2", "0"}, {"ExitIndicator3", "1"}, {"ExitIndicator4", "2"}, {"ExitIndicator5", "7"}, {"ExitIndicator6", "8"}, {"ExitIndicator1Direction", "0"},//1 {"ExitIndicator2Direction", "0"}, {"ExitIndicator3Direction", "0"}, {"ExitIndicator4Direction", "0"}, {"ExitIndicator5Direction", "0"},//0 {"ExitIndicator6Direction", "0"},//0 {"ExitIndicator1Operator", "1"},//0//1 {"ExitIndicator2Operator", "1"},//0//0 {"ExitIndicator3Operator", "0"},//1 {"ExitIndicator4Operator", "1"},//0//0 {"ExitIndicator5Operator", "1"},//0 {"EntrySMAFastPeriod", "20"}, {"EntrySMASlowPeriod", "200"}, {"ExitSMAFastPeriod", "20"}, {"ExitSMASlowPeriod", "220"}, {"EntryIndicatorSignalCount", "6"}, {"ExitIndicatorSignalCount", "6"} }; public override void Initialize() { SetCash(startingCash: accountsize); var startDate = GetGeneDateTimeFromKey("startDate", new DateTime(2017, 1, 1)); //SetStartDate(startDate); SetStartDate(startDate); SetEndDate(GetGeneDateTimeFromKey("endDate", new DateTime(2017, 10, 10))); //SetEndDate(startDate.AddMonths(months: 20)); takeProfit = GetGeneDecimalFromKey("takeProfit", 0.05m); _riskPerTrade = GetGeneDecimalFromKey("riskPerTrade", 0.01m); _entryIndicatorSignalCount = GetGeneIntFromKey("EntryIndicatorSignalCount", 2); _exitIndicatorSignalCount = GetGeneIntFromKey("ExitIndicatorSignalCount", 2); List<string> list = new List<string>(); list.AddRange(OandaFXMajors); Symbols = list.ToArray(); foreach (var symbol in Symbols) { AddSecurity(SecurityType.Forex, symbol, _resolution, Market.Oanda, true, _leverage, false); SetBrokerageModel(BrokerageName.OandaBrokerage); } SetParameters(parametersToBacktest); SetUpRules(); } public void OnData(QuoteBars data) { /* foreach (var symbol in Symbols) { decimal smaVal = sma[symbol].AddSample(data[symbol].Close); decimal stdVal = std[symbol].AddSample(data[symbol].Close, sma[symbol]); if(sma[symbol].Ready) { decimal upband = smaVal + stdVal; decimal downband = smaVal - stdVal; } } */ foreach (var entry in _entryradingRule) { if (entry.IsReady) { EntrySignal(data, entry); } } foreach (var entry in _exitTradingRule) { if (entry.IsReady) { ExitSignal(entry); } } RiskManager.UpdateTrailingStopOrders(data); var openStopLossOrders = Portfolio.Transactions.GetOrderTickets(o => o.OrderType == OrderType.StopMarket && o.Status == OrderStatus.Submitted); if (verbose) { foreach (var ticket in openStopLossOrders) { Log(String.Format("{0} {1} {2} {3}", ticket.OrderId, ticket.Symbol, ticket.AverageFillPrice, ticket.Quantity)); } } } public void ExitSignal(TradingRuleQCVersion signal) { if (verbose && signal.TradeRuleSignal) { Log(string.Format("signal symbol:: {0}", signal.Symbol)); } if (Portfolio[signal.Symbol].Invested && signal.TradeRuleSignal) { Liquidate(signal.Symbol); } else if (Portfolio[signal.Symbol].Invested && Portfolio[signal.Symbol].UnrealizedProfitPercent > takeProfit) { //safeguard profits, //liquidate half MarketOrder(signal.Symbol, Portfolio[signal.Symbol].Quantity/2); } // Log("MarketOrder:: " +activeSignal.Symbol + " :: "+ signal.meta.direction); } public void EntrySignal(QuoteBars data, TradingRuleQCVersion signal) { if (verbose && signal.TradeRuleSignal) { Log(string.Format("signal symbol:: {0}", signal.Symbol)); } if (!Portfolio[signal.Symbol].Invested) { if (signal.TradeRuleSignal) { var openPrice = Securities[signal.Symbol].Price; int size = (int)_lotSize; var actualAction = AgentAction.GoLong; var entryValues = RiskManager.CalculateEntryOrders(data, signal.Symbol, actualAction); if (entryValues.Item1 != 0) { var ticket = MarketOrder(signal.Symbol, entryValues.Item1); StopMarketOrder(signal.Symbol, -entryValues.Item1, entryValues.Item2, tag: entryValues.Item3.ToString("0.000000")); if (verbose) { Log(string.Format("MarketOrder:: {0} {1}", signal.Symbol, size)); } } //MarketOrder(signal.Symbol, size, false, ""); } } // Log("MarketOrder:: " +activeSignal.Symbol + " :: "+ signal.meta.direction); } /// <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 = (TradeRuleDirection)intDirection; 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: GetGeneIntFromKey(ruleAction + "SMAFastPeriod")); var slow = SMA(pair, period: GetGeneIntFromKey(ruleAction + "SMASlowPeriod")); technicalIndicator = new CrossingMovingAverages(fast, slow, direction); break; case TechicalIndicators.ExponentialMovingAverage: // Canonical cross moving average parameters. var fastema = EMA(pair, period: 50); var slowema = EMA(pair, period: 200); technicalIndicator = new CrossingMovingAverages(fastema, slowema, 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); oscillatorThresholds.Lower = 20; oscillatorThresholds.Upper = 80; technicalIndicator = new OscillatorSignal(sto.StochD, oscillatorThresholds, direction, this); break; case TechicalIndicators.RelativeStrengthIndex: var rsi = RSI(pair, period: 14); oscillatorThresholds.Lower = 30; oscillatorThresholds.Upper = 70; technicalIndicator = new OscillatorSignal(rsi, oscillatorThresholds, direction, this); break; case TechicalIndicators.CommodityChannelIndex: var cci = CCI(pair, period: 20); oscillatorThresholds.Lower = -100; oscillatorThresholds.Upper = 100; technicalIndicator = new OscillatorSignal(cci, oscillatorThresholds, direction, this); break; case TechicalIndicators.MomentumPercent: var pm = MOMP(pair, period: 60); //in percentage, above 5% means bull, lower than -5% bear oscillatorThresholds.Lower = -5; oscillatorThresholds.Upper = 5; technicalIndicator = new OscillatorSignal(pm, oscillatorThresholds, direction, this); break; case TechicalIndicators.WilliamsPercentR: var wr = WILR(pair, period: 14); oscillatorThresholds.Lower = -80; oscillatorThresholds.Upper = -20; technicalIndicator = new OscillatorSignal(wr, oscillatorThresholds, direction, this); 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: //Log("setting BB"); var signalb = BB(pair, period: 20, k: 2); //technicalIndicator = new BolingerBandsIndicator(signalb, this, direction); technicalIndicator = new BBOscillatorSignal(signalb, direction, this); //throw new NotImplementedException("WIP"); break; case TechicalIndicators.ClenowBreakout: //Set up Indicators: var _atr = ATR(pair, 100, MovingAverageType.Simple, Resolution.Daily); var _max = MAX(pair, 50, Resolution.Daily); var _min = MIN(pair, 50, Resolution.Daily); var _maxC = MAX(pair, 25, Resolution.Daily); var _minC = MIN(pair, 25, Resolution.Daily); //Log("setting BB"); var signalt = BB(pair, period: 20, k: 2); technicalIndicator = new BolingerBandsIndicator(signalt, this, direction); //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 gene = Config.GetInt(key, int.MinValue); var gene = int.MinValue; if (gene == int.MinValue)//not found in config, then get from parameter { try { gene = int.Parse(GetParameter(key)); if (verbose) { Log(string.Format("Parameter {0} set to {1}", key, gene)); } } #pragma warning disable CS0168 // Variable is declared but never used catch (ArgumentNullException e) #pragma warning restore CS0168 // Variable is declared but never used { throw new ArgumentNullException(key, "The gene " + key + " is not present either as Config or as Parameter"); } } return gene; } /// <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, int def) { // I'll keep this line as in the original code. //var gene = Config.GetInt(key, int.MinValue); var gene = int.MinValue; if (gene == int.MinValue)//not found in config, then get from parameter { try { gene = int.Parse(GetParameter(key)); if (verbose) { Log(string.Format("Parameter {0} set to {1}", key, gene)); } } #pragma warning disable CS0168 // Variable is declared but never used catch (ArgumentNullException e) #pragma warning restore CS0168 // Variable is declared but never used { return def; } } return gene; } /// <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 decimal GetGeneDecimalFromKey(string key, decimal def) { // I'll keep this line as in the original code. //var gene = Config.GetValue<decimal>(key); var gene = decimal.MinValue; if (gene == decimal.MinValue)//not found in config, then get from parameter { try { gene = decimal.Parse(GetParameter(key)); if (verbose) { Log(string.Format("Parameter {0} set to {1}", key, gene)); } } #pragma warning disable CS0168 // Variable is declared but never used catch (ArgumentNullException e) #pragma warning restore CS0168 // Variable is declared but never used { return def; } } return gene; } /// <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 DateTime GetGeneDateTimeFromKey(string key, DateTime def) { // I'll keep this line as in the original code. //var gene = Config.GetValue<DateTime>(key, null); var gene = DateTime.MinValue; if (gene == DateTime.MinValue)//not found in config, then get from parameter { try { gene = DateTime.Parse(GetParameter(key)); if (verbose) { Log(string.Format("Parameter {0} set to {1}", key, gene)); } } #pragma warning disable CS0168 // Variable is declared but never used catch (ArgumentNullException e) #pragma warning restore CS0168 // Variable is declared but never used { return def; } } return gene; } private void SetUpRules() { bool[] bools = { true, false }; _entryradingRule = new List<TradingRuleQCVersion>(); _exitTradingRule = new List<TradingRuleQCVersion>(); RiskManager = new FxRiskManagment(Portfolio, _riskPerTrade, _maxExposurePerTrade, _maxExposure, _lotSize); foreach (var symbol in Symbols) { Securities[symbol].VolatilityModel = new ThreeSigmaVolatilityModel(STD(symbol: symbol, period: 12 * 60, resolution: _resolution), 20.0m); foreach (var isEntryRule in bools) { var technicalIndicatorSignals = new List<ITechnicalIndicatorSignal>(); var ruleOperators = new List<RuleOperators>(); var ruleAction = isEntryRule ? "Entry" : "Exit"; var indicatorSignalCount = isEntryRule ? _entryIndicatorSignalCount : _exitIndicatorSignalCount; for (var i = 1; i <= indicatorSignalCount; i++) { var indicatorSignal = SetUpIndicatorSignal(symbol, i, ruleAction); technicalIndicatorSignals.Add(indicatorSignal); } for (var i = 1; i < indicatorSignalCount; i++) { var key = ruleAction + "Indicator" + i + "Operator"; ruleOperators.Add((RuleOperators)GetGeneIntFromKey(key)); } if (isEntryRule) { _entryradingRule.Add(new TradingRuleQCVersion(technicalIndicatorSignals.ToArray(), ruleOperators.ToArray(), isEntryRule, symbol)); } else { _exitTradingRule.Add(new TradingRuleQCVersion(technicalIndicatorSignals.ToArray(), ruleOperators.ToArray(), isEntryRule, symbol)); } } } } } }
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 HasSignal(); ActiveSignal GetSignal(); TradeRuleDirection GetDirection(); } /// <summary> /// The <see cref="TradingStrategiesBasedOnGeneticAlgorithms" /> implementation requires a direction for every /// technical indicator. /// </summary> public enum TradeRuleDirection { LongOnly = 1, ShortOnly = 0, Any = -2 } /// <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, ExponentialMovingAverage = 9, ClenowBreakout = 10 } }
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 : SignalsBase { 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 override bool IsReady { get { return _moving_average_difference.IsReady; } } public override TradeRuleDirection GetDirection() { return _tradeRuleDirection; } /// <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 override bool HasSignal() { var signal = false; if (IsReady) { switch (_tradeRuleDirection) { case TradeRuleDirection.LongOnly: signal = Signal == CrossingMovingAveragesSignals.FastCrossSlowFromBelow; break; case TradeRuleDirection.ShortOnly: signal = Signal == CrossingMovingAveragesSignals.FastCrossSlowFromAbove; break; case TradeRuleDirection.Any: signal = Signal == CrossingMovingAveragesSignals.FastCrossSlowFromAbove || Signal == CrossingMovingAveragesSignals.FastCrossSlowFromBelow; 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.Algorithm.CSharp.Algo { /// <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 : SignalsBase { private decimal _previousIndicatorValue; private OscillatorSignals _previousSignal; private OscillatorThresholds _thresholds; private TradeRuleDirection _tradeRuleDirection; QCAlgorithm _log; /// <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, QCAlgorithm log) { SetUpClass(ref indicator, ref thresholds, tradeRuleDirection); _log = log; } /// <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, TradeRuleDirection tradeRuleDirection, QCAlgorithm log) { var defaultThresholds = new OscillatorThresholds { Lower = 20, Upper = 80 }; _log = log; SetUpClass(ref indicator, ref defaultThresholds, tradeRuleDirection); } /// <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 override bool IsReady { get { return Indicator.IsReady; } } public override TradeRuleDirection GetDirection() { return _tradeRuleDirection; } /// <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 override bool HasSignal() { var signal = false; if (IsReady) { switch (_tradeRuleDirection) { case TradeRuleDirection.LongOnly: signal = Signal == OscillatorSignals.CrossLowerThresholdFromBelow; break; case TradeRuleDirection.ShortOnly: signal = Signal == OscillatorSignals.CrossUpperThresholdFromAbove; break; case TradeRuleDirection.Any: signal = Signal == OscillatorSignals.CrossUpperThresholdFromAbove || Signal == OscillatorSignals.CrossLowerThresholdFromBelow; 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> protected virtual 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 { public enum LotSize { Standard = 100000, Mini = 10000, Micro = 1000, Nano = 100, } public enum AgentAction { GoShort = -1, DoNothing = 0, GoLong = 1 } public class FxRiskManagment { // Maximum equity proportion to put at risk in a single operation. private decimal _riskPerTrade; // Maximum equity proportion at risk in open positions in a given time. private decimal _maxExposure; // Maximum equity proportion at risk in a single trade. private decimal _maxExposurePerTrade; private int _lotSize; private int _minQuantity; private SecurityPortfolioManager _portfolio; /// <summary> /// Initializes a new instance of the <see cref="FxRiskManagment"/> class. /// </summary> /// <param name="portfolio">The QCAlgorithm Portfolio.</param> /// <param name="riskPerTrade">The max risk per trade.</param> /// <param name="maxExposurePerTrade">The maximum exposure per trade.</param> /// <param name="maxExposure">The maximum exposure in all trades.</param> /// <param name="lotsize">The minimum quantity to trade.</param> /// <exception cref="System.NotImplementedException">The pairs should be added to the algorithm before initialize the risk manager.</exception> public FxRiskManagment(SecurityPortfolioManager portfolio, decimal riskPerTrade, decimal maxExposurePerTrade, decimal maxExposure, LotSize lotsize = LotSize.Micro, int minQuantity = 5) { _portfolio = portfolio; if (_portfolio.Securities.Count == 0) { throw new NotImplementedException("The pairs should be added to the algorithm before initialize the risk manager."); } this._riskPerTrade = riskPerTrade; _maxExposurePerTrade = maxExposurePerTrade; this._maxExposure = maxExposure; _lotSize = (int)lotsize; _minQuantity = minQuantity; } /// <summary> /// Calculates the entry orders and stop-loss price. /// </summary> /// <param name="pair">The Forex pair Symbol.</param> /// <param name="action">The order direction.</param> /// <returns>a Tuple with the quantity as Item1 and the stop-loss price as Item2. If quantity is zero, then means that no trade must be done.</returns> public Tuple<int, decimal, decimal> CalculateEntryOrders(QuoteBars data, Symbol pair, AgentAction action) { // If exposure is greater than the max exposure, then return zero. if (_portfolio.TotalMarginUsed > _portfolio.TotalPortfolioValue * _maxExposure) { return Tuple.Create(0, 0m, 0m); } var closePrice = _portfolio.Securities[pair].Price; var leverage = _portfolio.Securities[pair].Leverage; var exchangeRate = _portfolio.Securities[pair].QuoteCurrency.ConversionRate; var volatility = _portfolio.Securities[pair].VolatilityModel.Volatility; // Estimate the maximum entry order quantity given the risk per trade. var moneyAtRisk = _portfolio.TotalPortfolioValue * _riskPerTrade; var maxQuantitybyRisk = moneyAtRisk / (volatility * exchangeRate); // Estimate the maximum entry order quantity given the exposure per trade. var maxBuySize = Math.Min(_portfolio.MarginRemaining, _portfolio.TotalPortfolioValue * _maxExposurePerTrade) * leverage; var maxQuantitybyExposure = maxBuySize / (closePrice * exchangeRate); // The final quantity is the lowest of both. var quantity = (int)(Math.Round(Math.Min(maxQuantitybyRisk, maxQuantitybyExposure) / _lotSize, 0) * _lotSize); // If the final quantity is lower than the minimum quantity of the given lot size, then return zero. if (quantity < _lotSize * _minQuantity) return Tuple.Create(0, 0m, 0m); quantity = action == AgentAction.GoLong ? quantity : -quantity; var stopLossPrice = closePrice + (action == AgentAction.GoLong ? -volatility : volatility); return Tuple.Create(quantity, stopLossPrice, action == AgentAction.GoLong ? data[pair].Ask.Close : data[pair].Bid.Close); } /// <summary> /// Updates the stop-loss price of all open StopMarketOrders. /// </summary> public void UpdateTrailingStopOrders(QuoteBars data) { // Get all the spot-loss orders. var openStopLossOrders = _portfolio.Transactions.GetOrderTickets(o => o.OrderType == OrderType.StopMarket && o.Status == OrderStatus.Submitted); foreach (var ticket in openStopLossOrders) { var stopLossPrice = ticket.SubmitRequest.StopPrice; var volatility = _portfolio.Securities[ticket.Symbol].VolatilityModel.Volatility; var actualPrice = _portfolio.Securities[ticket.Symbol].Price; // The StopLossOrder has the opposite direction of the original order. var originalOrderDirection = ticket.Quantity > 0 ? OrderDirection.Sell : OrderDirection.Buy; if (originalOrderDirection == OrderDirection.Sell) { actualPrice = data[ticket.Symbol].Ask.Close; } if (originalOrderDirection == OrderDirection.Buy) { actualPrice = data[ticket.Symbol].Bid.Close; } var newStopLossPrice = actualPrice + (volatility * (originalOrderDirection == OrderDirection.Buy ? -1 : 1)); if ((originalOrderDirection == OrderDirection.Buy && newStopLossPrice > stopLossPrice) || (originalOrderDirection == OrderDirection.Sell && newStopLossPrice < stopLossPrice)) { /* if (originalOrderDirection == OrderDirection.Sell && data[ticket.Symbol].Ask.Close < System.Convert.ToDecimal(ticket.SubmitRequest.Tag)) { if(newStopLossPrice>System.Convert.ToDecimal(ticket.SubmitRequest.Tag)) { newStopLossPrice = System.Convert.ToDecimal(ticket.SubmitRequest.Tag); } } if (originalOrderDirection == OrderDirection.Buy && data[ticket.Symbol].Bid.Close > System.Convert.ToDecimal(ticket.SubmitRequest.Tag)) { //price is on right direction if(newStopLossPrice<System.Convert.ToDecimal(ticket.SubmitRequest.Tag)) { newStopLossPrice = System.Convert.ToDecimal(ticket.SubmitRequest.Tag); } } */ ticket.Update(new UpdateOrderFields { Quantity = _portfolio[ticket.Symbol].Quantity, StopPrice = newStopLossPrice }); } } } } }
namespace QuantConnect { /// <summary> /// Provides an implementation of <see cref="IVolatilityModel"/> that computes the /// relative standard deviation as the volatility of the security /// </summary> public class ThreeSigmaVolatilityModel : IVolatilityModel { private readonly TimeSpan _periodSpan; private StandardDeviation _standardDeviation; private decimal _percentage; /// <summary> /// Gets the volatility of the security as a percentage /// </summary> public decimal Volatility { get { return _standardDeviation * _percentage; } } /// <summary> /// Initializes a new instance of the <see cref="QuantConnect.Securities.RelativeStandardDeviationVolatilityModel"/> class /// </summary> /// <param name="periodSpan">The time span representing one 'period' length</param> /// <param name="periods">The nuber of 'period' lengths to wait until updating the value</param> public ThreeSigmaVolatilityModel(StandardDeviation standardDeviation, decimal percentage = 2.5m) { _standardDeviation = standardDeviation; _percentage = percentage; _periodSpan = TimeSpan.FromMinutes(standardDeviation.Period); } /// <summary> /// Updates this model using the new price information in /// the specified security instance /// </summary> /// <param name="security">The security to calculate volatility for</param> /// <param name="data"></param> public void Update(Security security, BaseData data) { } public IEnumerable<HistoryRequest> GetHistoryRequirements(Security security, DateTime utcTime) { return Enumerable.Empty<HistoryRequest>(); } } }
namespace QuantConnect { /// <summary> /// Possibles states of two moving averages. /// </summary> public enum BolingerBandsSignals { Bullish = 1, Bearish = -1, Middle = 0 } /// <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 BolingerBandsIndicator : SignalsBase { private BollingerBands _bb; private TradeRuleDirection _tradeRuleDirection; private int _lastSignal; QCAlgorithm _log; /// <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 BolingerBandsIndicator(BollingerBands bb, QCAlgorithm log, TradeRuleDirection? tradeRuleDirection = null) { _bb = bb; // _bb.Updated += new IndicatorUpdatedHandler(ma_Updated); _log = log; SetUpClass(ref bb, tradeRuleDirection); //if (tradeRuleDirection != null) _tradeRuleDirection = (TradeRuleDirection)tradeRuleDirection; } private void Indicator_Updated(object sender, IndicatorDataPoint updated) { if (!IsReady) { return; } var actualSignal = GetActualPositionSignal(updated); _lastSignal = (int)actualSignal; } private void SetUpClass(ref BollingerBands indicator, TradeRuleDirection? tradeRuleDirection = null) { _bb = indicator; indicator.Updated += new IndicatorUpdatedHandler(Indicator_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 override bool IsReady { get { return _bb.IsReady; } } public override TradeRuleDirection GetDirection() { return _tradeRuleDirection; } /// <summary> /// Gets the actual state of both moving averages. /// </summary> public BolingerBandsSignals 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 override bool HasSignal() { var signal = false; if (IsReady) { switch (_tradeRuleDirection) { case TradeRuleDirection.LongOnly: signal = _lastSignal == (int)BolingerBandsSignals.Bullish; break; case TradeRuleDirection.ShortOnly: signal = _lastSignal == (int)BolingerBandsSignals.Bearish; break; case TradeRuleDirection.Any: signal = _lastSignal == (int)BolingerBandsSignals.Bearish || _lastSignal == (int)BolingerBandsSignals.Bullish; break; } } return signal; } /// <summary> /// Gets the actual position respect to the thresholds. /// </summary> /// <param name="indicatorCurrentValue">The indicator current value.</param> /// <returns></returns> private BolingerBandsSignals GetActualPositionSignal(decimal indicatorCurrentValue) { var positionSignal = BolingerBandsSignals.Middle; if (indicatorCurrentValue > _bb.UpperBand) { _lastActiveSignal = new ActiveSignal(); _lastActiveSignal.IsShort = true; positionSignal = BolingerBandsSignals.Bearish;//Bearish } else if (indicatorCurrentValue < _bb.LowerBand) { _lastActiveSignal = new ActiveSignal(); _lastActiveSignal.IsLong = true; positionSignal = BolingerBandsSignals.Bullish;//Bullish } /* if(positionSignal!=BolingerBandsSignals.Middle) { _log.Log("indicatorCurrentValue:: " + indicatorCurrentValue); _log.Log("UpperBand:: "+_bb.UpperBand); _log.Log("LowerBand:: "+_bb.LowerBand); _log.Log("positionSignal:: "+positionSignal); } */ return positionSignal; } } }
namespace QuantConnect { public enum RuleOperators { AND = 1, OR = 0 } public class TradingRuleQCVersion { private readonly ITechnicalIndicatorSignal[] _technicalIndicatorSignals; private readonly RuleOperators[] _ruleOperators; private readonly bool _isEntryRule; private Symbol _symbol; public TradingRuleQCVersion(ITechnicalIndicatorSignal[] technicalIndicatorSignals, RuleOperators[] ruleOperators, bool isEntryRule, Symbol symbol) { _technicalIndicatorSignals = technicalIndicatorSignals; _isEntryRule = isEntryRule; _symbol = symbol; _ruleOperators = ruleOperators; } public bool IsReady { get { var isReady = true; foreach (var indicator in _technicalIndicatorSignals) { isReady = indicator.IsReady && isReady; } return isReady; } } public Symbol Symbol { get { return _symbol; } } 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].HasSignal(); for (var i = 1; i < _technicalIndicatorSignals.Length; i++) { signal = op(i, signal, i-1); } /*&& // Long SimpleMovingAverage _technicalIndicatorSignals[1].HasSignal() || // Long Stochastic _technicalIndicatorSignals[2].HasSignal() && // Long RelativeStrengthIndex _technicalIndicatorSignals[3].HasSignal() || // Short MovingAverageConvergenceDivergence _technicalIndicatorSignals[4].HasSignal() )|| // Short MomentumPercent _technicalIndicatorSignals[5].HasSignal(); // Long BollingerBands signal = signal || _technicalIndicatorSignals[5].HasSignal(); // Long BollingerBands */ } /* else { signal = _technicalIndicatorSignals[0].HasSignal() || // Short CommodityChannelIndex _technicalIndicatorSignals[1].HasSignal() && // Long RelativeStrengthIndex _technicalIndicatorSignals[2].HasSignal() || // Short Stochastic _technicalIndicatorSignals[3].HasSignal() && // Long MovingAverageConvergenceDivergence _technicalIndicatorSignals[4].HasSignal(); //|| // Long Stochastic //_technicalIndicatorSignals[5].GetSignal(); // Short BollingerBands } */ return signal; } private bool op(int ind1, bool ind2, int op) { if(_ruleOperators[op]==RuleOperators.AND) { return _technicalIndicatorSignals[ind1].HasSignal() && ind2; } return _technicalIndicatorSignals[ind1].HasSignal() || ind2; } } }
namespace QuantConnect { public abstract class SignalsBase : ITechnicalIndicatorSignal { protected ActiveSignal _lastActiveSignal; protected SignalsBase() { } public abstract bool IsReady { get; } public abstract TradeRuleDirection GetDirection(); public ActiveSignal GetSignal() { return _lastActiveSignal; } public abstract bool HasSignal(); } }
using Newtonsoft.Json; namespace QuantConnect { public class ActiveSignal { public int Id { get; set; } public bool IsCompleted { get; set; } public bool IsLong { get; set; } public bool IsShort { get; set; } public string Symbol { get; set; } public DateTime Expiration { get; set; } public DateTime SetupDate { get; set; } public decimal Forecast { get; set; } public string GenSignalTag() { var orderTag = new OrderTag { SignalId = Id, Expiration = Expiration, Target = Forecast }; return JsonConvert.SerializeObject(orderTag); } } }
namespace QuantConnect { public class OrderTag { public int SignalId { get; set; } = 0; public DateTime Expiration { get; set; } = DateTime.MaxValue; public decimal Target { get; set; } = 0; } }
namespace QuantConnect.Algorithm.CSharp.Algo { /// <summary> /// This class keeps track of an oscillator respect to its thresholds and updates an <see cref="BBOscillatorSignal" /> /// for each given state. /// </summary> /// <seealso cref="QuantConnect.Algorithm.CSharp.ITechnicalIndicatorSignal" /> public class BBOscillatorSignal : OscillatorSignal { private BollingerBands _bb; /// <summary> /// Initializes a new instance of the <see cref="BBOscillatorSignal" /> class. /// </summary> /// <param name="indicator">The indicator.</param> /// <remarks>The oscillator must be registered BEFORE being used by this constructor.</remarks> public BBOscillatorSignal(BollingerBands indicator, TradeRuleDirection tradeRuleDirection, QCAlgorithm log) : base(indicator, tradeRuleDirection, log) { _bb = indicator; } /// <summary> /// Gets the actual position respect to the thresholds. /// </summary> /// <param name="indicatorCurrentValue">The indicator current value.</param> /// <returns></returns> protected override OscillatorSignals GetActualPositionSignal(decimal indicatorCurrentValue) { var positionSignal = OscillatorSignals.BetweenThresholds; if (indicatorCurrentValue > _bb.UpperBand) { positionSignal = OscillatorSignals.AboveUpperThreshold; } else if (indicatorCurrentValue < _bb.LowerBand) { positionSignal = OscillatorSignals.BellowLowerThreshold; } return positionSignal; } } }