Chapter 6: Fundamentals of Markov Models in Financial Markets

Haiyue
11min

Chapter 6: Fundamentals of Markov Models in Financial Markets

Learning Objectives
  • 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
Weak Form Efficient Market Hypothesis and Markov Property

In a weak form efficient market, prices fully reflect historical information, implying: P(Rt+1Rt,Rt1,)=P(Rt+1Rt)P(R_{t+1} | R_t, R_{t-1}, \ldots) = P(R_{t+1} | R_t) where RtR_t is the return at time tt.

2. Markov Regime-Switching Models (MS Models)

Basic Form: yt=μst+ϵt,ϵtN(0,σst2)y_t = \mu_{s_t} + \epsilon_t, \quad \epsilon_t \sim N(0, \sigma_{s_t}^2)

where:

  • st{1,2,,k}s_t \in \{1, 2, \ldots, k\} is an unobservable state variable
  • {st}\{s_t\} follows a Markov chain with transition probabilities Pij=P(st+1=jst=i)P_{ij} = P(s_{t+1} = j | s_t = i)
🔄 正在渲染 Mermaid 图表...

3. State-Dependent Financial Risk

Risk characteristics in different market states:

Market StateAverage ReturnVolatilityTail RiskLiquidity
Bull MarketHighLowLowAbundant
Bear MarketNegativeHighHighTight
Volatile MarketNear ZeroMediumMediumNormal

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: P(Rt+1Ft)=P(Rt+1Rt,St)P(R_{t+1} | \mathcal{F}_t) = P(R_{t+1} | R_t, S_t)

where Ft\mathcal{F}_t is the information set at time tt, and StS_t is the market state.

Likelihood Function of Regime-Switching Model: L(θ)=t=1Tst=1kf(rtst,θ)P(stst1,θ)L(\theta) = \prod_{t=1}^T \sum_{s_t=1}^k f(r_t | s_t, \theta) P(s_t | s_{t-1}, \theta)

Statistical Inference for State Identification

Filtering Probability: P(St=jr1,,rt)=αt(j)i=1kαt(i)P(S_t = j | r_1, \ldots, r_t) = \frac{\alpha_t(j)}{\sum_{i=1}^k \alpha_t(i)}

Smoothing Probability: P(St=jr1,,rT)=αt(j)βt(j)i=1kαt(i)βt(i)P(S_t = j | r_1, \ldots, r_T) = \frac{\alpha_t(j) \beta_t(j)}{\sum_{i=1}^k \alpha_t(i) \beta_t(i)}

Financial Risk Measures

State-Dependent VaR: VaRα(j)=μj+σjΦ1(α)\text{VaR}_\alpha^{(j)} = \mu_j + \sigma_j \Phi^{-1}(\alpha)

Expected Shortfall: ESα(j)=μj+σjϕ(Φ1(α))α\text{ES}_\alpha^{(j)} = \mu_j + \sigma_j \frac{\phi(\Phi^{-1}(\alpha))}{\alpha}

Mathematical Formula Summary

  1. Regime-switching model: yt=μst+σstϵty_t = \mu_{s_t} + \sigma_{s_t} \epsilon_t

  2. Transition probability: Pij=P(St+1=jSt=i)P_{ij} = P(S_{t+1} = j | S_t = i)

  3. Log-likelihood function: (θ)=t=1Tlogj=1kP(St=jr1:t)f(rtSt=j)\ell(\theta) = \sum_{t=1}^T \log \sum_{j=1}^k P(S_t = j | r_{1:t}) f(r_t | S_t = j)

  4. Expected duration: E[Di]=11PiiE[D_i] = \frac{1}{1 - P_{ii}}

  5. Stationary probability: πj=1i=1kμji\pi_j = \frac{1}{\sum_{i=1}^k \mu_{ji}}, where μji\mu_{ji} is the mean first passage time from state jj to state ii

Model Application Considerations
  • 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