Overall Statistics
Total Trades
10
Average Win
0%
Average Loss
0%
Compounding Annual Return
11.223%
Drawdown
0.300%
Expectancy
0
Net Profit
0.555%
Sharpe Ratio
7.295
Probabilistic Sharpe Ratio
97.248%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0.113
Beta
0.262
Annual Standard Deviation
0.017
Annual Variance
0
Information Ratio
2.123
Tracking Error
0.04
Treynor Ratio
0.47
Total Fees
$40.50
from QuantConnect.Securities.Option import OptionPriceModels
from datetime import timedelta
import decimal as d
#import pandas as pd

class PutMarginCalc(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 1, 6)
        self.SetEndDate(2017, 1, 24)
        self.SetCash(1000000)
        
        self.Log("PERIOD: JAN 2017")
        
        # ----------------------------------------------------------------------
        # Algo params
        # ----------------------------------------------------------------------
        self.MIN_EXPIRY = 40 # min num of days to expiration => for uni selection
        self.MAX_EXPIRY = 55 # max num of days to expiration => for uni selection
        self.MIN_DELTA = .21 #lower delta = lower risk = less returns
        self.MAX_DELTA = .40 #higher delta = higher risk = more returns
        self._no_K = 25 # no of strikes around ATM => for uni selection
        self.resol = Resolution.Minute   # Resolution.Minute #Options only supports minute currently
        self.tkr = "SPY"
        self.Lev = d.Decimal(1.0)
        self.single_trade_portfolio = .2 #max portfolio value to place one single trade
        self.single_trade_margin = .5 #max margin value to place one single trade
        # add underlying Equity 
        equity = self.AddEquity(self.tkr, self.resol)  
        equity.SetDataNormalizationMode(DataNormalizationMode.Raw) # IMPORTANT: default
        self.equity_symbol = equity.Symbol
        
        # Add options
        option = self.AddOption(self.tkr, self.resol)
        option.SetDataNormalizationMode(DataNormalizationMode.Raw) # IMPORTANT: default
        self.option_symbol = option.Symbol

        # filter options
        option.SetFilter(self.UniverseFunc) # option.SetFilter(-2, +2, timedelta(0), timedelta(30))

        #seed greeks
        option.PriceModel = OptionPriceModels.CrankNicolsonFD()  # both European & American, automatically
        self.SetWarmUp(TimeSpan.FromDays(7))    # timedelta(7)
        
        self.call, self.put = None, None
   
            
    def OnData(self, slice):
        if self.IsWarmingUp: return
        if not self.HourMinuteIs(10, 1): return ##trade once a day max
        
        
        self._assignedOption = False

        if not self.Portfolio.Invested or self.Portfolio.GetBuyingPower(self.tkr) > self.Portfolio.TotalPortfolioValue * self.single_trade_portfolio:
            
            # select contract
            #self.Log("get contracts")
            self.get_contracts(slice)
            if (not self.put): return
    
            #unit_price = self.put.UnderlyingLastPrice * d.Decimal(100.0)
            #unit_price =  self.Securities[self.equity_symbol].Price * d.Decimal(100.0) 

            unit_price = self.put.Strike * d.Decimal(100.0)
            qty = int((self.Portfolio.GetBuyingPower(self.tkr) * self.single_trade_margin) / unit_price)
            bp = str(self.Portfolio.GetBuyingPower(self.tkr))
            tv = str(self.Portfolio.TotalPortfolioValue)
            mr = str(self.Portfolio.MarginRemaining)
            mu = str(self.Portfolio.TotalMarginUsed)
            cash = str(self.Portfolio.Cash) #settled only
            u_cash = str(self.Portfolio.UnsettledCash) #settled only
            
            #blah = str(self.CalculateOrderQuantity(self.tkr, .2)) #Calculates for buying shares, not contracts

           
            self.Debug("Starting BP: " + bp + " Margin Remaining: " + mr + " Margin Used: " + mu)
            self.Debug("Selling puts: " + str(qty) + "@" + str(self.put.Strike))
            if self.put is not None:  
                order = self.MarketOrder(self.put.Symbol, -qty)
                
            bp = str(self.Portfolio.GetBuyingPower(self.tkr))
            mr = str(self.Portfolio.MarginRemaining)
            mu = str(self.Portfolio.TotalMarginUsed)
            self.Debug("Ending BP: " + bp + " Margin Remaining: " + mr + " Margin Used: " + mu)



    def get_contracts(self, slice):
        
        for kvp in slice.OptionChains:
            if kvp.Key != self.option_symbol: continue
            optionchain = kvp.Value   # option contracts for each 'subscribed' symbol/key 
            puts = [x for x in optionchain if x.Right == 1]
            contracts_by_T = sorted(puts, key = lambda x: x.Expiry, reverse = True)
            if not contracts_by_T: return
            self.expiry = contracts_by_T[0].Expiry.date() # furthest expiry 
            slice_T = [i for i in puts if i.Expiry.date() == self.expiry]
            sorted_contracts = sorted(slice_T, key = lambda x: x.Strike, reverse = False)

            self.put = puts[0] if puts else None 
            #self.Log("found contract: " + str(self.put))


       
    def UniverseFunc(self, universe):
        return universe.IncludeWeeklys()\
                    .Strikes(-self._no_K, self._no_K)\
                    .Expiration(timedelta(self.MIN_EXPIRY), timedelta(self.MAX_EXPIRY))
    
    # ----------------------------------------------------------------------
    # Other ancillary fncts
    # ----------------------------------------------------------------------   
    def OnOrderEvent(self, orderEvent):
    #   self.Log("Order Event -> {}" .format(orderEvent))
        pass

    def TimeIs(self, day, hour, minute):
        return self.Time.day == day and self.Time.hour == hour and self.Time.minute == minute
    
    def HourMinuteIs(self, hour, minute):
        return self.Time.hour == hour and self.Time.minute == minute