Overall Statistics
Total Orders
1502
Average Win
0.10%
Average Loss
-0.11%
Compounding Annual Return
12.457%
Drawdown
13.100%
Expectancy
0.296
Start Equity
100000
End Equity
125371.36
Net Profit
25.371%
Sharpe Ratio
0.42
Sortino Ratio
0.558
Probabilistic Sharpe Ratio
39.685%
Loss Rate
31%
Win Rate
69%
Profit-Loss Ratio
0.88
Alpha
-0.018
Beta
0.591
Annual Standard Deviation
0.102
Annual Variance
0.01
Information Ratio
-0.705
Tracking Error
0.085
Treynor Ratio
0.073
Total Fees
$1540.75
Estimated Strategy Capacity
$78000000.00
Lowest Capacity Asset
IAU T5QHGBE8K3TX
Portfolio Turnover
4.43%
# 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.set_start_date(2022, 7, 4)
        self.set_cash(100000)
        tickers = ["SPY", "QQQ", "DIA", 
                "AAPL", "MSFT", "TSLA", 
                "IEF", "TLT", "SHV", "SHY", 
                "GLD", "IAU", "SLV", 
                "USO", "XLE", "XOM"]
        self.symbols = [self.add_equity(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.date_rules.every(DayOfWeek.SUNDAY), self.time_rules.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 on_data(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.set_holdings([PortfolioTarget(symbol, size) for symbol in assets_in_cluster])

    def on_end_of_algorithm(self):
        model_key = "model_test.hdf5"
        file_name = self.object_store.get_file_path(model_key)
        import os
        os.remove(file_name)
        self.model.to_hdf5(file_name)