Note
The following implementations and documentation closely follow the below work:
OU Model Mudchanatongsuk
Introduction
In the paper corresponding to this module, the authors implement a stochastic control-based approach to the problem of pairs trading. The paper models the log-relationship between a pair of stock prices as an Ornstein-Uhlenbeck process and use this to formulate a portfolio optimization based stochastic control problem. This problem is constructed in such a way that one may either trade based on the spread (by buying and selling equal amounts of the stocks in the pair) or place money in a risk-free asset. Then the optimal solution to this control problem is obtained in closed form via the corresponding Hamilton-Jacobi-Bellman equation under a power utility on terminal wealth.
Modelling
Note
In this module and the corresponding paper,
\(k\) denotes the rate of mean reversion of the spread, \(\theta\) denotes the long run mean and, \(\eta\) denotes the standard deviation of the spread.
Let \(A(t)\) and \(B(t)\) denote respectively the prices of the pair of stocks \(A\) and \(B\) at time \(t\). The authors assume that stock \(B\) follows a geometric Brownian motion,
where \(\mu\) is the drift, \(\sigma\) is the volatility, and \(Z(t)\) is a standard Brownian motion.
Let \(X(t)\) denote the spread of the two stocks at time \(t\), defined as
The authors assume that the spread follows an Ornstein-Uhlenbeck process
where \(k\) is the rate of reversion, \(\eta\) is the standard deviation and \(\theta\) is the long-term equilibrium level to which the spread reverts.
\(\rho\) denotes the instantaneous correlation coefficient between \(Z(t)\) and \(W(t)\).
Let \(V(t)\) be the value of a self-financing pairs-trading portfolio and let \(h(t)\) and \(-h(t)\) denote respectively the portfolio weights for stocks \(A\) and \(B\) at time \(t\).
The wealth dynamics of the portfolio value is given by,
Given below is the formulation of the portfolio optimization pair-trading problem as a stochastic optimal control problem. The authors assume that an investor’s preference can be represented by the utility function \(U(x) = \frac{1}{\gamma} x^\gamma\) with \(x ≥ 0\) and \(\gamma < 1\). In this formulation, our objective is to maximize expected utility at the final time \(T\). Thus, the authors seek to solve
Finally, the optimal weights are given by,
How to use this submodule
This submodule contains three public methods. One for estimating the parameters of the model using training data, and the second method is for calculating the final optimal portfolio weights using evaluation data.
Step 1: Model fitting
We input the training data to the fit method which calculates the spread and the estimators of the parameters of the model.
Implementation
Note
Although the paper provides closed form solutions for parameter estimation, this module uses log-likelihood maximization to estimate the parameters as we found the closed form solutions provided to be unstable.
Tip
To view the estimated model parameters from training data, call the describe
function.
Step 2: Getting the Optimal Portfolio Weights
In this step we input the evaluation data and specify the utility function parameter \(\gamma\).
Warning
As noted in the paper, please make sure the value of gamma is less than 1.
Implementation
Tip
The spread_calc
method can be used to calculate the spread on the test data.
Example
We use GLD and GDX tickers from Yahoo Finance as the dataset for this example.
import yfinance as yf
data1 = yf.download("GLD GDX", start="2012-03-25", end="2016-01-09")
data2 = yf.download("GLD GDX", start="2016-02-21", end="2020-08-15")
data_train_dataframe = data1["Adj Close"][["GLD", "GDX"]]
data_test_dataframe = data2["Adj Close"][["GLD", "GDX"]]
In the following code block, we are initializing the class and firstly,
we use the fit method to generate the parameters of the model.
Then, we call describe
to view the estimated parameters.
Finally, we use the out-of-sample test data to calculate the optimal portfolio weights using the fitted model.
from arbitragelab.stochastic_control_approach.ou_model_mudchanatongsuk import OUModelMudchanatongsuk
sc = OUModelMudchanatongsuk()
sc.fit(data_train_dataframe)
print(sc.describe())
plt.plot(sc.optimal_portfolio_weights(data_test_dataframe))
plt.show()
Research Notebook
The following research notebook can be used to better understand the approach described above.