Note
The following implementations and documentation closely follow the publication by Zhengqin Zeng & Chi-Guhn Lee: Pairs trading: optimal thresholds and profitability. Quantitative Finance, 14(11): 1881–1893.
OU Model Optimal Trading Thresholds Zeng
In this paper, the authors enhance the work in Bertram(2010), which assumes no short selling of the synthetic asset when finding the optimal trading thresholds. To resolve the assumption, they derive a polynomial expression for the expectation of the first-passage time of an O-U process with two-sided boundary. Then they simplify the problem of optimizing the expected return per unit of time for choosing optimal trading thresholds to an equation solving problem.
Warning
We use \(\theta\) for long-term mean, \(\mu\) for mean-reversion speed and \(\sigma\) for amplitude of randomness of the O-U process, which is different from the reference paper.
Assumptions
Price of the Traded Security
It models the price of the traded security \(p_t\) as,
where \(X_t\) satisfies the following stochastic differential equation,
where \(\theta\) is the long-term mean, \(\mu\) is the speed at which the values will regroup around the long-term mean and \(\sigma\) is the amplitude of randomness of the O-U process.
Trading Strategy
The Trading strategy is defined as:
where \(Y_t\) is a dimensionless series transformed from the original time series \(X_t\),
\(a_d\) and \(b_d\) is the entry and exit thresholds in the dimensionless system, respectively.
Trading Cycle
The trading cycle is completed as \(Y_t\) change from \(a_d\) to \(b_d\), then back to \(a_d\) or \(-a_d\), and the trade length \(T\) is defined as the time needed to complete a trading cycle.
Analytic Formulae
Mean and Variance of the Trade Length
where
\(V[T_{a_d,\ b_d}]\) is the variance of the time taken for the O-U process to travel from \(a_d\) to \(b_d\),
\(V[T_{-a_d,\ a_d,\ b_d}]\) is the variance of the time taken for the O-U process to travel from \(b_d\) to \(a_d\) or -\(a_d\).
where
\(w_1(z) = (\frac{1}{2} \sum_{k=1}^{\infty} \Gamma(\frac{k}{2}) (\sqrt{2}z)^k / k! )^2 - (\frac{1}{2} \sum_{n=1}^{\infty} (-1)^k \Gamma(\frac{k}{2}) (\sqrt{2}z)^k / k! )^2\),
\(w_2(z) = \sum_{k=1}^{\infty} \Gamma(\frac{2k - 1}{2}) \Psi(\frac{2k - 1}{2}) (\sqrt{2}z)^{2k - 1} / (2k - 1)!\).
where
\(E[T_{-a_d,\ a_d,\ b_d}] = \frac{1}{2}\sum_{k=1}^{\infty} \Gamma(k)((\sqrt{2}a_d)^{2k} - (\sqrt{2}b_d)^{2k})/ (2k)!\),
\(E[T^{2}_{-a_d,\ a_d,\ b_d}] = e^{(b^2_d - a^2_d)/4}[g_1(a_d,\ b_d) - g_2(a_d,\ b_d)]\),
where
\(g_1(a_d,\ b_d) = [\frac{(m^{''}(\lambda,\ b_d)\ m(\lambda,\ a_d) - m^{'}(\lambda,\ a_d)\ m^{'}(\lambda,\ b_d))}{m^2(\lambda,\ a_d)}]|_{\lambda = 0}\),
\(g_2(a_d,\ b_d) =[\frac{(m^{''}(\lambda,\ a_d)\ m(\lambda,\ b_d) + m^{'}(\lambda,\ a_d)\ m^{'}(\lambda,\ b_d))}{m^2(\lambda,\ a_d)} - 2\frac{(m^{'}(\lambda,\ a_d))^2\ m(\lambda,\ b_d)}{m^3(\lambda,\ a_d)}]|_{\lambda = 0}\),
where \(m(\lambda, x) = D_{-\lambda}(x) + D_{-\lambda}(−x)\),
where \(D_{-\lambda}(x) = \sqrt{\frac{2}{\pi}} e^{x^2/4} \int_{0}^{\infty} t^{-\lambda} e^{-t^2/2} \cos(xt + \frac{\lambda\pi}{2})dt\).
Mean and Variance of the Return per Unit of Time
where \(r(a,\ b,\ c) = (|a − b| − c)\) gives the continuously compound rate of return for a single trade accounting for transaction cost,
where \(a\), \(b\) denotes the entry and exit thresholds, respectively.
Optimal Strategies
To calculate an optimal trading strategy, we seek to choose optimal entry and exit thresholds that maximise the expected return per unit of time for a given transaction cost.
Get Optimal Thresholds by Maximizing the Expected Return
\(Case\ 1 \ \ 0 \leqslant b_d \leqslant a_d\)
This paper shows that the maximum expected return occurs when \(b_d = 0\). Therefore, for a given transaction cost, the following equation can be solved to find optimal \(a_d\).
\(Case\ 2 \ \ -a_d \leqslant b_d \leqslant 0\)
This paper shows that the maximum expected return occurs when \(b_d = -a_d\). Therefore, for a given transaction cost, the following equation can be solved to find optimal \(a_d\).
Back Transform from the Dimensionless System
After calculating optimal thresholds in the dimensionless system, we need to use the following formula to transform them back to the original system.
where \(k_d\) = \(a_d\), \(b_d\), \(-a_d\), \(-b_d\) and \(k\) = \(a_s\), \(b_s\), \(a_l\), \(a_l\),
where
\(a_s\), \(b_s\) denotes the entry and exit thresholds for a short position,
\(a_l\), \(b_l\) denotes the entry and exit thresholds for a long position.
Implementation
Initializing OU-Process Parameters
One can initialize the O-U process by directly setting its parameters or by fitting the process to the given data. The fitting method can refer to pp. 12-13 in the following book: Tim Leung and Xin Li, Optimal Mean reversion Trading: Mathematical Analysis and Practical Applications.
Getting Optimal Thresholds
This paper examines the problem of choosing an optimal strategy under two different cases. Case 1 corresponds to the ‘Conventional Optimal Rule’, and case 2 corresponds to the ‘New Optimal Rule’. One can choose either to get the thresholds. The following functions will return a tuple contains \(a_s\), \(b_s\), \(a_l\) and \(a_l\), where \(a_s\), \(b_s\) denotes the entry and exit thresholds for a short position, \(a_l\), \(b_l\) denotes the entry and exit thresholds for a long position.
Note
initial_guess
is used to speed up the process and ensure the target equation can be solved by
scipy.optimize
. If the value of initial_guess
is not given, the default value will be
\((c + 10^{-2})\sqrt{2\mu} / \sigma\). From our experiment, the default value is suited for most of the cases.
If you observe that the thresholds got by the functions is odd or the running time is larger than 5 second,
please try a initial_guess
on different scales.
Calculating Metrics
One can calculate performance metrics for the trading strategy using the following functions.
Plotting Comparison
One can use the following functions to observe the impact of transaction costs and risk-free rates on the optimal thresholds and performance metrics under the optimal thresholds.
Examples
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from arbitragelab.time_series_approach.ou_optimal_threshold_zeng import (
... OUModelOptimalThresholdZeng,
... )
>>> ouotz = OUModelOptimalThresholdZeng()
>>> # Init OU-process parameter
>>> ouotz.construct_ou_model_from_given_parameters(theta=3.4241, mu=0.0237, sigma=0.0081)
>>> # Getting optimal thresholds by Conventional Optimal Rule.
>>> a_s, b_s, a_l, b_l = ouotz.get_threshold_by_conventional_optimal_rule(c=0.02)
>>> # When we enter a short position
>>> a_s
3.47...
>>> # When we exit a short position
>>> b_s
3.42...
>>> # When we enter a long position
>>> a_l
3.37...
>>> # When we exit a long position
>>> b_l
3.42...
>>> # Get the expected return and the variance for both long and short trades
>>> # Short
>>> ouotz.expected_return(a=a_s, b=b_s, c=0.02)
0.0003...
>>> ouotz.return_variance(a=a_s, b=b_s, c=0.02)
2.54...e-05
>>> # Long
>>> ouotz.expected_return(a=a_l, b=b_l, c=0.02)
0.0003...
>>> ouotz.return_variance(a=a_l, b=b_l, c=0.02)
2.207...e-05
>>> # Getting optimal thresholds by New Optimal Rule.
>>> a_s, b_s, a_l, b_l = ouotz.get_threshold_by_new_optimal_rule(c=0.02)
>>> # When we enter a short position
>>> a_s
3.460...
>>> # When we exit a short position
>>> b_s
3.38...
>>> # When we enter a long position
>>> a_l
3.38...
>>> # When we exit a long position
>>> b_l
3.460...
>>> # Get the expected return and the variance for both long and short trade
>>> # Short
>>> ouotz.expected_return(a=a_s, b=b_s, c=0.02)
0.00043...
>>> ouotz.return_variance(a=a_s, b=b_s, c=0.02)
3.467...e-05
>>> # Long
>>> ouotz.expected_return(a=a_l, b=b_l, c=0.02)
0.00043...
>>> ouotz.return_variance(a=a_l, b=b_l, c=0.02)
3.467...e-05
>>> # Setting a array contains transaction costs
>>> c_list = np.linspace(0, 0.02, 30)
>>> # Comparison of the expected return between the Conventional Optimal Rule and New Optimal Rule.
>>> fig_con = ouotz.plot_target_vs_c(
... target="expected_return", method="conventional_optimal_rule", c_list=c_list
... )
>>> fig_con
<Figure...>
>>> fig_new = ouotz.plot_target_vs_c(
... target="expected_return", method="new_optimal_rule", c_list=c_list
... )
>>> fig_new
<Figure...>
Research Notebooks
The following research notebook can be used to better understand the method described above.