<jupyter_start><jupyter_text>![QuantConnect Logo](https://cdn.quantconnect.com/web/i/icon.png)  Research to Production Hidden Markov Model--------- IntroductionThis notebook explains how to you can use the Research Environment to develop and test a Hidden Markov Model hypothesis, then put the hypothesis in production.Documentation Create HypothesisA Markov process is a stochastic process where the possibility of switching to another state depends only on the current state of the model by the current state's probability distribution (it is usually represented by a state transition matrix). It is history-independent, or memoryless. While often a Markov process's state is observable, the states of a Hidden Markov Model (HMM) is not observable. This means the input(s) and output(s) are observable, but their intermediate, the state, is non-observable/hidden.A 3-state HMM example, where S are the hidden states, O are the observable states and a are the probabilities of state transition.Image source: Modeling Strategic Use of Human Computer Interfaces with Novel Hidden Markov Models. <NAME>, et. al. (2015). Frontiers in Psychology 6:919. DOI:10.3389/fpsyg.2015.00919In finance, HMM is particularly useful in determining the market regime, usually classified into "Bull" and "Bear" markets. Another popular classification is "Volatile" vs "Involatile" market, such that we can avoid entering the market when it is too risky. We hypothesis a HMM could be able to do the later, so we can produce a SPY-out-performing portfolio (positive alpha).  Import LibrariesWe'll need to import libraries to help with data processing, validation and visualization. Import statsmodels, scipy, numpy, matplotlib and pandas libraries by the following:<jupyter_code>from statsmodels.tsa.regime_switching.markov_regression import MarkovRegression
from scipy.stats import multivariate_normal
import numpy as np

from matplotlib import pyplot as plt
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()<jupyter_output><empty_output><jupyter_text>Get Historical DataTo begin, we retrieve historical data for research.<jupyter_code># Instantiate a QuantBook.
qb = QuantBook()

# Select the desired tickers for research.
asset = "SPX"

# Call the AddIndex method with the tickers, and its corresponding resolution. Then store their Symbols. Resolution.Minute is used by default. 
qb.AddIndex(asset, Resolution.Minute)

# Call the History method with qb.Securities.Keys for all tickers, time argument(s), and resolution to request historical data for the symbol.
history = qb.History(qb.Securities.Keys, datetime(2019, 1, 1), datetime(2021, 12, 31), Resolution.Daily)
history<jupyter_output><empty_output><jupyter_text>Preparing DataWe'll have to process our data to get the volatility of the market for classification.<jupyter_code># Get the close price daily return.
close = history['close'].unstack(level=0)

# Call pct_change to obtain the daily return
returns = close.pct_change().iloc[1:]

# Initialize the HMM, then fit by the daily return data. Note that we're using varinace as switching regime, so switching_variance is set as True.
model = MarkovRegression(returns, k_regimes=2, switching_variance=True).fit()
display(model.summary())<jupyter_output>/opt/miniconda3/lib/python3.6/site-packages/statsmodels/tsa/base/tsa_model.py:218: ValueWarning: A date index has been provided, but it has no associated frequency information and so will be ignored when e.g. forecasting.
  ' ignored when e.g. forecasting.', ValueWarning)<jupyter_text>All p-values of the regime self-transition coefficients and the regime transition probability matrix's coefficient is < 0.05, indicating the model should be able to classify the data into 2 different volatility regimes.  Test the HypothesisWe now verify if the model can detect high and low volatility period effectively.<jupyter_code># Get the regime as a column, 1 as Low Variance Regime, 2 as High Variance Regime.
regime = pd.Series(model.smoothed_marginal_probabilities.values.argmax(axis=1)+1, 
                      index=returns.index, name='regime')
df_1 = close.loc[returns.index][regime == 1]
df_2 = close.loc[returns.index][regime == 2]

# Get the mean and covariance matrix of the 2 regimes, assume 0 covariance between the two.
mean = np.array([returns.loc[df_1.index].mean(), returns.loc[df_2.index].mean()])
cov = np.array([[returns.loc[df_1.index].var(), 0], [0, returns.loc[df_2.index].var()]])

# Fit a 2-dimensional multivariate normal distribution by the 2 means and covriance matrix.
dist = multivariate_normal(mean=mean.flatten(), cov=cov)
mean_1, mean_2 = mean[0], mean[1]
sigma_1, sigma_2 = cov[0,0], cov[1,1]

# Get the normal distribution of each of the distribution.
x = np.linspace(-0.05, 0.05, num=100)
y = np.linspace(-0.05, 0.05, num=100)
X, Y = np.meshgrid(x,y)
pdf = np.zeros(X.shape)
for i in range(X.shape[0]):
    for j in range(X.shape[1]):
        pdf[i,j] = dist.pdf([X[i,j], Y[i,j]])

# Plot the probability of data in different regimes.
fig, axes = plt.subplots(2, figsize=(15, 10))
ax = axes[0]
ax.plot(model.smoothed_marginal_probabilities[0])
ax.set(title='Smoothed probability of Low Variance Regime')
ax = axes[1]
ax.plot(model.smoothed_marginal_probabilities[1])
ax.set(title='Smoothed probability of High Variance Regime')
fig.tight_layout()
plt.show()
# Plot the series into regime-wise.
df_1.index = pd.to_datetime(df_1.index)
df_1 = df_1.sort_index()
df_2.index = pd.to_datetime(df_2.index)
df_2 = df_2.sort_index()
plt.figure(figsize=(15, 10))
plt.scatter(df_1.index, df_1, color='blue', label="Low Variance Regime")
plt.scatter(df_2.index, df_2, color='red', label="High Variance Regime")
plt.title("Price series")
plt.ylabel("Price ($)")
plt.xlabel("Date")
plt.legend()
plt.show()
# Plot the distribution surface.
fig = plt.figure(figsize=(20, 10))
ax = fig.add_subplot(122, projection = '3d')
ax.plot_surface(X, Y, pdf, cmap = 'viridis')
ax.axes.zaxis.set_ticks([])
plt.xlabel("Low Volatility Regime")
plt.ylabel("High Volatility Regime")
plt.title('Bivariate normal distribution of the Regimes')
plt.tight_layout()
plt.show()
# plot the contour
plt.figure(figsize=(12, 8))
plt.contourf(X, Y, pdf, cmap = 'viridis')
plt.xlabel("Low Volatility Regime")
plt.ylabel("High Volatility Regime")
plt.title('Bivariate normal distribution of the Regimes')
plt.tight_layout()
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10259598942950413, 'fraction_numerical': 0.02098554329239857, 'mean_word_length': 5.538617886178862, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 17, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '232262', 'license': 'Apache-2.0', 'n_tokens_mistral': 2122, 'n_tokens_neox': 1945, 'n_words': 698, 'path': '03 Research Environment/12 Applying Research/06 Hidden Markov Models/00 Hidden Markov Model.ipynb', 'repo_name': 'LouisSzeto/Documentation', 'size': '6440'}