Created with Highcharts 12.1.2EquityJan 2019Jan…Jul 2019Jan 2020Jul 2020Jan 2021Jul 2021Jan 2022Jul 2022Jan 2023Jul 2023Jan 2024Jul 2024Jan 2025Jul 20250100k200k300k-20-1000101202.5M5M02.5M5M05010005001000
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)