I am only getting about 18 days worth of backtesting before getting an out of memory error. This is with the free research account. Is there anything I am doing that can be improved to get a longer length of testing time?
Thanks,
#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 EodIronFly : QCAlgorithm
{
private Symbol _spySymbol;
private List<OrderTicket> _ironFlies = new List<OrderTicket>();
private decimal _profit = .10m;
private decimal _loss = .10m;
public override void Initialize()
{
SetStartDate(2023, 8, 29);
SetEndDate(2023,10,6);
SetCash(GetParameter("InitialCashAmount", 10000));
_profit = GetParameter("TakeProfitPercent", 10m)/100m;
_loss = GetParameter("StopLossPercent", 10m)/100m;
var option = AddOption("SPY");
_spySymbol = option.Symbol;
//option.SetFilter(universe => universe.IncludeWeeklys().Strikes(-10,10).Expiration(0,360));
option.SetFilter(-12,12,TimeSpan.Zero,TimeSpan.FromDays(7));
option.SetFilter(universe => universe.IncludeWeeklys().OnlyApplyFilterAtMarketOpen());
}
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// Slice object keyed by symbol containing the stock data
public override void OnData(Slice slice)
{
if (!IsMarketOpen(_spySymbol))
{
return;
}
if (_ironFlies.Where(x => x.Time.Date == Time.Date).Count() > 0) return;
if (!slice.OptionChains.TryGetValue(_spySymbol, out var chain)) return;
decimal debitToClose;
decimal creditToOpen;
decimal sellLastPrice;
decimal sellBidPrice;
decimal sellAskPrice;
//decimal sellBidAskSpread;
DateTime expiry;
List<Leg> sellLegs = new List<Leg>();
List<DateTime> activeOrderDates = _ironFlies.Select(x => x.Time.Date).Distinct().ToList();
foreach(DateTime activeOrderDate in activeOrderDates)
{
debitToClose = 0;
creditToOpen = 0;
sellLastPrice = 0;
sellBidPrice = 0;
sellAskPrice = 0;
//sellBidAskSpread = 0;
sellLegs.Clear();
List<OrderTicket> orderTickets = _ironFlies.Where(x => x.Time.Date == activeOrderDate).ToList();
creditToOpen = Math.Abs(orderTickets.Sum(x => x.AverageFillPrice * x.QuantityFilled));
expiry = chain.Where(x => x.Symbol == orderTickets.First().Symbol).First().Expiry;
foreach(OrderTicket orderTicket in orderTickets)
{
//Debug("orderTicket " + orderTicket.ToString());
//debitToClose = debitToClose + (orderTicket.AverageFillPrice * orderTicket.QuantityFilled);
//Debug("filledPrice " + filledPrice);
//sellLastPrice = sellLastPrice + (chain.Where(x => x.Symbol == orderTicket.Symbol).Select(x => x.LastPrice).First() * orderTicket.QuantityFilled);
//Debug("currentPrice " + sellLastPrice);
sellBidPrice = sellBidPrice + (chain.Where(x => x.Symbol == orderTicket.Symbol).Select(x => x.BidPrice).First() * orderTicket.QuantityFilled);
sellAskPrice = sellAskPrice + (chain.Where(x => x.Symbol == orderTicket.Symbol).Select(x => x.AskPrice).First() * orderTicket.QuantityFilled);
sellLegs.Add(Leg.Create(orderTicket.Symbol, (int)orderTicket.Quantity));
}
//sellBidAskSpread = Math.Abs(sellAskPrice) - Math.Abs(sellBidPrice);
//sellLastPrice = Math.Abs(sellLastPrice);
sellBidPrice = Math.Abs(sellBidPrice);
sellAskPrice = Math.Abs(sellAskPrice);
if (sellAskPrice < (creditToOpen - (creditToOpen * _profit)))
{
//Debug("sell bid price " + sellBidPrice);
//Debug("sell ask price " + sellAskPrice);
var profitIronFly = ComboMarketOrder(sellLegs, -1);
debitToClose = profitIronFly.Sum(x => x.AverageFillPrice * x.QuantityFilled);
Log("Sold for Profit " + expiry.ToShortDateString()
+ " Credit to Open " + creditToOpen
+ " Debit to Close " + debitToClose
+ " Profit " + (creditToOpen - debitToClose));
//Debug("filledPrice " + filledPrice);
//Debug("currentPrice " + sellLastPrice);
//Debug("soldPrice " +soldPrice);
//Debug("Sold for Profit " + (filledPrice - soldPrice));
_ironFlies = _ironFlies.Except(orderTickets).ToList();
}
else if (sellBidPrice > (creditToOpen + (creditToOpen * _loss)))
{
//Debug("sell bid price " + sellBidPrice);
//Debug("sell ask price " + sellAskPrice);
var lossIronFly = ComboMarketOrder(sellLegs, -1);
debitToClose = lossIronFly.Sum(x => x.AverageFillPrice * x.QuantityFilled);
Log("Sold for Loss " + expiry.ToShortDateString()
+ " Credit to Open " + creditToOpen
+ "Debit to Close " + debitToClose
+ " Loss " + (debitToClose - creditToOpen));
//Debug("filledPrice " + filledPrice);
//Debug("currentPrice " + sellLastPrice);
//Debug("soldPrice " +soldPrice);
//Debug("Sold for Loss " + (filledPrice - soldPrice));
_ironFlies = _ironFlies.Except(orderTickets).ToList();
}
else if (Time.Hour >= 15 && activeOrderDate.AddDays(1).Date == expiry.Date)
{
var expiredIronFly = ComboMarketOrder(sellLegs, -1);
debitToClose = expiredIronFly.Sum(x => x.AverageFillPrice * x.QuantityFilled);
Log("Sold before Expiration " + expiry.ToShortDateString()
+ " Credit to Open " + creditToOpen
+ " Debit to Close " + debitToClose
+ " Closed P/L " + (creditToOpen - debitToClose));
//Debug("filledPrice " + filledPrice);
//Debug("currentPrice " + sellLastPrice);
//Debug("soldPrice " +soldPrice);
//Debug("Sold for Loss " + (filledPrice - soldPrice));
_ironFlies = _ironFlies.Except(orderTickets).ToList();
}
}
if ( !(Time.Hour == 15 && Time.Minute == 30)) return;
expiry = DateTime.MinValue;
switch (Time.DayOfWeek )
{
case DayOfWeek.Monday: case DayOfWeek.Tuesday:
expiry = Time.AddDays(3);
break;
case DayOfWeek.Wednesday: case DayOfWeek.Thursday: case DayOfWeek.Friday:
expiry = Time.AddDays(5);
break;
}
var expiryTradingDay = TradingCalendar.GetTradingDay(expiry);
if (!expiryTradingDay.BusinessDay)
{
Log(expiry.ToString() + " is not a trading day, so no order created");
return;
}
if (expiry > Time && expiry <= DateTime.Now)
{
OptionContract atmCall = chain.Where(x => x.Expiry.Date == expiry.Date && x.Right == OptionRight.Call)
.OrderBy(x => Math.Abs(x.UnderlyingLastPrice - x.Strike))
.First();
decimal atmStrike = atmCall.Strike;
OptionContract atmPut = chain.Where(x => x.Expiry.Date == expiry.Date && x.Right == OptionRight.Put && x.Strike == atmStrike)
.OrderBy(x => x.Strike)
.First();
OptionContract longCall = chain.Where(x => x.Expiry.Date == expiry.Date && x.Right == OptionRight.Call && x.Strike >= (atmStrike + 7))
.OrderBy(x => x.Strike).First();
OptionContract longPut = chain.Where(x => x.Expiry.Date == expiry.Date && x.Right == OptionRight.Put && x.Strike <= (atmStrike - 7))
.OrderByDescending(x => x.Strike).First();
List<Leg> orderLegs = new List<Leg>()
{
Leg.Create(longPut.Symbol, 1),
Leg.Create(atmPut.Symbol, -1),
Leg.Create(atmCall.Symbol, -1),
Leg.Create(longCall.Symbol, 1)
};
/*
Debug("Long Put Bid " + longPut.BidPrice);
Debug("Long Put Ask " + longPut.AskPrice);
Debug("Long Put Last Price " + longPut.LastPrice);
Debug("ATM Put Bid " + atmPut.BidPrice);
Debug("ATM Put Ask " + atmPut.AskPrice);
Debug("ATM Put Last Price " + atmPut.LastPrice);
Debug("ATM Call Bid " + atmCall.BidPrice);
Debug("ATM Call Ask " + atmCall.AskPrice);
Debug("ATM Call Last Price " + atmCall.LastPrice);
Debug("Long Call Bid " + longCall.BidPrice);
Debug("Long Call Ask " + longCall.AskPrice);
Debug("Long Call Last Price " + longCall.LastPrice);
*/
var newIronFly = ComboMarketOrder(orderLegs, 1);
/*
decimal buyBidPrice = longPut.BidPrice - atmPut.BidPrice - atmCall.BidPrice + longCall.BidPrice;
//Debug("buyBidPrice " + buyBidPrice);
decimal buyAskPrice = longPut.AskPrice - atmPut.AskPrice - atmCall.AskPrice + longCall.AskPrice;
//Debug("buyAskPrice " + buyAskPrice);
decimal buyLastPrice = longPut.LastPrice - atmPut.LastPrice - atmCall.LastPrice + longCall.LastPrice;
//Debug("buyLastPrice " + buyLastPrice);
*/
_ironFlies.AddRange(newIronFly);
Log("New Iron Fly Order for "
+ expiry.ToShortDateString()
+ " Price " + newIronFly.Sum(x => x.AverageFillPrice * x.QuantityFilled)
+ " Long Put Strike " + longPut.Strike
+ " ATM Put Strike " + atmPut.Strike
+ " ATM Call Strike " + atmCall.Strike
+ " Long Call Strike " + longCall.Strike);
}
}
}
}
Stephan Cox
Nevermind. I switched it over to use OptionChainProvider.GetOptionContractList(), and that solved my issue. No memory issue, and runs super fast!
Mia Alissi
I'm glad to hear that using
OptionChainProvider.GetOptionContractList()
solved your issue. This method is indeed efficient for getting options contracts in QuantConnect. It returns a list ofSymbol
objects, each representing an option contract, and contains the details of the contract, such as the underlying symbol, market, option style, option right, strike price, and expiration date.Here's a basic example of how to use it:
Remember to replace
symbol
anddata.Time
with your specific symbol and time. If you encounter any more issues or have further questions, feel free to ask.The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Stephan Cox
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!