Skip to content

Instantly share code, notes, and snippets.

@flipdazed
Created April 30, 2019 22:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save flipdazed/9fce0f461e03d5f1e0f8186153d9423c to your computer and use it in GitHub Desktop.
Save flipdazed/9fce0f461e03d5f1e0f8186153d9423c to your computer and use it in GitHub Desktop.
Linear Almgren-Chriss python model
import numba as nb
import numpy as np
def impact_perm(nu, gamma, beta):
"""Returns the permenant dollar price impact per unit time
In paper as :math:`g(\nu)`
Args:
nu: rate of trading :math:`\nu = n_k / \tau`.
gamma: Const. the $ move per share traded
beta: Const. power law scaling of nu for perm impact.
Default is .5 for square root rule
"""
return gamma * nu ** beta
def impact_temp(nu, eps, eta, alpha):
"""Returns the temporary dollar price impact
In 2005 paper as :math:`h(\nu)`
Args:
nu: rate of trading :math:`\nu = n_k / \tau`.
eps: Const. $ move
eta: Const. the $ move per trading speed
alpha: Const. power law scaling of nu for temp impact.
Deafult if 1 for arbitrage free linear model
"""
return eps * np.sign(nu) + eta * nu ** alpha
def is_expected(n_t, gamma, eta, eps, tau):
"""The expected implementation shortfall
Args:
n_t: array of units executed at each time step where
``len(n_t * tau)`` is the time taken to execute
gamma: Const. the $ move per share traded
eta: Const. the $ move per trading speed
eps: Const. the $ move per share traded
tau: Time step between each element of n_t
"""
x_k = np.cumsum(n_t[::-1]) # units left to execute
nu_t = n_t / tau
return (
np.sum(tau * x_k * impact_perm(nu_t)) +
np.sum(n_t * impact_temp(nu_t))
)
def is_var(n_t, sigma, tau):
"""The vairance of the implementation shortfall
Args:
n_t: array of units executed at each time step where
``len(n_t * tau)`` is the time taken to execute
sigma: The vol of underlying securities
tau: Time step between each element of n_t
"""
x_k = np.cumsum(n_t[::-1]) # units left to execute
return sigma**2 + tau * np.dot(x_k.T, x_k)
def is_objective(n_t, risk_tol, gamma, eta, eps, tau, sigma):
"""Almgren-Chriss objective function
Args:
n_t: array of units executed at each time step where
``len(n_t * tau)`` is the time taken to execute
gamma: Const. the $ move per share traded
eta: Const. the $ move per trading speed
eps: Const. the $ move per share traded
tau: Time step between each element of n_t
"""
return (is_expected(n_t, gamma, eta, eps, tau) +
risk_tol*is_var(n_t, sigma, tau))
def trade_decay_rate(tau, risk_tol, sigma, eta, gamma):
"""Also known as :math:`\kappa` in the paper
Note:
:math:`\kappa^{-1}` is the time it takes to
deplete the portfolio by a factor of :math:`e`
If :math:`\lambda > 0` the trader will still liquidate the
position on a time scale `:math:`\kappa^{-1}` so
:math:`\kappa^{-1}` is the intrinsic time scale of the trade.
Args:
tau: Time step between each element of :math:`n_t`
risk_tol: The risk tolerance
sigma: volatility of the unit price
eta: Const. the $ move per trading speed
gamma: Const. the $ move per share traded
"""
return np.sqrt(risk_tol*sigma**2 / (eta * (1 + .5*gamma*tau/eta)))
def trading_traj(trading_time, tau, risk_tol, sigma, eta, gamma):
"""Returns the optimal trading trajectory :math:`n_t`
(allocates the distribution of units over tau)
Args:
trading_time: Total time to trade
tau: Time step between each element of :math:`n_t`
risk_tol: The risk tolerance
sigma: volatility of the unit price
eta: Const. the $ move per trading speed
gamma: Const. the $ move per share traded
"""
k = trade_decay_rate(risk_tol, sigma, eta, gamma, tau)
tj = np.arange(trading_time/tau) * tau
return 2*np.sinh(.5*k*tau)/np.sinh(k*trading_time)*np.cosh(k*tj*trading_time)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment