Overall Statistics
Total Trades
10
Average Win
1.27%
Average Loss
-1.70%
Compounding Annual Return
12.202%
Drawdown
1.700%
Expectancy
0.311
Net Profit
2.957%
Sharpe Ratio
2.94
Loss Rate
25%
Win Rate
75%
Profit-Loss Ratio
0.75
Alpha
0.092
Beta
-0.012
Annual Standard Deviation
0.031
Annual Variance
0.001
Information Ratio
0.816
Tracking Error
0.129
Treynor Ratio
-7.846
Total Fees
$10.00
from datetime import datetime, timedelta
from decimal import Decimal
from QuantConnect.Securities.Option import OptionPriceModels

class BasicTemplateOptionsAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetEndDate(2015, 3, 31)
        self.SetCash(1000000)
        equity = self.AddEquity("SPY", Resolution.Minute)
        option = self.AddOption("SPY", Resolution.Minute)
        self.symbol = option.Symbol
        self.SetWarmup(TimeSpan.FromDays(5))
        
        option.PriceModel = OptionPriceModels.CrankNicolsonFD()

        # set our strike/expiry filter for this option chain
        option.SetFilter(-3, +3, timedelta(28), timedelta(39))
        # use the underlying equity as the benchmark
        self.SetBenchmark(equity.Symbol)
        self.put = "SPY" # Initialize the put contract
        self.call = "SPY" # Initialize the call contract
        self.expiration = datetime.now() + timedelta(28)
        
    def OnData(self,slice):
        if not self.Portfolio[self.put].Invested and not self.Portfolio[self.call].Invested and self.Time.hour != 0 and self.Time.minute == 02:
#            self.Log("Reached OnData 1")
            self.TradeOptions(slice) # sell the options
        if ((slice.Time + timedelta(2)) > self.expiration) and self.Time.hour != 0 and self.Time.minute == 01 and self.Portfolio[self.put].Invested and self.Portfolio[self.call].Invested:
            self.Buy(self.put, self.quantity_put) # buy back the put options
            self.Buy(self.call, self.quantity_call) # buy back the call options
        else: return
#            self.Log("1 Date: {0}".format(datetime.now() + timedelta(21)))
#            self.Log("2 Date: {0}".format(self.expiration))
#            self.Log("3 Date: {0}".format(((datetime.now() + timedelta(21)) > self.expiration)))
        # if the option contract expires, print out the price and position information      
#        if slice.Delistings.Count > 0:
#            if [x.Key == self.put for x in slice.Delistings]:
#                self.Log("stock SPY quantity: {0}".format(self.Portfolio["SPY"].Quantity))
#                self.Log("{0} quantity: {1}".format(self.put.Value, self.Portfolio[self.put].Quantity))
#                self.Log("The stock price at Expiry S(T): {}".format(self.Securities["SPY"].Price))

    def TradeOptions(self,slice):
        if slice.OptionChains.Count == 0: return   
        for i in slice.OptionChains:
#            self.Log("Reached TradeOptions 1")
            if i.Key != self.symbol: continue
#            self.Log("Reached TradeOptions 1.5")
            chain = i.Value
            underlying_price = Decimal(chain.Underlying.Price)
            put_point = Decimal(-0.5)
            call_point = Decimal(0.5)
            put = [x for x in chain if x.Right == 1] # filter the put options contracts
            # sorted the contracts according to their expiration dates and choose the ATM options
            put_contracts = sorted(sorted(put, key = lambda x: abs(put_point - x.Greeks.Delta)), 
                                            key = lambda x: x.Expiry, reverse=True)
#            self.Log("put contracts: {0}, {1}, {2}, {3}, {4}, {5}".format(put_contracts[0], put_contracts[1], put_contracts[2], put_contracts[3], put_contracts[4], self.Securities["SPY"].Price))
            if len(put_contracts) == 0: return
#            self.Log("Reached TradeOptions 2")
            put_contract = put_contracts[0] 
            put_delta = put_contract.Greeks.Delta
            put_iv = put_contract.ImpliedVolatility
            self.put = put_contract.Symbol
            call = [x for x in chain if x.Right == 0] # filter the call options contracts
            # sorted the contracts according to their expiration dates and choose the ATM options
            call_contracts = sorted(sorted(call, key = lambda x: abs(call_point - x.Greeks.Delta)), 
                                            key = lambda x: x.Expiry, reverse=True)
#            self.Log("call contracts: {0}, {1}, {2}, {3}, {4}, {5}".format(call_contracts[0], call_contracts[1], call_contracts[2], call_contracts[3], call_contracts[4], self.Securities["SPY"].Price))
            if len(call_contracts) == 0: return  
#            self.Log("Reached TradeOptions 3")
            call_contract = call_contracts[0]
            call_delta = call_contract.Greeks.Delta
            call_iv = call_contract.ImpliedVolatility
            self.call = call_contract.Symbol
            self.quantity_put = int(self.Portfolio.TotalPortfolioValue / put_contract.Strike / 100)
            self.quantity_call = int(self.Portfolio.TotalPortfolioValue / call_contract.Strike / 100)
            self.Sell(self.put, self.quantity_put) # short the put options
            self.Sell(self.call, self.quantity_call) # short the call options
#            self.Log("Correct Strike vs Current Strike: {0}, {1}".format(self.Securities["SPY"].Price, call_contract.Strike))
            self.Log("Put/Call Delta: {0}, {1}".format(put_delta, call_delta))
            self.Log("Put/Call IV: {0}, {1}".format(put_iv, call_iv))
            self.expiration = put_contract.Expiry
#            self.Log("Reached TradeOptions 4")
  #          if self.Portfolio["SPY"].Quantity == 0:
  #              self.Buy("SPY",1)     # buy 100 the underlying stock
  #              self.Log("The stock price at time 0 S(0): {}".format(self.Securities["SPY"].Price))