Overall Statistics
Total Orders
513
Average Win
1.19%
Average Loss
-1.09%
Compounding Annual Return
5.730%
Drawdown
19.200%
Expectancy
0.124
Start Equity
100000
End Equity
134628.64
Net Profit
34.629%
Sharpe Ratio
0.203
Sortino Ratio
0.153
Probabilistic Sharpe Ratio
5.422%
Loss Rate
46%
Win Rate
54%
Profit-Loss Ratio
1.08
Alpha
-0.02
Beta
0.42
Annual Standard Deviation
0.106
Annual Variance
0.011
Information Ratio
-0.612
Tracking Error
0.126
Treynor Ratio
0.051
Total Fees
$837.51
Estimated Strategy Capacity
$750000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
22.08%
# region imports
from AlgorithmImports import *
import xgboost as xgb
import joblib
# endregion

class XGBoostExampleAlgorithm(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2019, 1, 1)
        self.SetEndDate(2024, 5, 1)
        self.SetCash(100000)
        self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol

        training_length = 252*2
        self.training_data = RollingWindow[float](training_length)
        history = self.History[TradeBar](self.symbol, training_length, Resolution.Daily)
        for trade_bar in history:
            self.training_data.Add(trade_bar.Close)

        if self.ObjectStore.ContainsKey("model"):
            file_name = self.ObjectStore.GetFilePath("model")
            self.model = joblib.load(file_name)
        else:
            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_and_labels(self, n_steps=5):
        close_prices = np.array(list(self.training_data)[::-1])
        df = (np.roll(close_prices, -1) - close_prices) * 0.5 + close_prices * 0.5
        df = df[:-1]

        features = []
        labels = []
        for i in range(len(df)-n_steps):
            features.append(df[i:i+n_steps])
            labels.append(df[i+n_steps])

        features = np.array(features)
        labels = np.array(labels)
        features = (features - features.mean()) / features.std()
        labels = (labels - labels.mean()) / labels.std()

        d_matrix = xgb.DMatrix(features, label=labels)

        return d_matrix

    def my_training_method(self):
        d_matrix = self.get_features_and_labels()
        params = {
            'booster': 'gbtree',
            'colsample_bynode': 0.8,
            'learning_rate': 0.1,
            'lambda': 0.1,
            'max_depth': 5,
            'num_parallel_tree': 100,
            'objective': 'reg:squarederror',
            'subsample': 0.8,
        }
        self.model = xgb.train(params, d_matrix, num_boost_round=2)

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

        new_d_matrix = self.get_features_and_labels()
        prediction = self.model.predict(new_d_matrix)
        prediction = prediction.flatten()

        if float(prediction[-1]) > float(prediction[-2]):
            self.SetHoldings(self.symbol, 1)
        else:            
            self.SetHoldings(self.symbol, 0)

    def OnEndOfAlgorithm(self):
        #model_key = "model"
        #file_name = self.ObjectStore.GetFilePath(model_key)
        #joblib.dump(self.model, file_name)
        #self.ObjectStore.Save(model_key)
        pass