| Overall Statistics |
|
Total Orders 4391 Average Win 0.11% Average Loss -0.12% Compounding Annual Return 12.977% Drawdown 20.600% Expectancy 0.292 Start Equity 100000 End Equity 207940.44 Net Profit 107.940% Sharpe Ratio 0.585 Sortino Ratio 0.635 Probabilistic Sharpe Ratio 16.399% Loss Rate 31% Win Rate 69% Profit-Loss Ratio 0.88 Alpha 0.027 Beta 0.741 Annual Standard Deviation 0.142 Annual Variance 0.02 Information Ratio 0.089 Tracking Error 0.084 Treynor Ratio 0.112 Total Fees $4557.83 Estimated Strategy Capacity $10000000.00 Lowest Capacity Asset PG R735QTJ8XC9X Portfolio Turnover 2.63% Drawdown Recovery 208 |
from AlgorithmImports import *
from hmmlearn import hmm
class HMMRegimeDetection(QCAlgorithm):
"""
Hidden Markov Model (HMM) based regime detection.
Uses QuantConnect Algorithm Framework structure:
https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/overview
Based on:
https://www.quantconnect.com/research/17900/intraday-application-of-hidden-markov-models/p1
"""
def initialize(self):
# keep in mind data snooping bias:
# always leave some (typically more recent) time period for testing!
self.set_start_date(2017, 1, 1)
self.set_end_date(2022, 12, 31)
#self.set_start_date(2020, 1, 1)
#self.set_end_date(2020, 12, 31)
# key settings
self.minute_bars = 30
self.roc_window_size = 25
n_top_stocks = 10
# dynamic universe selection
self.add_universe(lambda fundamental: [f.symbol for f in sorted(fundamental, key=lambda x: x.market_cap, reverse=True)[:n_top_stocks]])
# alternative: static universe (uncomment below instead)
#tickers = ["QQQ", "IWM", "GLD", "TLT"]
#symbols = [self.add_equity(ticker, Resolution.MINUTE).Symbol for ticker in tickers]
#self.add_universe_selection(ManualUniverseSelectionModel(symbols))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(timedelta(minutes=self.minute_bars)))
self.settings.rebalance_portfolio_on_insight_changes = False
self.set_warm_up(timedelta(self.minute_bars))
def on_securities_changed(self, changes):
for added in changes.added_securities:
added.roc = RateOfChange(1)
added.roc.window.size = self.roc_window_size
# HMM detection assuming 3 regimes
added.model = hmm.GaussianHMM(n_components = 3, n_iter = 100, random_state = 100)
added.model_month = -1
added.consolidator = TradeBarConsolidator(timedelta(minutes = self.minute_bars))
added.consolidator.data_consolidated += self.on_consolidated
self.subscription_manager.add_consolidator(added.symbol, added.consolidator)
def on_consolidated(self, _, bar):
security = self.securities[bar.symbol]
security.roc.update(bar.end_time, bar.price)
if security.roc.window.is_ready:
if security.model_month != bar.end_time.month:
security.model.fit(np.array([point.value for point in security.roc.window])[::-1].reshape(-1, 1))
security.model_month = bar.end_time.month
post_prob = security.model.predict_proba(np.array([security.roc.current.value]).reshape(1, -1)).flatten()
if post_prob[2] > post_prob[0]:
direction = InsightDirection.UP
else:
direction = InsightDirection.DOWN
self.emit_insights(Insight.price(bar.symbol, timedelta(minutes = self.minute_bars), direction))