Chapter 6: Fundamentals of Markov Models in Financial Markets
Chapter 6: Fundamentals of Markov Models in Financial Markets
- Understand Markov properties of financial time series
- Master applications of regime-switching models in finance
- Learn Markov regime-switching models
- Understand state-dependent financial risk
Knowledge Summary
1. Markov Properties of Financial Time Series
Financial markets have the following characteristics, making Markov models suitable modeling tools:
- Limited Memory: The efficient market hypothesis implies price changes depend primarily on current information
- State Dependence: Market volatility, liquidity, etc. behave differently in different market states
- Regime Switching: Markets switch between different regimes like bull and bear markets
In a weak form efficient market, prices fully reflect historical information, implying: where is the return at time .
2. Markov Regime-Switching Models (MS Models)
Basic Form:
where:
- is an unobservable state variable
- follows a Markov chain with transition probabilities
3. State-Dependent Financial Risk
Risk characteristics in different market states:
| Market State | Average Return | Volatility | Tail Risk | Liquidity |
|---|---|---|---|---|
| Bull Market | High | Low | Low | Abundant |
| Bear Market | Negative | High | High | Tight |
| Volatile Market | Near Zero | Medium | Medium | Normal |
4. State Identification and Classification
Common methods for defining states:
- Return Level: Divide states based on average returns
- Volatility Clustering: Classify by volatility levels
- Macroeconomic Indicators: Combine with business cycle indicators
- Technical Indicators: Use moving averages and other technical analysis tools
Example Code
Example 1: Simple Two-State Market Model
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
from scipy.optimize import minimize
import seaborn as sns
class MarkovSwitchingModel:
"""
Markov Regime-Switching Model
"""
def __init__(self, n_states=2):
self.n_states = n_states
self.transition_matrix = None
self.mu = None # Mean of each state
self.sigma = None # Standard deviation of each state
self.initial_probs = None
def simulate_data(self, n_periods=1000, seed=42):
"""
Simulate Markov regime-switching model data
Parameters:
n_periods: Number of simulation periods
seed: Random seed
Returns:
returns: Return sequence
states: State sequence
"""
np.random.seed(seed)
# Set model parameters (two-state example)
self.transition_matrix = np.array([
[0.85, 0.15], # Bull market persistence 0.85
[0.20, 0.80] # Bear market persistence 0.80
])
self.mu = np.array([0.05, -0.03]) # Bull market 5%, Bear market -3%
self.sigma = np.array([0.15, 0.25]) # Bull market 15%, Bear market 25%
self.initial_probs = np.array([0.6, 0.4])
# Simulate state sequence
states = np.zeros(n_periods, dtype=int)
returns = np.zeros(n_periods)
# Initial state
states[0] = np.random.choice(self.n_states, p=self.initial_probs)
# Generate state and return sequences
for t in range(n_periods):
if t > 0:
# Determine next state based on transition probabilities
states[t] = np.random.choice(
self.n_states,
p=self.transition_matrix[states[t-1]]
)
# Generate returns based on current state
current_state = states[t]
returns[t] = np.random.normal(
self.mu[current_state],
self.sigma[current_state]
)
return returns, states
def calculate_state_statistics(self, returns, states):
"""
Calculate statistical characteristics for each state
"""
stats_dict = {}
for state in range(self.n_states):
state_returns = returns[states == state]
if len(state_returns) > 0:
stats_dict[f'State_{state}'] = {
'count': len(state_returns),
'mean': np.mean(state_returns),
'std': np.std(state_returns),
'skewness': stats.skew(state_returns),
'kurtosis': stats.kurtosis(state_returns),
'min': np.min(state_returns),
'max': np.max(state_returns)
}
return stats_dict
# Create model and simulate data
ms_model = MarkovSwitchingModel(n_states=2)
returns, true_states = ms_model.simulate_data(n_periods=500)
# Calculate statistical characteristics
state_stats = ms_model.calculate_state_statistics(returns, true_states)
print("Markov Regime-Switching Model Parameters:")
print(f"Transition matrix:\n{ms_model.transition_matrix}")
print(f"State means: {ms_model.mu}")
print(f"State standard deviations: {ms_model.sigma}")
print(f"\nStatistical characteristics for each state:")
for state, stats_data in state_stats.items():
print(f"\n{state}:")
for key, value in stats_data.items():
print(f" {key}: {value:.4f}")
# Visualize simulated data
fig, axes = plt.subplots(3, 2, figsize=(15, 12))
# Subplot 1: Return time series
axes[0, 0].plot(returns, linewidth=1, alpha=0.8)
axes[0, 0].set_title('Simulated Return Sequence')
axes[0, 0].set_ylabel('Return')
axes[0, 0].grid(True, alpha=0.3)
# Subplot 2: State-dependent returns
state_colors = ['green', 'red']
for state in range(2):
mask = (true_states == state)
axes[0, 1].scatter(np.where(mask)[0], returns[mask],
c=state_colors[state], s=10, alpha=0.6,
label=f'State {state}')
axes[0, 1].set_title('State-Dependent Returns')
axes[0, 1].set_ylabel('Return')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)
# Subplot 3: State sequence
axes[1, 0].plot(true_states, linewidth=2)
axes[1, 0].set_title('True State Sequence')
axes[1, 0].set_ylabel('State')
axes[1, 0].set_ylim(-0.5, 1.5)
axes[1, 0].grid(True, alpha=0.3)
# Subplot 4: Return distribution (grouped by state)
for state in range(2):
state_returns = returns[true_states == state]
axes[1, 1].hist(state_returns, bins=30, alpha=0.6,
label=f'State {state}', density=True,
color=state_colors[state])
axes[1, 1].set_title('Return Distribution by State')
axes[1, 1].set_xlabel('Return')
axes[1, 1].set_ylabel('Density')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
# Subplot 5: Cumulative returns
cumulative_returns = np.cumprod(1 + returns) - 1
axes[2, 0].plot(cumulative_returns, linewidth=2)
axes[2, 0].set_title('Cumulative Returns')
axes[2, 0].set_xlabel('Time')
axes[2, 0].set_ylabel('Cumulative Return')
axes[2, 0].grid(True, alpha=0.3)
# Subplot 6: Regime duration analysis
def analyze_regime_duration(states):
"""Analyze regime duration"""
durations = {0: [], 1: []}
current_state = states[0]
current_duration = 1
for t in range(1, len(states)):
if states[t] == current_state:
current_duration += 1
else:
durations[current_state].append(current_duration)
current_state = states[t]
current_duration = 1
# Add last regime duration
durations[current_state].append(current_duration)
return durations
durations = analyze_regime_duration(true_states)
for state in range(2):
if durations[state]:
axes[2, 1].hist(durations[state], bins=20, alpha=0.6,
label=f'State {state} duration', color=state_colors[state])
axes[2, 1].set_title('Regime Duration Distribution')
axes[2, 1].set_xlabel('Duration (periods)')
axes[2, 1].set_ylabel('Frequency')
axes[2, 1].legend()
axes[2, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"\nAverage regime duration:")
for state in range(2):
if durations[state]:
avg_duration = np.mean(durations[state])
print(f"State {state}: {avg_duration:.2f} periods")
Theoretical Analysis
Theoretical Foundation of Financial Markov Models
Efficient Market Hypothesis and Markov Property: In a semi-strong efficient market, price changes satisfy:
where is the information set at time , and is the market state.
Likelihood Function of Regime-Switching Model:
Statistical Inference for State Identification
Filtering Probability:
Smoothing Probability:
Financial Risk Measures
State-Dependent VaR:
Expected Shortfall:
Mathematical Formula Summary
-
Regime-switching model:
-
Transition probability:
-
Log-likelihood function:
-
Expected duration:
-
Stationary probability: , where is the mean first passage time from state to state
- Choice of number of states requires balancing model complexity and fit quality
- Parameter estimation may have multiple local optima
- Out-of-sample predictive ability should be carefully evaluated
- Regime identification has inherent lag