# 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,4) #Set Start Date
self.SetEndDate(2019,4,30) #Set End Date
self.SetCash(1000000) #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.Securities["SPY"].MarginModel = PatternDayTradingMarginModel()
self.Portfolio.MarginCallModel = MarginCallModel.Null
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.reset_all_levels = None
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(-20, +20, timedelta(60), 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.BeforeMarketClose("SPY",10), self.LiquidateUnrealizedProfits)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY",10), self.CancelOpenOrders)
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.Portfolio.GetBuyingPower("SPY", OrderDirection.Buy) or self.Portfolio.GetBuyingPower('SPY', OrderDirection.Hold) < 10000:
# self.Debug('Insufficient buying power of %s' % self.Portfolio.GetBuyingPower("SPY", OrderDirection.Buy))
# self.Debug('Insufficient spy hold is %s' % self.Portfolio.GetBuyingPower('SPY', OrderDirection.Hold))
# return
#self.Debug('Portfolio Margin Remaining %s' % self.Portfolio.MarginRemaining)
#if self.Portfolio.MarginRemaining < 10000:
# self.Debug('Portfolio Margin Remaining %s' % self.Portfolio.MarginRemaining)
# return
elif (self.Securities[self.vix].Price >= 19) 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) and self.MarketOpen():
#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('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.TotalPortfolioValue * 0.25) / self.Securities[self.spy].Price)
#self.Debug('Quantity to buy %s' % self.quantity)
self.limitPrice = self.find_support()
lastClose = self.Securities[self.spy].Price
#limitPrice = round(lastClose * decimal.Decimal(.99),2)
newTicket = self.LimitOrder("SPY", self.quantity, self.limitPrice)
# 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=5)
self.last_transaction_date = self.Time.date()
self.number_of_spy_transactions +=1
#if newTicket.Status == OrderStatus.Filled:
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('Limit Order filled at price %s' % newTicket.AverageFillPrice)
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) and self.MarketOpen():
#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.TotalPortfolioValue * 0.30) / self.Securities[self.spy].Price)
#self.Debug('Quantity to buy %s' % self.quantity)
self.limitPrice = self.find_support()
lastClose = self.Securities[self.spy].Price
#limitPrice = round(lastClose * decimal.Decimal(.99),2)
newTicket = self.LimitOrder("SPY",self.quantity, self.limitPrice)
# 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=5)
self.buy_second_level = True
self.last_transaction_date = self.Time.date()
self.number_of_spy_transactions +=1
# Run the strike_selection function to sell Call and Puts contracts based on the Strike prices
self.strike_selection(slice)
#if newTicket.Status == OrderStatus.Filled:
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('Limit Order filled at price %s' % newTicket.AverageFillPrice)
# Run the strike_selection function to sell Call and Puts contracts based on the Strike prices
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 and self.MarketOpen() :
#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.TotalPortfolioValue * 0.35) / self.Securities[self.spy].Price)
#self.Debug('Quantity to buy %s' % self.quantity)
lastClose = self.Securities[self.spy].Price
#limitPrice = round(lastClose * decimal.Decimal(.99),2)
self.limitPrice = self.find_support()
newTicket = self.LimitOrder("SPY", self.quantity,self.limitPrice)
# 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=5)
self.buy_third_level = True
self.number_of_spy_transactions +=1
self.last_transaction_date = self.Time.date()
# Run the strike_selection function to sell Call and Puts contracts based on the Strike prices
self.strike_selection(slice)
#if newTicket.Status == OrderStatus.Filled:
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('Limit Order filled at price %s' % newTicket.AverageFillPrice)
elif self.Securities[self.vix].Price >= 37 and self.Securities[self.vix].Price < 45 and (self.Time > self.date_four_level) and self.MarketOpen():
#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.TotalPortfolioValue * 0.35) / self.Securities[self.spy].Price)
#self.Debug('Quantity to buy %s' % self.quantity)
self.limitPrice = self.find_support()
lastClose = self.Securities[self.spy].Price
limitPrice = round(lastClose * decimal.Decimal(.99))
newTicket = self.LimitOrder("SPY", self.quantity,self.limitPrice)
# 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=5)
self.buy_four_level = True
self.last_transaction_date = self.Time.date()
# Run the strike_selection function to Sell Put and Call contracts based on the Strike price
self.strike_selection(slice)
self.number_of_spy_transactions +=1
#if newTicket.Status == OrderStatus.Filled:
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('Limit Order filled at price %s' % newTicket.AverageFillPrice)
days_without_purchases = (self.Time.date() - self.last_transaction_date).days
if days_without_purchases >= 21 and self.Securities[self.vix].Price <=13 and not self.reset_all_levels:
# # 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
self.reset_all_levels = True
if self.Time.hour == 15 and self.Time.minute == 30:
self.Debug('At date %s' % self.Time.date())
option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
self.Debug('Length of options in Portfolio is %s' % len(option_invested))
self.Debug('Unrealized profits of SPY 500 is %s ' % round(self.Portfolio[self.spy].UnrealizedProfit,2))
self.Debug('Total Portfolio Value %s' % self.Portfolio.TotalPortfolioValue)
self.Debug('Total Holding Value %s' % self.Portfolio.TotalHoldingsValue)
self.Debug('Quantity hold is %s' % self.Portfolio[self.spy].Quantity)
# 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 CancelOpenOrders(self):
openOrders = self.Transactions.GetOpenOrders()
if len(openOrders)> 0:
for x in openOrders:
self.Transactions.CancelOrder(x.Id)
self.Debug('Cancel Open Order %s' % x)
def find_support(self):
#if not self.sma_365d.IsReady:
# return
# 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])
# self.Debug(sma_array)
# 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())
return
for kvp in slice.OptionChains:
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 <=90 ]
# 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,reverse=True)
calls
strikes_calls_list = [x.Strike for x in calls]
# If there are any call contracts that meet above conditions, pass
if len(calls) == 0:
self.Debug('There are not call contracts that meet conditions')
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.04 and (x.Expiry.date() - self.Time.date()).days <=90 ]
# 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:
self.Debug('There are not put contracts that meet conditions')
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]
#call_quantity = []
#for kvp in self.Portfolio:
# if kvp.Value.Type == SecurityType.Option and kvp.Key.ID.OptionRight == OptionRight.Call:
# call_quantity.append(kvp.Value.Quantity)
if self.Portfolio["SPY"].Invested:
#self.Debug('Call quantity sold is %s' % sum(call_quantity))
#quantity_invested = self.Portfolio['SPY'].Quantity
#lots = quantity_invested/100
#option_quantity_sold = sum(call_quantity)
#free_SPY_quantity = int(lots - option_quantity_sold)
#free_ratio = round(free_SPY_quantity / quantity_invested,2)
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%
#self.Debug('Quantity of SPY in portfolio %s' % quantity_invested)
#self.Debug('Free SPY %s' % free_SPY_quantity)
#self.Debug('Free ratio %s' % free_ratio)
#if len(calls_options) > 0 and (self.Portfolio['SPY'].UnrealizedProfitPercent) >= 0.03:
# self.Sell('SPY',free_SPY_quantity)
# self.Debug('Sell free %s quantity of SPY, free ratio is %s' % (free_SPY_quantity,free_ratio))
if (len(calls_options) == 0) and (self.Portfolio['SPY'].UnrealizedProfitPercent) >= 0.04:
self.Log("Liquidated unrealized profits at: {0}".format(self.Time))
self.Debug("Liquidated unrealized profits at time %s with profit percentage %s" % (self.Time.date(),self.Portfolio['SPY'].UnrealizedProfitPercent))
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 MarketOpen(self):
return self.Time.hour != 0 #and self.Time.minute == 1
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)
#self.Debug("{0}: {1}: {2}".format(self.Time, order.Type, orderEvent))
class QuandlVix(PythonQuandl):
def __init__(self):
self.ValueColumnName = "vix Close"