| Overall Statistics |
|
Total Orders 104 Average Win 5.32% Average Loss -4.33% Compounding Annual Return 30.544% Drawdown 48.500% Expectancy 0.603 Start Equity 100000 End Equity 405753.16 Net Profit 305.753% Sharpe Ratio 0.842 Sortino Ratio 0.926 Probabilistic Sharpe Ratio 32.182% Loss Rate 28% Win Rate 72% Profit-Loss Ratio 1.23 Alpha 0.088 Beta 1.244 Annual Standard Deviation 0.263 Annual Variance 0.069 Information Ratio 0.712 Tracking Error 0.16 Treynor Ratio 0.178 Total Fees $280.24 Estimated Strategy Capacity $1900000000.00 Lowest Capacity Asset NVDA RHM8UTD8DT2D Portfolio Turnover 1.31% |
# region imports
from AlgorithmImports import *
import torch
from chronos import ChronosPipeline
from scipy.optimize import minimize
from transformers import set_seed
# endregion
class HuggingFaceBaseModelDemo(QCAlgorithm):
"""
This algorithm demonstrates how to use a pre-trained HuggingFace
model. It uses the "amazon/chronos-t5-tiny" model to forecast the
future equity curves of the 5 most liquid assets in the market,
then it uses the SciPy package to find the portfolio weights
that will maximize the future Sharpe ratio of the portfolio.
The portfolio is rebalanced every 3 months.
"""
def initialize(self):
self.set_start_date(2019, 1, 1)
self.set_end_date(2024, 4, 1)
self.set_cash(100_000)
self.settings.min_absolute_portfolio_target_percentage = 0
# Enable reproducibility.
set_seed(1, True)
# Load the pre-trained model.
self._pipeline = ChronosPipeline.from_pretrained(
"amazon/chronos-t5-tiny",
device_map="cuda" if torch.cuda.is_available() else "cpu",
torch_dtype=torch.bfloat16,
)
# Define the universe.
spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
self.universe_settings.schedule.on(self.date_rules.month_start(spy))
self.universe_settings.resolution = Resolution.DAILY
self._universe = self.add_universe(
self.universe.dollar_volume.top(
self.get_parameter('universe_size', 5)
)
)
# Define some trading parameters.
self._lookback_period = timedelta(
365 * self.get_parameter('lookback_years', 1)
)
self._prediction_length = 3*21 # Three months of trading days
# Schedule rebalances.
self._last_rebalance = datetime.min
self.schedule.on(
self.date_rules.month_start(spy, 1),
self.time_rules.midnight,
self._trade
)
# Add warm up so the algorithm trades on deployment.
self.set_warmup(timedelta(31))
def on_warmup_finished(self):
# Trade right after warm up is done.
self._trade()
def _sharpe_ratio(
self, weights, returns, risk_free_rate, trading_days_per_year=252):
# Define how to calculate the Sharpe ratio so we can use
# it to optimize the portfolio weights.
# Calculate the annualized returns and covariance matrix.
mean_returns = returns.mean() * trading_days_per_year
cov_matrix = returns.cov() * trading_days_per_year
# Calculate the Sharpe ratio.
portfolio_return = np.sum(mean_returns * weights)
portfolio_std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_std
# Return negative Sharpe ratio because we minimize this
# function in optimization.
return -sharpe_ratio
def _optimize_portfolio(self, equity_curves):
returns = equity_curves.pct_change().dropna()
num_assets = returns.shape[1]
initial_guess = num_assets * [1. / num_assets,]
# Find portfolio weights that mazimize the forward Sharpe
# ratio.
result = minimize(
self._sharpe_ratio,
initial_guess,
args=(
returns,
self.risk_free_interest_rate_model.get_interest_rate(self.time)
),
method='SLSQP',
bounds=tuple((0, 1) for _ in range(num_assets)),
constraints=(
{'type': 'eq', 'fun': lambda weights: np.sum(weights) - 1}
)
)
return result.x
def _trade(self):
# Don't rebalance during warm-up.
if self.is_warming_up:
return
# Only rebalance on a quarterly basis.
if self.time - self._last_rebalance < timedelta(80):
return
self._last_rebalance = self.time
symbols = list(self._universe.selected)
# Get historical equity curves.
history = self.history(symbols, self._lookback_period)['close'].unstack(0)
# Forecast the future equity curves.
all_forecasts = self._pipeline.predict(
[
torch.tensor(history[symbol].dropna())
for symbol in symbols
],
self._prediction_length
)
# Take the median forecast for each asset.
forecasts_df = pd.DataFrame(
{
symbol: np.quantile(
all_forecasts[i].numpy(), 0.5, axis=0 # 0.5 = median
)
for i, symbol in enumerate(symbols)
}
)
# Find the weights that maximize the forward Sharpe
# ratio of the portfolio.
optimal_weights = self._optimize_portfolio(forecasts_df)
# Rebalance the portfolio.
self.set_holdings(
[
PortfolioTarget(symbol, optimal_weights[i])
for i, symbol in enumerate(symbols)
],
True
)