Note
The following implementations and documentation closely follow the publication by Bock, M. and Mestel, R: A regime-switching relative value arbitrage rule. Operations Research Proceedings 2008, pages 9–14. Springer..
Regime-Switching Arbitrage Rule
The traditional pairs trading strategy usually fails when fundamental or economic reasons cause a structural break on one of the stocks in the pair. This break will cause the temporary spread deviations formed by the pair to become persistent spread deviations which will not revert. Under these circumstances, betting on the spread to revert to its historical mean would imply a loss.
To overcome the problem of detecting whether the deviations are temporary or longer-lasting, the paper by Bock and Mestel bridges the literature on Markov regime-switching and the scientific work on statistical arbitrage to develop useful trading rules for pairs trading.
Assumptions
Series Formed by the Trading Pair
It models the series \(X_t\) formed by the trading pair as,
where
\(E[\epsilon_t] = 0\), \(\sigma^2_{\epsilon_t} = \sigma^2_{s_t}\) and \(s_t\) denotes the current regime.
Markov Regime-Switching Model
A two-state, first-order Markov-switching process for \(s_t\) is considered with the following transition probabilities:
where
\(1\) indicates a regime with a higher mean (\(\mu_{1}\)) while \(2\) indicates a regime with a lower mean (\(\mu_{2}\)).
Strategy
The trading signal \(z_t\) is determined in the following way:
\(Case\ 1 \ \ current\ regime = 1\)
\(Case\ 2 \ \ current\ regime = 2\)
where
\(P(\cdot)\) denotes the smoothed probabilities for each state,
\(\delta\) and \(\rho\) denote the standard deviation sensitivity parameter and the probability threshold of the trading strategy, respectively.
To be more specific, the trading signal can be described as,
\(Case\ 1 \ \ current\ regime = 1\)
\(Case\ 2 \ \ current\ regime = 2\)
Steps to Execute the Strategy
Step 1: Select a Trading Pair
In this paper, they used the DJ STOXX 600 component as the asset pool and applied the cointegration test for the pairs selection. One can use the same method as the paper did or other pairs selection algorithms like the distance approach for finding trading pairs.
Step 2: Construct the Spread Series
In this paper, they used \(\frac{P^A_t}{P^B_t}\) as the spread series. One can use the same method as the paper did or other formulae like \((P^A_t/P^A_0) - \beta \cdot (P^B_t/P^B_0)\) and \(ln(P^A_t/P^A_0) - \beta \cdot ln(P^B_t/P^B_0)\) for constructing the spread series.
Step 3: Estimate the Parameters of the Markov Regime-Switching Model
Fit the Markov regime-switching model to the spread series with a rolling time window to estimate \(\mu_1\), \(\mu_2\), \(\sigma_1\), \(\sigma_2\) and the current regime.
Step 4: Determine the Signal of the Strategy
Determine the current signal based on the strategy and estimated parameters.
Step 5: Decide the Trade
Decide the trade based on the signal at time \(t\) and the position at \(t - 1\). Possible combinations are listed below:
\(Position_{t - 1}\) |
\(Open\ a\ long\ trade\) |
\(Close\ a\ long\ trade\) |
\(Open\ a\ short\ trade\) |
\(Close\ a\ short\ trade\) |
\(Trade\ Action\) |
\(Position_{t}\) |
---|---|---|---|---|---|---|
0 |
True |
False |
False |
X |
Open a long trade |
+1 |
0 |
False |
X |
True |
False |
Open a short trade |
-1 |
0 |
Otherwise |
Do nothing |
0 |
|||
+1 |
False |
True |
False |
X |
Close a long trade |
0 |
+1 |
False |
X |
True |
False |
Close a long trade and open a short trade |
-1 |
+1 |
Otherwise |
Do nothing |
+1 |
|||
-1 |
False |
X |
False |
True |
Close a short trade |
0 |
-1 |
True |
False |
False |
X |
Close a short trade and open a long trade |
+1 |
-1 |
Otherwise |
Do nothing |
-1 |
where X denotes the don’t-care term, the value of X could be either True or False.
Implementation
Tip
If the user is not satisfied with the default trading strategy described in the paper, one can use
the change_strategy
method to modify it.
Examples
Code Example
>>> import matplotlib.pyplot as plt
>>> import yfinance as yf
>>> from arbitragelab.time_series_approach.regime_switching_arbitrage_rule import (
... RegimeSwitchingArbitrageRule,
... )
>>> data = yf.download("CL=F NG=F", start="2015-01-01", end="2020-01-01", progress=False)[
... "Adj Close"
... ]
>>> # Construct spread series
>>> ratt = data["NG=F"] / data["CL=F"]
>>> rsar = RegimeSwitchingArbitrageRule(delta=1.5, rho=0.6)
>>> window_size = 60
>>> # Get the current signal
>>> signal = rsar.get_signal(
... ratt[-window_size:], switching_variance=False, silence_warnings=True
... )
>>> # [Open long, close long, open short, close short]
>>> list(signal)
[True, False, False, True]
>>> signals = rsar.get_signals(
... ratt, window_size, switching_variance=True, silence_warnings=True
... )
>>> signals
array(...)
>>> signals.shape
(1256, 4)
>>> # Decide on trades based on the signals
>>> trades = rsar.get_trades(signals)
>>> trades
array(...)
>>> trades.shape
(1256, 4)
>>> # Plot trades
>>> rsar.plot_trades(ratt, trades)
<Figure...>
>>> # Changing rules
>>> cl_rule = lambda Xt, mu, delta, sigma: Xt >= mu
>>> cs_rule = lambda Xt, mu, delta, sigma: Xt <= mu
>>> rsar.change_strategy("High", "Long", "Open", cl_rule)
>>> rsar.change_strategy("High", "Short", "Close", cs_rule)
>>> # Get signals on a rolling basis
>>> signals = rsar.get_signals(
... ratt, window_size, switching_variance=True, silence_warnings=True
... )
>>> signals
array(...)
>>> signals.shape
(1256, 4)
>>> # Deciding the trades based on the signals
>>> trades = rsar.get_trades(signals)
>>> trades
array(...)
>>> trades.shape
(1256, 4)
>>> # Plotting trades
>>> rsar.plot_trades(ratt, trades)
<Figure...>
Research Notebook
The following research notebook can be used to better understand the strategy described above.