arbitragelab.stochastic_control_approach.ou_model_jurek

Module implements the optimal dynamic portfolio strategy for arbitrageurs with a finite horizon and non-myopic preferences facing a mean-reverting arbitrage opportunity using OU process.

This module is a realization of the methodology in the following paper: Jurek, J.W. and Yang, H., 2007, April. Dynamic portfolio selection in arbitrage. In EFA 2006 Meetings Paper.

Module Contents

Classes

OUModelJurek

This class derives the optimal dynamic strategy for arbitrageurs with a finite horizon

class OUModelJurek

This class derives the optimal dynamic strategy for arbitrageurs with a finite horizon and non-myopic preferences facing a mean-reverting arbitrage opportunity (e.g. an equity pairs trade).

To capture the presence of horizon and divergence risk, we model the dynamics of the mispricing using a mean-reverting OU process. Under this process, although the mispricing is guaranteed to be eliminated at some future date, the timing of convergence, as well as the maximum magnitude of the mispricing prior to convergence, are uncertain. With this assumption, we are able to derive the arbitrageur’s optimal dynamic portfolio policy for a set of general, non-myopic preference specifications, including CRRA utility defined over wealth at a finite horizon and Epstein-Zin utility defined over intermediate cash flows (e.g. fees).

fit(prices: pandas.DataFrame, delta_t: float = 1 / 252, adf_test: bool = False, significance_level: float = 0.95)

This method uses inputted training data to calculate the spread and estimate the parameters of the corresponding OU process.

The spread construction implementation follows Section IV A in Jurek (2007).

Parameters:
  • prices – (pd.DataFrame) Contains price series of both stocks in spread.

  • delta_t – (float) Time difference between each index of data, calculated in years.

  • adf_test – (bool) Flag which defines whether the adf statistic test should be conducted.

  • significance_level – (float) This significance level is used in the ADF statistic test. Value can be one of the following: (0.90, 0.95, 0.99).

spread_calc(prices: pandas.DataFrame) tuple

This method calculates the spread on test data using the scaled weights from training data.

Parameters:

prices – (pd.DataFrame) Contains price series of both stocks in spread.

Returns:

(tuple) Consists of time remaining array and spread numpy array.

optimal_portfolio_weights(prices: pandas.DataFrame, utility_type: int = 1, gamma: float = 1, beta: float = 0.01, r: float = 0.05) numpy.array

Implementation of Theorem 1 and Theorem 2 in Jurek (2007).

This method implements the optimal portfolio strategy for two types of investors with varying utility functions.

The first type of investor is represented by utility_type = 1. This agent has constant relative risk aversion preferences(CRRA investor) with utility defined over terminal wealth. For this type of investor,

gamma = 1 implies log utility investor, and gamma != 1 implies general CRRA investor.

The second type of investor is represented by utility_type = 2. This agent has utility defined over intermediate consumption, with agent’s preferences described by Epstein-Zin recursive utility with psi(elasticity of intertemporal substitution) = 1. For this type of investor,

gamma = 1 reduces to standard log utility investor, and gamma > 1 implies more risk averse investors, whereas gamma < 1 implies more risk tolerance in comparison to investor with log utility.

What is beta? Beta signifies the constant fraction of total wealth an investor chooses to consume. This is analogous to a hedge fund investor who cares both about terminal wealth in some risk-averse way and consumes a constant fraction, β, of assets under management (the management fee). For utility_type = 2, C(Consumption) = beta * W.

Parameters:
  • prices – (pd.DataFrame) Contains price series of both stocks in spread.

  • utility_type – (int) Flag signifies type of investor preferences. (Either 1 or 2).

  • gamma – (float) coefficient of relative risk aversion. (gamma > 0).

  • beta – (float) Subjective rate of time preference. (Only required for utility_type = 2).

  • r – (float) Rate of Returns. (r > 0).

Returns:

(np.array) Scaled optimal portfolio weights.

optimal_portfolio_weights_fund_flows(prices: pandas.DataFrame, f: float, gamma: float = 1, r: float = 0.05) numpy.array

Implementation of Theorem 4 in Jurek (2007).

This method calculates the optimal portfolio allocation of an agent with constant relative risk aversion with utility defined over terminal wealth, (utility_type = 1) , in the presence of fund flows.

Note: For all values of gamma, the presence of fund flows affect the general portfolio rule only by introducing a coefficient of proportionality.

Also note, this method is only applicable for investors with utility defined over terminal wealth.

What is f?

f is the coefficient of proportionality for fund flows and is the drift term in the stochastic process equation for fund flows. (Refer Appendix C)

Parameters:
  • prices – (pd.DataFrame) Contains price series of both stocks in spread.

  • gamma – (float) coefficient of relative risk aversion. (gamma > 0).

  • f – (float) coefficient of proportionality (f > 0).

  • r – (float) Rate of Returns. (r > 0).

Returns:

(np.array) Optimal weights with fund flows.

stabilization_region(prices: pandas.DataFrame, utility_type: int = 1, gamma: float = 1, beta: float = 0.01, r: float = 0.05) tuple

Implementation of Theorem 3 in Jurek (2007).

Parameters:
  • prices – (pd.DataFrame) Contains price series of both stocks in spread.

  • utility_type – (int) Flag signifies type of investor preferences.

  • gamma – (float) coefficient of relative risk aversion.

  • beta – (float) Subjective rate of time preference. (Only required for utility_type = 2).

  • r – (float) Rate of Returns.

Returns:

(tuple) Tuple of numpy arrays for spread, min bound and max bound.

describe() pandas.Series

Method returns values of instance attributes calculated from training data.

Returns:

(pd.Series) Series describing parameter values.

plot_results(prices: pandas.DataFrame, num_test_windows: int = 5, delta_t: float = 1 / 252, utility_type: int = 1, gamma: float = 10, beta: float = 0.01, r: float = 0.05, f: float = 0.1, figsize: tuple = (8, 4), fontsize: int = 8)

Method plots out of sample performance of the model on specified number of test windows. We use a backward looking rolling window as training data and its size depends on the number of test windows chosen. The length of training data is fixed for all test windows.

For example, if the total data is of length 16 years, with the number of test windows set to 5, the length of training data would be 16 - (5 + 1) = 10. (The last year is not considered for testing).

This method plots the stabilization region, optimal portfolio weights with and without fund flows, and the evolution of the wealth process with initial wealth normalized to 1.

Parameters:
  • prices – (pd.DataFrame) Contains price series of both stocks in spread with dates as index.

  • num_test_windows – (int) Number of out of sample testing windows to plot.

  • delta_t – (float) Time difference between each index of prices, calculated in years.

  • utility_type – (int) Flag signifies type of investor preferences.

  • gamma – (float) coefficient of relative risk aversion.

  • beta – (float) Subjective rate of time preference. (Only required for utility_type = 2).

  • r – (float) Rate of Returns.

  • f – (float) Coefficient of proportionality (assumed to be positive).

  • figsize – (tuple) Input to matplotlib figsize parameter for plotting.

  • fontsize – (int) general matplotlib font size for plotting.