Overall Statistics
Total Trades
7
Average Win
0%
Average Loss
0%
Compounding Annual Return
-50.527%
Drawdown
4.300%
Expectancy
0
Net Profit
-3.152%
Sharpe Ratio
-4.31
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
1.93
Beta
-149.612
Annual Standard Deviation
0.142
Annual Variance
0.02
Information Ratio
-4.417
Tracking Error
0.142
Treynor Ratio
0.004
Total Fees
$5.25
import numpy as np
import pandas as pd
from datetime import timedelta

### <summary>
### Basic template algorithm simply initializes the date range and cash. This is a skeleton
### framework you can use for designing an algorithm.
### </summary>
# Custom slippage implementation
class CustomSlippageModel:
    def GetSlippageApproximation(self, asset, order):
        return np.float( asset.Price ) * 0.002

class BasicTemplateAlgorithm(QCAlgorithm):
    '''Basic template algorithm simply initializes the date range and cash'''

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        self.SetStartDate(2017,1,1)
        self.SetEndDate(2017,2,5) 
        self.SetCash(30000)
        
        self.stocks = ["NUGT","DUST"]
        
        self.should_trade = False
        self.order_tickets = []
        self.open_trades = {}
        self.nearest_option = 27.0
        self.sell_with_days_left = 5.0
        self.layers = 4.0
        self.lev = 0.80
        
        self.short_call = True
        self.long_put = False
        self.short_put = False
        self.long_call = False
        
        self.weights = {}
        for stock in self.stocks:
            self.weights[stock] = 1.0/np.float(len(self.stocks))/self.layers
        
        for stock in self.stocks:
            self.AddEquity(stock, Resolution.Minute)
            self.Securities[stock].SetDataNormalizationMode(DataNormalizationMode.Raw)
            self.Securities[stock].SetSlippageModel(CustomSlippageModel())
            option = self.AddOption( stock, Resolution.Minute)
            # option.SetFilter(lambda u: u.IncludeWeeklys().Strikes(+3,+5).Expiration(timedelta(self.nearest_option), timedelta(30))) 
            option.SetFilter(self.UniverseFunc)
        
        
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.stocks[0], 10), Action(self.activate))
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.stocks[0], 6), Action(self.deactivate))

    def UniverseFunc(self, universe):
        return universe.IncludeWeeklys().Strikes(+3, +5).Expiration(timedelta(self.nearest_option), timedelta(45))

    def activate(self):
        self.should_trade = True
        
    def deactivate(self):
        self.should_trade = False

    def get_stat( self, hist ):
        '''converts to log returns and get avereage return per time point, then sums'''
        for stock in self.stocks:
            if stock not in hist.index:
                return False
        hist1 = hist.loc[ self.stocks[0] ].close
        hist2 = hist.loc[ self.stocks[1] ].close
        
        new_guy =pd.DataFrame( { self.stocks[0]: hist1, self.stocks[1]: hist2 } )
        lr = np.diff( np.log( new_guy ), axis=0 )
        w = np.array( [ self.weights[ self.stocks[0] ], self.weights[ self.stocks[1] ] ] )
        avg_lr = np.sum( lr * w, axis=1 )
        stats = np.sum( avg_lr )
        
        return stats

    # Override the base class event handler for order events
    # def OnOrderEvent(self, orderEvent):
    #     order = self.Transactions.GetOrderById(orderEvent.OrderId)
    #     self.Debug("{0}: {1}: {2}".format(self.Time, order.Type, orderEvent))

    def OnData(self, data):
        if self.should_trade:
            nav = np.float(self.Portfolio.TotalPortfolioValue)
            trade_data = {}
            for stock in self.stocks:
                trade_data[stock] = {}
            
            #
            #calculate # of shares/dollars we want to trae based on leverage number
            for stock in self.stocks:
                price_stock = np.float( data[ stock ].Price )
                wanted_shares = -np.round( ( nav * self.lev * self.weights[ stock ] ) / price_stock )
                current_shares = np.float( self.Portfolio[ stock ].Quantity )
                trade_data[ stock ]['dollars_to_trade'] = ( wanted_shares - current_shares ) * price_stock
                trade_data[ stock ]['shares_to_trade'] = wanted_shares - current_shares
                trade_data[ stock ]['price'] = price_stock
            #
            #make stats about history of price movement of underlyings
            # stats = self.get_stat( self.History(self.stocks, 10, Resolution.Minute) )

            # if stats > 0.00:
            #     make_trade = True
            # else:
            #     make_trade = False

            # if make_trade == False:
            #     return
            make_trade = True
                
            #check current option holdings
            #do not hold options to expiration
            option_holding_underlyings = []
            options_to_close = []
            for sym in self.Portfolio.Keys:
                holding = self.Portfolio[sym]
                if holding.HoldStock and holding.Symbol.SecurityType == SecurityType.Option:
                    self.Log( "HOLDING: " + str(holding.Symbol) )
                    info = self.open_trades[ holding.Symbol ]
                    options_to_close.append([sym.Value, holding.Quantity])
            
            options = {}
            for stock in self.stocks:
                options[stock] = { 'put': None, 'call': None, 'qnty': None }
            
            for i in data.OptionChains:
                optionchain = list( i.Value )
                
                if len(optionchain) == 0:
                    continue
                
                #get underlying symbol and last price 
                
                try:
                    usym = str( optionchain[0].UnderlyingSymbol )
                    upric = float( optionchain[0].UnderlyingLastPrice )
                except:
                    return
                
                # create a data frame to show the filtered contracts
                # type(call 0, put 1)
                df = pd.DataFrame([[ x.Right, np.float(x.Strike), x.Expiry, np.float(x.BidPrice), np.float(x.AskPrice), x.Symbol] for x in optionchain ],
                                  index=[ x.Symbol.Value for x in optionchain ],
                                  columns=[ 'type', 'strike', 'expiry', 'ask', 'bid', 'Symbol' ])
                
                # try to close trades here with market data
                for opt in options_to_close:
                    self.Log("got a trade to close")
                    self.Log( opt[0] )
                    
                    if opt[0] in df.index:
                        self.Log("looking at closing this")
                        p = df.loc[ opt[0] ]
                        qnty = opt[1]
                        self.Log( str( p ) )
                        
                        if int( ( p['expiry'] - data.Time ).days ) <= self.sell_with_days_left:
                            self.order_tickets.append( self.LimitOrder(p['Symbol'], -qnty, np.around( ( p['bid'] + p['ask'] ) / 2, 2) ) )
                        elif int( ( p['expiry'] - data.Time ).days ) > self.nearest_option - (self.nearest_option - self.sell_with_days_left)/self.layers:
                            if qnty > 0:
                                dir = "long"
                            else:
                                dir = "short"
                            
                            if p['type'] == 1:
                                option_holding_underlyings.append( [str(holding.Symbol.Underlying), "put", dir] )
                            else:
                                option_holding_underlyings.append( [str(holding.Symbol.Underlying), "call", dir] )
                    self.Log("----")
                        
                
                df = df.loc[ ( df['expiry'] > data.Time + timedelta(days=self.nearest_option) ) ]
                
                self.Log(usym+" "+str(upric))
                
                #get put
                puts = df.loc[ df['type'] == 1 ]
                
                # #get call
                calls = df.loc[ df['type'] == 0 ]
                
                if len( calls.index ) < 1 or len( puts.index ) < 1:
                    continue
                
                options[ usym ]['call'] = calls.sort_values(['expiry','strike'], ascending=[True, False]).iloc[0]
                options[ usym ]['call2'] = calls.sort_values(['expiry','strike'], ascending=[False, False]).iloc[0]
                options[ usym ]['put'] = puts.sort_values(['expiry','strike'], ascending=[True, True]).iloc[0]
                options[ usym ]['qnty'] = np.round( self.lev * nav * self.weights[ usym ] / np.float( np.mean([options[ usym ]['call'].strike, options[ usym ]['put'].strike]) ) / 100 )
            
                
            #only trade if we have all the data
            
            for stock in self.stocks:
                if options[ stock ]['call'] is None or options[ stock ]['put'] is None or options[ stock ]['qnty'] is None:
                    return
            
            for stock in set(self.stocks):
                if [stock,'put','long'] not in option_holding_underlyings and self.long_put == True:
                    p = options[ stock ]['put']
                    qnty = options[ stock ]['qnty']
                if [stock,'put','short'] not in option_holding_underlyings and self.short_put == True:
                    p = options[ stock ]['put']
                    qnty = -options[ stock ]['qnty']
                if [stock,'call','short'] not in option_holding_underlyings and self.short_call == True:
                    p = options[ stock ]['call']
                    qnty = -options[ stock ]['qnty']
                # if [stock,'call','long'] not in option_holding_underlyings:
                #     c = options[ stock ]['call2']
                #     self.MarketOrder( c['Symbol'] , options[ stock ]['qnty'] )
                #     self.open_trades[ c['Symbol'] ] = c
                else:
                    continue
                self.Log("making that limir order")
                self.order_tickets.append( self.LimitOrder(p['Symbol'], qnty, np.around( p['bid'], 2) ) )
                self.open_trades[ p['Symbol'] ] = p

            self.should_trade = False