import numpy as np
import pandas as pd
from datetime import timedelta
from decimal import Decimal
# Parameters are global variables (necessitated from indicator custom class)
pOne = 60
pTwo = 780
pThree = 13980
atrLen = 14000
tradeThresh = 15
fOne = 1.25
fTwo = -0.5
fThree = 1
class VXTFAlgo(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2012, 7, 1) # Set Start Date
#self.SetEndDate(2012, 12, 30) # Set End Date
self.SetCash(100000) # Set Strategy Cash
self.Reso = Resolution.Minute
# Add VIX futures contract data
#future = self.AddFuture(Futures.Indices.VIX).SetFilter(timedelta(8), timedelta(days=60))
future = self.AddFuture(Futures.Indices.VIX, self.Reso).SetFilter(lambda x: x.FrontMonth().OnlyApplyFilterAtMarketOpen())
# select the right VX future
self.frontVX = None
self.lastBid = 0
self.lastAsk = 0
self.orderTktEntry = None
self.orderTktExit = None
#self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerageModel)
self.quantity = 1
# var for instance of indicators
self.indicators = None
def OnData(self, data):
# Manually update indicators with recent values using manual method https://www.quantconnect.com/docs/algorithm-reference/indicators
# NB need to use lower case 'update' from custom class due to manual updates i.e. customIndicators.updateSMA()
if data.Bars.ContainsKey(self.frontVX.Symbol):
self.indicators.updateSMA(data[self.frontVX.Symbol].EndTime, data[self.frontVX.Symbol].Close)
self.indicators.updateATR(data.Bars[self.frontVX.Symbol])
if not self.indicators.is_ready(): return
# get trend and atr indicator values
atrAdjOne = self.indicators.atr.Current.Value * pOne ** 0.5
atrAdjTwo = self.indicators.atr.Current.Value * pTwo ** 0.5
atrAdjThree = self.indicators.atr.Current.Value * pThree ** 0.5
# value today minus vlaue of SMA @ lookback (windows are currently of IndicatorDataPoint class objects)
trendOne = 100 * ((self.indicators.smaWinOne[0].Value - self.indicators.smaWinOne[pOne/4].Value)/atrAdjOne) * fOne
trendTwo = 100 * ((self.indicators.smaWinTwo[0].Value - self.indicators.smaWinTwo[pTwo/4].Value)/atrAdjTwo) * fTwo
trendThree = 100 * ((self.indicators.smaWinThree[0].Value - self.indicators.smaWinThree[pThree/4].Value)/atrAdjThree) * fThree
# define indicator
trend = (trendOne + trendTwo + trendThree) / 3
## Check indicators and generate trading signals
#holding = None if self.symbol is None else self.Portfolio.get(self.frontVX)
bidPrice = self.frontVX.BidPrice
askPrice = self.frontVX.AskPrice
if trend > tradeThresh:
# cancel open sell orders
if self.OrderIsPlaced(self.frontVX.Symbol,-self.quantity):
self.orderTktExit.Cancel("trend value flipped; sell order cancelled")
## enter if not invested and BUY order not already placed
if not self.Portfolio.Invested and not self.OrderIsPlaced(self.frontVX.Symbol,self.quantity):
self.orderTktEntry = self.LimitOrder(self.frontVX.Symbol,self.quantity,bidPrice)
#self.Log(f"{self.Time} Trend above thresh: trend {trend} trendOne {trendOne} trendTwo {trendTwo} trendThree {trendThree} atrAdjOne {atrAdjOne} atrAdjTwo {atrAdjTwo} atrAdjThree {atrAdjThree}")
# order management for entry
if self.OrderIsPlaced(self.frontVX.Symbol,self.quantity) and self.lastBid != bidPrice:
updateFields = UpdateOrderFields()
updateFields.LimitPrice = bidPrice
self.orderTktEntry.Update(updateFields)
self.lastBid = bidPrice
else:
# cancel open buy orders
if self.OrderIsPlaced(self.frontVX.Symbol,self.quantity):
self.orderTktEntry.Cancel("trend value flipped; buy order cancelled")
## exit if invested with limit orders on the offer, and SELL order not already placed
if self.Portfolio.Invested and not self.OrderIsPlaced(self.frontVX.Symbol, -self.quantity):
self.orderTktExit = self.LimitOrder(self.frontVX.Symbol, -self.quantity,askPrice)
#self.Log(f"{self.Time} Trend below thresh: trend {trend} trendOne {trendOne} trendTwo {trendTwo} trendThree {trendThree} atrAdjOne {atrAdjOne} atrAdjTwo {atrAdjTwo} atrAdjThree {atrAdjThree}")
# order management for exit
if self.OrderIsPlaced(self.frontVX.Symbol,-self.quantity) and self.lastAsk != askPrice:
updateFields = UpdateOrderFields()
updateFields.LimitPrice = askPrice
self.orderTktExit.Update(updateFields)
self.lastAsk = askPrice
def OnSecuritiesChanged(self, changes):
# https://www.quantconnect.com/forum/discussion/8588/warming-up-an-indicator-rolling-window/p1
# Remove the data consolidator indicators and rolling windows for the previous contract
if len(changes.RemovedSecurities) > 0:
# and reset the indicators
if self.frontVX is not None:
self.indicators.smaOne.Reset()
self.indicators.smaTwo.Reset()
self.indicators.smaThree.Reset()
self.indicators.atr.Reset()
# Github example doesn't liquidate because auto-liquidates on expiry, but we do
self.Log(f"Old contract expiring: {self.frontVX.Symbol.Value}")
self.Liquidate(self.frontVX.Symbol)
# Only one security will be added: the new front contract
# NB assigning the entire security object here, NOT the symbol object
if len(changes.AddedSecurities) > 0:
self.frontVX = changes.AddedSecurities[0]
self.Log(f"Rolling to new contract: {self.frontVX.Symbol.Value}")
# history call to push through indicator values to the rolling windows
history = self.History(self.frontVX.Symbol, pThree * 2, self.Reso)
#self.Debug(history.head())
# initialise instance of customIndicators class and pass history call
self.indicators = customIndicators(self.frontVX.Symbol,history)
def OrderIsPlaced(self,symbol,quantity):
openOrders = self.Transactions.GetOpenOrders()
for order in openOrders:
if (order.Symbol == symbol and order.Quantity == quantity):
return True
return False
# Custom class for manually handling indicator warm up and updates: https://www.quantconnect.com/forum/discussion/7972/using-atr-and-other-039-complex-039-indicators-with-history/p1
class customIndicators():
def __init__(self, symbol, history):
self.frontVX = symbol
## Indicators - taken from https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/EmaCrossFuturesFrontMonthAlgorithm.py
# SMAs
self.smaOne = SimpleMovingAverage(pOne)
self.smaTwo = SimpleMovingAverage(pTwo)
self.smaThree = SimpleMovingAverage(pThree)
#ATR
self.atr = AverageTrueRange(self.frontVX, atrLen)
# create rollingwindows and bind their updates to an event handler called on Updated - equivalent to:
#self.smaOne.Update(time,close)
#self.smaWinOne.Add(self.smaOne.Current)
# see https://docs.microsoft.com/en-us/dotnet/standard/events/#event-handlers. I think lambda s,e refer to 'IsReady' and 'Current.Value'?
self.smaWinOne = RollingWindow[IndicatorDataPoint](pOne)
self.smaOne.Updated += lambda s,e: self.smaWinOne.Add(e)
self.smaWinTwo = RollingWindow[IndicatorDataPoint](pTwo)
self.smaTwo.Updated += lambda s,e: self.smaWinTwo.Add(e)
self.smaWinThree = RollingWindow[IndicatorDataPoint](pThree)
self.smaThree.Updated += lambda s,e: self.smaWinThree.Add(e)
# Loop over the history data and update the indicators
for tuple in history.itertuples():
#self.Debug(f"tuple.Index {tuple.Index[2]} tuple.close {tuple.close}")
bar = TradeBar(tuple.Index[2], self.frontVX, tuple.open, tuple.high, tuple.low, tuple.close, tuple.volume)
self.updateSMA(tuple.Index[2],tuple.close)
self.updateATR(bar)
# Returns true if all the data in this instance is ready (indicators, rolling windows, ect...)
def is_ready(self):
# taken from https://www.quantconnect.com/forum/discussion/3303/how-to-use-rollingwindow-on-multiple-equities-and-indicators/p1
return (self.smaOne.IsReady and
self.smaTwo.IsReady and
self.smaThree.IsReady and
self.atr.IsReady and
self.smaWinOne.IsReady and
self.smaWinTwo.IsReady and
self.smaWinThree.IsReady)
def updateSMA(self,time,close):
self.smaOne.Update(time,close)
self.smaTwo.Update(time,close)
self.smaThree.Update(time,close)
def updateATR(self,bar):
self.atr.Update(bar)