| 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