Overall Statistics |
Total Trades 10 Average Win 5.48% Average Loss -0.21% Compounding Annual Return 26.199% Drawdown 6.800% Expectancy 19.471 Net Profit 25.557% Sharpe Ratio 1.795 Probabilistic Sharpe Ratio 79.908% Loss Rate 25% Win Rate 75% Profit-Loss Ratio 26.29 Alpha 0.223 Beta -0.116 Annual Standard Deviation 0.109 Annual Variance 0.012 Information Ratio -0.21 Tracking Error 0.167 Treynor Ratio -1.695 Total Fees $10.00 |
# REF. https://www.quantconnect.com/forum/discussion/2657/a-simple-vix-strategy from QuantConnect.Python import PythonQuandl # quandl data not CLOSE from QuantConnect.Python import PythonData # custom data from QuantConnect.Data import SubscriptionDataSource from datetime import datetime, timedelta import decimal class CboeVix(PythonData): '''CBOE Vix Download Custom Data Class''' def GetSource(self, config, date, isLiveMode): url_vix = "http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vixcurrent.csv" return SubscriptionDataSource(url_vix, SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLiveMode): if not (line.strip() and line[0].isdigit()): return None # New CboeVix object index = CboeVix(); index.Symbol = config.Symbol try: # Example File Format: # Date VIX Open VIX High VIX Low VIX Close # 01/02/2004 17.96 18.68 17.54 18.22 #print line data = line.split(',') date = data[0].split('/') index.Time = datetime(int(date[2]), int(date[0]), int(date[1])) index.Value = decimal.Decimal(data[4]) index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) except ValueError: # Do nothing return None # except KeyError, e: # print 'I got a KeyError - reason "%s"' % str(e) return index # NB: CboeVxV class == CboeVix class, except for the URL class CboeVxV(PythonData): '''CBOE VXV Download Custom Data Class''' def GetSource(self, config, date, isLiveMode): url_vxv = "http://www.cboe.com/publish/scheduledtask/mktdata/datahouse/vix3mdailyprices.csv" return SubscriptionDataSource(url_vxv, SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLiveMode): if not (line.strip() and line[0].isdigit()): return None index = CboeVxV(); index.Symbol = config.Symbol try: # Example File Format: # OPEN HIGH LOW CLOSE # 12/04/2007 24.8 25.01 24.15 24.65 data = line.split(',') date = data[0].split('/') index.Time = datetime(int(date[2]), int(date[0]), int(date[1])) index.Value = decimal.Decimal(data[4]) index["Open"] = float(data[1]) index["High"] = float(data[2]) index["Low"] = float(data[3]) index["Close"] = float(data[4]) except ValueError: # Do nothing return None return index # for using VIX futures settle in calc. ratios like VIX/VIX1 class QuandlFuture(PythonQuandl): '''Custom quandl data type for setting customized value column name. Value column is used for the primary trading calculations and charting.''' def __init__(self): # Define ValueColumnName: cannot be None, Empty or non-existant column name # If ValueColumnName is "Close", do not use PythonQuandl, use Quandl: # self.AddData[QuandlFuture](self.VIX1, Resolution.Daily) self.ValueColumnName = "Settle"
# REF. https://www.quantconnect.com/forum/discussion/6931/from-research-to-production-long-short-term-memory from MyLSTM import MyLSTM, LOOKBACK_DAYS, FEATURE_LEN from my_custom_data import CboeVix, CboeVxV import numpy as np import pandas as pd initial_lookback = LOOKBACK_DAYS*30 training_lookback = LOOKBACK_DAYS*10 predict_lookback = FEATURE_LEN*10 # look back seems like needs to be multiplied by a factor depend on number of symbols? class MultidimensionalHorizontalFlange(QCAlgorithm): def Initialize(self): # November 12, 2007 vxv +4 #self.SetStartDate(2014, 12, 1) # ^^^ Algo doesn't really trade. Likely bug, need research to debug # PSR: 59%, win loss rate: 100/0 is just wrong... self.SetStartDate(2019, 1, 1) # cherry picked date range...for wow factor. # PSR: 84% , win loss rate: 75/25. self.SetCash(10000) # Set Strategy Cash self.SetBrokerageModel(AlphaStreamsBrokerageModel()) self.SetExecution(ImmediateExecutionModel()) #self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) self.UniverseSettings.Resolution = Resolution.Daily #self.SetUniverseSelection(LiquidETFUniverse()) self.short_volatility = self.AddEquity('SPY', Resolution.Daily).Symbol self.long_volatility = self.AddEquity('SHY', Resolution.Daily).Symbol self.spy = self.AddEquity('SPY', Resolution.Daily).Symbol self.vix = self.AddData(CboeVix, "VIX").Symbol self.vxv = self.AddData(CboeVxV, "VXV").Symbol self.SetWarmUp(timedelta(LOOKBACK_DAYS)) self.models = { self.spy:None, } self.macro_symbols = { 'Bull':self.spy, } # Use Train() method to avoid runtime error self.Train(self.TrainMyModel) self.Train(self.DateRules.MonthEnd(), self.TimeRules.At(8,0), self.TrainMyModel) # Schedule prediction and plotting self.AddEquity('SPY') self.Schedule.On(self.DateRules.EveryDay(self.spy), self.TimeRules.AfterMarketOpen(self.spy, 5), self.Predict) #self.Schedule.On(self.DateRules.EveryDay(self.spy), self.TimeRules.BeforeMarketClose(self.spy, 5), self.ClosePosition) self.Schedule.On(self.DateRules.EveryDay(self.spy), self.TimeRules.BeforeMarketClose(self.spy, 5), self.PlotMe) # Create custom charts prediction = Chart('Prediction Plot') prediction.AddSeries(Series('Actual Bull', SeriesType.Line, 0)) prediction.AddSeries(Series('Predicted Bull', SeriesType.Line, 0)) prediction.AddSeries(Series('Actual Bear', SeriesType.Line, 1)) prediction.AddSeries(Series('Predicted Bear', SeriesType.Line, 1)) self.ready = False def get_data(self,lookback): history = self.History([self.spy,self.vix,self.vxv], lookback, Resolution.Daily) df = pd.DataFrame() #df['date'] = history.loc[self.spy].index df['SPY'] = history.loc[self.spy].close df['VIX'] = history.loc[self.vix].close df['VXV'] = history.loc[self.vxv].close return df def TrainMyModel(self): qb = self for key, symbol in self.macro_symbols.items(): if self.models[symbol] is None: df = self.get_data(initial_lookback) else: df = self.get_data(training_lookback) self.Log('data {}...train'.format(df.shape)) if df.shape[0] < predict_lookback: # dont really have that much training_lookback, nor initial_lookback self.ready = False continue self.Log('shape {}'.format(df.shape)) # Build model layers if self.models[symbol] is None: # Initialize LSTM class instance lstm = MyLSTM() # Prepare data features_set, labels, stratify = lstm.ProcessData(df) # Create model lstm.CreateModel() # Fit model lstm.FitModel(features_set, labels, stratify) # Add LSTM class to dictionary to store later self.models[symbol] = lstm self.ready = True self.Log('training done') else: lstm = self.models[symbol] # uncomment for real test, might take a while or timeout... #features_set, labels, stratify = lstm.ProcessData(df) #lstm.FitModel(features_set, labels) self.Log('no training done') # close position at the day. def ClosePosition(self): self.Log('closing position') self.SetHoldings([PortfolioTarget(self.short_volatility, 0.0)]) self.SetHoldings([PortfolioTarget(self.long_volatility, 0.0)]) def Predict(self): delta = {} qb = self for key, symbol in self.macro_symbols.items(): self.Log('predict') self.Log('ready is {}'.format(self.ready)) if self.ready is False: continue self.Log('fetch data.') # Fetch history df = self.get_data(predict_lookback) self.Log('data {}...predict'.format(df.shape)) if df.shape[0] < predict_lookback: raise ValueError('not enough data {}'.format(df.shape)) continue # Fetch LSTM class lstm = self.models[symbol] # Predict predictions = lstm.PredictFromModel(df) # Grab latest prediction and calculate if predict symbol to go up or down delta[key] = predictions # Plot prediction self.Plot('Prediction Plot', f'Predicted {key}', predictions) #confidence = np.clip(np.abs(predictions-0.5)/0.10,0,1) #insight = Insight.Price(symbol, timedelta(1), InsightDirection.Up if predictions > 0.5 else InsightDirection.Down, confidence) confidence = np.clip(np.abs(predictions)/0.10,0,1) insight = Insight.Price(symbol, timedelta(1), InsightDirection.Up if predictions > 0.0 else InsightDirection.Down, confidence) self.EmitInsights(insight) #if predictions > 0.5: if predictions > 0.0: self.Log('Long!') self.SetHoldings([PortfolioTarget(self.short_volatility, 1.0)]) self.SetHoldings([PortfolioTarget(self.long_volatility, 0.0)]) else: self.Log('Short!') self.SetHoldings([PortfolioTarget(self.short_volatility, 0.0)]) self.SetHoldings([PortfolioTarget(self.long_volatility, 0.5)]) def PlotMe(self): # Plot current price of symbols to match against prediction for key, symbol in self.macro_symbols.items(): up = 1.0 if (self.Securities[symbol].Close-self.Securities[symbol].Open) > 0 else 0.0 self.Plot('Prediction Plot', f'Actual {key}', up) self.Plot('Prediction Plot', f'Actual {key}', self.Securities[symbol].Price)