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,

\[{p_t = e^{X_t}};\quad{X_{t_0} = x_0}\]

where \(X_t\) satisfies the following stochastic differential equation,

\[{dX_t = {\mu}({\theta} - X_t)dt + {\sigma}dW_t}\]

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:

\[\begin{split}\left\{ \begin{array}{**lr**} Open\ a\ short\ trade\ when\ Y_t = a_d\ and\ close\ the\ exiting\ short\ trade\ at\ Y_t = b_d.\\ Open\ a\ long\ trade\ when\ Y_t = -a_d\ and\ close\ the\ exiting\ long\ trade\ at\ Y_t = -b_d.\\ \end{array} \right.\end{split}\]

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

\[E[T] = \frac{1}{2\mu}\sum_{k=0}^{\infty} \Gamma(\frac{2k + 1}{2})((\sqrt{2}a_d)^{2k + 1} - (\sqrt{2}b_d)^{2k + 1})/ (2k + 1)!,\]
\[V[T] = \frac{1}{\mu^2}(V[T_{a_d,\ b_d}] + V[T_{-a_d,\ a_d,\ b_d}]),\]

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\).

\[V[T_{a_d,\ b_d}] = {w_1(a_d)} - {w_1(b_d)} - {w_2(a_d)} + {w_2(b_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)!\).

\[V[T_{-a_d,\ a_d,\ b_d}] = E[T^{2}_{-a_d,\ a_d,\ b_d}] - E[T_{-a_d,\ a_d,\ b_d}]^2,\]

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

\[\mu_s(a,\ b,\ c) = \frac{r(a,\ b,\ c)}{E [T]}\]
\[\sigma_s(a,\ b,\ c) = \frac{{r(a,\ b,\ c)}^2{V[T]}}{{E[T]}^3}\]

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\).

\[\frac{1}{2}\sum_{k=0}^{\infty} \Gamma(\frac{2k + 1}{2})((\sqrt{2}a_d)^{2k + 1} / (2k + 1)! = (a - c) \frac{\sqrt{2}}{2}\sum_{k=0}^{\infty} \Gamma(\frac{2k}{2})((\sqrt{2}a_d)^{2k} / (2k + 1)!\]

\(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\).

\[\frac{1}{2}\sum_{k=0}^{\infty} \Gamma(\frac{2k + 1}{2})((\sqrt{2}a_d)^{2k + 1} / (2k + 1)! = (a - \frac{c}{2}) \frac{\sqrt{2}}{2}\sum_{k=0}^{\infty} \Gamma(\frac{2k}{2})((\sqrt{2}a_d)^{2k} / (2k + 1)!\]

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.

\[k = k_d \frac{\sigma}{\sqrt{2\mu}} + \theta,\]

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.

Research Article


Presentation Slides


References