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)