Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return 1.941% Drawdown 8.500% Expectancy 0 Net Profit 2.139% Sharpe Ratio 0.295 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.013 Beta 0.363 Annual Standard Deviation 0.059 Annual Variance 0.003 Information Ratio 0.055 Tracking Error 0.1 Treynor Ratio 0.048 Total Fees $1.00 |
# Your New Python File # def butterflyspread(self,slice): # for kvp in slice.OptionChains: # if kvp.Key != self.symbol: continue # chain = kvp.Value # option contracts for each 'subscribed' symbol/key # sorted the optionchain by expiration date and choose the furthest date # expiry = sorted(chain,key = lambda x: x.Expiry, reverse=True)[0].Expiry # filter the call options from the contracts expires on that date # call = [i for i in chain if i.Expiry == expiry and i.Right == 0] # sorted the contracts according to their strike prices # call_contracts = sorted(call,key = lambda x: x.Strike) # if len(call_contracts) == 0: continue # choose OTM call # self.otm_call = call_contracts[-1] # choose ITM call # self.itm_call = call_contracts[0] # choose ATM call # self.atm_call = sorted(call_contracts,key = lambda x: abs(chain.Underlying.Price - x.Strike))[0] # if not self.otm_call: return # if not self.itm_call: return # if not self.atm_call: return # self.Sell(self.atm_call.Symbol ,2) # self.Buy(self.itm_call.Symbol ,1) # self.Buy(self.otm_call.Symbol ,1) # self.Debug('buy 1 otm call option %s at price %s on date %s' % (self.otm_call.Symbol, self.otm_call.LastPrice, self.Time.date())) # self.Debug('buy 1 itm call option %s at price %s on date %s' % (self.itm_call.Symbol, self.itm_call.LastPrice, self.Time.date())) # self.Debug('sell 2 atm call option %s at price %s on date %s' % (self.atm_call.Symbol, self.atm_call.LastPrice, self.Time.date()))
# Your New Python File import numpy as np from datetime import datetime from datetime import timedelta import decimal import time import pandas as pd from QuantConnect.Algorithm import * from QuantConnect.Data import * ### <summary> ### Basic template algorithm simply initializes the date range and cash. This is a skeleton ### framework you can use for designing an algorithm. ### </summary> class VIX_SPY_OPTIONS(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.SetStartDate(2018,1,3) #Set Start Date self.SetEndDate(2019,2,8) #Set End Date self.SetCash(100000) #Set Strategy Cash self.vix = 'CBOE/VIX' self.spy = "SPY" # self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) equity = self.AddEquity("SPY", Resolution.Minute) equity.SetDataNormalizationMode(DataNormalizationMode.Raw) self.underlyingsymbol = equity.Symbol self.options_profits = 0 self.option_invested = None # Add Quandl VIX price (daily) vix_symbol = self.AddData(QuandlVix, "CBOE/VIX", Resolution.Daily) option = self.AddOption("SPY", Resolution.Minute) self.option_symbol = option.Symbol self.Debug("numpy test >>> print numpy.pi: " + str(np.pi)) self.date_first_level = (self.Time) self.date_second_level = self.Time self.date_third_level = self.Time self.date_four_level = self.Time self.last_transaction_date = self.Time.date() self.buy_first_level = None self.buy_second_level = None self.buy_third_level = None self.buy_four_level = None self.option_invested= False self.number_of_spy_transactions = 0 option.SetFilter(-30, +30, timedelta(0), timedelta(90)) # Warm up period self.SetWarmUp(TimeSpan.FromDays(7)) # Add differents EMA for SPY self.sma_15m = self.EMA(self.spy, 15, Resolution.Minute) self.sma_60m = self.EMA(self.spy, 60, Resolution.Minute) self.sma_9d = self.EMA(self.spy, 9, Resolution.Daily) self.sma_14d = self.EMA(self.spy, 14, Resolution.Daily) self.sma_20d = self.EMA(self.spy, 20, Resolution.Daily) self.sma_50d = self.EMA(self.spy, 50, Resolution.Daily) self.sma_100d = self.EMA(self.spy, 100, Resolution.Daily) self.sma_200d = self.EMA(self.spy, 200, Resolution.Daily) self.sma_365d = self.EMA(self.spy, 365, Resolution.Daily) self.SetBenchmark("SPY") self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(minutes=120)), self.LiquidateUnrealizedProfits) def OnData(self, slice): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data ''' if self.IsWarmingUp: return if (self.Securities[self.vix].Price >= 18) and (self.Securities[self.vix].Price < 25) and (self.Time >= self.date_first_level) and not (self.buy_second_level or self.buy_third_level or self.buy_four_level): #self.Debug('Buying Power %s' % self.Portfolio.GetBuyingPower('SPY', OrderDirection.Hold)) #self.Debug('GetBuyingPower Before:%s ' % self.Portfolio.GetBuyingPower("SPY", OrderDirection.Buy)) # Calculate the order quantity to achieve target-percent holdings. #self.Debug('CalculateOrderQuantity:%s ' % self.CalculateOrderQuantity("SPY", 0.25)) self.Debug('Buy at level I, VIX price is %s on date %s SPY price is %s' % (self.Securities[self.vix].Price, self.Time.date(),self.Securities[self.spy].Price)) self.Debug('SPY: 50, 100, 200 , 365 EMAs are %s %s %s %s' % (self.sma_50d, self.sma_100d,self.sma_200d,self.sma_365d)) # Get the quantity, the limit price and finally send a limit order self.quantity = int((self.Portfolio.Cash * 0.40) / self.Securities[self.spy].Price) if self.quantity > 0: self.Debug('Quantity to buy %s' % self.quantity) self.limit_price = self.find_support(slice) self.LimitOrder("SPY", self.quantity,self.limit_price) # Run the strike_selection function to sell Call and Puts contracts based on the Strike prices self.strike_selection(slice) # Define self.date_first_level to buy again in this level after 1 day if the VIX price is between this price interval self.date_first_level = self.Time + timedelta(days=2) self.last_transaction_date = self.Time.date() self.number_of_spy_transactions +=1 elif self.Securities[self.vix].Price > 25 and self.Securities[self.vix].Price < 30 and (self.Time >= self.date_second_level) and not (self.buy_third_level or self.buy_four_level): self.Debug('Buy at level II, VIX price is %s on date %s SPY price is %s' % (self.Securities[self.vix].Price,self.Time.date(),self.Securities[self.spy].Price)) self.Debug('SPY 50, 100, 200 , 365 EMAs are %s %s %s %s' % (self.sma_50d, self.sma_100d,self.sma_200d,self.sma_365d)) # Get the quantity, the limit price and finally send a limit order self.quantity = int((self.Portfolio.Cash * 0.30) / self.Securities[self.spy].Price) self.Debug('Quantity to buy %s' % self.quantity) self.limit_price = self.find_support(slice) self.LimitOrder("SPY", self.quantity,self.limit_price) # Run the strike_selection function to sell Call and Puts contracts based on the Strike prices self.strike_selection(slice) # Define the variable self.date_second_level to buy after 1 day in the same level and the variable self.buy_second_level to prevent purchases # in the previous level self.date_second_level = self.Time + timedelta(days=2) self.buy_second_level = True self.last_transaction_date = self.Time.date() self.number_of_spy_transactions +=1 elif self.Securities[self.vix].Price > 31 and self.Securities[self.vix].Price < 36 and (self.Time >= self.date_third_level) and not self.buy_four_level: self.Debug('Buy at level III, VIX price is %s on date %s SPY price is %s' % (self.Securities[self.vix].Price,self.Time.date(),self.Securities[self.spy].Price)) self.Debug('SPY 50, 100, 200 , 365 EMAs are %s %s %s %s' % (self.sma_50d, self.sma_100d,self.sma_200d,self.sma_365d)) # Get the quantity, the limit price and finally send a limit order self.quantity = int((self.Portfolio.Cash * 0.30) / self.Securities[self.spy].Price) self.Debug('Quantity to buy %s' % self.quantity) self.limit_price = self.find_support(slice) self.LimitOrder("SPY", self.quantity,self.limit_price) # Run the strike_selection function to sell Call and Puts contracts based on the Strike prices self.strike_selection(slice) # Define the variable self.date_third_level to buy after 1 day in the same level and the variable self.buy_third_level to prevent purchases # at previous levels self.date_third_level = self.Time + timedelta(days=2) self.buy_third_level = True self.number_of_spy_transactions +=1 self.last_transaction_date = self.Time.date() elif self.Securities[self.vix].Price >= 37 and self.Securities[self.vix].Price < 45 and (self.Time > self.date_four_level): self.Debug('Buy at level IV, VIX price is %s on date %s SPY price is %s' % (self.Securities[self.vix].Price,self.Time.date(),self.Securities[self.spy].Price)) self.Debug('SPY 50, 100, 200 , 365 EMAs are %s %s %s %s' % (self.sma_50d, self.sma_100d,self.sma_200d,self.sma_365d)) # Get the quantity, the limit order price and finally send a limit order self.quantity = int((self.Portfolio.Cash * 0.40) / self.Securities[self.spy].Price) self.Debug('Quantity to buy %s' % self.quantity) self.limit_price = self.find_support(slice) self.LimitOrder("SPY", self.quantity,self.limit_price) # Run the strike_selection function to Sell Put and Call contracts based on the Strike price self.strike_selection(slice) # Define the variable self.date_four_level to buy after 1 day in the same level and the variable self.buy_four_level to prevent purchases # at previous levels self.date_four_level = self.Time + timedelta(days=1) self.buy_four_level = True self.last_transaction_date = self.Time.date() self.number_of_spy_transactions +=1 #days_without_purchases = (self.Time.date() - self.last_transaction_date).days #if days_without_purchases > 21 and self.Securities[self.vix].Price <=17: # # Reset all levels if no SPY purchases during 30 days # self.Debug('Reset all levels to buy') # self.buy_first_level = None # self.buy_second_level = None # self.buy_third_level = None # self.buy_four_level = None if self.Time.hour == 15 and self.Time.minute == 30: option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option] self.Debug('date %s vix price is %s' % (self.Time.date(), self.Securities[self.vix].Price)) # self.Debug('Total profits for options trading is %s' % self.options_profits) if self.Portfolio.Invested: self.Debug(self.Time.date()) #self.Debug('Portfolio Margin Remaining %s' % self.Portfolio.MarginRemaining) #self.Debug('Total Portfolio Value %s' % self.Portfolio.TotalPortfolioValue) #self.Debug('Buying Power %s' % self.Portfolio.GetBuyingPower('SPY', OrderDirection.Hold)) #self.Debug('GetBuyingPower Before:%s ' % self.Portfolio.GetBuyingPower("SPY", OrderDirection.Buy)) #self.Debug('CalculateOrderQuantity:%s ' % self.CalculateOrderQuantity("SPY", 0.25)) self.Debug('Holdings of SPY invested in portfolio are %s' % round(self.Portfolio[self.underlyingsymbol].AbsoluteHoldingsCost,2)) #round(self.Portfolio["SPY"].AbsoluteHoldingsCost, 2)) self.Debug('Buying Power %s' % self.Portfolio.GetBuyingPower('SPY', OrderDirection.Buy)) #self.Debug('Holdings of option invested in portfolio are %s' % self.Portfolio[self.symbol].AbsoluteHoldingsValue) #round(self.Portfolio[self.symbol].AbsoluteHoldingsCost, 2)) self.Debug('Cash in the portfolio is %s' % self.Portfolio.Cash) self.Debug('Unrealized profits of SPY 500 is %s ' % round(self.Portfolio[self.spy].UnrealizedProfit,2)) #self.Debug('days without purchases are %s' % days_without_purchases) if len(option_invested) > 0: self.Debug('----------------------------------------------------------------------------') self.Debug('number of option contracts in portfolio on date %s is %s' % (self.Time.date(), len(option_invested))) # self.Debug('----------------------------------------------------------------------------') # for contract in option_invested: # underlying = self.Securities[self.spy].Price # quantity = self.Portfolio[contract].Quantity # lastPrice = self.Securities[contract].Price # profits = round(self.Portfolio[contract].UnrealizedProfit,0) # profit_percentage = self.Portfolio[contract].UnrealizedProfitPercent # self.Debug("Contract: " + str(contract) + " - Underlying Price: " + str(underlying) + " - Quantity: " + str(quantity) + " - Last Price: " + str(lastPrice)) # #self.Debug("Contract: " + str(contract) + " - Quantity: " + str(quantity) + " - Last Price: " + str(lastPrice)) # self.Debug('Unrealized profits and profit percentage at date %s for contract %s are %s and %s' % (self.Time.date(), contract, profits,"{0:.0%}".format(profit_percentage))) # self.Debug('-------------------------------------------------------------------------------') #self.Debug('Unrealized profits percentage for contract %s is %s' % (contract,round(self.Portfolio[contract].UnrealizedProfitPercent,2))) #self.Debug('----------------------------------------------------------------------------------') def find_support(self,slice): # Get the current price of SPY current_spy = self.Securities[self.spy].Price # Create a numpy array with the different SMA's values sma_array = np.array([self.sma_15m.Current.Value, self.sma_60m.Current.Value,self.sma_9d.Current.Value,self.sma_14d.Current.Value,self.sma_20d.Current.Value,self.sma_50d.Current.Value,self.sma_100d.Current.Value,self.sma_200d.Current.Value,self.sma_365d.Current.Value]) # Get the index of the neareast point in the sma_array to the current price nearest_sma_idx = np.abs(sma_array-current_spy).argmin() nearest_sma = sma_array[nearest_sma_idx] self.Debug('Nearest SMAs array is %s' % nearest_sma) # If the nearest SMA is lower than the current price the limit order price is equal to the nearest_sma if nearest_sma < current_spy: self.limit_order_price = round(nearest_sma,2) # If the nearest sma is above current price, limit price is equal current price else: self.limit_order_price = current_spy self.Debug('limit order price is %s current price is %s' % (self.limit_order_price, current_spy)) return self.limit_order_price def strike_selection(self,slice): # Loop over the Option chain if (slice.OptionChains.Count == 0): self.Debug('There are not options contracts in this chain at date %s' % self.Time.date()) for kvp in slice.OptionChains: if kvp.Key != self.option_symbol: continue chain = kvp.Value contract_list = [x for x in chain] #self.Debug('length contract list is %s' % len(contract_list)) # If there is no optionchain or no contracts in this optionchain, pass the instance if (slice.OptionChains.Count == 0) or len(contract_list) == 0: return self.Debug('First contracts from contract_list are %s' % contract_list[0:3]) # Filter the call and put options from the chain call = [i for i in chain if i.Right == 0] # Select call contracts with difference between Strike and Underlying price greater or equal than 7% and with a difference between expiration date and actual date greater or equal than 60 days calls = [x for x in call if ((x.Strike - x.UnderlyingLastPrice)/x.UnderlyingLastPrice) >=0.04 and (x.Expiry.date() - self.Time.date()).days >=60 ] # Order call contracts by Strike price, so the first one is the contract with the less Strike price calls = sorted(calls, key=lambda x : x.Strike) strikes_calls_list = [x.Strike for x in calls] # If there are any call contracts that meet above conditions, pass if len(calls) == 0: return # Select the first call contract from the calls list call_contract = calls[0] #self.Debug('call contract is %s with Strike Price %s underlying Price %s and expiration %s current date %s' % (call_contract, call_contract.Strike, call_contract.UnderlyingLastPrice, call_contract.Expiry.date(), self.Time.date())) #self.Debug('list of calls strike prices is %s' % strikes_calls_list) put = [i for i in chain if i.Right == 1] # Select put contracts with difference between Strike and Underlying price greater or equal than -7% and with a difference between expiration date and actual date greater or equal than 60 days puts = [x for x in put if ((x.Strike - x.UnderlyingLastPrice)/x.UnderlyingLastPrice) >=-0.06 and (x.Expiry.date() - self.Time.date()).days >=60 ] # Order put contracts by Strike price, so the first one is the contract with the less Strike price puts = sorted(puts, key=lambda x : x.Strike) strikes_puts_list = [x.Strike for x in puts] # If there are any put contracts that meet above conditions, pass if len(puts) == 0: return put_contract = puts[0] #self.Debug('put contract is %s with Strike Price %s underlying Price %s and expiration %s current date %s' % (put_contract, put_contract.Strike, put_contract.UnderlyingLastPrice, put_contract.Expiry.date(), self.Time.date())) #self.Debug('list of puts strike prices is %s' % strikes_puts_list) # Calculate the number of contracts to Sell based in the self.quantity variable defined in OnData function number_of_contracts = int(np.ceil(self.quantity/100)) self.Debug('Number of call/put contracts to sell is %s' % number_of_contracts) self.call_contract = call_contract.Symbol self.put_contract = put_contract.Symbol # Sell put and call contracts self.Sell(self.call_contract,number_of_contracts) self.Sell(self.put_contract, number_of_contracts) self.option_invested = True def LiquidateUnrealizedProfits(self): '''Sell with 5 % of profits''' # Get options in the portfolio with the option_invested list and next get calls options from the portfolio in the calls_options list option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option] calls_options = [x for x in option_invested if x.ID.OptionRight == OptionRight.Call] if self.Portfolio["SPY"].Invested: unrealized_profit_spy = round(self.Portfolio['SPY'].UnrealizedProfitPercent,2) # Liquidate SPY, if there are not call options in the portfolio and the SPY profit is higher than 5% if (len(calls_options) == 0) and (self.Portfolio['SPY'].UnrealizedProfitPercent) >= 0.4: self.Log("Liquidated unrealized profits at: {0}".format(self.Time)) self.Debug("Liquidated unrealized profits at time %s with profit percentage %s" % (self.Time,unrealized_profit_spy)) self.Liquidate('SPY') self.buy_first_level = None self.buy_second_level = None self.buy_third_level = None self.buy_four_level = None if len(option_invested) > 0: for contract in option_invested: quantity = self.Portfolio[contract].Quantity lastPrice = self.Securities[contract].Price unrealized_profits_option = round(self.Portfolio[contract].UnrealizedProfit,2) unrealized_profit_percentage = round(self.Portfolio[contract].UnrealizedProfitPercent,2) if unrealized_profit_percentage >= 99999: self.Liquidate(contract) self.Debug("Liquidate option contract %s with price %s and a profit percentage of %s on date %s" % (contract ,lastPrice, "{0:.0%}".format(unrealized_profit_percentage),self.Time.date())) self.buy_first_level = None self.buy_second_level = None self.buy_third_level = None self.buy_four_level = None self.options_profits += unrealized_profits_option elif self.Time >= contract.ID.Date: self.Liquidate(contract) self.Debug("Liquidate option contract %s at expiration time, in time %s with quantity %s last price %s and profit percentage %s" % (contract, self.Time, quantity,lastPrice,"{0:.0%}".format(unrealized_profit_percentage))) self.buy_first_level = None self.buy_second_level = None self.buy_third_level = None self.buy_four_level = None self.buy_fifth_level = None self.options_profits += unrealized_profits_option if len(option_invested) == 0: self.option_invested = False def OnOrderEvent(self, orderEvent): ''' Event when the order is filled. Debug log the order fill. :OrderEvent:''' self.Log(str(orderEvent)) order = self.Transactions.GetOrderById(orderEvent.OrderId) class QuandlVix(PythonQuandl): def __init__(self): self.ValueColumnName = "vix Close"