Overall Statistics |
Total Trades 5218 Average Win 0.59% Average Loss -0.26% Compounding Annual Return 23.073% Drawdown 46.000% Expectancy 0.108 Net Profit 85.256% Sharpe Ratio 0.84 Probabilistic Sharpe Ratio 31.709% Loss Rate 66% Win Rate 34% Profit-Loss Ratio 2.30 Alpha 0 Beta 0 Annual Standard Deviation 0.216 Annual Variance 0.047 Information Ratio 0.84 Tracking Error 0.216 Treynor Ratio 0 Total Fees $11218.70 Estimated Strategy Capacity $76000000.00 Lowest Capacity Asset ES Y4D62XFM9IPT |
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.Slippage; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.Interfaces; using QuantConnect.Storage; using QuantConnect.Data.Custom.AlphaStreams; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace QuantConnect.Algorithm.CSharp { public class FuturesAlgo: QCAlgorithm { #region Global Vars private Future ContinuousContract; OrderTicket future_order; OrderTicket _stopLoss = null; OrderTicket _takeProfit = null; private Future ContinuousContract_1; private Security CurrentContract; private Symbol CurrentContract_Symbol; private Security PreviousContract; private AverageDirectionalIndex AdxFast; private AverageDirectionalIndex AdxSlow; private AverageDirectionalIndex AdxUltra; private decimal Tolerance = .02m; private bool IsLong = false; private bool IsShort = false; private bool IsFlat = true; ExponentialMovingAverage _ema; Decimal AdxFast_Plus_1 = 0.0m; // I declared these as decimals as the adx values are also decimals and needed the same type to do math later. Not sure if we need to recast as double or floats? Decimal AdxFast_Minus_1 = 0.0m; Decimal AdxFast_Adx_1 = 0.0m; Decimal AdxSlow_Plus_1 = 0.0m; Decimal AdxSlow_Minus_1 = 0.0m; Decimal AdxSlow_Adx_1 = 0.0m; Decimal AdxUltra_Plus_1 = 0.0m; Decimal AdxUltra_Minus_1 = 0.0m; private TradeBar slowbar; private TradeBar fastbar; private TradeBarConsolidator slow_consolidator; private TradeBarConsolidator fast_consolidator; Decimal AdxSlow_Adx_Lastbar = 0.0m; Decimal AdxSlow_Plus_Lastbar = 0.0m; Decimal AdxSlow_Minus_Lastbar = 0.0m; Decimal AdxFast_Adx_Lastbar = 0.0m; Decimal AdxFast_Plus_Lastbar = 0.0m; Decimal AdxFast_Minus_Lastbar = 0.0m; Decimal AdxFast_Adx; // Added these which are the ADX indicators in addition to the +/- that you had already Decimal AdxSlow_Adx; Decimal AdxFast_Plus; Decimal AdxFast_Minus; Decimal AdxSlow_Plus; Decimal AdxSlow_Minus; Decimal AdxSlow_Plus_from_last = 0.0m; Decimal AdxSlow_Minus_from_last = 0.0m; Decimal AdxFast_Plus_from_last = 0.0m; Decimal AdxFast_Minus_from_last = 0.0m; Decimal stop_percent = 0.0m; Decimal take_profit = 0.0m; Decimal fillPrice = 0.0m; Decimal stop_price = 0.0m; Decimal limit_price = 0.0m; int slow_time; int fast_time; #endregion public override void Initialize() { //SetTimeZone(TimeZones.LosAngeles); SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);// changed to Cash. Not sure if it mattered SetStartDate(2020, 01, 01); //SetEndDate(2022, 01 ,01); SetCash(100000); ContinuousContract = AddFuture(Futures.Indices.SP500EMini, fillDataForward: true, resolution: Resolution.Second, dataNormalizationMode: DataNormalizationMode.Raw, extendedMarketHours: true, dataMappingMode: DataMappingMode.OpenInterest, //changed to Open Interest as this is more indicative of when we should switch the trade to new contract than just last day contractDepthOffset: 0); //ContinuousContract.SetFilter(0,90); //var CurrentContract = ContinuousContract; fast_time = GetParameter("Fast", 180); slow_time = GetParameter("Slow", 1800); AdxFast = ADX(ContinuousContract.Symbol, fast_time , Resolution.Second); AdxSlow = ADX(ContinuousContract.Symbol, slow_time , Resolution.Second); //AdxUltra = ADX(ContinuousContract.Symbol, 1 , Resolution.Second); // This is maybe important later. Ignore for now slow_consolidator = new TradeBarConsolidator(TimeSpan.FromSeconds(slow_time)); slow_consolidator.DataConsolidated += SlowBarHandler; SubscriptionManager.AddConsolidator(ContinuousContract.Symbol, slow_consolidator); fast_consolidator = new TradeBarConsolidator(TimeSpan.FromSeconds(fast_time)); fast_consolidator.DataConsolidated += FastBarHandler; SubscriptionManager.AddConsolidator(ContinuousContract.Symbol, fast_consolidator); SetWarmUp(AdxSlow.WarmUpPeriod); SetWarmup(AdxFast.WarmUpPeriod); //OnWarmupFinished(FastBarHandler); } public override void OnData(Slice data) { if(!AdxSlow.IsReady || !AdxFast.IsReady) { return; } SetContract(data); AdxFast_Adx = AdxFast.Current.Value; AdxSlow_Adx = AdxSlow.Current.Value; AdxFast_Plus = AdxFast.PositiveDirectionalIndex.Current.Value; AdxFast_Minus = AdxFast.NegativeDirectionalIndex.Current.Value; AdxSlow_Plus = AdxSlow.PositiveDirectionalIndex.Current.Value; AdxSlow_Minus = AdxSlow.NegativeDirectionalIndex.Current.Value; var AdxFast_Plus_Dif = AdxFast_Plus-AdxFast_Plus_1; var AdxFast_Minus_Dif = AdxFast_Minus-AdxFast_Minus_1; decimal max_dif = GetParameter("max_dif", 5.0m); bool dif = (Math.Abs(AdxSlow_Plus-AdxSlow_Minus)) > max_dif; int max_vol = GetParameter("max_vol", 15); int min_adx = GetParameter("min_adx", 20); //var AdxUltra_Plus = AdxUltra.PositiveDirectionalIndex.Current.Value; //var AdxUltra_Minus = AdxUltra.NegativeDirectionalIndex.Current.Value; //var Ultra_dif = Math.Abs(AdxUltra_Plus-AdxUltra_Minus); AdxSlow_Plus_from_last = AdxSlow_Plus- AdxSlow_Plus_Lastbar; AdxSlow_Minus_from_last = AdxSlow_Minus - AdxSlow_Minus_Lastbar; AdxFast_Plus_from_last = AdxFast_Plus - AdxFast_Plus_Lastbar; AdxFast_Minus_from_last = AdxFast_Minus - AdxFast_Minus_Lastbar; //Defining Vars like TV //not_first : We set the "last bar" value equal to the current at the end of a bars consolidation. So we don't want to react immediately after that. Hence we will mostly use !not_first bool not_first = AdxSlow_Plus != AdxSlow_Plus_Lastbar || AdxFast_Plus != AdxFast_Plus_Lastbar || AdxSlow_Minus != AdxSlow_Minus_Lastbar || AdxFast_Minus != AdxFast_Minus_Lastbar; // enterLong_fast: our Crossover(Plus,Minus) - As we have consolidated on the short time frame, we want to know what was below on the last consolidated bar (Plus <= Minus) // and if it's currently gone up above the Minus during this bar. bool enterLong_fast = AdxFast_Plus_Lastbar <= AdxFast_Minus_Lastbar && AdxFast_Plus > AdxFast_Minus; //enterShort_fast: Same thing as above but inverted -Crossover(Minus,Plus) bool enterShort_fast = AdxFast_Minus_Lastbar <= AdxFast_Plus_Lastbar && AdxFast_Minus > AdxFast_Plus; //fallingLong: Here we are using a similar cross but this time we are looking at the Slow consolidator when Plus crosses under the ADX. // First we check to see if the Adx was below the Plus on the last consolidated Bar // Then we check that the Plus is currently greater than the Adx. // We also set a (currently) abritray number that the ADX has to be below. Values over 25 are seen to be trending so we give the some room to run with an ADX ~20. bool fallingLong = AdxSlow_Adx_Lastbar < AdxSlow_Plus_Lastbar && AdxSlow_Plus < AdxSlow_Adx && AdxSlow_Adx < 20; //faningShort: Invesion of falling long. But still referencing the Slow Consolidator bool fallingShort = AdxSlow_Adx_Lastbar < AdxSlow_Minus_Lastbar && AdxSlow_Minus < AdxSlow_Adx && AdxSlow_Adx < 20; //go_long: conditions to initiate long position. We check for the crossover from enterLong_fast and to make sure there wasn't too much volatility between the current slow bar and the last one // so we don't get caught off guard in the middle of a highly volatile move bool go_long = enterLong_fast && AdxSlow_Plus_from_last < max_vol; // go_short: inverse of go_long including comparing change of previous Minus as opposed to previous Plus value on slow consolidator bool go_short = enterShort_fast && AdxSlow_Minus_from_last < max_vol; //exit_long: here we find falling long true OR we let a cross under of the Fast consolidator for Crossunder(Plus,Adx) but only if both ADX and the MINUS indicator is very high bool exit_long = fallingLong || (AdxFast_Adx_Lastbar < AdxFast_Plus_Lastbar && AdxFast_Plus < AdxFast_Adx && AdxFast_Adx > 35 && AdxFast_Minus > 35); //exit_short: inverse of exit long bool exit_short = fallingShort || (AdxFast_Adx_Lastbar < AdxFast_Minus_Lastbar && AdxFast_Minus < AdxFast_Adx && AdxFast_Adx > 35 && AdxFast_Plus > 35); if (AdxFast_Adx_1 == 0) //we pass this to any variable of _Previous that is defined as 0 so that it skips the first loop { AdxFast_Adx_Lastbar = AdxFast_Adx; AdxFast_Plus_Lastbar = AdxFast_Plus; AdxFast_Minus_Lastbar = AdxFast_Minus; AdxSlow_Adx_Lastbar = AdxSlow_Adx; AdxSlow_Plus_Lastbar = AdxSlow_Plus; AdxSlow_Minus_Lastbar = AdxSlow_Minus; goto after_flat; } foreach (var changedEvent in data.SymbolChangedEvents.Values) { Debug($"{Time} - SymbolChanged event: {changedEvent}"); if (Time.TimeOfDay == TimeSpan.Zero) { continue; } throw new Exception($"{Time} unexpected symbol changed event {changedEvent}!"); } if (IsFlat && AdxFast_Adx_1 != 0 && not_first) { if(go_long && !exit_long ) { future_order = Buy(CurrentContract_Symbol, 1); fillPrice = future_order.AverageFillPrice; stop_price = fillPrice - 40; //limit_price = 40 + fillPrice; IsFlat = false; IsLong = true; //goto after_trade; } if(go_short && !exit_short) { future_order = Sell(CurrentContract_Symbol, 1); fillPrice = future_order.AverageFillPrice; stop_price = fillPrice + 40; //limit_price = fillPrice - 40; IsFlat = false; IsShort = true; //goto after_trade; } } if(IsLong && not_first) { if(exit_long || (AdxFast_Minus > 26 && AdxFast_Minus_from_last > 20)|| ContinuousContract.Price <= stop_price) //We close if exit_long is true, if the Minus on Fast Spikes { Liquidate(CurrentContract_Symbol); IsFlat = true; IsLong = false; IsShort = false; if (go_short) // we also flip to short if the situation says we should with go_short { future_order = Sell(CurrentContract_Symbol, 1); fillPrice = future_order.AverageFillPrice; stop_price = fillPrice + 40; IsFlat = false; IsShort = true; } } } if(IsShort && not_first) { if(exit_short || (AdxFast_Plus > 26 && AdxFast_Plus_from_last > 20)|| ContinuousContract.Price >= stop_price)// Same as above but with the Plus on Fast Consolidator { Liquidate(CurrentContract_Symbol); IsFlat = true; IsLong = false; IsShort = false; if (go_long) { future_order = Buy(CurrentContract_Symbol, 1); fillPrice = future_order.AverageFillPrice; stop_price = fillPrice - 40; IsFlat = false; IsLong = true; } } } after_flat: AdxFast_Adx_1 = AdxFast_Adx; // these were 0.0 in the first pass, assuming this will update them after the first? correct me if I'm wrong. AdxSlow_Adx_1 = AdxSlow_Adx; AdxFast_Plus_1 = AdxFast_Plus; AdxFast_Minus_1 = AdxFast_Minus; AdxSlow_Plus_1 = AdxSlow_Plus; AdxSlow_Minus_1 = AdxSlow_Minus; PreviousContract = CurrentContract; } private void SetContract(Slice slice) { CurrentContract = Securities[ContinuousContract.Mapped]; CurrentContract_Symbol = CurrentContract.Symbol; if(PreviousContract!= null && CurrentContract != PreviousContract ) { Liquidate(PreviousContract.Symbol); SubscriptionManager.RemoveConsolidator(PreviousContract.Symbol, fast_consolidator); SubscriptionManager.AddConsolidator(ContinuousContract.Symbol, fast_consolidator); SubscriptionManager.RemoveConsolidator(PreviousContract.Symbol, slow_consolidator); SubscriptionManager.AddConsolidator(ContinuousContract.Symbol, slow_consolidator); } } private void FastBarHandler(object sender, TradeBar consolidated) { /*if (fastbar != null) { AdxFast_Adx_Lastbar = AdxFast_Adx; AdxFast_Plus_Lastbar = AdxFast_Plus; AdxFast_Minus_Lastbar = AdxFast_Minus; }*/ AdxFast_Adx_Lastbar = AdxFast_Adx; AdxFast_Plus_Lastbar = AdxFast_Plus; AdxFast_Minus_Lastbar = AdxFast_Minus; fastbar = consolidated; } private void SlowBarHandler(object sender, TradeBar consolidated) { /*if (slowbar != null) { AdxSlow_Adx_Lastbar = AdxSlow_Adx; AdxSlow_Plus_Lastbar = AdxSlow_Plus; AdxSlow_Minus_Lastbar = AdxSlow_Minus; }*/ AdxSlow_Adx_Lastbar = AdxSlow_Adx; AdxSlow_Plus_Lastbar = AdxSlow_Plus; AdxSlow_Minus_Lastbar = AdxSlow_Minus; slowbar = consolidated; } public void OnData(TradeBars bars) { // we need to declare this method } public void Update(IBaseData data) { } } }