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