Overall Statistics
Total Orders
4494
Average Win
0.11%
Average Loss
-0.12%
Compounding Annual Return
13.910%
Drawdown
38.700%
Expectancy
0.292
Start Equity
100000
End Equity
200382.46
Net Profit
100.382%
Sharpe Ratio
0.57
Sortino Ratio
0.517
Probabilistic Sharpe Ratio
21.223%
Loss Rate
32%
Win Rate
68%
Profit-Loss Ratio
0.91
Alpha
0.017
Beta
0.665
Annual Standard Deviation
0.144
Annual Variance
0.021
Information Ratio
-0.156
Tracking Error
0.106
Treynor Ratio
0.124
Total Fees
$4882.48
Estimated Strategy Capacity
$200000000.00
Lowest Capacity Asset
SHV TP8J6Z7L419H
Portfolio Turnover
4.46%
# region imports
from AlgorithmImports import *
from tslearn.barycenters import softdtw_barycenter
from tslearn.clustering import TimeSeriesKMeans
import joblib
# endregion

class TslearnExampleAlgorithm(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2019, 1, 1)
        self.SetEndDate(2024,5,1)
        self.SetCash(100000)
        tickers = ["SPY", "QQQ", "DIA", 
                "AAPL", "MSFT", "TSLA", 
                "IEF", "TLT", "SHV", "SHY", 
                "GLD", "IAU", "SLV", 
                "USO", "XLE", "XOM"]
        self.symbols = [self.AddEquity(ticker, Resolution.Daily).Symbol for ticker in tickers]

        training_length = 252
        self.training_data = {}
        history = self.History(self.symbols, training_length, Resolution.Daily).unstack(0).close
        for symbol in self.symbols:
            self.training_data[symbol] = RollingWindow[float](training_length)
            for close_price in history[symbol]:
                self.training_data[symbol].Add(close_price)

        self.model = TimeSeriesKMeans(n_clusters=6,   # We have 6 main groups
                                          metric="dtw")

        self.Train(self.my_training_method)
        self.Train(self.DateRules.Every(DayOfWeek.Sunday), self.TimeRules.At(8,0), self.my_training_method)
        
    def get_features(self):
        close_price = pd.DataFrame({symbol: list(data)[::-1] for symbol, data in self.training_data.items()})
        log_price = np.log(close_price)
        log_normal_price = (log_price - log_price.mean()) / log_price.std()

        return log_normal_price

    def my_training_method(self):
        features = self.get_features()
        self.model.fit(features.T.values)

    def OnData(self, slice: Slice) -> None:
        for symbol in self.symbols:
            if symbol in slice.Bars:
                self.training_data[symbol].Add(slice.Bars[symbol].Close)

        features = self.get_features()
        self.labels = self.model.predict(features.T.values)

        for i in set(self.labels):
            assets_in_cluster = features.columns[[n for n, k in enumerate(self.labels) if k == i]]
            size = 1/6/len(assets_in_cluster)
            self.SetHoldings([PortfolioTarget(symbol, size) for symbol in assets_in_cluster])

    def OnEndOfAlgorithm(self):
        #model_key = "model"
        #file_name = self.ObjectStore.GetFilePath(model_key)
        #self.model.to_hdf5(file_name + ".hdf5")
        pass