#!/usr/bin/env python
# coding: utf-8
# # Davis-Panas-Zariphopoulou model
# This notebook presents a model for pricing options in a market with proportional transaction costs. The model is taken from the celebrated work of Davis-Panas-Zariphopoulou 1993 [*link-to-the-paper*](https://web.ma.utexas.edu/users/zariphop/pdfs/TZ-7.pdf).
#
# #### This is a very powerful model!
#
# However, due to its complexity (and time complexity), it is not very well known to the practitioners.
#
# The purpose of this notebook is to explain **in simple terms** the main ideas of the model, and show how to implement it numerically. **The results will surprise you!**
#
# ## Contents
# - [Model description](#sec1)
# - [Portfolio dynamics (original)](#sec1.1)
# - [Some definitions](#sec1.2)
# - [Singular control problem](#sec2)
# - [Maximization problem](#sec2.1)
# - [Indifference pricing](#sec2.2)
# - [Variable reduction](#sec3)
# - [Minimization problem](#sec3.1)
# - [Portfolio dynamics (2 state variables)](#sec3.2)
# - [HJB variational inequality](#sec3.3)
# - [Indifference price (explicit form)](#sec3.4)
# - [Numerical Solution](#sec4)
# - [Discrete SDE](#sec4.1)
# - [Algorithm](#sec4.2)
# - [Numerical Computations](#sec5)
# - [Time complexity](#sec5.1)
# - [Is the risk aversion important?](#sec5.2)
# - [Is the drift important?](#sec5.3)
# In[1]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from IPython.display import display
get_ipython().run_line_magic('matplotlib', 'inline')
#
# # Model Description
#
# ### Portfolio dynamics (original)
# Let us consider a portfolio composed by:
# - A bank account $\mathbf{B}$ paying an interest rate $r > 0$.
# - A stock $\mathbf{S}$.
# - A number of shares $\mathbf{Y}$ of the stock $\mathbf{S}$.
#
# The 3-D state of the portfolio at time $t\in [t_0,T]$ is $(B_t,Y_t,S_t)$ and evolves following the SDE:
#
# \begin{equation}
# \begin{cases}
# dB_t &= rB_t dt - (1+\theta_b)S_{t} dL_t + (1-\theta_s) S_{t} dM_t \\
# dY_t &= dL_t - dM_t \\
# dS_t &= S_{t} \left( \mu dt + \sigma dW_t \right).
# \end{cases}
# \end{equation}
#
# The parameters $\theta_b$, $\theta_s \geq 0$ are the proportional transaction costs when buying and selling respectively.
#
# The processes $\{(L_t, M_t)\}_{t \in [t_0,T]}$ are the **trading strategies**, i.e. the **controls** of the problem.
#
# The process $\{L_t\}_{t}$ represents the cumulative number of shares bought up to time $t$, and $\{M_t\}_{t}$ represents the number of shares sold up to time $t$.
# They are right-continuous, finite variation, non-negative and increasing processes. If the time $t$ is a discontinuous point (there is a transaction), the variation of the processes are indicated as
# $$ \Delta L_t= L(t)-L(t^-) \quad \quad \Delta M_t= M(t)-M(t^-) $$
#
# Let us consider an example. If at time $t$, the investor wants to buy $\Delta L_t$ shares. Then the portfolio changes as
#
# $$ \Delta Y_t = \underbrace{\Delta L_t}_{\text{shares bought}} \quad \quad
# \Delta B_t = - \underbrace{(1+\theta_b)S_t}_{\text{adjusted price}} \Delta L_t $$
#
# where the **adjusted price** is the real cost of the stock (incorporating the transaction cost).
#
# If there are no transactions, the portfolio has the simple well known evolution:
#
# $$
# \begin{cases}
# dB_t &= rB_t dt \\
# dY_t &= 0 \\
# dS_t &= S_{t} \left( \mu dt + \sigma dW_t \right).
# \end{cases}
# $$
#
#
# ### Some definitions
#
# The **cash value** function $c(y,s) : \mathbb{R} \times \mathbb{R}^+ \to \mathbb{R}$, is defined as the value in cash when the shares in the portfolio are liquidated i.e.
# long positions are sold and short positions are covered.
#
# $$
# c(y,s) := \begin{cases}
# (1+\theta_b)ys, & \text{if } y\leq 0 \\
# (1-\theta_s)ys, & \text{if } y>0 .
# \end{cases}
# $$
#
# For $t\in [t_0,T]$, the **total wealth** process in a portfolio with zero options is defined as:
#
# $$ \mathcal{W}^0_t := B_t + c(Y_t,S_t). $$
#
# If the portfolio contains an option with maturity $T$ and strike $K$, then the wealth process becomes:
# **Writer**:
#
# $$ \mathcal{W}^{w}_t = \; B_t + c(Y_t,S_t) \mathbb{1}_{\{t < T\}} +
# c(Y_t,S_t) \mathbb{1}_{\{t = T,\, S_t(1+ \theta_b ) \leq K\}} +
# \biggl( c\bigl( Y_t-1,S_t \bigr) + K \biggr) \mathbb{1}_{\{t=T,\, S_t(1+ \theta_b ) > K \}} $$
#
# **Buyer**:
#
# $$ \mathcal{W}^{b}_t = \; B_t + c(Y_t,S_t) \mathbb{1}_{\{t < T\}} +
# c(Y_t,S_t) \mathbb{1}_{\{t = T,\, S_t(1+ \theta_b ) \leq K\}} +
# \biggl( c\bigl( Y_t+1,S_t \bigr) - K \biggr) \mathbb{1}_{\{t=T,\, S_t(1+ \theta_b ) > K \}} $$
#
# For $t_0 \leq t K$, because the true price of the share incorporates the value of the transaction costs. Let's see the plot:
# In[2]:
S = np.linspace(14, 16, 100)
K = 15 # strike
cost_b = 0.01 # transaction cost
plt.plot(S, np.maximum(S - K, 0), color="blue", label="Zero cost Payoff")
plt.plot(S, np.maximum(S * (1 + cost_b) - K, 0), color="red", label="Transaction costs Payoff")
plt.xlabel("S")
plt.ylabel("Payoff")
plt.title("Payoff comparison")
plt.legend(loc="upper left")
plt.show()
#
# ## Singular control problem
#
# ### Maximization problem
#
# The **value function** of the maximization problem for $j=0,w,b$ (corresponding to the three portfolios: no option, writer, buyer) is defined as:
#
# \begin{equation}
# V^j(t,b,y,s) = \sup_{L,M} \; \mathbb{E}\biggl[ \; \mathcal{U}( \mathcal{W}^{j}_T ) \; \bigg| \; B_{t} = b, Y_{t} = y, S_{t} = s \biggr],
# \end{equation}
#
# where $\mathcal{U}: \mathbb{R} \to \mathbb{R}$ is a concave increasing **utility function**. The **exponential utility** is what we are looking for:
#
# \begin{equation}
# \mathcal{U}(x) := 1- e^{-\gamma x} \quad \quad \gamma >0 .
# \end{equation}
#
# ### Indifference pricing
#
# The writer (buyer) option price is defined as the amount of cash to add (subtract) to the bank account,
# such that the maximal expected utility of wealth of the writer (buyer) is the same he could get with
# the zero-option portfolio.
#
# * The **writer price** is the value $p^w>0$ such that
# \begin{equation}
# V^0(t,b,y,s) = V^w(t,b+p^w,y,s),
# \end{equation}
#
# * The **buyer price** is the value $p^b>0$ such that
# \begin{equation}
# V^0(t,b,y,s) = V^b(t,b-p^b,y,s).
# \end{equation}
#
# ## Variable reduction
# Using the properties of the exponential utility, it is possible to remove $\mathbf{B}$ from the state variables.
#
# $$ V^j(t,b,y,s) = \sup_{L,M} \; \mathbb{E}_{t,b,y,s}\biggl[ 1- e^{-\gamma \mathcal{W}^j(T) } \biggr]
# = 1- e^{-\gamma \frac{b}{\delta(t,T)}} Q^j(t,y,s), $$
#
# where $\delta(t,T) = e^{-r(T-t)}$. (for the full calculations, check the paper. Equations 4.21 -4.25).
#
#
# ### Minimization problem
#
# \begin{equation}
# Q^j(t,y,s) = \inf_{L,M} \; \mathbb{E}_{t,y,s}\biggl[ \;
# e^{-\gamma \bigl[ -\int_{t}^T (1+\theta_b) \frac{S_u}{\delta(u,T)} dL_u +
# \int_{t}^T (1-\theta_s) \frac{S_u}{\delta(u,T)} dM_u \bigr] } \; H^j(Y_T,S_T) \bigg]
# \end{equation}
#
# The exponential term inside the expectation can be considered as a discount factor, and the second term is the terminal payoff:
# - No option:
#
# $$ H^0(y,s) = e^{-\gamma \, c(y,s)}. $$
#
# - Writer:
#
# $$ H^w(y,s) = e^{-\gamma \bigl[ c(y,s)\mathbb{1}_{\{s(1+\theta_b) \leq K\}} +
# \bigl( c( y-1,s) + K \bigr) \mathbb{1}_{\{s(1+\theta_b)>K\}} \bigr] }.$$
#
# - Buyer:
#
# $$ H^b(y,s) = e^{-\gamma \bigl[ c(y,s)\mathbb{1}_{\{s(1+\theta_b) \leq K\}} +
# \bigl( c( y+1,s) - K \bigr) \mathbb{1}_{\{s(1+\theta_b)>K\}} \bigr] }.
# $$
#
# ### Portfolio dynamics (2 state variables)
#
# In order to simplify the numerical computations,let us pass to the log-variable $X_t = \log(S_t)$.
#
# The resulting portfolio dynamics is:
#
# \begin{equation}
# \begin{cases}
# dY_t &= dL_t - dM_t \\
# dX_t &= \biggl( \mu - \frac{1}{2} \sigma^2 \biggr) dt + \sigma dW_t.
# \end{cases}
# \end{equation}
#
#
# ### HJB variational inequality
#
# The Hamilton Jacobi Bellman equation associated to the minimization problem is:
#
# $$
# \min \; \biggl\{ \; \frac{\partial Q^j}{\partial t} + (\mu-\frac{1}{2}\sigma^2) \frac{\partial Q^j}{\partial x}
# + \frac{1}{2}\sigma^2 \frac{\partial^2 Q^j}{\partial x^2} ,
# \; \frac{\partial Q^j}{\partial y} +(1+\theta_b) e^x \frac{\gamma}{\delta(t,T)}Q^j \; ,
# \; -\biggl( \frac{\partial Q^j}{\partial y}+(1-\theta_s)e^x \frac{\gamma}{\delta(t,T)} Q^j
# \biggr) \biggr\} = 0.
# $$
#
# ### Indifference price (explicit form)
#
# Using again the explicit form of the utility function, we obtain formulas for the option prices:
#
# $$
# p^w(t_0,y,x) = \frac{\delta(t_0,T)}{\gamma} \log \biggl( \frac{Q^w(t_0,y,e^x)}{Q^0(t_0,y,e^x)} \biggr), $$
#
# $$ p^b(t_0,y,x) = \frac{\delta(t_0,T)}{\gamma} \log \biggl( \frac{Q^0(t_0,y,e^x)}{Q^b(t_0,y,e^x)} \biggr).
# $$
#
# # Numerical Solution
#
# ### Discrete SDE
#
# As usual, we introduced the time step $\Delta t = \frac{T}{N}$, where we assumed $t_0 = 0$ and $N \in \mathbb{N}$.
# The time $t_n = n \Delta t$, for $n \in \{0,1,2, ..., N\}$.
#
# The space discretization has 2 dimensions:
# - The space step $h_x$ is defined as $h_x = \sigma \sqrt{\Delta t}$.
# - The space step is $h_y$. In this computations we choose $h_x = h_y$.
#
# The discretized version of the Stochastic Differential equation is:
#
# $$
# \begin{cases}
# \Delta Y_n &= \; \Delta L_n - \Delta M_n \\
# \Delta X_n &= \; (\mu - \frac{1}{2} \sigma^2 ) \Delta t + \sigma \Delta W_n
# \end{cases}
# $$
#
# Both $\Delta L_n$ and $\Delta M_n$ at each time $t_n$ can assume values in $\{0,h_y\}$. They cannot be different from zero at the same time (It is quite strange to buy and sell at the same time, right?)
#
# The variable $\Delta W_n$ has $\frac{1}{2}$ probability of being equal to $h_x$ and $\frac{1}{2}$ probability of being equal to $-h_x$.
#
# The variation $\Delta X_n$ is $\pm h_x$ plus the drift component. We obtain a recombining **binomial tree**.
# ### Binomial tree with drift
# In[3]:
N = 6
dt = 1 / N
S0 = 15
x0 = np.log(S0)
mu = 0.1
sig = 0.25
h_x = sig * np.sqrt(dt)
for n in range(N):
x = np.array([x0 + (mu - 0.5 * sig**2) * dt * n + (2 * i - n) * h_x for i in range(n + 1)])
print(x)
#
# ### Algorithm
#
# Using the Dynamic Programming Principle (DPP) on the minimization problem we obtain a recursive algorithm on the nodes of the grid.
#
# $$ \begin{aligned}
# Q^{j}(t_n,Y_n,X_n) = \min
# & \; \biggl\{ \mathbb{E}_n \biggl[ Q \bigl( t_{n+1}, Y_n, X_n + \Delta X_n \bigr) \biggr], \\ \nonumber
# & \; \exp \biggl(\frac{\gamma}{\delta(t_n,T)} (1+\theta_b) e^{X_n} \Delta L_n \biggr)
# \mathbb{E}_n \biggl[ Q^{j} \bigl( t_{n+1}, Y_n+\Delta L_n, X_n + \Delta X_n \bigr) \biggr], \\ \nonumber
# & \; \exp \biggl(\frac{-\gamma}{\delta(t_n,T)} (1-\theta_s) e^{X_n} \Delta M_n \biggr)
# \mathbb{E}_n \biggl[ Q^{j} \bigl( t_{n+1}, Y_n-\Delta M_n, X_n + \Delta X_n \bigr) \biggr]
# \biggr\}.
# \end{aligned} $$
#
# #### How to solve this problem?
# - Create a **binomial tree** with N time steps.
# - Create a "shares vector" **y** with dimension M.
# - Initialize a two dimensional grid of size $(N+1)\times M$, to be filled with the values of the terminal conditions $H^j(y,e^x)$ for $j=0,w,b$ (see [Minimization problem](#sec3.1))
# - Create a backward loop over time and find $Q^j(0,0,X_0)$
#
# #### Computational complexity? Well... Quite high.
# - A binomial tree with N time steps has $\sum_{n=0}^N (n+1) = \frac{N(N+1)}{2} + (N+1) = (N+1)(\frac{N}{2}+1)$ nodes.
# The loop over all the nodes is $\mathcal{O}(N^2)$.
# - If we assume $M \sim N$, the loop over all the values of **y** has another $\mathcal{O}(N)$.
# - The minimum search for every point in **y**, produces another $\mathcal{O}(N)$ operations.
#
# Therefore the total computational complexity is of $\mathcal{O}(N^4)$.
#
#
# #### For space reasons, I will not expose the code here in the notebook. The interested reader can peek the (clear and commented) code inside the class TC_pricer. [code](./functions/TC_pricer.py)
#
# # Numerical computations
#
# Let us import the classes we need:
# - **Option_param**: creates the option object
# - **Diffusion_process**: creates the process object
# - **TC_pricer**: creates the pricer for this model
# - **BS_pricer**: creates the Black-Scholes pricer, useful for making comparisons.
# In[4]:
from FMNM.Parameters import Option_param
from FMNM.Processes import Diffusion_process
from FMNM.TC_pricer import TC_pricer
from FMNM.BS_pricer import BS_pricer
# Creates the object with the parameters of the option
opt_param = Option_param(S0=15, K=15, T=1, exercise="European", payoff="call")
# Creates the object with the parameters of the process
diff_param = Diffusion_process(r=0.1, sig=0.25, mu=0.1)
# Creates the object of the Transaction Costs pricer
TC = TC_pricer(opt_param, diff_param, cost_b=0, cost_s=0, gamma=0.0001)
# Creates the object of the Black-Scholes pricer
BS = BS_pricer(opt_param, diff_param)
# We expect that if the transaction costs are **zero**, and the risk aversion coefficient $\gamma \to 0$ (i.e. the investor becomes risk neutral), the price should **converge** to the **Black-Scholes price**
# In[5]:
tc = TC.price(N=2000)
bs = BS.closed_formula()
print("Zero TC price: ", tc)
print("Black Scholes price:", bs)
print("Difference:", np.abs(tc - bs))
# #### Wait a second!!! WE CAN DO BETTER!
#
# ##### Let us analyze the the writer and buyer prices, for different initial stock values.
# In[6]:
S = list(range(5, 21))
price_0 = []
price_w = []
price_b = []
for s in S:
TC.S0 = s
TC.cost_b = 0
TC.cost_s = 0
price_0.append(TC.price(N=400)) # zero costs
TC.cost_b = 0.05
TC.cost_s = 0.05
price_w.append(TC.price(N=400, TYPE="writer"))
price_b.append(TC.price(N=400, TYPE="buyer"))
TC.cost_b = 0
TC.cost_s = 0 # set to 0 for future computations
# In[7]:
plt.plot(S, price_0, color="blue", marker="s", label="Zero costs")
plt.plot(S, price_w, color="green", marker="o", label="Writer")
plt.plot(S, price_b, color="magenta", marker="*", label="Buyer")
BS.plot(axis=[10, 20, 0, 8]) # plot of the Black Scholes line
#
# ### Time complexity
#
# If we set the "Time" argument to True, the method also returns the execution time.
# Let us verify that the algorithm has time complexity of order $\mathcal{O}(N^4)$
#
# The following operation will be very time consuming. In case you are in a hurry, reduce the NUM.
# In[8]:
NUM = 7
price_table = pd.DataFrame(columns=["N", "Price", "Time"])
for j, n in enumerate([50 * 2**i for i in range(NUM)]):
price_table.loc[j] = [n] + list(TC.price(n, Time=True))
display(price_table)
# Using the computational times we can estimate the exponent $\alpha$ of the polinomial growth $\mathcal{O}(N^\alpha)$.
#
# For higher values of N, the exponent converges to the expected value of $\alpha = 4$.
#
# Here we are quite close.
# In[9]:
print("The exponent is: ", np.log2(price_table["Time"][6] / price_table["Time"][5]))
#
# ### Is the risk aversion important?
#
# The coefficient $\gamma$ measure the risk aversion of the investor. We can see how the option price is affected by this coefficient:
# In[10]:
price_ww = []
price_bb = []
gammas = [0.0001, 0.001, 0.01, 0.05, 0.1, 0.3, 0.5]
TC.cost_b = 0.01
TC.cost_s = 0.01
for gamma in gammas:
TC.gamma = gamma
price_ww.append(TC.price(N=400, TYPE="writer"))
price_bb.append(TC.price(N=400, TYPE="buyer"))
plt.plot(gammas, price_ww, color="green", marker="o", label="Writer")
plt.plot(gammas, price_bb, color="magenta", marker="*", label="Buyer")
plt.xlabel("gamma")
plt.ylabel("price")
plt.legend()
plt.show()
# So far we have found that:
#
# - The option pricing is an increasing function of the risk aversion coefficient for the writer, and a decreasing function for the buyer.
#
# - The option pricing is an increasing function of the transaction costs for the writer, and a decreasing function for the buyer.
#
# ### Is the drift important?
#
# As we know from the "classical" no-arbitrage martingale pricing theory, the option price does not depend on the stock expected value.
#
# However, this model is a utility based model i.e. a model that does not consider a risk neutral investor.
#
# We can see that in this model the option price depends on the drift.
#
# If we consider a high risk aversion coefficient, the option price is not very sensitive to the drift. If instead we choose a small value of $\gamma$, i.e. the investor is risk neutral, the drift plays the role of the risk neutral expected return $r$ and therefore changing $\mu$, is like changing $r$.
#
# Following Hodges-Neuberger [2], in the practical computations **it is better to set $\mu=r$.**
# In[11]:
price_mu1 = []
price_mu2 = []
mus = [0.01, 0.05, 0.1, 0.2, 0.3]
TC.gamma = 1 # high value of risk aversion
TC.cost_b = 0.01
TC.cost_s = 0.01
for mu in mus:
TC.mu = mu
price_mu1.append(TC.price(N=400, TYPE="writer"))
price_mu2.append(TC.price(N=400, TYPE="buyer"))
plt.plot(mus, price_mu1, color="green", marker="o", label="Writer")
plt.plot(mus, price_mu2, color="magenta", marker="*", label="Buyer")
plt.xlabel("mu")
plt.ylabel("price")
plt.legend()
plt.show()
# ### Other references
#
# [1] Cantarutti, N., Guerra, J., Guerra, M., and Grossinho, M. (2019). Option pricing in exponential Lévy models with transaction costs. [*ArXiv*](https://arxiv.org/abs/1611.00389).
#
# [2] Hodges, S. D. and Neuberger, A. (1989). Optimal replication of contingent claims under transaction costs. The Review of Future Markets, 8(2):222–239.