Overall Statistics
Total Trades
97
Average Win
0%
Average Loss
0.00%
Compounding Annual Return
-11.346%
Drawdown
0.100%
Expectancy
-1
Net Profit
-0.112%
Sharpe Ratio
-10.893
Probabilistic Sharpe Ratio
0%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0.005
Annual Variance
0
Information Ratio
-10.893
Tracking Error
0.005
Treynor Ratio
0
Total Fees
$49.00
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Securities.Option;

namespace QuantConnect.Algorithm.CSharp
{
    public class OptionExpirationCancelTest : QCAlgorithm
    {
		private static readonly DateTime Start = new DateTime(2020, 11, 6);
		private static readonly DateTime End = new DateTime(2020, 11, 9);
		private static readonly string UnderlyingTicker = "AAPL";
		
		private static readonly Symbol OptionSymbol = QuantConnect.Symbol.Create(
	        UnderlyingTicker,
	        SecurityType.Option,
	        Market.USA
        );

		private static readonly Symbol UnderlyingSymbol = QuantConnect.Symbol.Create(
	        UnderlyingTicker,
	        SecurityType.Equity,
	        Market.USA
        );
		
        public override void Initialize()
        {
	        SetStartDate(Start);
            SetEndDate(End);
            SetCash(100000);

            AddEquity(UnderlyingSymbol, Resolution.Daily, Market.USA, false);
            Option option = AddOption(UnderlyingSymbol, Resolution.Minute, Market.USA, false);
            
            //Grab OTM Puts for the current weekly expiration
	        option.SetFilter(u => u
			        .PutsOnly()
			        .Expiration(TimeSpan.Zero, TimeSpan.FromDays(7))
			        .WeeklysOnly()
	            	.Strikes(Int32.MaxValue, -5)
            );
	        
	        DefaultOrderProperties.TimeInForce = TimeInForce.Day; //Cancel all orders at end of day
	        
	        Schedule.On(DateRules.EveryDay(UnderlyingSymbol), TimeRules.AfterMarketOpen(UnderlyingSymbol, -1), AtStartOfDayHandler); // 9:29 AM
	        Schedule.On(DateRules.EveryDay(UnderlyingSymbol), TimeRules.Every(TimeSpan.FromMinutes(60)), PeriodicCycleHandler);
        }

        private void PeriodicCycleHandler(string arg1, DateTime arg2)
        {
	        Transactions.CancelOpenOrders();
        }

        public override void OnData(Slice slice)
        {
	        if (!Portfolio.Invested)
	        {
		        OptionChain chain;
		        if (CurrentSlice.OptionChains.TryGetValue(OptionSymbol, out chain))
		        {
			        IEnumerable<OptionContract> currentWeeklies = chain
				        .Where(oc => oc.Expiry < CurrentSlice.Time.AddDays(2));
			        foreach (var optionContract in currentWeeklies)
			        {
				        if(IsMarketOpen(optionContract.Symbol))
							MarketOrder(optionContract.Symbol, 1);
			        }
		        }
	        }
        }

        private void AtStartOfDayHandler()
        {
	        // Check if any positions are expired but somehow left over
	        IEnumerable<Option> expiredPos = Portfolio.Where(kvp => kvp.Value.Invested)
	            .Select(kvp => Securities[kvp.Key] as Option)
	            .Where(opt => opt != null)
	            .Where(opt => opt.Expiry.Date < CurrentSlice.Time.Date);
            if(expiredPos.Any())
            {
	            string msg = string.Join(", ", expiredPos.Select(option => $"{option.Symbol}, {Portfolio[option.Symbol].Quantity}, {Securities[option.Symbol].Price}"));
	            Quit($"There are expired holdings on {CurrentSlice.Time.Date.ToShortDateString()} -> {msg}");
            }
        }
    }
}