Overall Statistics
Total Orders
299
Average Win
2.19%
Average Loss
-1.28%
Compounding Annual Return
22.807%
Drawdown
41.900%
Expectancy
0.802
Start Equity
100000.0
End Equity
488189.87
Net Profit
388.190%
Sharpe Ratio
0.794
Sortino Ratio
0.812
Probabilistic Sharpe Ratio
31.602%
Loss Rate
34%
Win Rate
66%
Profit-Loss Ratio
1.71
Alpha
0.065
Beta
0.96
Annual Standard Deviation
0.187
Annual Variance
0.035
Information Ratio
0.531
Tracking Error
0.115
Treynor Ratio
0.155
Total Fees
$841.69
Estimated Strategy Capacity
$3400000.00
Lowest Capacity Asset
BTCUSD E3
Portfolio Turnover
4.51%
# region imports
from AlgorithmImports import *

from sklearn.tree import DecisionTreeRegressor
from sklearn.preprocessing import StandardScaler
# endregion


class AIStocksBondsRotationAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2017, 1, 1)
        self.settings.daily_precise_end_time = False
        self._bitcoin = self.add_crypto("BTCUSD", market=Market.BITFINEX, leverage=2).symbol
        self._equities = [self.add_equity(ticker).symbol for ticker in ['SPY', 'GLD', 'BND']]
        self._factors = [self.add_data(Fred, ticker, Resolution.DAILY).symbol for ticker in ['VIXCLS', 'T10Y3M', 'DFF']]
        self._model = DecisionTreeRegressor(max_depth=12, random_state=1)
        self._scaler = StandardScaler()
        self.schedule.on(self.date_rules.month_start(self._equities[0]), self.time_rules.after_market_open(self._equities[0], 1), self._rebalance)
        self._lookback_years = self.get_parameter('lookback_years', 4)
        self._max_bitcoin_weight = self.get_parameter('max_bitcoin_weight', 0.1)
    
    def _rebalance(self):
        factors = self.history(self._factors, timedelta(self._lookback_years*365), Resolution.DAILY)['value'].unstack(0).dropna()
        label = self.history(self._equities + [self._bitcoin], timedelta(self._lookback_years*365), Resolution.DAILY, data_normalization_mode=DataNormalizationMode.TOTAL_RETURN)['close'].unstack(0).dropna().pct_change(21).shift(-21).dropna()
        prediction_by_symbol = pd.Series()
        for symbol in self._equities + [self._bitcoin]:
            asset_labels = label[symbol].dropna()
            idx = factors.index.intersection(asset_labels.index)
            self._model.fit(self._scaler.fit_transform(factors.loc[idx]), asset_labels.loc[idx])
            prediction = self._model.predict(self._scaler.transform([factors.iloc[-1]]))[0]
            if prediction > 0:
                prediction_by_symbol.loc[symbol] = prediction
        weight_by_symbol = 1.5 * prediction_by_symbol / prediction_by_symbol.sum()
        if self._bitcoin in weight_by_symbol and weight_by_symbol.loc[self._bitcoin] > self._max_bitcoin_weight:
            weight_by_symbol.loc[self._bitcoin] = self._max_bitcoin_weight
            if len(weight_by_symbol) > 1:
                equities = [symbol for symbol in self._equities if symbol in weight_by_symbol]
                weight_by_symbol.loc[equities] = 1.5 * weight_by_symbol.loc[equities] / weight_by_symbol.loc[equities].sum()
        self.set_holdings([PortfolioTarget(symbol, weight) for symbol, weight in weight_by_symbol.items()], True)