Overall Statistics
Total Orders
1489
Average Win
0.09%
Average Loss
-0.11%
Compounding Annual Return
9.681%
Drawdown
10.900%
Expectancy
0.226
Start Equity
100000
End Equity
118156.66
Net Profit
18.157%
Sharpe Ratio
0.236
Sortino Ratio
0.32
Probabilistic Sharpe Ratio
28.085%
Loss Rate
34%
Win Rate
66%
Profit-Loss Ratio
0.86
Alpha
-0.026
Beta
0.615
Annual Standard Deviation
0.108
Annual Variance
0.012
Information Ratio
-0.677
Tracking Error
0.087
Treynor Ratio
0.042
Total Fees
$1528.03
Estimated Strategy Capacity
$300000000.00
Lowest Capacity Asset
SHV TP8J6Z7L419H
Portfolio Turnover
4.89%
# 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"
        file_name = self.object_store.get_file_path(model_key)
        self.model.to_hdf5(file_name + ".hdf5")