| Overall Statistics |
|
Total Trades 187 Average Win 13.74% Average Loss -0.66% Compounding Annual Return 18.651% Drawdown 30.200% Expectancy 3.950 Net Profit 341.596% Sharpe Ratio 0.661 Loss Rate 77% Win Rate 23% Profit-Loss Ratio 20.97 Alpha 0.152 Beta -0.111 Annual Standard Deviation 0.214 Annual Variance 0.046 Information Ratio 0.21 Tracking Error 0.252 Treynor Ratio -1.275 Total Fees $4255.00 |
using System;
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using System.Linq;
using QuantConnect.Interfaces;
using QuantConnect.Indicators;
using QuantConnect.Securities;
using QuantConnect.Orders;
using QuantConnect.Data.Consolidators;
namespace QuantConnect.Algorithm.CSharp
{
public class TestAlgo : QCAlgorithm, IRegressionAlgorithmDefinition
{
public RollingWindow<decimal> BidPrice = new RollingWindow<decimal>(4);
public RollingWindow<decimal> AskPrice = new RollingWindow<decimal>(4);
public RollingWindow<decimal> Volume = new RollingWindow<decimal>(4);
private readonly List<OrderTicket> _openStopMarketOrders = new List<OrderTicket>();
public override void Initialize()
{
SetStartDate(2011, 01, 01);
SetEndDate(DateTime.Now);
SetCash(1000000);
var futureSP500 = AddFuture(Futures.Indices.SP500EMini);
futureSP500.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(182));
var benchmark = AddEquity("SPY");
SetBenchmark(benchmark.Symbol);
}
public override void OnData(Slice slice)
{
if (!Portfolio.Invested)
{
foreach(var chain in slice.FutureChains)
{
// find the front contract expiring no earlier than in 90 days
var contract = (from futuresContract in chain.Value.OrderBy(x => x.Expiry) where futuresContract.Expiry > Time.Date.AddDays(90) select futuresContract).FirstOrDefault();
// if found, perform logic
if (contract != null)
{
BidPrice.Add(contract.BidPrice);
AskPrice.Add(contract.AskPrice);
Volume.Add(contract.Volume);
if (!BidPrice.IsReady || !AskPrice.IsReady || !Volume.IsReady)
continue;
// a long stop is triggered when the price rises above the value
// so we'll set a long stop 0.00001% above the current bar's close
var Long = (BidPrice[0] > AskPrice[1]);
var closeLong = BidPrice[0];
var stopPriceLong = closeLong * 1.0000001m;
var newTicketLong = StopMarketOrder(contract.Symbol, 10, stopPriceLong);
_openStopMarketOrders.Add(newTicketLong);
// a long stop is triggered when the price rises above the value
// so we'll set a long stop 0.00001% above the current bar's close
var Short = (AskPrice[0] < BidPrice[1]);
var closeShort = BidPrice[0];
var stopPriceShort = closeShort * 1.0000001m;
var newTicketShort = StopMarketOrder(contract.Symbol, 10, stopPriceShort);
_openStopMarketOrders.Add(newTicketShort);
if (Long)
Log("Submitting StopMarketOrder for LongTrade");
StopMarketOrder(contract.Symbol, 10, stopPriceLong);
// a short stop is triggered when the price falls below the value
// so we'll set a short stop 1% below the current bar's close
stopPriceLong = closeLong * .99m;
newTicketLong = StopMarketOrder(contract.Symbol, -10, stopPriceLong);
_openStopMarketOrders.Add(newTicketLong);
// when we submitted new stop market orders we placed them into this list,
// so while there's two entries they're still open and need processing
if (_openStopMarketOrders.Count == 2)
{
// check if either is filled and cancel the other
var longOrder = _openStopMarketOrders[0];
var shortOrder = _openStopMarketOrders[1];
if (CheckPairOrdersForFills(longOrder, shortOrder))
{
_openStopMarketOrders.Clear();
return;
}
// if niether order has filled, bring in the stops by a penny
var newLongStop = longOrder.Get(OrderField.StopPrice) - 0.01m;
var newShortStop = shortOrder.Get(OrderField.StopPrice) + 0.01m;
Log("Updating stops - Long: " + newLongStop.ToString("0.00") + " Short: " + newShortStop.ToString("0.00"));
longOrder.Update(new UpdateOrderFields{StopPrice = newLongStop, Tag = "Update #" + (longOrder.UpdateRequests.Count + 1)});
shortOrder.Update(new UpdateOrderFields{StopPrice = newShortStop,Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1)});
}
if (Short)
Log("Submitting StopMarketOrder for ShortTrade");
StopMarketOrder(contract.Symbol, 10, stopPriceShort);
// a short stop is triggered when the price falls below the value
// so we'll set a short stop 1% below the current bar's close
stopPriceShort = closeShort * .99m;
newTicketShort = StopMarketOrder(contract.Symbol, -10, stopPriceShort);
_openStopMarketOrders.Add(newTicketShort);
// when we submitted new stop market orders we placed them into this list,
// so while there's two entries they're still open and need processing
if (_openStopMarketOrders.Count == 2)
{
// check if either is filled and cancel the other
var longOrder = _openStopMarketOrders[0];
var shortOrder = _openStopMarketOrders[1];
if (CheckPairOrdersForFills(longOrder, shortOrder))
{
_openStopMarketOrders.Clear();
return;
}
// if niether order has filled, bring in the stops by a penny
var newLongStop = longOrder.Get(OrderField.StopPrice) - 0.01m;
var newShortStop = shortOrder.Get(OrderField.StopPrice) + 0.01m;
Log("Updating stops - Long: " + newLongStop.ToString("0.00") + " Short: " + newShortStop.ToString("0.00"));
longOrder.Update(new UpdateOrderFields{StopPrice = newLongStop,Tag = "Update #" + (longOrder.UpdateRequests.Count + 1)});
shortOrder.Update(new UpdateOrderFields{StopPrice = newShortStop,Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1)});
}
}
}
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
var order = Transactions.GetOrderById(orderEvent.OrderId);
Console.WriteLine("{0}: {1}: {2}", Time, order.Type, orderEvent);
}
private bool CheckPairOrdersForFills(OrderTicket longOrder, OrderTicket shortOrder)
{
if (longOrder.Status == OrderStatus.Filled)
{
Log(shortOrder.OrderType + ": Cancelling short order, long order is filled.");
shortOrder.Cancel("Long filled.");
return true;
}
if (shortOrder.Status == OrderStatus.Filled)
{
Log(longOrder.OrderType + ": Cancelling long order, short order is filled.");
longOrder.Cancel("Short filled");
return true;
}
return false;
}
private bool TimeIs(int day, int hour, int minute)
{
return Time.Day == day && Time.Hour == hour && Time.Minute == minute;
}
/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;
/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };
/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "0"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"}
};
}
}