In this tutorial, we apply Ornstein-Uhlenbeck model to a Pairs Trading process and derive the Optimal Entry and Liquidation levels.


Pairs trading is holding one stock while simultaneously shorting another stock, typically in an attempt to profit from the convergence of the spread between these two stocks. One method of execution is to apply a Kalman Filter, which we have implemented in this post. However, today, we will model the portfolio values of holding positions in a pair of stocks as an Ornstein-Uhlenbeck (OU) process in order to derive the optimal values to enter and liquidate the position in the pair of stocks. Let (number) refer to the corresponding equation in the given paper, which can be found under the References section. For example, (2.1) refers to equation 2.1 in the paper.


Computing the OU Coefficients

Say there are two arbitrary stocks A and B. For each value \(β\) in .01, .02, .03, …, 1.00:

  1. We compute the portfolio values over 252 days of the pairs trade by computing the value of holding $1 of A minus the value of holding $\(β\) B (we short B) on each of the 252 days
  2. Then we compute the OU coefficients (\(\theta\), \(\mu\), \(\sigma\)) of the 252 portfolio values using the Maximum Likelihood Estimation method, maximizing the average log-likelihood defined by (2.2):

\[\begin{equation} \begin{aligned} & -\frac{1}{2}ln(2\pi)-ln(\widetilde{\sigma}) \\ & -\frac{1}{2n{\widetilde{\sigma}}^2}\sum_{i=1}^{n}[x_i-x_{i-1}e^{-\mu\Delta t}-\theta(1-e^{-\mu\Delta t})]^2 \end{aligned} \end{equation}\]


  • \(x_j\) = the value at the \(j\)-th index of the portfolio values
  • \(\widetilde{\sigma}^2 = \sigma ^2\frac{1-e^{-2\mu\Delta t}}{2\mu}\)
  • \(n\) = the number of portfolio values
  • \(Δt\) = 1 ÷ (days between the start and end dates of the portfolio values)

We then select the \(\beta\), which we differentiate as \(\beta^*\), that maximizes the average log-likelihood defined in the equation in step 2, while keeping the corresponding \(\theta^*\), \(\mu^*\), and \(\sigma^*\) values. The implementation of finding \(\theta^*\), \(\mu^*\), and \(\sigma^*\) can be found in ou_mle.py in the Algorithm section.

Deriving the Optimal Entry and Liquidation Levels

With the OU coefficients in hand, we can now calculate the optimal entry level, the portfolio value at which we buy, and liquidation level, the portfolio value at which we sell. We don’t buy the portfolio of $1 of A and -$\(\beta^*\) of B immediately (note: to scale up a position, for each additional dollar we invest in A, we short an additional -$\(\theta^*\) of B). Instead, we wait until our portfolio reaches the computed entry level, before we buy the portfolio of the pair of stocks. Once we bought this portfolio, we wait until the portfolio value reaches the computed liquidation level before we liquidate our portfolio. By waiting for our optimal entry and liquidation values to be reached, we can mathematically maximize our expected gain. To compute the optimal entry and liquidation levels, we first need to define a few equations:

\[F(x):=\int_{0}^{\infty}u^{\frac{r}{\mu}-1}e^{\sqrt{\frac{2\mu}{\sigma^2}}(x-\theta)u-\frac{u^2}{2}}du\quad(3.3)\] \[G(x):=\int_{0}^{\infty}u^{\frac{r}{\mu}-1}e^{\sqrt{\frac{2\mu}{\sigma^2}}(\theta-x)u-\frac{u^2}{2}}du\quad(3.4)\] \[V(x)=(x\in (-\infty, b^*)\ ?\ (b^*-c) \frac{F(x)}{F(b^*)}:x-c)\quad(4.2)\] Where constants \(c\) = the cost of transaction and \(r\) = investor’s subject discount rate. We choose to set \(c = r = .05\) as these were the values given in the paper. 

With these equations, we can now solve for the optimal liquidation level, which we denote as \(b*\), as well as the optimal entry level, which we denote at \(d*\). Note: the reason we derive the optimal liquidation level first is because we use \(b*\) in the computation of \(d*\).

As deriving \(b*\) and \(d*\) require the derivative of the functions given above, given an arbitrary function input \(x\) and an arbitrary function \(f(x)\), we approximate \(f'(x)\) with the following equation equation:

\[f'(x) = \frac{f(x+h)-f(x)}{h}\] where \(h\) is some arbitrarily small value (we set \(h = 1 \times 10^{-4}\) in our implementation).

To find \(b*\), we solve for \(b\) in the following equation:

\[F(b)=(b-c)F'(b)\quad(4.3)\] We solve this equation by getting all terms to one side:

\[F(b)-(b-c)F'(b)=0\] Finding the root of the above equation results in \(b*\)

Now to derive \(d*\), we solve for \(d\) in the following equation:

\[G(d)(V'(d)-1)=G'(d)(V(d)-d-c)\] Again, we will move all terms to one side:

\[G(d)(V'(d)-1)-G'(d)(V(d)-d-c)=0\] And finding the root of this equation results in \(d*\).

The full method of finding \(b*\) and \(d*\) can be found in the OptimalStopping.py file in the Algorithm section.


Our trading logic is very simple. First, we feed in 252 points of the most recent data for the daily closing prices for stocks A and B. We then train our model on these data points, and we get the \(b*\), \(d*\), and \(\beta^*\) values. We then keep track of a hypothetical portfolio of holding $1 of A and -$\(\beta^*\) of B. Once the value of our hypothetical portfolio is less than or equal to \(b*\), we allocate 1.0 of our capital to long of A and short B using (\(\beta^*\) x capital used for A) worth of stock. Once the hypothetical portfolio value we tracking reaches \(d*\), we liquidate our positions. We repeat these trading rules when possible. On the first day of every quarter, we retrain our model with the most recent 252 points of data to update our \(b*\), \(d*\), and \(\beta^*\) values.

Video Walkthrough


Our algorithm yielded a Sharpe ratio of 0.815 over a five year period, while holding SPY over the same period yielded a Sharpe ratio of 0.612. However it should be noted, due to the fact the algorithm had to wait periods of time before our optimal entry and liquidation levels were reached, our algorithm only made twelve trades over the entire backtest duration. To increase the number of trades, we can add additional pairs, such as GLD-GDX. We encourage our users to clone this algorithm and and to experiment with different ideas for pairs and to play with the parameters of the algorithm.


  1. Leung, Tim and Li, Xin, Optimal Mean Reversion Trading with Transaction Costs and Stop-Loss Exit (April 26, 2015). International Journal of Theoretical and Applied Finance, Vol. 18, No. 3, 2015. Online Copy.