Overall Statistics
Total Trades
446
Average Win
0.20%
Average Loss
-0.29%
Compounding Annual Return
-24.983%
Drawdown
7.100%
Expectancy
-0.072
Net Profit
-5.834%
Sharpe Ratio
-3.465
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
0.68
Alpha
-0.246
Beta
0.152
Annual Standard Deviation
0.065
Annual Variance
0.004
Information Ratio
-2.904
Tracking Error
0.126
Treynor Ratio
-1.471
Total Fees
$426.00
from clr import AddReference
from QuantConnect.Securities.Option import OptionPriceModels
import decimal as d
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
import numpy as np
from datetime import timedelta


class My1(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 6, 1)
        self.SetEndDate(2019, 8, 15)
        self.SetCash(25000)
        self.date = None
        # add the underlying asset
        self.stock= "SPY"
        self.equity = self.AddEquity(self.stock, Resolution.Minute)
        self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        option = self.AddOption(self.stock)
        option.SetFilter(lambda universe: universe.IncludeWeeklys().Strikes(-5, +5).Expiration(timedelta(0), timedelta(7)))
        # for greeks and pricer (needs some warmup) - https://github.com/QuantConnect/Lean/blob/21cd972e99f70f007ce689bdaeeafe3cb4ea9c77/Common/Securities/Option/OptionPriceModels.cs#L81
        option.PriceModel = OptionPriceModels.CrankNicolsonFD()  # both European & American, automatically
        # this is needed for Greeks calcs
        self.SetWarmUp(TimeSpan.FromDays(3))    # timedelta(7)
        self.contract = str()
        self.contractsAdded = set()
        self.delta_treshold = 0.1
        self.Delta = 0.0


    def OnData(self, data):
        
        if self.Time.date != self.date:
            self.date = self.Time.date
            for chain in data.OptionChains.Values:
            # sort contracts to find at the money (ATM) contract with the farthest expiration
                contracts = sorted(sorted(chain, 
                                   key = lambda x: abs(chain.Underlying.Price - x.Strike)), \
                                   key = lambda x: x.Expiry, reverse=True)
                                   
                expiry = sorted(contracts,key = lambda x: x.Expiry, reverse=True)[0].Expiry
                #self.Debug("best expiry is" + str(expiry))
                calls = [i for i in contracts if i.Expiry == expiry and i.Right == 0 and i.Strike > chain.Underlying.Price]
                            # sorted the contracts according to their strike prices 
                call_contracts = sorted(calls,key = lambda x: x.Strike)    
                if len(call_contracts) == 0: continue
                # choose the deep OTM call option
                self.call = call_contracts[0]
                # select the put options which have the same expiration date with the call option 
                puts = [i for i in contracts if i.Expiry == expiry and i.Right == 1 and i.Strike < chain.Underlying.Price]
                # sort the put options by strike price
                put_contracts = sorted(puts, key = lambda x: x.Strike)
                # choose the deep OTM put option
                self.put = put_contracts[-1]
                if not self.Portfolio.Invested:
                    #self.Transactions.CancelOpenOrders("SPY")
                    self.Debug("------------------------------- price is " + str(self.Securities[self.stock].Close))
                    self.Debug("Trade: Call " + str(self.call.Strike) + " " + str(self.call.Expiry))
                    self.Debug("Trade: Put " + str(self.put.Strike) + " " + str(self.put.Expiry))        
                    self.Buy(self.call.Symbol ,1)
                    self.Buy(self.put.Symbol ,1)
    
                    #self.StopMarketOrder(self.stock, 50, self.call.Strike)
                    #self.StopMarketOrder(self.stock, -50, self.put.Strike)
                else:
                    #if  self.Time.minute == 59:
                    self.get_greeks(data)
                    self.doDeltaHedge()
                 
                        
    def doDeltaHedge(self):
        fut = self.Portfolio[self.stock].Quantity
        #self.Debug("+++ time: "+ str(self.Time) +" delta is " + str(self.Delta) + " fut pos: " + str(fut))
        if abs(self.Delta) > self.delta_treshold:
            needfut = round(self.Delta*-100)
            self.MarketOrder(self.stock,needfut)
            self.Debug(str(self.Time) + " delta_hedging: self.Delta {}  add fut contracts: {}" .format(self.Delta, needfut))
            
  
                    
    def get_greeks(self, slice):

            sumdelta = 0.00
            symbols = set()
            for sym in self.Portfolio:
                symbols.add(sym.Value.Symbol)
                opt = self.Securities[sym.Value.Symbol]
                

            for kvp in slice.OptionChains:
                chain = kvp.Value   # option contracts for each 'subscribed' symbol/key 
                traded_contracts = filter(lambda x: x.Symbol in symbols, chain)
                                           
            #if not traded_contracts: self.Log("No traded cointracts"); return
                    
            deltas = [i.Greeks.Delta*self.Portfolio[i.Symbol].Quantity for i in traded_contracts]

            sumdelta=sum(deltas)
            futpos = self.Portfolio[self.stock].Quantity
    
            self.Delta=sumdelta + futpos/100