Regime methodology: how the hidden Markov model works
Portfolio Lab's hidden Markov model (HMM) splits the weekly or monthly returns of a price series into two to four regimes, each with its own mean and volatility, from “Calm” to “Turbulent”. The Regime page uses it to show, in hindsight, when each regime was likely; the tactical model Regime (HMM) uses the same calculation as a signal, without look-ahead. This methodology describes what the code computes: model, estimation, filtering and smoothing, metrics, model selection, walk-forward and limitations. The idea and the research are covered in the article Regime models.
The model: hidden regimes, normally distributed returns
The model follows Hamilton (1989) and the presentation in Rabiner (1989): a hidden regime jumps from period to period according to a Markov chain. Which regime comes next depends only on the current one. Within each regime, the return is normally distributed, with its own mean and its own volatility. Only the return is observed; the model infers the regime as a probability (hidden Markov model).
P(s_t = j | s_t−1 = i) = a_ij; x_t | s_t = k ~ N(μ_k, σ_k²); x_t = 100 × ln(P_t / P_t−1)- s_t
- regime in period t, numbered from 0 to K − 1 in order of increasing volatility
- a_ij
- entry of the transition matrix A: probability of moving from regime i to regime j; each row sums to 1
- μ_k, σ_k
- mean and volatility of the log return in regime k, in percent per week or month
- P_t
- closing price of week or month t
- π
- initial distribution: probabilities of the regimes in the first period, estimated along with the other parameters
- Log returns in percent: 100 × ln(P_t / P_t−1). Log returns can be added up over time; a mean of 0.25% per week is 13% a year.
- Weekly or monthly: the weekly close is the last trading day up to Friday, the monthly close the last trading day of the month, each with its actual date. Over ten years, weekly returns give 520 observations, monthly returns only 120.
- Completed periods only: the current week or month only counts once its regular last trading day (Friday, or the last business day of the month) is in the price data or the period is over, for example after a Friday holiday. Otherwise half a week would be treated like a full one.
- One series: the computational core can also estimate several series with a full covariance matrix per regime. Both pages use a single series: the selected price series on the Regime page, the base portfolio or a ticker on the Tactical page.
Estimation with the Baum-Welch algorithm
The code estimates the parameters π, A, μ and σ by maximum likelihood using the Baum-Welch algorithm of Baum et al. (1970), a special case of the EM algorithm of Dempster et al. (1977). Each iteration has two steps:
- E-step: using the current parameters, the forward-backward algorithm computes for each period the probability γ_t(k) of each regime and, for each pair of consecutive periods, the probability ξ_t(i, j) of a switch from i to j.
- M-step: new parameters as weighted averages, namely the initial distribution π = γ_1, the transitions a_ij = Σ ξ_t(i, j) / Σ γ_t(i), and the mean and variance of each regime with the weights γ_t(k).
μ_k = Σ_t γ_t(k) × x_t / Σ_t γ_t(k); σ_k² = Σ_t γ_t(k) × (x_t − μ_k)² / Σ_t γ_t(k) + 10⁻⁴ × Var(x)- γ_t(k)
- probability that period t was in regime k, given all returns and the parameters of the current iteration
- Var(x)
- variance of the whole series; the add-on is the covariance floor
To keep the probabilities from drifting toward zero over thousands of periods, the algorithm works in scaled form, as in Rabiner (1989): the forward quantities are normalized to sum to 1 in every period, and the logarithms of the normalization factors add up to the log-likelihood. The code also divides the densities of each period by their maximum; this factor, too, enters log L.
α_0 ∝ π ∘ b_0; α_t ∝ (α_t−1 · A) ∘ b_t; c_t = sum of the entries of (α_t−1 · A) ∘ b_t; log L = Σ_t ln c_t- b_t
- densities of the return x_t in the K regimes
- ∘
- element-wise product
- α_t
- filtered probabilities of the regimes after period t (sum 1)
- c_t
- normalization factor: density of the new return, given all earlier ones; c_0 is the sum of π ∘ b_0
- Stopping rule: when log L rises by less than 10⁻⁶ in an iteration, or after 300 iterations at the latest. If the estimate shown has not converged by then, the Regime page says so in a note.
- Five starting values, fixed seed: EM only finds a local maximum. The first start assigns the periods by rolling volatility (centered window of 9 periods, sample data only): with two regimes, the calmer half goes into regime 0 and the more volatile half into regime 1. The other four draw random boundaries between the 15th and 95th percentiles of this volatility and blend each assignment half and half with random weights from a Dirichlet distribution. The random number generator always starts with the seed 1989, so the same data always produce the same model.
- Starting parameters: one M-step from this assignment. The code counts the transitions from consecutive periods, adding 1 to each cell of the matrix; the initial distribution is uniform.
- All starts at once: the starts run side by side until each one stops. The one with the highest log-likelihood wins.
- Floors: 10⁻⁴ of the series' total variance is added to the variance of each regime. A regime's volatility is therefore at least 1% of the total volatility; without this floor, a regime could explain a single value with a volatility close to zero and drive the likelihood to infinity. The code raises entries of π and A to at least 10⁻¹².
- Minimum data: the estimation needs at least 2K + 2 returns, the Regime page 100, and the tactical model 260 weekly or 120 monthly returns for its first estimate.
Ordering and naming the regimes
For the likelihood, it makes no difference which regime carries which number (label switching). After each estimation, the code therefore sorts the regimes by variance in ascending order (regime 0 is always the calmest) and assigns names according to the number of regimes:
| Number of regimes | Names in order of increasing volatility |
|---|---|
| 1 | Single |
| 2 | Calm, Turbulent |
| 3 | Calm, Normal, Turbulent |
| 4 | Calm, Normal, Unsettled, Turbulent |
The names only describe the volatility ranking. “Turbulent” does not mean crisis or recession, and the mean of a regime can have either sign.
Sorting alone does not always solve label switching: if two regimes are close in volatility, a new estimate can swap their order even though the regimes have barely changed. The code therefore checks three things and displays them:
- Matching with the previous estimate (Tactical): at each annual re-estimation, the code looks for the assignment of new to old regimes with the smallest sum of Bhattacharyya distances, across all K! assignments. If it differs from the sorting, the Regime tab reports a role swap with its date. The names still follow volatility: “Turbulent” remains the most volatile regime, which is what the trading rule relies on.
- Gap between volatilities: each estimate comes with the smallest ratio of the volatilities of adjacent regimes. Below 1.2, the Regime page and the Tactical page point out that the assignment of names is uncertain.
- Starting values at the same optimum: the code counts how many starting values lead to the same solution, i.e. to the same regimes (Bhattacharyya distance per regime at most 0.02) and the same probabilities of staying (at most 5 percentage points apart); the initial distribution does not count. If at most half of them reach the best solution, the likelihood has several peaks, and the page says so.
D_B = 1/8 · (μ₁ − μ₂)² / σ̄² + 1/2 · ln(σ̄² / (σ₁ · σ₂)), σ̄² = (σ₁² + σ₂²) / 2- μ, σ
- mean and volatility of two regimes (percent per period)
- D_B
- Bhattacharyya distance: 0 for identical distributions, larger for different means or different dispersion
On the computational side, the code runs the forward and backward recursions in blocks of 32 periods as matrix products, for all starting values at once. Mathematically, this is the ordinary forward-backward algorithm; the tests compare it with a simple loop over all periods. Because the block boundaries do not depend on the length of the series, the filter for the first t returns equals the filter of the whole series at that point, up to rounding errors (below 10⁻¹²). A later return does not change any earlier filtered probability.
Filtered, smoothed, Viterbi: what the page shows
A single estimate yields three views of the regimes. They differ in which returns they use:
| Quantity | Uses returns | On the Regime page | On the Tactical page |
|---|---|---|---|
| Filtered: P(s_t | x_1 … x_t) | up to period t | “Current regime”, “Today” column, “Filtered” chart | signal at each month-end |
| Smoothed: P(s_t | x_1 … x_T) | all, up to the end of the series | background of the price chart, “Share of time”, “Smoothed” chart (default) | not used |
| Viterbi path | all, up to the end of the series | “Since (Viterbi)”: start of the last segment | not used |
The filtered probability is the Hamilton filter from the formula above: prediction with A, weighting with the densities, normalization. The smoothed probability γ_t comes from the forward-backward algorithm; Kim (1994) described it for Markov-switching models (filtered and smoothed probability). The Viterbi algorithm of Viterbi (1967) uses dynamic programming in log space to find the single sequence of regimes with the highest joint probability.
Smoothing contains look-ahead: the probability for week t also uses the returns of the weeks that follow. If an unremarkable week is followed by a slump, the smoothed curve already colors that week as more turbulent, even though nobody could have known it at the time. The chart below shows this. The Regime page adds a second kind of look-ahead: the parameters, too, come from the full history, so even the filtered curve there is a look back. The page says so in a note; only the tactical model computes without either kind of look-ahead (look-ahead bias).
In the price chart on a logarithmic axis, the page colors each period by the regime with the highest smoothed probability. “Current regime” is the regime with the highest filtered probability in the last completed period. “Since (Viterbi)” gives the closing date of the first period in the last contiguous segment of the Viterbi path, together with its length; if the path ends in a regime other than the current one, the name of that regime appears instead of the length.
Worked example: one step of the Hamilton filter
Two regimes with weekly returns; the figures are chosen freely. Calm (regime 0): mean 0.25%, volatility 1.5% per week, a₀₀ = 0.98. Turbulent (regime 1): mean −0.4%, volatility 4.0%, a₁₁ = 0.90. After the previous week, the filtered probability of “Turbulent” was 10%. Now a week brings a log return of −3%.
Example parameters from above, initial distribution = stationary distribution. Weeks 1 to 15 and 26 to 40 alternate between +1% and −0.5%, weeks 16 to 25 between −4% and +3%. The filter only exceeds 50% in week 18 and only falls back below it in week 27. The smoothed curve is already at 34.1% in week 15 (before the first slump) and falls below 50% one week earlier; the Viterbi path hits the turbulent weeks exactly, because it knows the end of the series.
Metrics per regime
The “Regimes at a glance” table annualizes each regime from its estimated parameters, with n = 52 periods for weekly and n = 12 for monthly returns:
Mean p.a. = μ_k × n; Vol p.a. = σ_k × √n; geometric return p.a. ≈ exp(μ_k × n / 100) − 1; expected duration = 1 / (1 − a_kk)- μ_k, σ_k
- mean and volatility of the log returns in regime k, in percent per period
- a_kk
- probability of staying in regime k (diagonal of A)
- expected duration
- in periods; the page shows it in weeks or months
- Mean and vol p.a.: mean and volatility of the log returns in the regime, scaled up by n or √n. The geometric return is the performance that a full year in this regime would produce.
- Share of time: average of the smoothed probabilities over all periods of the series.
- Expected duration: a regime ends in each period with probability 1 − a_kk. The duration is therefore geometrically distributed with a mean of 1 / (1 − a_kk) periods. The “Expected duration” tile shows it for the current regime.
- Today: filtered probability of the last completed period.
- Stationary distribution: the long-run share of each regime implied by A alone (π A = π). The code computes it as well, but the table shows the share of time. The two need not agree: the stationary distribution comes from the model, the share of time from the actual series.
- Transition matrix: row = regime in this period, column = regime in the next, in percent.
Model selection: log L, AIC, BIC and Auto
For each series, the Regime page estimates four models with one to four regimes, those with several regimes each from five starting values. More regimes almost always raise the log-likelihood; information criteria subtract a penalty per parameter to compensate. The count includes K − 1 initial probabilities, K(K − 1) transition probabilities and, per regime, d means and d(d + 1)/2 variances and covariances. For a single series (d = 1), that makes 2, 7, 14 and 23 parameters for one to four regimes.
p = (K − 1) + K(K − 1) + K·d + K·d(d + 1)/2; AIC = 2p − 2 log L; BIC = p × ln n − 2 log L- log L
- maximized log-likelihood of the estimate
- n
- number of returns
- AIC, BIC
- criteria of Akaike (1974) and Schwarz (1978); smaller is better
From eight returns upwards, ln n is greater than 2: the BIC therefore penalizes each parameter more heavily than the AIC, with 6.91 instead of 2 for 1,000 weekly returns. Schwarz (1978) derived it as a large-sample approximation to Bayesian model selection. With Auto, the page shows the model with the smallest BIC among two, three and four regimes; in a tie, the smaller number wins. If the model with just one regime has the smallest BIC, a note says that the BIC prefers a single regime. The page still shows the best model with at least two. The “Model selection” table marks the model shown and the one with the smallest BIC.
An example with 1,000 weekly returns (ln 1,000 = 6.908) and freely chosen values for log L:
| Regimes K | Parameters p | log L | AIC | BIC |
|---|---|---|---|---|
| 1 | 2 | −2,250.0 | 4,504.0 | 4,513.8 |
| 2 | 7 | −2,180.0 | 4,374.0 | 4,408.4 |
| 3 | 14 | −2,170.0 | 4,368.0 | 4,436.7 |
| 4 | 23 | −2,165.0 | 4,376.0 | 4,488.9 |
The AIC would be smallest with three regimes, the BIC with two. The third regime improves log L by 10, i.e. −2 log L by 20; in return, it adds 7 parameters, which the AIC penalizes with 14 and the BIC with 7 × 6.908 = 48.4. Auto therefore shows two regimes. The usual likelihood ratio test does not help with this choice, because the parameters of an additional regime are not even identified under the null hypothesis (regime models).
Tactical: the regime model without look-ahead
The tactical model Regime (HMM) runs the same estimation walk-forward: at each month-end, it uses only returns up to that date and parameters from an estimate that extends at most to that date.
- Signal: the return of the base portfolio (with the base weights, reset to the weights at each month-end, from daily prices in the base currency) or of a separate ticker that only supplies the signal and is not held. The page loads its prices in addition; if they start later than those of the portfolio, the period is shortened.
- First estimate at the first month-end with at least 260 weekly returns (five years) or 120 monthly returns (ten years) since the start of the common prices in the selected period, with five starting values.
- Re-estimation at every 12th month-end thereafter, i.e. annually, with all returns up to that point (expanding window). The starting values are the parameters of the last estimate (warm start) and a fresh start based on rolling volatility; the higher likelihood wins.
- Only filtering in between: the probability at month-end t is the Hamilton filter over all returns up to t, with the parameters of the last estimate up to t. At month-end, the last completed week counts; the days between it and the month-end only enter the following month.
- P(Turbulent) is the filtered probability of the regime with the highest volatility. With three regimes, “Normal” counts like “Calm”.
- Rule: with Threshold (default), the entire base portfolio moves into the fallback asset when P(Turbulent) is above the threshold (default 50%, selectable from 30 to 90%); otherwise the base weights apply. With Share by probability, 1 − P(Turbulent) is held in the base portfolio and the rest in the fallback asset.
- Execution, costs and comparison as for all tactical models: at the closing price of the same month-end or one month later, optionally with trading costs per order, compared with buying and holding the base weights from the first execution, i.e. only from the first estimate onwards (Tactical methodology).
- Monthly prices only: if the median gap between two prices is more than 20 days, as with asset classes that only have monthly values, the model uses monthly instead of weekly returns and says so in a note.
| Setting | Default | Selectable |
|---|---|---|
| Number of regimes | 2 (Calm, Turbulent) | 2 or 3 (with Normal) |
| Returns | weekly | weekly or monthly |
| Rule | Threshold | Threshold or Share by probability |
| Threshold for P(Turbulent) | 50% | 30 to 90% |
| Signal from | Base portfolio | Base portfolio or own ticker (signal only) |
| First estimate | after 260 weekly or 120 monthly returns | – |
| Re-estimation | at every 12th month-end | – |
The Regime tab in the results shows P(Turbulent) at each month-end (under the Threshold rule, with the threshold line), the regimes at the last month-end with mean, volatility, expected duration and probability from the estimate valid at that time, the transition matrix, and the number and dates of the estimates. Notes give the source of the signal and the first estimate with its number of returns.
Data and settings of the Regime page
| Setting | Default | Selectable |
|---|---|---|
| Series | S&P 500 (^GSPC) | the 30 markets of the Bubbles page in four groups, or your own symbol |
| Number of regimes | 2 | 2, 3, 4 or Auto (BIC) |
| Returns | weekly | weekly or monthly |
| Start | full history | a year from 1900 to the current year |
- Prices from the app's price cache in trading currency or index points, without conversion (unlike the Tactical page, which calculates in the base currency).
- At least 100 returns; otherwise the page states the available number and the minimum.
- Monthly values only: if the median gap between two of the last 260 prices is more than 20 days, the page uses monthly returns and says so.
- Cache: the four estimates per series, frequency and start year, and the response per number of regimes, for at most 12 hours and only until midnight, because the next day brings new closing prices.
Limitations
- Normal distribution per regime: within a regime, the return has no fat tails, no skewness and no volatility dynamics of its own; heavy tails arise only from the mixture of regimes. Using ten subseries of around 17,000 daily S&P 500 returns, Rydén et al. (1998) show that an HMM built from normal distributions reproduces most stylized facts well. The estimated parameters, however, sometimes varied considerably from one subseries to the next.
- Fixed parameters: within one estimate, μ, σ and A apply to the whole series. Nystrup et al. (2017) let the parameters drift over time; this way, even a model with two regimes reproduces the long memory of squared daily returns, which until then had been the hardest feature for an HMM to capture. Portfolio Lab adapts the parameters only through the annual re-estimation on the Tactical page.
- Parameter uncertainty: the pages show point estimates without standard errors. With few switches, the transition probabilities in particular, and with them the expected durations, are uncertain.
- Number of regimes: the number of parameters grows quadratically with K; according to Nystrup, Madsen and Lindström (2015), this is why such models for financial returns rarely have more than two states. AIC and BIC can contradict each other, and the Tactical page only allows two or three regimes.
- Label switching: sorting by volatility determines which regime is called “Turbulent”. If two regimes are close in volatility, a re-estimation can swap their roles; the pages report this (matching, gap, starting values), but it cannot be fixed. Stephens (2000) shows for Bayesian mixture models that such artificial ordering constraints do not solve the problem in general. If you see frequent swaps or narrow gaps, you are better off choosing fewer regimes.
- Local maxima: five starting values reduce the risk of landing on a poor local maximum, but do not rule it out.
- Frequent trading with the proportional rule: 1 − P(Turbulent) changes every month, so the portfolio is reallocated every month, with costs and, in a real brokerage account, taxes. The threshold trades less often, but all or nothing. In Nystrup et al. (2018), a penalty on frequent trading made the approach more robust, and an optimization using forecasts from the HMM beat a fixed decision rule like this one.
- Statistical, not economic regimes: Calm and Turbulent are volatility ranks of a series, not phases of the business cycle. Rule-based macro regimes are described in the Macro methodology.
- Hindsight on the Regime page: smoothed probabilities, the Viterbi path and parameters from the full history change with every new period, even for weeks far in the past. The page is an analysis tool, not investment advice.
Frequently asked questions
Why does the Regime page spot crises earlier than the tactical model?
Because it calculates in hindsight: the smoothed probabilities also use later returns, and the parameters come from the full history. The tactical model only uses the filter, with parameters estimated up to the respective month-end. It therefore only detects a turbulent regime after a few weak or highly volatile weeks.
What does “Turbulent” mean?
The regime with the highest volatility in the estimated series. The names only follow the volatility ranking; a turbulent regime need not mean a crisis or a negative return.
How many regimes should I choose?
For a single price series, two or three are common; each additional regime costs considerably more parameters. Auto picks the smallest BIC among two to four regimes; the Model selection table also shows whether a single regime would already have the smallest BIC. The Tactical page offers two or three regimes.
Why do earlier regimes change when new prices come in?
On the Regime page, the parameters are re-estimated with every new period, and the smoothed probabilities use all returns up to the end. Only filtered values with fixed parameters stay the same. That is how the tactical model calculates between two estimates.
Does the result depend on chance?
No. The starting values use a fixed seed (1989); the same data and settings always produce the same model.
How long does a regime last on average?
1 / (1 − a_kk) periods, where a_kk is the probability of staying in the regime. With a_kk = 0.98 that is 50 weeks, with 0.90 ten weeks.
Literature
- Akaike, H. (1974): A new look at the statistical model identification. IEEE Transactions on Automatic Control 19 (6), pp. 716–723. doi:10.1109/tac.1974.1100705
- Baum, Leonard E.; Petrie, Ted; Soules, George; Weiss, Norman (1970): A Maximization Technique Occurring in the Statistical Analysis of Probabilistic Functions of Markov Chains. The Annals of Mathematical Statistics 41 (1), pp. 164–171. doi:10.1214/aoms/1177697196
- Dempster, A. P.; Laird, N. M.; Rubin, D. B. (1977): Maximum Likelihood from Incomplete Data Via the EM Algorithm. Journal of the Royal Statistical Society Series B: Statistical Methodology 39 (1), pp. 1–22. doi:10.1111/j.2517-6161.1977.tb01600.x
- Hamilton, James D. (1989): A New Approach to the Economic Analysis of Nonstationary Time Series and the Business Cycle. Econometrica 57 (2), pp. 357–384. doi:10.2307/1912559
- Kim, Chang-Jin (1994): Dynamic linear models with Markov-switching. Journal of Econometrics 60 (1–2), pp. 1–22. doi:10.1016/0304-4076(94)90036-1
- Nystrup, Peter; Madsen, Henrik; Lindström, Erik (2015): Stylised facts of financial time series and hidden Markov models in continuous time. Quantitative Finance 15 (9), pp. 1531–1541. doi:10.1080/14697688.2015.1004801
- Nystrup, Peter; Madsen, Henrik; Lindström, Erik (2017): Long Memory of Financial Time Series and Hidden Markov Models with Time-Varying Parameters. Journal of Forecasting 36 (8), pp. 989–1002. doi:10.1002/for.2447
- Nystrup, Peter; Madsen, Henrik; Lindström, Erik (2018): Dynamic portfolio optimization across hidden market regimes. Quantitative Finance 18 (1), pp. 83–95. doi:10.1080/14697688.2017.1342857
- Rabiner, L. R. (1989): A tutorial on hidden Markov models and selected applications in speech recognition. Proceedings of the IEEE 77 (2), pp. 257–286. doi:10.1109/5.18626
- Rydén, Tobias; Teräsvirta, Timo; Åsbrink, Stefan (1998): Stylized facts of daily return series and the hidden Markov model. Journal of Applied Econometrics 13 (3), pp. 217–244. doi:10.1002/(sici)1099-1255(199805/06)13:3<217::aid-jae476>3.0.co;2-v
- Schwarz, Gideon (1978): Estimating the Dimension of a Model. The Annals of Statistics 6 (2), pp. 461–464. doi:10.1214/aos/1176344136
- Stephens, Matthew (2000): Dealing With Label Switching in Mixture Models. Journal of the Royal Statistical Society Series B: Statistical Methodology 62 (4), pp. 795–809. doi:10.1111/1467-9868.00265
- Viterbi, A. (1967): Error bounds for convolutional codes and an asymptotically optimum decoding algorithm. IEEE Transactions on Information Theory 13 (2), pp. 260–269. doi:10.1109/tit.1967.1054010
This page describes how Portfolio Lab calculates; past results are not a forecast. All terms in the glossary.