Introduction
A Hidden Markov Model (HMM) estimates the probability an asset will continue similar behavior or jump to another state, making it a great tool to predict the current market regime (trending up, ranging, or trending down). In this micro-study, we implement a 3-component HMM strategy to detect market regimes and generate timely buy signals for liquid stocks on an intraday basis.
Background
A HMM is a probabilistic model representing a sequence of events or observations, where the underlying process that generates the observations is not directly observable.

The use of HMMs in financial market analysis has gained traction in recent years. HMMs are powerful statistical models that can effectively capture the underlying, unobservable market states or "regimes" that drive asset price movements.
Their only input is price, so we wanted to focus on intraday timeframes to reduce the impact of the economy, politics and other macro factors. To start we chose 5-minute close price returns of the top 10 market cap stocks. These stocks tend to be highly liquid and less volatile, making them suitable for a rapid, intraday trading strategy. The 5-minute bar timeframe was sufficient to identify market regime shifts while maintaining a rapid response to intraday market fluctuations.
Implementation
To implement this on QuantConnect, we used universe selection, a data consolidator, and a returns indicator.
1. First, the strategy selects the top 10 US stocks by market capitalization. This is a fairly stable list as the top-10 don't change very frequently. By default this subscribes at minute resolution and updates daily.
def initialize(self):
self.add_universe(lambda fundamental: [f.symbol for f in sorted(fundamental, key=lambda x: x.market_cap, reverse=True)[:10]])
2. We create a HMM object for the assets added to the algorithm, and set the random seed state to ensure the backtest result is repeatable. To remember the training state of the model we've created a model_month flag.
def on_securities_changed(self, changes):
for added in changes.added_securities:
added.model = hmm.GaussianHMM(n_components=3, n_iter=100, random_state=100)
added.model_month = -1
3. Then we consolidate the minute data, and pipe the 5-minute bars into the on_consolidated event handler. We also create the rate-of-change indicator which will record the history of returns for our model.
added.roc = RateOfChange(1)
added.roc.window.size = 150
added.consolidator = TradeBarConsolidator(timedelta(minutes=5))
added.consolidator.data_consolidated += self.on_consolidated
self.subscription_manager.add_consolidator(added.symbol, added.consolidator)
4. Later, when five minute bars are produced, we update the RateOfChange indicator. This automatically adds new return values to its rolling window, building the training data for our model. When we have enough data, the model is trained on the points available.
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
5. The model can then use the latest 5-minute return to predict the current market regime for each asset. If P(positive) > P(negative), buy the stock, else short sell it. We use a fixed weight of 10% to ensure equal exposure.
post_prob = security.model.predict_proba(np.array([security.roc.current.value]).reshape(1, -1)).flatten()
self.set_holdings(bar.symbol, 0.1 if post_prob[2] > post_prob [0] else -0.1)
Results
The implementation of a three-component Hidden Markov Model strategy for the top 10 market capitalization stocks yielded exceptional performance, with a Sharpe Ratio of 1.9. The model could quickly identify market regimes and capitalize on shifts in market conditions. By using a universe of liquid stocks we could efficiently trade intraday, and place multiple concurrent bets to smooth the overall return.

The algorithm parameter values (5, 150) were chosen at random. The strategy was resilient to a wide range of different parameters with all tests falling between 1.1-1.9 Sharpe Ratio.
As with any investment strategy, ongoing monitoring, optimization, and risk management practices are essential to maintain robust performance over time.
Lee Ashley
Very nice
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Remus Miclea
Thank for this, Louis, it got me started on the ML path, and look forward to learning more about it!
About the results: I cloned the code and checked the “baseline” results, given that the market was mostly in an uptrend, for the period considered (2023-Jan-01 till 2024-Sep-01, to be precise), and simply buying and holding could have been a successful strategy. To my surprise, I got better results, both as absolute return, as well as Sharpe ratio, when I forced the insights to be always UP (i.e. ignored any output of the HMM).
In other words, just buy-and-hold top 10 stocks, by market cap, outperforms the HMM model, for the said period. So, then, what is its value?
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Piyush Ravi
Hey!I was wondering why did you choose “n_components=3”. I tried your code with with
“n_components = 2” and
“direction = InsightDirection.UP if post_prob[-1] > post_prob [0] else InsightDirection.DOWN”.
It was giving slightly better results.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Ross Bernstein
Could this be tested on a basket of futures?
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Brandon Goyette
That would be interesting or maybe a basket of sector ETFs, across equities and maybe some with other less correlated exposures like a bitcoin ETF etc.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Kal Seehra
I played around with the asset selection a but and noticed that even in down markets, this HMM does not like to go short (typical short expose is less than 10%, even in a down market). Do you know why that would be? Is there a parameter that is biasing towards long exposure?
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Starteleport
Hi,
Thanks for the great article!
I've got a question on how exactly the mapping between hidden state index and a market regime is established?
I am referring to the following line:
Here, how do we arrive that we need to compare probability of the state #2 to the probability of the state #0? Could the meaning of these indexes/states suddenly change as we re-fit the model as the time goes? Does the meaning of indexes/states change with the change of the random_state parameter?
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Louis Szeto
Hi Starteleport
That was indeed a good question! Now, the state with the highest prevalence is at the lowest index by this library, but we can optimize the code by adding an identifier for which state is volatile and which is stable. You may just ask the model to predict the states of the data points it was used to train and check which state is more volatile to decide.
Best
Louis
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Erik Bengtson
Cool James. I will take look. I might migrate what I'm doing to yours baseline implementation.I was just about to create an implementation for turtle soup strategy, better to start from a good basis
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Erik Bengtson
Thanks Jay, I will take a look at that option.
James, here my version of your baseline project. I will try to merge with the changes I've done on the previous project. The code looks cleaner.
GIT GenericTree updates
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Erik Bengtson
and of course, the current BB strategy implementation there must be only for trading ranges
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Erik Bengtson
James,
I updated some code with fixes and to make it compatible with QC
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
James Smith
Thanks Erik. I'm going to try to integrate your changes as soon as I can. I can work without a pull request but you can go that route if you prefer. I'm giving genetic programming using this setup a lot of attention so feel free to suggest improvements or report any issues. The number one thing that helps me out is getting a third-party opinion on things. I have made quite a lot of changes to this and the genetic optimizer project and am getting fairly pleasing results.
In terms of an optimization rig, I have an old 4 slot server capable of 24 cores that I obtained for basically peanuts. I don't know how the costs stack up over time against cloud compute. I imagine it would even out after a few weeks of running 24/7 as long as power costs aren't too high.
For live hosting of trading algorithms +1 for digitalocean.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Erik Bengtson
James, I will check how to do a pull request. Not really familiar with that.
Next steps for me are the integration of additional signals in order of creating a few strategies. The additional signals I'm looking at is the Autochartist, integration with rest based NN services.
I will provide further feedback as soon as I progress using this framework, but immeditelly I think the configuration is rather verbose, I will change it into 2 steps, to make it more human friendly.
Thanks for this.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
James Smith
I've merged the Bollinger and Channel breakout from your fork. Seems like a great idea to allow a survival period for the approximate coincidence of signals. I'm wondering whether this could use QuantConnect.Indicators.RollingWindow?
You're right that the Optimizer configuration is unwieldy for this level of complexity. I may address that sometime soon. In the meantime I'm using a few scripts and tools to shift json around.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!