Skip to content

Instantly share code, notes, and snippets.

@sebjai
Created October 30, 2019 00:47
Show Gist options
  • Save sebjai/f87146f994cd5a115b123613ac5bece8 to your computer and use it in GitHub Desktop.
Save sebjai/f87146f994cd5a115b123613ac5bece8 to your computer and use it in GitHub Desktop.
Optimal Execution with linear and non-linear impact. Chapter 6 of Algorithmic and High-Frequency Trading (c) Cartea, Jaimungal, & Penalva, 2015 Cambridge University Press
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from ipywidgets import *\n",
"import ipywidgets as widgets"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def inter_extrapolation(x, y, e):\n",
" \"\"\" Extrapolation and interpolation.\n",
" \n",
" :param x: a numpy array\n",
" :param y: a numpy array\n",
" :param e: a numpy array, equivalent of x\n",
" :return: a numpy array\n",
" \"\"\"\n",
" new_x = np.sort(x)\n",
" new_y = y[np.argsort(x)]\n",
"\n",
" def point_wise(ep):\n",
" if ep < new_x[0]:\n",
" return new_y[0] + (ep - new_x[0]) * (new_y[1] - new_y[0]) / (new_x[1] - new_x[0])\n",
" elif ep > new_x[-1]:\n",
" return new_y[-1] + (ep - new_x[-1]) * (new_y[-1] - new_y[-2]) / (new_x[-1] - new_x[-2])\n",
" else:\n",
" return np.interp([ep], x, y)[0]\n",
" return np.array([point_wise(i) for i in e])"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def calculate_inventory_trading_speed(alpha, phi, t, tt, T, b, k):\n",
" \"\"\" For given points t, this function solve for the optimal speed of trading as nu, and investor's inventory along the\n",
" optimal path as Q. \n",
" This function also returns optimal speed of trading as nut, and investor's inventory along the optimal path Qt as a\n",
" function of time, tt, which is a vector of \"infinitely\" small time interval.\n",
" \"\"\"\n",
" tau = T - t\n",
" zeta = ((alpha - 0.5 * b) + np.sqrt(k * phi)) / ((alpha - 0.5 * b) - np.sqrt(k * phi))\n",
" gamma = np.sqrt(phi / k)\n",
" chi = np.sqrt(k * phi) * np.divide(1 + zeta * np.exp(2 * gamma * tau), 1 - zeta * np.exp(2 * gamma * tau))\n",
" Q = np.divide(zeta * np.exp(gamma * tau) - np.exp(-gamma * tau), zeta * np.exp(gamma * T) - np.exp(-gamma * T))\n",
" nu = np.multiply(-chi, Q) / k\n",
" Qt = inter_extrapolation(t, Q, tt)\n",
" nut = inter_extrapolation(t, nu, tt)\n",
" return Q, nu, Qt, nut\n",
"\n",
"\n",
"def plot_inventory_trading_speed(alpha0, phi, symb, t, tt, T, b, k, labels, main):\n",
" \"\"\" This function plots Fig 6.2 using above function to calculate inventory and speed of tading vs time.\n",
" \"\"\"\n",
" fig, (ax_inv, ax_trad) = plt.subplots(ncols=2)\n",
" fig.set_size_inches(10.5, 5.5)\n",
" color_idx = np.linspace(0, 1, phi.shape[0])\n",
" for i, line in zip(color_idx, range(0, phi.shape[0])):\n",
" inv_line, trad_line, inv_dot, trad_dot = calculate_inventory_trading_speed(alpha0, phi[line], t, tt, T, b, k)\n",
" plt1, = ax_inv.plot(tt, inv_dot, color=plt.cm.rainbow(i), label=labels[line], marker=symb[line], linestyle='None')\n",
" plt2, = ax_trad.plot(tt, trad_dot, color=plt.cm.rainbow(i), label=labels[line], marker=symb[line], linestyle='None')\n",
" plt3, = ax_inv.plot(t, inv_line, linestyle='-', color=plt.cm.rainbow(i))\n",
" plt4, = ax_trad.plot(t, trad_line, linestyle='-', color=plt.cm.rainbow(i))\n",
" ax_inv.legend()\n",
" ax_inv.set_xlabel(r\"Time\", fontsize=18)\n",
" ax_inv.set_ylabel(r\"Inventory\", fontsize=18)\n",
" ax_trad.legend()\n",
" ax_trad.set_xlabel(r\"Time\", fontsize=18)\n",
" ax_trad.set_ylabel(r\"Trading Speed\", fontsize=18)\n",
" ax_trad.yaxis.set_label_coords(-0.1,0.5)\n",
" plt.suptitle(main, fontsize=20)\n",
" fig.canvas.draw()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def slider_plot_inventory_trading_speed(phi, symb, t, tt, T, b, k, labels):\n",
" \"\"\" This function returns a dynamic version of above plot with penalty term alpha controlled by a dynamic slider.\n",
" For demonstration purposes, only alpha = 0.01, and alpha = 100 (as an approximation of positive infinity) are plotted.\n",
" To use this function, call this function on the main file using same argument as above function, but remove 'alpha0' \n",
" parameter since you do not need to specify alpha here. Then, mannually recompile Liquidation_Permanent_Price_Impact file\n",
" to use the slider.\n",
" \"\"\"\n",
" def update(alpha):\n",
" fig, (ax_inv, ax_trad) = plt.subplots(ncols=2)\n",
" color_idx = np.linspace(0, 1, phi.shape[0])\n",
" for i, line in zip(color_idx, range(0, phi.shape[0])):\n",
" inv_line, trad_line, inv_dot, trad_dot = calculate_inventory_trading_speed(alpha, phi[line], t, tt, T, b, k)\n",
" plt1, = ax_inv.plot(tt, inv_dot, color=plt.cm.rainbow(i), label=labels[line], marker=symb[line], linestyle='None')\n",
" plt2, = ax_trad.plot(tt, trad_dot, color=plt.cm.rainbow(i), label=labels[line], marker=symb[line], linestyle='None')\n",
" plt3, = ax_inv.plot(t, inv_line, linestyle='-', color=plt.cm.rainbow(i))\n",
" plt4, = ax_trad.plot(t, trad_line, linestyle='-', color=plt.cm.rainbow(i))\n",
" ax_inv.legend()\n",
" ax_inv.set_xlabel(r\"Time\", fontsize=18)\n",
" ax_inv.set_ylabel(r\"Inventory\", fontsize=18)\n",
" ax_trad.legend()\n",
" ax_trad.set_xlabel(r\"Time\", fontsize=18)\n",
" ax_trad.set_ylabel(r\"Trading Speed\", fontsize=18)\n",
" ax_trad.yaxis.set_label_coords(-0.1,0.5)\n",
" plt.suptitle(r\"\\alpha = \" + str(alpha), fontsize=20)\n",
" fig.canvas.draw()\n",
" return interactive(update, {'manual': False}, alpha=widgets.FloatSlider(min=0.01, max=100, step=0.01, value=alpha0))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def solve_pde(T, dt, qmax, dq, k, a, b, alpha, phi, Ndt, Ndq):\n",
" \"\"\" This function solves for optimal trading speed in feed back form as nus, and inventory of optimal path as Qs.\n",
" They are used to be presented as a function of time, t.\n",
" \"\"\"\n",
" t = np.arange(0, T+dt, dt)\n",
" q = np.arange(0, qmax+dq, dq)\n",
"\n",
" myleg = []\n",
"\n",
" nus = np.full((Ndt+1, a.shape[0]), np.NaN)\n",
" Qs = np.full((Ndt+1, a.shape[0]), np.NaN)\n",
" Qs[0,:] = qmax\n",
"\n",
" for i in range(0, a.shape[0], 1):\n",
" xi = (a[i] * k) / ((1 + a[i]) * k) ** (1 + 1/a[i])\n",
"\n",
" g = np.full((Ndq+1, Ndt+1), np.NaN)\n",
" nu = np.full((Ndq+1, Ndt+1), np.NaN)\n",
" \n",
" g[:, g.shape[1]-1] = -(alpha ** a[i]) / k ** (a[i] - 1) * np.power(q, 1 + a[i]) + 0.5 * b * np.power(q, 2)\n",
"\n",
" for j in range(Ndt-1, -1, -1):\n",
"\n",
" # Explicit Scheme\n",
" dqg = (g[1:g.shape[0], j+1] - g[0:(g.shape[0]-1), j+1]) / dq\n",
" g[1:g.shape[0], j] = g[1:g.shape[0], j+1] + dt * (-phi * np.power(q[1:q.shape[0]], 2) + xi * (np.power(-dqg, 1 + 1 / a[i])))\n",
" g[0, j] = 0\n",
"\n",
" dqg = (g[1:g.shape[0], j] - g[0:(g.shape[0]-1), j]) / dq\n",
" nu[1:nu.shape[0], j] = np.power(-dqg / ((1 + a[i]) * k), 1 / a[i])\n",
" nu[0, j] = 0\n",
"\n",
" # Implicit-explicit scheme\n",
" g[:, j] = np.real(g[:, j])\n",
" nu[:, j] = np.real(nu[:, j])\n",
"\n",
" for j in range(0, Ndt, 1):\n",
" nus[j, i] = inter_extrapolation(q, nu[:, j], [Qs[j, i]])\n",
" Qs[j+1, i] = Qs[j, i] - nus[j, i] * dt\n",
"\n",
" myleg.append(\"$a=\" + str(a[i]) + \"$\")\n",
" return nus, Qs, myleg, t, q"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def plot_multiple(x, y, xlab=None, ylab=None, main=None, labels=None):\n",
" \"\"\" This is a plotting function intended to plot multiple Ys for a single x.\n",
" \"\"\"\n",
" color_idx = np.linspace(0, 1, y.shape[0])\n",
" for i, line in zip(color_idx, range(0, y.shape[0])):\n",
" if labels is not None:\n",
" plt.plot(x, y[line, :], color=plt.cm.rainbow(i), label=labels[line], linestyle='-')\n",
" else:\n",
" plt.plot(x, y[line, :], color=plt.cm.rainbow(i), linestyle='-')\n",
" if xlab is not None:\n",
" plt.xlabel(xlab, fontsize=18)\n",
" if ylab is not None:\n",
" plt.ylabel(ylab, fontsize=18)\n",
" if main is not None:\n",
" plt.title(main, fontsize=12)\n",
" if labels is not None:\n",
" plt.legend()\n",
" plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment