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