Overall Statistics |
Total Trades 53 Average Win 1.03% Average Loss -1.19% Compounding Annual Return 33.867% Drawdown 9.400% Expectancy 0.271 Net Profit 19.885% Sharpe Ratio 1.868 Loss Rate 32% Win Rate 68% Profit-Loss Ratio 0.87 Alpha 0.244 Beta -0.036 Annual Standard Deviation 0.128 Annual Variance 0.016 Information Ratio 0.707 Tracking Error 0.143 Treynor Ratio -6.695 Total Fees $373.70 |
using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Brokerages; using QuantConnect.Data; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Orders; using QuantConnect.Securities; namespace QuantConnect.Algorithm.CSharp { public class FuturesAlgo: QCAlgorithm { int frequency = 90; // in minutes, resets every day int barCounter = 0; //used to count bars and consolidate FuturesContract _contract = null; BollingerBands _bb = null; bool _newDay = true; decimal prevPrice = 0; decimal prevUp = 0; decimal prevDown = 0; bool reset = true; public override void Initialize() { SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin); //SetStartDate(year: 2011, month: 8, day: 15); SetStartDate(year: 2017, month: 1, day: 1); SetEndDate(year: 2017, month: 8, day: 15); SetCash(100000); //SetWarmUp(TimeSpan.FromDays(5)); var future = AddFuture(Futures.Indices.SP500EMini, Resolution.Minute); future.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(360)); } public override void OnData(Slice slice) { if (!InitContract(slice)) { return; } if(reset) { //need to roll/close contract in position if any reset = false; Log("RESET closing all positions"); foreach(var s in Portfolio.Securities) { SetHoldings(s.Key, 0); } } if (_contract != null && barCounter % frequency == 0 && slice.Bars.ContainsKey(_contract.Symbol)) { OnCustomBarUpdate(null, slice.Bars[_contract.Symbol]); } barCounter++; return; } private bool InitContract(Slice slice) { if (!_newDay)//reset daily everyday we chekc whther futures need to be rolled { return true; } // if (_contract != null) // Log(_contract.Expiry.ToString()); if (_contract != null && (_contract.Expiry -Time.Date).TotalDays >=3) //rolling 3 days before expiry { return true; } if (slice.FutureChains.Count() == 0) { return false; } //On Expiry we will reset contracts foreach (var chain in slice.FutureChains) { //when selecting contract if on expiry date, then skip first as it woudl be the same one! int skip = 0; if (_contract != null) Log("Expiry days away " + (_contract.Expiry - Time.Date).TotalDays + " - " + _contract.Expiry + " - " + Time.Date); _newDay = false; Log("future count " + chain.Value.Count() + ":" + string.Join(",", chain.Value)); foreach (var newContract in chain.Value.OrderBy(x => x.Expiry)) { Log("evaluating " + newContract.ToString()); if ((newContract.Expiry - Time.Date).TotalDays > 3 && (_contract==null ||_contract.ToString() != newContract.ToString())) { _contract = newContract; //warming up the indicators _bb = new BollingerBands(_contract.Symbol, 20, 2, MovingAverageType.Exponential); var history = History(_contract.Symbol, 50 * frequency, Resolution.Minute); if (history.Count() == 0) { //should never happen but it does for ESZ16... so as a workaround Log("no historical data for " + newContract.ToString()); _bb = null; _contract = null; continue; } int counter = 0; DateTime dt = history.First().EndTime; foreach (var bar in history) { if (bar.EndTime.DayOfWeek != dt.DayOfWeek)//every day we reset and start counting again! { counter = 0; dt = bar.EndTime; } if (counter % frequency == 0) // every frequency minutes { _bb.Update(new IndicatorDataPoint(_contract.Symbol, bar.EndTime, bar.Close)); if (_bb.IsReady) { var std = 0.5m * (_bb.MiddleBand-_bb.LowerBand); Log("BB:"+ bar.EndTime + ","+ bar.Close + "," +_bb.MiddleBand.ToString() + "," + std.ToString()); } } counter++; } //setting the consolidator //can't seem to get it to work properly, so dealing with it in OnData // Log("Before SubscriptionManager.Count " + SubscriptionManager.Count.ToString()); // var customFrequency = new TradeBarConsolidator(TimeSpan.FromMinutes(frequency)); // customFrequency.DataConsolidated += OnCustomBarUpdate; // SubscriptionManager.AddConsolidator(_contract.Symbol, customFrequency); // Log("After SubscriptionManager.Count " + SubscriptionManager.Count); //resetting comparisons prices as they wont work on the new one prevPrice = 0; prevUp = 0; prevDown = 0; Log("INIT DONE " + _contract.ToString() + " - BB READY " + _bb.IsReady.ToString()); return true; } } } return false; } public void OnCustomBarUpdate(object sender, TradeBar bar) { if (_contract == null || bar.Symbol!=_contract.Symbol) // can happend during roll period { return; } Log("OnCustomBarUpdate"); if (_contract != null) { //Log(bar.Close + " - " + _bb.LowerBand + " - " + _bb.UpperBand + " - " +prevPrice + " - " + prevDown + " - " + prevUp); } if (_bb.Current.EndTime < bar.EndTime) { //Log("P," + bar.Close + "," + _bb.Current.EndTime.ToString() + ",PPrice," + _bb.Current.Price); _bb.Update(new IndicatorDataPoint(_contract.Symbol, bar.EndTime, bar.Close)); } if (prevPrice>0) { if (bar.Close >_bb.LowerBand && prevPrice < prevDown) { Log("BUY SIGNAL"); if (Portfolio.ContainsKey(_contract.Symbol)) { var existing = Portfolio[_contract.Symbol].Quantity; if (existing < 0) { Log("reversing LONG"); MarketOrder(_contract.Symbol, -1 * existing * 2); } else if (existing == 0) { Log("Going LONG"); MarketOrder(_contract.Symbol, 2); } //else if already long nothign to do } } else if (bar.Close <_bb.UpperBand && prevPrice > prevUp) { Log("SELL SIGNAL"); var existing = Portfolio[_contract.Symbol].Quantity; if (existing > 0) { Log("reversing SHORT"); MarketOrder(_contract.Symbol, -1 * existing * 2); } else if (existing == 0) { Log("Going SHORT"); MarketOrder(_contract.Symbol, -2); } //else if already shorstring nothign to do } } prevPrice = bar.Close; prevUp = _bb.UpperBand; prevDown = _bb.LowerBand; } public override void OnEndOfDay() { _newDay = true; barCounter = 0; } public override void OnOrderEvent(OrderEvent orderEvent) { var order = Transactions.GetOrderById(orderEvent.OrderId); string msg = "ORDER " + order.Status.ToString() + ": " + order.Direction.ToString() + " " + order.Quantity.ToString("#.0") + " " + order.Symbol; if (order.Type == OrderType.StopMarket) { var o = ((QuantConnect.Orders.StopMarketOrder)order); msg += " - STOP @ " + o.StopPrice.ToString("#.00000"); } else if (order.Type == OrderType.Market) { var o = order; } else if (order.Type == OrderType.Limit) { var o = ((QuantConnect.Orders.LimitOrder)order); msg += " - LIMIT @ " + o.LimitPrice.ToString("#.00000"); } if (order.Status == OrderStatus.Filled) { msg += " - filled @ " + order.Price.ToString("#.00000"); Log(msg); } } } }