Overall Statistics
Total Orders
9720
Average Win
0.10%
Average Loss
-0.09%
Compounding Annual Return
8.752%
Drawdown
22.900%
Expectancy
0.198
Start Equity
100000
End Equity
169113.25
Net Profit
69.113%
Sharpe Ratio
0.312
Sortino Ratio
0.313
Probabilistic Sharpe Ratio
8.261%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
1.19
Alpha
0.005
Beta
0.421
Annual Standard Deviation
0.121
Annual Variance
0.015
Information Ratio
-0.293
Tracking Error
0.137
Treynor Ratio
0.089
Total Fees
$9827.69
Estimated Strategy Capacity
$890000.00
Lowest Capacity Asset
SST V2245V5VOQQT
Portfolio Turnover
16.80%
# region imports
from AlgorithmImports import *
# endregion
import numpy as np
import pandas as pd

from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Dropout
from keras.models import Sequential
from sklearn.preprocessing import MinMaxScaler

class MyLSTM:
    
    def __init__(self):
        self.model = None
        self.scaler = MinMaxScaler(feature_range = (0, 1))

    def ProcessData(self, data, n = 60):
        # Split the data
        training_data = data[:1260]
        test_data = data[1260:]
        
        # Transform data
        training_data_array = np.array(training_data).reshape((len(training_data), 1))
    
        training_data_scaled = self.scaler.fit_transform(training_data_array)
        
        # Get features and labels
        features_set = []
        labels = []
        for i in range(60, 1260):
            features_set.append(training_data_scaled[i-60:i, 0])
            labels.append(training_data_scaled[i, 0])
            
        features_set, labels = np.array(features_set), np.array(labels)
        features_set = np.reshape(features_set, (features_set.shape[0], features_set.shape[1], 1))
        return features_set, labels, training_data, test_data
    
    
    def CreateModel(self, features_set, labels):
    
        # Create Model
        self.model = Sequential()
        self.model.add(LSTM(units = 50, return_sequences=True, input_shape=(features_set.shape[1], 1)))
        self.model.add(Dropout(0.2))
        self.model.add(LSTM(units=50, return_sequences=True))
        self.model.add(Dropout(0.2))
        self.model.add(LSTM(units=50, return_sequences=True))
        self.model.add(Dropout(0.2))
        self.model.add(LSTM(units=50))
        self.model.add(Dropout(0.2))
        self.model.add(Dense(units = 1))
        self.model.compile(optimizer = 'adam', loss = 'mean_squared_error')
        
    def FitModel(self, features_set, labels):
        self.model.fit(features_set, labels, epochs = 50, batch_size = 32)
    
        
    def PredictFromModel(self, test_data):
        test_inputs = test_data[-80:].values
        test_inputs = test_inputs.reshape(-1,1)
        test_inputs = self.scaler.transform(test_inputs)
        test_features = []
        for i in range(60, 80):
            test_features.append(test_inputs[i-60:i, 0])
        test_features = np.array(test_features)
        test_features = np.reshape(test_features, (test_features.shape[0], test_features.shape[1], 1))
    
        predictions = self.model.predict(test_features)
        predictions = self.scaler.inverse_transform(predictions)
        
        return predictions
# region imports
from AlgorithmImports import *
# endregion
from MyLSTM import MyLSTM

class MultidimensionalHorizontalFlange(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 1, 4)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash
        
        self.SetBrokerageModel(AlphaStreamsBrokerageModel())
        
        self.SetExecution(ImmediateExecutionModel())

        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())

        self.UniverseSettings.Resolution = Resolution.Minute
        self.SetUniverseSelection(LiquidETFUniverse())
        
        # Helper dictionaries
        self.macro_symbols = {'Bull' : Symbol.Create('SPY', SecurityType.Equity, Market.USA)}
        self.models = {'Bull': None, 'Bear': None}

        # 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('SPY'), self.TimeRules.AfterMarketOpen('SPY', 5), self.Predict)
        self.Schedule.On(self.DateRules.EveryDay('SPY'), self.TimeRules.AfterMarketOpen('SPY', 6), 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))
        
    def TrainMyModel(self):
        qb = self
        
        # Fetch history
        history = qb.History([symbol for key, symbol in self.macro_symbols.items()], 1280, Resolution.Daily)
        
        # Iterate over macro symbols
        for key, symbol in self.macro_symbols.items():
            # Initialize LSTM class instance
            lstm = MyLSTM()
            # Prepare data
            features_set, labels, training_data, test_data = lstm.ProcessData(history.loc[symbol].close)
            # Build model layers
            lstm.CreateModel(features_set, labels)
            # Fit model
            lstm.FitModel(features_set, labels)
            # Add LSTM class to dictionary to store later
            self.models[key] = lstm

    def Predict(self):
        delta = {}
        qb = self
        for key, symbol in self.macro_symbols.items():
            # Fetch LSTM class
            lstm = self.models[key]
            # Fetch history
            history = qb.History([symbol for key, symbol in self.macro_symbols.items()], 80, Resolution.Daily)
            # Predict
            predictions = lstm.PredictFromModel(history.loc[symbol].close)
            # Grab latest prediction and calculate if predict symbol to go up or down
            delta[key] = ( predictions[-1] / self.Securities[symbol].Price ) - 1
            # Plot prediction
            self.Plot('Prediction Plot', f'Predicted {key}', predictions[-1])
        
        insights = []
        # Iterate over macro symbols
        for key, change in delta.items():
            if key == 'Bull':
                insights += [Insight.Price(symbol, timedelta(1), InsightDirection.Up if change > 0 else InsightDirection.Flat) for symbol in LiquidETFUniverse.SP500Sectors.Long if self.Securities.ContainsKey(symbol)]
                insights += [Insight.Price(symbol, timedelta(1), InsightDirection.Up if change > 0 else InsightDirection.Flat) for symbol in LiquidETFUniverse.Treasuries.Inverse if self.Securities.ContainsKey(symbol)]
                insights += [Insight.Price(symbol, timedelta(1), InsightDirection.Flat if change > 0 else InsightDirection.Up) for symbol in LiquidETFUniverse.Treasuries.Long if self.Securities.ContainsKey(symbol)]
        self.EmitInsights(insights)
        
    def PlotMe(self):
        # Plot current price of symbols to match against prediction
        for key, symbol in self.macro_symbols.items():
            self.Plot('Prediction Plot', f'Actual {key}', self.Securities[symbol].Price)