import numpy as np
from math import exp , log , sqrt
import scipy.stats as st
import matplotlib.pyplot as plt
import time
import pandas as pd
from copy import deepcopy
import seaborn as sns
r = 0
mu = 0
S_0 = 100 # initial stock price
sigma = 0.3 # stock process vol
B = 0.25 # IV parameter
alpha = 0.5 # IV parameter
size_0 = 1000 # this is theta, PF size of JHEQX to begin with, ie in the first quarter
psi = 0.005 # this controls degree of feedback
phi = 0.05 # this controls decay of delta hedge impact
little_gamma = 0.05 # controls the shape of the vol term structure
zeta = 0.1 # this controls price process vol
dt = 1/(365 * 24 * (1/6)) # every 6 hours of each day the year
period = int(1/dt) # number of periods in in year
The implied volatility function is
$$ \sigma_{IV}(K,\tau,S) = Be^{\gamma \tau} +\frac{\alpha}{\alpha(K + S) + 1} $$The parameter $\gamma$ controls the term structure and can be finetuned.
if we set $\gamma > 0$, the volatility will be greater the further we are from expiry (ceteris paribus). $\sigma$ is also bounded below by B
if we set $\gamma < 0$, the volatility will be greater the closer we are to expiry (ceteris paribus). $\sigma$ is bounded above by B
def sigma_iv(S,K,tau,model=True):
if model:
return sigma # will return the specified sigma. relevant for BSM Greeks
else:
return B * exp(little_gamma * tau) + alpha/(alpha * (K + S) + 1) # will return implied sigma (as above). relevant for IV adjusted Greeks
As is evident, this function produced 2 kinds of volatility, pre-specified or implied. This function helps in quickly specifiying the sea of greeks you will see below
def d1(S,K,tau,model=True):
s_iv = sigma_iv(S,K,tau,model)
return (log(S/K) + (r + s_iv ** 2 / 2) * tau) / (s_iv * sqrt(tau))
def d2(S,K,tau,model=True):
return d1(S,K,tau,model) - sigma_iv(S,K,tau,model) * sqrt(tau)
def C(S,K,tau,model=True): # call option price
return S * st.norm.cdf(d1(S,K,tau,model)) - K * exp(-r * tau) * st.norm.cdf(d2(S,K,tau,model))
def P(S,K,tau,model=True): # put of option price
return -S * st.norm.cdf(-d1(S,K,tau,model)) + K * exp(-r * tau) * st.norm.cdf(-d2(S,K,tau,model))
The arguments I have used are
def delta_C_BS(S,K,tau,model=True):
return st.norm.cdf(d1(S,K,tau,model))
def delta_P_BS(S,K,tau,model=True):
return -st.norm.cdf(-d1(S,K,tau,model))
def gamma_BS(S,K,tau,model=True):
return st.norm.pdf(d1(S,K,tau,model))/(S * sigma_iv(S,K,tau,model) * sqrt(tau))
def vega_BS(S,K,tau,model=True):
return S * st.norm.pdf(d1(S,K,tau,model)) * sqrt(tau)
def vanna_BS(S,K,tau,model=True):
return sqrt(tau) * st.norm.pdf(d1(S,K,tau,model)) * (1 - d1(S,K,tau,model))
def volga_BS(S,K,tau,model=True):
return S * d1(S,K,tau,model) * d2(S,K,tau,model) * st.norm.pdf(d1(S,K,tau,model)) / sigma_iv(S,K,tau,model)
def speed_BS(S,K,tau,model=True):
return - gamma_BS(S,K,tau,model) * ((d1(S,K,tau,model)/(sigma_iv(S,K,tau,model) * sqrt(tau))) + 1) / S
def zomma_BS(S,K,tau,model=True):
return gamma_BS(S,K,tau,model) * (d1(S,K,tau,model) * d2(S,K,tau,model) - 1)/sigma_iv(S,K,tau,model)
def vegavanna_BS(S,K,tau,model=True):
return (vanna_BS(S,K,tau,model) * d1(S,K,tau,model) * d2(S,K,tau,model) + vega_BS(S,K,tau,model) + (d1(S,K,tau,model) + d2(S,K,tau,model)) / (S * sigma_iv(S,K,tau,model) * tau) ) / sigma_iv(S,K,tau,model)
def ultima(S,K,tau,model=True):
return - vega_BS(S,K,tau,model) * ((d1(S,K,tau,model) * d2(S,K,tau,model) * (1 - d1(S,K,tau,model) * d2(S,K,tau,model))) + d1(S,K,tau,model) ** 2 + d2(S,K,tau,model) ** 2) / sigma_iv(S,K,tau,model) ** 2
The arguments for the Greeks specificed above are as discussed before. At first glance, you may find it unintuitive to include a model specifiying variable when the function name clearly indicates the greeks are under BSM. However, I retain this variable as the IV-adjusted greeks require evaluating BSM Greeks at $\sigma_{IV}$.
def derivative_1(S,K):
return -((alpha /(alpha * (K + S) + 1)) ** 2)
def derivative_2(S,K):
return 2 * (alpha /(alpha * (K + S) + 1)) ** 3
def derivative_3(S,K):
return -6 * (alpha /(alpha * (K + S) + 1)) ** 4
def delta_C(S,K,tau):
return delta_C_BS(S,K,tau,False) + vega_BS(S,K,tau,False) * derivative_1(S,K)
def delta_P(S,K,tau):
return delta_P_BS(S,K,tau,False) + vega_BS(S,K,tau,False) * derivative_1(S,K)
def gamma(S,K,tau):
drv1,drv2 = derivative_1(S,K),derivative_2(S,K)
return gamma_BS(S,K,tau,False) + 2 * vanna_BS(S,K,tau,False) * drv1 + volga_BS(S,K,tau,False) * drv1 ** 2 + vega_BS(S,K,tau,False) * drv2
def vanna(S,K,tau):
return vanna_BS(S,K,tau,False) + volga_BS(S,K,tau,False) * derivative_1(S,K)
def speed(S,K,tau):
drv1,drv2,drv3 = derivative_1(S,K),derivative_2(S,K),derivative_3(S,K)
return speed_BS(S,K,tau,False) + 3 * zomma_BS(S,K,tau,False) * drv1 + 3 * vanna_BS(S,K,tau,False) * drv2 + vega_BS(S,K,tau,False) * drv3 + 3 * vegavanna_BS(S,K,tau,False) * (drv1**2) + ultima(S,K,tau,False) * (drv1**3) + 3 * volga_BS(S,K,tau,False) * drv1 *drv2
NOTES
Since the fund resets options at strikes in reference to the stock price at the start of the quarter, the followinh functions help us reset the option strikes when the position is rolled over
def P1(S): # short put
return 0.8*S
def P2(S): # long put
return 0.95*S
def C1(S): #short call
return 1.05*S
def pf_delta(S,tau,q,reset=S_0):
return sizes[q-1] * (delta_P(S,P1(reset),tau) - delta_P(S,P2(reset),tau) + delta_C(S,C1(reset),tau))
def pf_gamma(S,tau,q,reset=S_0):
return sizes[q-1] * (gamma(S,P1(reset),tau) - gamma(S,P2(reset),tau) + gamma(S,C1(reset),tau))
def pf_vega(S,tau,q,reset=S_0):
return sizes[q-1] * (vega_BS(S,P1(reset),tau,False) - vega_BS(S,P2(reset),tau,False) + vega_BS(S,C1(reset),tau,False))
def pf_volga(S,tau,q,reset=S_0):
return sizes[q-1] * (volga_BS(S,P1(reset),tau,False) - volga_BS(S,P2(reset),tau,False) + volga_BS(S,C1(reset),tau,False))
def pf_vanna(S,tau,q,reset=S_0):
return sizes[q-1] * (vanna(S,P1(reset),tau) - vanna(S,P2(reset),tau) + vanna(S,C1(reset),tau))
def pf_zomma(S,tau,q,reset=S_0):
return sizes[q-1] * (zomma_BS(S,P1(reset),tau,False) - zomma_BS(S,P2(reset),tau,False) + zomma_BS(S,C1(reset),tau,False))
def pf_speed(S,tau,q,reset=S_0):
return sizes[q-1] * (speed(S,P1(reset),tau) - speed(S,P2(reset),tau) + speed(S,C1(reset),tau))
Our process is
$$ dS_t = \psi_tM^{\Delta}_tdt + \sigma S_t^{1-\zeta}dW_t$$if we discretize this, we have
$$ S_{t+1}-S_t = \psi S_t M_t^{\Delta} + \sigma S_t^{1-\zeta}(W_t - W_{t-1}) $$where
$$M_t^{\Delta} = \Sigma_{u=1}^t e^{-\varphi (t-u)}(\bar\Delta_{u}-\bar\Delta_{u-1})$$and $\bar\Delta_{u}-\bar\Delta_{u-1}$ is $\Delta_{u}^d-\Delta_{u-1}^d$, ie., the change in market makers $\Delta$ for all times but the reset dates. On the reset dates, it also includes JHEQX's change in portfolio size ($\theta$).
# Porfolio sizes
sizes = np.zeros(4)
sizes[0] = size_0
# Delta values
d_delta = np.zeros(period)
d_delta[0] = pf_delta(S_0,0.25,0)
# Stock prices (all beginning from S0)
price_process = np.zeros(period)
price_process[0] = S_0
# JHEQX strategy values per 1 unit of position
strat_value = np.zeros(period)
strat_value[0] = S_0 - P(S_0,P1(S_0),0.25,False) + P(S_0,P2(S_0),0.25,False) - C(S_0,C1(S_0),0.25,False)
# Fund values
fund_value = np.zeros(period)
fund_value[0] = size_0 * strat_value[0]
# Other greeks (d for dealer)
d_gamma = np.zeros(period)
d_vega = np.zeros(period)
d_volga = np.zeros(period)
d_vanna = np.zeros(period)
d_zomma = np.zeros(period)
d_speed = np.zeros(period)
#Implied vol tracking
imp_vol = np.zeros(period)
imp_vol[0] = sigma_iv(S_0,S_0,0.25,model=False)
def init():
price_process.fill(0)
price_process[0] = S_0
d_delta.fill(0)
d_delta[0] = pf_delta(S_0,0.25,0)
strat_value.fill(0)
strat_value[0] = S_0 - P(S_0,P1(S_0),0.25,False) + P(S_0,P2(S_0),0.25,False) - C(S_0,C1(S_0),0.25,False)
fund_value.fill(0)
fund_value[0] = size_0 * strat_value[0]
sizes.fill(0)
sizes[0] = size_0
imp_vol.fill(0)
imp_vol[0] = sigma_iv(S_0,S_0,0.25,model=False)
d_gamma.fill(0)
d_vega.fill(0)
d_volga.fill(0)
d_vanna.fill(0)
d_zomma.fill(0)
d_speed.fill(0)
def M_del(t):
if t == 0: # accounts for first period, since there no delta "action"
return 0
else:
phi_decay = np.exp(-phi * np.arange(0,t)[::-1]) # the decay component of the drift
diff_delta = d_delta[1:t+1] - d_delta[0:t] # the delta change
return sum(phi_decay * diff_delta)
def dSt(S,Z,t):
return psi * S * M_del(t) * dt + sigma * S ** (1 - zeta) * Z # price process defined above
NOTES
def generate_path(dWt,verbose = False):
strike_reference = S_0 # option strike to begin simulation with
for i in range(1,period):
# Compute quarter
q = 1 + int(4*i/period)
# Compute Time To Maturity (TTM)
TTM = q * 0.25 - i * dt
# Compute stock price
price_process[i] = price_process[i-1] + dSt(price_process[i-1],dWt[i],i-1)
if TTM == 0.25:
# we get this from expiring PF
payoff = sizes[q-2] * (price_process[i] - max(P1(strike_reference) - price_process[i],0) + max(P2(strike_reference) - price_process[i],0) - max(price_process[i] - C1(strike_reference),0))
# Update reference if necessary
strike_reference = price_process[i]
# find value of 1 unit strat_t given S_t, TTM, strike reference,
strat_value[i] = price_process[i] - P(price_process[i],P1(strike_reference),TTM,False) + P(price_process[i],P2(strike_reference),TTM,False) - C(price_process[i],C1(strike_reference),TTM,False)
imp_vol[i] = sigma_iv(price_process[i],strike_reference,TTM,model=False) * 100
if TTM == 0.25 :
# we liquidate old PF with expiring options and buy new ones. This sets up next PF size
sizes[q-1] = payoff / strat_value[i]
# we have now bought the new PF
# add to dealer delta the change in sizes on reset
d_delta[i] += sizes[q-1] - sizes[q-2]
if verbose and i == 1440:
print("\nPF sizes for the 4 quarters are: {} \n".format(sizes))
# Now we want to find the greeks. For these we want new size, new strike and new quarter (whenever
# it is updated).
d_delta[i] += pf_delta(price_process[i],TTM,q,strike_reference)
#rest of the greeks
d_gamma[i] = pf_gamma(price_process[i],TTM,q,strike_reference)
d_vega[i] = pf_vega(price_process[i],TTM,q,strike_reference)
d_volga[i] = pf_volga(price_process[i],TTM,q,strike_reference)
d_vanna[i] = pf_vanna(price_process[i],TTM,q,strike_reference)
d_zomma[i] = pf_zomma(price_process[i],TTM,q,strike_reference)
d_speed[i] = pf_speed(price_process[i],TTM,q,strike_reference)
# finally, the fund value
fund_value[i] = sizes[q-1] * strat_value[i]
Let's create a multi-indexed dataframe containing some upward, flat and downward paths. We will use it as a database for the next part of the code.
%%time
# Seed = 0 -> d path
# Seed = 2 -> d path
# Seed = 4 -> somehow flat, bit up
# Seed = 100 -> somehow flat, bit down
# Seed = 1475 -> u path
# Seed = 3574 -> u path
verbose = False
seeds = [0,2,4,100,1475,3574]
descs = ["price","fund_value","delta","gamma","vega","volga","vanna","zomma","speed","imp_vol"]
lines = []
for s in seeds :
if verbose :
print("Generating for seed",s)
np.random.seed(s)
generate_path(np.random.normal(0,np.sqrt(dt),period))
lines += [deepcopy(price_process),deepcopy(fund_value),deepcopy(d_delta),deepcopy(d_gamma),deepcopy(d_vega),deepcopy(d_volga),deepcopy(d_vanna),deepcopy(d_zomma),deepcopy(d_speed),deepcopy(imp_vol)]
if verbose:
print("Done")
tuples = [(i//len(descs),descs[i%len(descs)]) for i in range(len(seeds)*len(descs))]
index = pd.MultiIndex.from_tuples(tuples, names=["Number", "Description"])
df = pd.DataFrame(lines,index=index)
Generating for seed 0 Done Generating for seed 2 Done Generating for seed 4 Done Generating for seed 100 Done Generating for seed 1475 Done Generating for seed 3574 Done CPU times: user 57.3 s, sys: 140 ms, total: 57.5 s Wall time: 57.3 s
df
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 1450 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Number | Description | |||||||||||||||||||||
0 | price | 100.000000 | 100.198233 | 100.911625 | 102.250912 | 103.411094 | 103.124256 | 103.808222 | 103.924751 | 104.057000 | 104.444628 | ... | 9.092229e+01 | 9.124453e+01 | 9.140413e+01 | 9.170698e+01 | 9.171248e+01 | 9.136570e+01 | 9.091136e+01 | 9.046162e+01 | 8.984401e+01 | 8.936735e+01 |
fund_value | 99567.473585 | 99636.171232 | 99876.186159 | 100313.622025 | 100679.555699 | 100593.484554 | 100806.248485 | 100844.307678 | 100887.015412 | 101005.901870 | ... | 9.429595e+04 | 9.429596e+04 | 9.429596e+04 | 9.429596e+04 | 9.429596e+04 | 9.429596e+04 | 9.429596e+04 | 9.429596e+04 | 9.429596e+04 | 9.429596e+04 | |
delta | 0.000000 | 663.489795 | 669.168059 | 681.097966 | 692.530399 | 689.315698 | 696.295203 | 697.327926 | 698.544393 | 702.634147 | ... | 1.131385e+03 | 1.131403e+03 | 1.131406e+03 | 1.131406e+03 | 1.131406e+03 | 1.131406e+03 | 1.131406e+03 | 1.131406e+03 | 1.131406e+03 | 1.131406e+03 | |
gamma | 0.000000 | 8.019091 | 8.572467 | 9.631588 | 10.515349 | 10.310431 | 10.821350 | 10.915734 | 11.021670 | 11.304213 | ... | 4.791194e-02 | 8.998535e-03 | 1.706929e-03 | 1.230024e-04 | 1.142964e-05 | 2.040021e-06 | 2.483145e-07 | 5.791646e-09 | 1.091493e-11 | 1.815130e-21 | |
vega | 0.000000 | 5173.433282 | 5593.561614 | 6434.145375 | 7164.422388 | 6966.341575 | 7387.842638 | 7447.913965 | 7517.981718 | 7746.128424 | ... | 6.938106e-01 | 1.181005e-01 | 1.998192e-02 | 1.268192e-03 | 1.010171e-04 | 1.491184e-05 | 1.437708e-06 | 2.490210e-08 | 3.086276e-11 | 2.539120e-21 | |
volga | 0.000000 | 87468.580716 | 73156.908259 | 46476.358782 | 24401.946653 | 29319.945348 | 16682.585713 | 14311.471691 | 11686.713012 | 4845.161113 | ... | 5.541116e+02 | 1.196608e+02 | 2.510367e+01 | 2.089032e+00 | 2.099736e-01 | 3.758125e-02 | 4.528954e-03 | 1.073758e-04 | 2.052844e-07 | 4.123820e-17 | |
vanna | 0.000000 | 120.614926 | 127.296860 | 136.506754 | 140.938786 | 140.267423 | 141.851743 | 142.082988 | 142.291783 | 142.486318 | ... | -4.165889e-02 | -8.394024e-03 | -1.653126e-03 | -1.276187e-04 | -1.203485e-05 | -2.043803e-06 | -2.327635e-07 | -5.146555e-09 | -9.072496e-12 | -1.618911e-21 | |
zomma | 0.000000 | 36.332577 | 22.411833 | -2.919918 | -23.150254 | -18.625554 | -30.021105 | -32.115963 | -34.438898 | -40.507005 | ... | 2.979342e+00 | 6.806208e-01 | 1.520565e-01 | 1.354789e-02 | 1.478230e-03 | 2.928809e-04 | 3.997048e-05 | 1.109334e-06 | 2.644306e-09 | 7.643969e-19 | |
speed | 0.000000 | 0.779024 | 0.795953 | 0.780366 | 0.729408 | 0.752706 | 0.714404 | 0.711586 | 0.707144 | 0.680835 | ... | -9.987049e-02 | -1.819601e-02 | 1.215639e-04 | 5.662719e-03 | 7.611484e-03 | 1.006288e-02 | 1.408550e-02 | 2.169860e-02 | 3.987025e-02 | 1.127466e-01 | |
imp_vol | 0.258095 | 25.808159 | 25.805553 | 25.801455 | 25.797822 | 25.797636 | 25.795149 | 25.794008 | 25.792829 | 25.791051 | ... | 2.558895e+01 | 2.558701e+01 | 2.558562e+01 | 2.558375e+01 | 2.558288e+01 | 2.558318e+01 | 2.558385e+01 | 2.558452e+01 | 2.558576e+01 | 2.558653e+01 | |
1 | price | 100.000000 | 99.972126 | 99.367853 | 100.605068 | 100.132571 | 100.115996 | 100.745451 | 100.492318 | 100.314537 | 100.193843 | ... | 8.543952e+01 | 8.488349e+01 | 8.504406e+01 | 8.495797e+01 | 8.513435e+01 | 8.504243e+01 | 8.469455e+01 | 8.479897e+01 | 8.493158e+01 | 8.514543e+01 |
fund_value | 99567.473585 | 99559.881034 | 99355.751565 | 99776.331255 | 99619.626053 | 99615.872767 | 99829.053539 | 99746.376556 | 99688.484035 | 99649.580991 | ... | 8.846706e+04 | 8.790380e+04 | 8.805784e+04 | 8.796513e+04 | 8.814085e+04 | 8.804371e+04 | 8.768337e+04 | 8.778937e+04 | 8.792622e+04 | 8.814760e+04 | |
delta | 0.000000 | 1325.190568 | 1326.073254 | 1347.454545 | 1354.832499 | 1351.262513 | 1363.129069 | 1361.834085 | 1361.380689 | 1364.278727 | ... | 1.159000e+03 | 1.172202e+03 | 1.157103e+03 | 1.152528e+03 | 1.141526e+03 | 1.138076e+03 | 1.138329e+03 | 1.132995e+03 | 1.131485e+03 | 1.131406e+03 | |
gamma | 0.000000 | 7.843119 | 7.362571 | 8.317765 | 7.926385 | 7.899062 | 8.402172 | 8.182075 | 8.021950 | 7.907648 | ... | -2.656149e+01 | -4.958177e+01 | -3.567021e+01 | -3.319898e+01 | -1.896357e+01 | -1.463512e+01 | -1.706542e+01 | -5.238839e+00 | -3.913992e-01 | -5.337788e-05 | |
vega | 0.000000 | 5037.187678 | 4658.799349 | 5379.452879 | 5064.203690 | 5030.888114 | 5403.334803 | 5220.648943 | 5085.969310 | 4987.252379 | ... | -3.400356e+02 | -5.637748e+02 | -3.618718e+02 | -2.940934e+02 | -1.445806e+02 | -9.277977e+01 | -8.584392e+01 | -1.981245e+01 | -9.898434e-01 | -6.783195e-05 | |
volga | 0.000000 | 91904.788997 | 103491.095971 | 79093.045248 | 88350.762833 | 88541.890559 | 75748.439082 | 80685.014433 | 84115.912600 | 86403.676446 | ... | -5.067566e+04 | -8.074545e+04 | -6.941594e+04 | -6.719648e+04 | -4.682426e+04 | -3.787084e+04 | -3.899037e+04 | -1.491235e+04 | -1.496576e+03 | -3.322631e-01 | |
vanna | 0.000000 | 118.274175 | 111.491575 | 124.664080 | 120.112723 | 119.996711 | 126.144117 | 123.873824 | 122.198884 | 121.032916 | ... | 9.517901e+00 | 8.856425e+00 | 7.249696e+00 | 6.242023e+00 | 4.050964e+00 | 2.974686e+00 | 2.865560e+00 | 9.819119e-01 | 8.498548e-02 | 1.539073e-05 | |
zomma | 0.000000 | 40.763445 | 52.710908 | 28.379640 | 37.749387 | 38.123070 | 25.563891 | 30.617558 | 34.219490 | 36.701323 | ... | -2.237597e+02 | -3.638208e+02 | -3.671192e+02 | -3.955274e+02 | -3.196136e+02 | -2.924044e+02 | -3.390376e+02 | -1.582723e+02 | -2.037284e+01 | -6.634125e-03 | |
speed | 0.000000 | 0.772520 | 0.753939 | 0.800853 | 0.798906 | 0.805767 | 0.823935 | 0.828545 | 0.832842 | 0.837640 | ... | 4.332303e+01 | 5.593114e+01 | 4.756086e+01 | 4.717904e+01 | 3.308117e+01 | 2.904403e+01 | 3.745801e+01 | 1.585633e+01 | 1.870818e+00 | 5.942884e-02 | |
imp_vol | 0.258095 | 25.808712 | 25.809331 | 25.805432 | 25.805719 | 25.804892 | 25.802490 | 25.802239 | 25.801807 | 25.801235 | ... | 2.558429e+01 | 2.558528e+01 | 2.558389e+01 | 2.558332e+01 | 2.558188e+01 | 2.558133e+01 | 2.558163e+01 | 2.558043e+01 | 2.557913e+01 | 2.557756e+01 | |
2 | price | 100.000000 | 100.247670 | 100.436087 | 101.432582 | 101.858923 | 101.672419 | 101.925506 | 102.783963 | 103.489165 | 103.419060 | ... | 1.492055e+02 | 1.486385e+02 | 1.478845e+02 | 1.489482e+02 | 1.486720e+02 | 1.482893e+02 | 1.465835e+02 | 1.463581e+02 | 1.473131e+02 | 1.469890e+02 |
fund_value | 99567.473585 | 99652.797406 | 99717.910329 | 100049.508963 | 100190.137506 | 100131.971597 | 100216.146472 | 100492.115234 | 100714.087045 | 100695.125627 | ... | 1.113431e+05 | 1.113331e+05 | 1.113095e+05 | 1.113687e+05 | 1.113723e+05 | 1.113745e+05 | 1.113204e+05 | 1.113378e+05 | 1.113886e+05 | 1.113918e+05 | |
delta | 0.000000 | 1989.076817 | 1991.264355 | 2020.952444 | 2031.987280 | 2026.459116 | 2040.426820 | 2047.214253 | 2053.795094 | 2055.713991 | ... | 1.894490e+03 | 1.899029e+03 | 1.865295e+03 | 1.902343e+03 | 1.893132e+03 | 1.890066e+03 | 1.833717e+03 | 1.836505e+03 | 1.896807e+03 | 1.903116e+03 | |
gamma | 0.000000 | 8.057744 | 8.193935 | 8.982930 | 9.321374 | 9.168915 | 9.372374 | 10.060561 | 10.608487 | 10.564859 | ... | 2.479467e+01 | 3.094537e+01 | 4.283292e+01 | 2.002009e+01 | 2.025583e+01 | 2.191096e+01 | 6.987131e+01 | 7.447653e+01 | 1.460128e+01 | 3.874081e+00 | |
vega | 0.000000 | 5203.481329 | 5296.450568 | 5905.355506 | 6162.042572 | 6022.155863 | 6168.985977 | 6714.773164 | 7157.505137 | 7098.225204 | ... | 9.582990e+02 | 1.068245e+03 | 1.301021e+03 | 5.397207e+02 | 4.663216e+02 | 4.181867e+02 | 1.042482e+03 | 8.308139e+02 | 1.100015e+02 | 1.452867e+01 | |
volga | 0.000000 | 86494.971297 | 82619.109166 | 62573.589085 | 53886.936386 | 57339.459351 | 52066.061806 | 35086.245126 | 21671.423062 | 22608.566575 | ... | 1.244489e+05 | 1.287131e+05 | 1.304125e+05 | 1.080739e+05 | 1.047742e+05 | 1.038598e+05 | 1.274547e+05 | 1.292037e+05 | 6.369904e+04 | 1.924420e+04 | |
vanna | 0.000000 | 121.111174 | 123.013502 | 131.448324 | 134.378516 | 133.274787 | 134.961135 | 139.286876 | 141.484072 | 141.423564 | ... | -5.672488e+00 | -5.495125e+00 | -4.858888e+00 | -4.442239e+00 | -4.087362e+00 | -3.811463e+00 | -3.480335e+00 | -3.454033e+00 | -1.736826e+00 | -4.184324e-01 | |
zomma | 0.000000 | 35.364688 | 31.694621 | 12.324593 | 4.160871 | 7.562006 | 2.653386 | -13.185551 | -25.440245 | -24.540941 | ... | 1.686628e+02 | 1.706588e+02 | 1.488328e+02 | 1.985895e+02 | 2.118312e+02 | 2.319987e+02 | 1.714552e+02 | 2.311611e+02 | 2.553254e+02 | 1.190096e+02 | |
speed | 0.000000 | 0.780226 | 0.791210 | 0.800253 | 0.798990 | 0.810192 | 0.811004 | 0.783800 | 0.748489 | 0.759304 | ... | -1.333008e+01 | -1.651785e+01 | -2.160999e+01 | -1.456562e+01 | -1.623332e+01 | -1.934973e+01 | -4.648065e+01 | -5.997534e+01 | -2.478337e+01 | -1.181698e+01 | |
imp_vol | 0.258095 | 25.808038 | 25.806711 | 25.803424 | 25.801529 | 25.801111 | 25.799635 | 25.796713 | 25.794170 | 25.793470 | ... | 2.535522e+01 | 2.535505e+01 | 2.535510e+01 | 2.535296e+01 | 2.535244e+01 | 2.535204e+01 | 2.535326e+01 | 2.535268e+01 | 2.535066e+01 | 2.535020e+01 | |
3 | price | 100.000000 | 100.169760 | 101.651709 | 102.408201 | 103.764265 | 104.864710 | 105.786111 | 106.011846 | 106.663027 | 107.520097 | ... | 1.272542e+02 | 1.266852e+02 | 1.263247e+02 | 1.266796e+02 | 1.275737e+02 | 1.268855e+02 | 1.261380e+02 | 1.264268e+02 | 1.259216e+02 | 1.260776e+02 |
fund_value | 99567.473585 | 99626.586444 | 100118.633602 | 100363.662302 | 100787.485106 | 101117.996151 | 101384.946844 | 101451.271973 | 101633.343753 | 101864.131232 | ... | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | 1.007938e+05 | |
delta | 0.000000 | 2652.339137 | 2666.979563 | 2703.571587 | 2728.268835 | 2734.764048 | 2759.376898 | 2768.702913 | 2783.146994 | 2795.802219 | ... | 2.784714e+03 | 2.789253e+03 | 2.755519e+03 | 2.792567e+03 | 2.783356e+03 | 2.780291e+03 | 2.723941e+03 | 2.726729e+03 | 2.787031e+03 | 2.793340e+03 | |
gamma | 0.000000 | 7.996855 | 9.161236 | 9.753948 | 10.768712 | 11.514697 | 12.068869 | 12.209537 | 12.549101 | 12.922187 | ... | 2.520044e-05 | 1.750339e-05 | 6.280598e-06 | 2.176331e-07 | 3.621435e-10 | 3.244728e-11 | 9.730416e-13 | 2.876572e-18 | 5.320071e-26 | 6.963276e-55 | |
vega | 0.000000 | 5156.167716 | 6065.515490 | 6535.908438 | 7387.159841 | 8044.376487 | 8555.917494 | 8668.025349 | 8993.134099 | 9382.880975 | ... | 7.105092e-04 | 4.401870e-04 | 1.396001e-04 | 4.256273e-06 | 6.156098e-09 | 4.547058e-10 | 1.078078e-11 | 2.401147e-17 | 2.936921e-25 | 1.926694e-54 | |
volga | 0.000000 | 88028.790214 | 58459.216266 | 43439.003097 | 18132.754252 | -336.395138 | -13929.575310 | -17249.115420 | -25376.566138 | -34234.411464 | ... | 1.041381e+00 | 6.987675e-01 | 2.511955e-01 | 9.844456e-03 | 2.026620e-05 | 1.793332e-06 | 5.341535e-08 | 1.910346e-13 | 3.986591e-21 | 7.571601e-50 | |
vanna | 0.000000 | 120.326566 | 132.879466 | 137.289159 | 141.607723 | 141.893928 | 140.046917 | 139.362084 | 136.693615 | 131.957739 | ... | -4.319198e-05 | -2.805900e-05 | -9.641067e-06 | -3.493042e-07 | -6.493417e-10 | -5.448489e-11 | -1.525939e-12 | -4.900952e-18 | -9.259045e-26 | -1.532222e-54 | |
zomma | 0.000000 | 36.890230 | 8.271823 | -5.753159 | -28.788806 | -45.039494 | -56.674425 | -59.504584 | -66.289660 | -73.495077 | ... | 2.957552e-03 | 2.112584e-03 | 8.118147e-04 | 3.399690e-05 | 7.499865e-08 | 7.360910e-09 | 2.485105e-10 | 1.026057e-15 | 2.651760e-23 | 7.133924e-52 | |
speed | 0.000000 | 0.778296 | 0.789859 | 0.774519 | 0.706010 | 0.623503 | 0.540026 | 0.520820 | 0.454290 | 0.359875 | ... | 5.683963e-04 | 6.895659e-04 | 8.552022e-04 | 1.062161e-03 | 1.335497e-03 | 1.759751e-03 | 2.464955e-03 | 3.792368e-03 | 6.977348e-03 | 1.972881e-02 | |
imp_vol | 0.258095 | 25.808228 | 25.803762 | 25.801078 | 25.796987 | 25.793535 | 25.790524 | 25.789135 | 25.786768 | 25.783941 | ... | 2.543035e+01 | 2.543051e+01 | 2.543030e+01 | 2.542881e+01 | 2.542636e+01 | 2.542673e+01 | 2.542721e+01 | 2.542583e+01 | 2.542589e+01 | 2.542475e+01 | |
4 | price | 100.000000 | 99.736855 | 100.856210 | 101.245904 | 102.812483 | 103.702030 | 105.036425 | 106.137515 | 107.249725 | 107.723118 | ... | 1.621740e+02 | 1.607268e+02 | 1.608553e+02 | 1.604452e+02 | 1.592439e+02 | 1.587585e+02 | 1.600437e+02 | 1.597472e+02 | 1.585274e+02 | 1.577858e+02 |
fund_value | 99567.473585 | 99480.074051 | 99857.840017 | 99988.402966 | 100493.647242 | 100771.248552 | 101170.900627 | 101486.175597 | 101789.960507 | 101916.632123 | ... | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | 1.131765e+05 | |
delta | 0.000000 | 3312.220300 | 3335.674928 | 3375.410469 | 3414.650343 | 3430.148007 | 3469.451673 | 3491.727748 | 3519.926170 | 3538.518254 | ... | 3.585061e+03 | 3.589600e+03 | 3.555866e+03 | 3.592914e+03 | 3.583703e+03 | 3.580638e+03 | 3.524289e+03 | 3.527076e+03 | 3.587379e+03 | 3.593687e+03 | |
gamma | 0.000000 | 7.661732 | 8.528273 | 8.833111 | 10.068360 | 10.734854 | 11.637575 | 12.273907 | 12.794968 | 12.992323 | ... | 4.691448e-08 | 9.422943e-08 | 5.584899e-09 | 5.924946e-10 | 2.830703e-10 | 7.543093e-12 | 1.680422e-17 | 5.369569e-23 | 1.133883e-30 | 1.380889e-57 | |
vega | 0.000000 | 4897.663301 | 5558.631887 | 5785.559927 | 6780.821383 | 7334.472407 | 8133.870465 | 8734.351145 | 9270.240487 | 9469.376866 | ... | 2.141070e-06 | 3.801731e-06 | 2.006020e-07 | 1.852630e-08 | 7.473661e-09 | 1.649495e-10 | 2.987263e-16 | 7.132418e-22 | 9.888387e-30 | 5.965031e-57 | |
volga | 0.000000 | 96485.059023 | 74260.906748 | 66292.143467 | 35448.841856 | 18885.341636 | -3319.198944 | -18827.063219 | -31394.986878 | -35948.163349 | ... | 4.359846e-03 | 7.918667e-03 | 5.048836e-04 | 5.473571e-05 | 2.461069e-05 | 6.778201e-07 | 1.963441e-12 | 7.003396e-18 | 1.566439e-25 | 2.457856e-52 | |
vanna | 0.000000 | 115.716458 | 126.825440 | 130.063184 | 139.113456 | 141.603091 | 141.752151 | 138.902688 | 133.574739 | 130.648457 | ... | -1.198928e-07 | -2.144464e-07 | -1.278622e-08 | -1.309674e-09 | -5.653542e-10 | -1.450221e-11 | -3.689337e-17 | -1.191743e-22 | -2.416346e-30 | -3.296486e-57 | |
zomma | 0.000000 | 45.374758 | 23.487487 | 15.901626 | -13.069152 | -28.078826 | -47.629122 | -60.821420 | -71.150390 | -74.837686 | ... | 7.720720e-06 | 1.503743e-05 | 1.018409e-06 | 1.188669e-07 | 5.863697e-08 | 1.784102e-09 | 5.714644e-15 | 2.368711e-20 | 6.603092e-28 | 1.483610e-54 | |
speed | 0.000000 | 0.763973 | 0.795759 | 0.802175 | 0.763048 | 0.716110 | 0.612776 | 0.507612 | 0.388288 | 0.336259 | ... | 1.851035e-04 | 2.178527e-04 | 2.600805e-04 | 3.182357e-04 | 4.025381e-04 | 5.299169e-04 | 7.378958e-04 | 1.137215e-03 | 2.096599e-03 | 5.941570e-03 | |
imp_vol | 0.258095 | 25.809290 | 25.805687 | 25.803875 | 25.799245 | 25.796267 | 25.792267 | 25.788845 | 25.785425 | 25.783479 | ... | 2.534318e+01 | 2.534395e+01 | 2.534295e+01 | 2.534256e+01 | 2.534307e+01 | 2.534277e+01 | 2.534044e+01 | 2.533992e+01 | 2.534046e+01 | 2.534046e+01 | |
5 | price | 100.000000 | 100.733961 | 102.379652 | 103.234591 | 105.253986 | 107.078149 | 107.281941 | 108.505173 | 110.326323 | 111.807152 | ... | 1.621889e+02 | 1.605631e+02 | 1.608753e+02 | 1.598928e+02 | 1.606957e+02 | 1.611592e+02 | 1.599142e+02 | 1.596050e+02 | 1.596431e+02 | 1.592921e+02 |
fund_value | 99567.473585 | 99815.281278 | 100352.222216 | 100622.547516 | 101227.641371 | 101734.454736 | 101791.588501 | 102106.624885 | 102537.847503 | 102856.687442 | ... | 1.138286e+05 | 1.138285e+05 | 1.138286e+05 | 1.138286e+05 | 1.138286e+05 | 1.138286e+05 | 1.138286e+05 | 1.138286e+05 | 1.138286e+05 | 1.138286e+05 | |
delta | 0.000000 | 3980.108360 | 4018.251420 | 4066.332722 | 4127.684257 | 4165.250309 | 4206.973167 | 4244.951412 | 4297.300812 | 4335.722377 | ... | 4.352689e+03 | 4.357212e+03 | 4.323496e+03 | 4.360543e+03 | 4.351338e+03 | 4.348273e+03 | 4.291924e+03 | 4.294711e+03 | 4.355013e+03 | 4.361322e+03 | |
gamma | 0.000000 | 8.440308 | 9.730001 | 10.378420 | 11.731740 | 12.650727 | 12.755053 | 13.159241 | 13.433369 | 13.390524 | ... | 9.233595e-03 | 3.088938e-02 | 7.695000e-03 | 1.026958e-02 | 5.372246e-04 | 1.725403e-05 | 1.255894e-05 | 1.947609e-07 | 6.771931e-12 | 1.257587e-23 | |
vega | 0.000000 | 5503.357001 | 6534.487713 | 7066.838189 | 8280.082322 | 9214.233615 | 9299.303428 | 9785.752552 | 10297.509689 | 10511.221074 | ... | 4.213664e-01 | 1.243386e+00 | 2.763884e-01 | 3.188269e-01 | 1.443885e-02 | 3.886556e-04 | 2.228383e-04 | 2.581727e-06 | 5.987176e-11 | 5.534746e-23 | |
volga | 0.000000 | 76871.163712 | 44263.494977 | 27924.712188 | -5775.657543 | -28582.219596 | -30937.924167 | -40786.744526 | -47702.930078 | -47065.874080 | ... | 3.674794e+02 | 1.000468e+03 | 2.785214e+02 | 3.357366e+02 | 2.179574e+01 | 8.276076e-01 | 5.453344e-01 | 9.235610e-03 | 3.960720e-07 | 9.748180e-19 | |
vanna | 0.000000 | 125.689879 | 137.059635 | 140.398082 | 141.235954 | 134.458523 | 133.337948 | 125.059058 | 109.168786 | 94.157839 | ... | -1.230106e-02 | -3.366448e-02 | -8.642463e-03 | -1.008227e-02 | -5.810753e-04 | -1.968642e-05 | -1.221998e-05 | -1.837496e-07 | -6.703256e-12 | -1.357922e-23 | |
zomma | 0.000000 | 25.882697 | -5.075389 | -20.011107 | -49.701538 | -68.649102 | -70.607741 | -78.276073 | -82.933378 | -81.592692 | ... | 6.299783e-01 | 1.829431e+00 | 5.436124e-01 | 7.082461e-01 | 4.986474e-02 | 2.081929e-03 | 1.559101e-03 | 3.081260e-05 | 1.631306e-09 | 5.747047e-21 | |
speed | 0.000000 | 0.787925 | 0.769090 | 0.734063 | 0.583233 | 0.398916 | 0.379066 | 0.241229 | 0.033849 | -0.124779 | ... | -1.157353e-02 | -3.887360e-02 | -1.097714e-02 | -1.561134e-02 | -7.171661e-04 | 3.649914e-04 | 5.335329e-04 | 8.730488e-04 | 1.605314e-03 | 4.540816e-03 | |
imp_vol | 0.258095 | 25.806852 | 25.802013 | 25.799108 | 25.793494 | 25.788417 | 25.787085 | 25.783441 | 25.778500 | 25.774371 | ... | 2.533599e+01 | 2.533688e+01 | 2.533569e+01 | 2.533590e+01 | 2.533417e+01 | 2.533281e+01 | 2.533331e+01 | 2.533279e+01 | 2.533189e+01 | 2.533142e+01 |
60 rows × 1460 columns
def display(row,start=0,end=1460,window=120) :
s_plot = plt.subplot(521)
v_plot = plt.subplot(522)
del_plot = plt.subplot(523)
gam_plot = plt.subplot(524)
veg_plot = plt.subplot(525)
vog_plot = plt.subplot(526)
van_plot = plt.subplot(527)
zom_plot = plt.subplot(528)
spe_plot = plt.subplot(529)
imp_plot = plt.subplot(5,2,10)
plt.subplots_adjust(left=0, right=2)
plt.subplots_adjust(bottom=0, top=4)
t = np.linspace(start,end,end-start)
S,V = row.loc["price"][start:end],row.loc["fund_value"][start:end]
DEL,GAM = row.loc["delta"][start:end],row.loc["gamma"][start:end]
VEG,VOG = row.loc["vega"][start:end],row.loc["volga"][start:end]
VAN,ZOM = row.loc["vanna"][start:end],row.loc["zomma"][start:end]
SPE,IMP = row.loc["speed"][start:end],row.loc["imp_vol"][start:end]
s_plot.title.set_text("STOCK PRICE")
v_plot.title.set_text("FUND VALUE")
del_plot.title.set_text("DELTA")
gam_plot.title.set_text("GAMMA")
veg_plot.title.set_text("VEGA")
vog_plot.title.set_text("VOLGA")
van_plot.title.set_text("VANNA")
zom_plot.title.set_text("ZOMMA")
spe_plot.title.set_text("SPEED")
imp_plot.title.set_text("Implied volatility")
s_plot.plot(t,S)
v_plot.plot(t,V)
del_plot.plot(t,DEL)
gam_plot.plot(t,GAM)
veg_plot.plot(t,VEG)
vog_plot.plot(t,VOG)
van_plot.plot(t,VAN)
zom_plot.plot(t,ZOM)
spe_plot.plot(t,SPE)
imp_plot.plot(t,IMP)
plt.show()
def latex_display(row,start=0,end=1460,window=120) :
fig = plt.figure()
s_plot = plt.subplot(511)
del_plot = plt.subplot(512)
gam_plot = plt.subplot(513)
van_plot = plt.subplot(514)
zom_plot = plt.subplot(515)
t = np.linspace(start,end,end-start)
S = row.loc["price"][start:end]
DEL,GAM = row.loc["delta"][start:end],row.loc["gamma"][start:end]
VAN,ZOM = row.loc["vanna"][start:end],row.loc["zomma"][start:end]
s_plot.plot(t,S,label="Stock Price")
del_plot.plot(t,DEL,label='Delta')
gam_plot.plot(t,GAM,label= "Gamma")
van_plot.plot(t,VAN,label="Vanna")
zom_plot.plot(t,ZOM,label="Zomma")
s_plot.legend()
del_plot.legend()
gam_plot.legend()
van_plot.legend()
zom_plot.legend()
fig.set_figheight(10.5)
fig.set_figwidth(6.7747)
plt.savefig("path_3.pgf")
plt.show()
path = 5
a = 1
b = 1460
display(df.loc[path],start = a,end = b,window = 120)
path = 3
a = 1
b = 1460
latex_display(df.loc[path],start = a,end = b,window = 120)
Let's create two strategies to exploit the above graphs.
This is a stock strategy based on the fact the the drift of the process $d\bar\Delta$ is subject to a discrete jump, namely $\theta_{\frac{j}{4}} - \theta_{\frac{j-1}{4}} $. This is a non trivial amount given the size of JHEQX. The collar/spread derivative strategy they use(and the structure of the question) means that in quarter $j$, the fund's value is capped at $\theta_{\frac{j}{4}} 1.05S_{\frac{j}{4}}$. As such, if this barrier is breached, the fund effectively sustains losses as the short call will be exerised. Combined with high stock prices and sef-financingness, this means when the roll over their portfolio in quarter $j+1$:
$$\theta_{\frac{j}{4}} > \theta_{\frac{j+1}{4}}$$ $$\implies \theta_{\frac{j+1}{4}} - \theta_{\frac{j}{4}} < 0 $$So, we expect a drop in share price.
Further, as the market makers hold an ITM call, their delta as we approach quarter $j$ expiry is
$$\Delta_{MM,\frac{j}{4}} \ = \theta_{\frac{j}{4}}$$Now since we know $$(\Delta_{p,\frac{j+1}{4}}^{0.8} - \Delta_{p,\frac{j+1}{4}}^{0.95} + \Delta_{c,\frac{j+1}{4}}^{1.05} )< 1$$
$$\Delta_{MM,\frac{j+1}{4}} \ = \theta_{\frac{j+1}{4}}(\Delta_{p,\frac{j+1}{4}}^{0.8} - \Delta_{p,\frac{j+1}{4}}^{0.95} + \Delta_{c,\frac{j+1}{4}}^{1.05} ) < \theta_{\frac{j+1}{4}} $$So,
$$\Delta_{MM,\frac{j+1}{4}} - \Delta_{MM,\frac{j}{4}}<0$$This further bolsters our confidence in the conclusion that the stock price will fall
For each path:
If we want to modify this strategy to options (you can try to compare results), we do the following:
def sharpeRatio(returns):
return np.mean(returns)/np.sqrt(np.var(returns))
def strategy1(row, P0 = 1,j=50,verbose=False):
s_value = np.zeros(period)
s_value[0] = P0
invested = 0
strike_ref = 1.05*row.loc["price"][0]
for i in range(1,period):
q = 1 + int(4*i/period)
TTM = q * 0.25 - i * dt
s_value[i] = s_value[i-1] + invested * (row.loc["price"][i]-row.loc["price"][i-1])
if q*365-1 == i :
if strike_ref >= row.loc["price"][i]:
if verbose :
print("buy at",i)
invested = s_value[i-1]/row.loc["price"][i]
else :
if verbose :
print("sell at",i)
invested = - s_value[i-1]/row.loc["price"][i]
if (q-1)*365 + j == i :
if verbose :
print("clear at",i)
invested = 0
if TTM == 0.25 :
if verbose :
print('change at ',i)
strike_ref = row.loc["price"][i]
return s_value
def strategy2(row, P0 = 1,j=50,verbose=False):
s_value = np.zeros(period)
s_value[0] = P0
strike_ref = 1.05 * row.loc["price"][0]
call_strike = put_strike = row.loc["price"][0]
invested_put = invested_call = 0
for i in range(1,period):
q = 1 + int(4*i/period)
TTM = q * 0.25 - i * dt
s_value[i] = s_value[i-1]
if invested_put :
s_value[i] += invested_put * (P(row.loc["price"][i],put_strike,TTM,False) - P(row.loc["price"][i-1],put_strike,TTM-dt,False))
if invested_call :
s_value[i] += invested_call * (C(row.loc["price"][i],call_strike,TTM,False) - C(row.loc["price"][i-1],call_strike,TTM-dt,False))
if q*365-1 == i :
if strike_ref >= row.loc["price"][i]:
if verbose :
print("buy call at time",i)
call_strike = row.loc["price"][i]
price_call = C(row.loc["price"][i],call_strike,0.25+dt,False)
invested_call = s_value[i-1]/price_call
s_value[i] -= price_call
else :
if verbose :
print("buy put at time",i)
put_strike = row.loc["price"][i]
price_put = P(row.loc["price"][i],put_strike,0.25+dt,False)
invested_put = s_value[i-1]/price_put
s_value[i] -= price_put
if (q-1)*365 + j == i :
if verbose :
print("clear at",i)
invested_call,invested_put = 0,0
if TTM == 0.25 :
if verbose :
print('change at ',i)
strike_ref = row.loc["price"][i]
return s_value
def display_strategy(df,strat,P0):
fig,ax = plt.subplots(nrows=3, ncols=2)
plt.subplots_adjust(left=0, right=2)
plt.subplots_adjust(bottom=0, top=3)
t = np.linspace(0,period,period)
for i in range(len(seeds)) :
s1 = strat(df.loc[i],P0)
ax[i//2][i%2].plot(t,s1)
ax[i//2][i%2].title.set_text("Strategy Payoff for Seed " + str(seeds[i]))
returns = np.log(s1[1:]/s1[:-1])
print("Seed {} --> Strategy Return : {} % & Sharperatio : {}".format(seeds[i],100 * (s1[-1]-P0)/P0,sharpeRatio(returns)))
plt.show()
def get_latex_plot()
fig = plt.figure()
s1_plot = plt.subplot(211)
s2_plot = plt.subplot(212)
t = np.linspace(0,period,period)
s1 = strategy1(df.loc[3],100)
s2 = strategy2(df.loc[3],100)
s1_plot.plot(t,s1,label="Payoff for strategy 1")
s2_plot.plot(t,s2,label="Payoff for strategy 2")
s1_plot.legend()
s2_plot.legend()
fig.set_figheight(10.5)
fig.set_figwidth(6.7747)
plt.savefig("payoffs.pgf")
plt.show()
get_latex_plot()
display_strategy(df,strategy1,100)
Seed 0 --> Strategy Return : 0.7716332624216733 % & Sharperatio : 0.0032828261113285752 Seed 2 --> Strategy Return : 15.673609866025258 % & Sharperatio : 0.06349398621350966 Seed 4 --> Strategy Return : 6.360674283525697 % & Sharperatio : 0.028608302539928968 Seed 100 --> Strategy Return : 5.3924421950251125 % & Sharperatio : 0.023617230481493407 Seed 1475 --> Strategy Return : 6.949823412949712 % & Sharperatio : 0.025049096325178263 Seed 3574 --> Strategy Return : -7.17729467647942 % & Sharperatio : -0.031318757961904996
display_strategy(df,strategy2,100)
Seed 0 --> Strategy Return : -4.222698829492458 % & Sharperatio : -0.0017435131525656298 Seed 2 --> Strategy Return : 246.5273635732582 % & Sharperatio : 0.05814781308741152 Seed 4 --> Strategy Return : 62.59204755136278 % & Sharperatio : 0.021754130656368484 Seed 100 --> Strategy Return : 58.564381415274966 % & Sharperatio : 0.02046361390655567 Seed 1475 --> Strategy Return : 78.07843417518868 % & Sharperatio : 0.021718061746529224 Seed 3574 --> Strategy Return : -57.085142380357084 % & Sharperatio : -0.03493061675613743
def myriad(df,strat,P0,n,with_seeds=False,verbose=False):
pls = []
annualised_returns = []
vol_returns = []
sharpeRatios = []
if with_seeds:
for i in range(len(seeds)) :
if verbose :
print("Using known path seed",seeds[i])
s = strat(df.loc[i],P0)
returns = np.log(s[1:]/s[:-1])
pls.append(s[-1]-s[0])
annualised_returns.append((s[-1]-P0)/P0)
vol_returns.append(np.sqrt(np.var(returns)))
sharpeRatios.append(sharpeRatio(returns))
if verbose :
print("Done")
for u in range(n) :
s = int(100000 * np.random.random())
if verbose :
print("Generating path from seed",s)
np.random.seed(s)
init()
generate_path(np.random.normal(0,np.sqrt(dt),period),False)
row = pd.DataFrame([deepcopy(price_process),deepcopy(fund_value),deepcopy(d_delta),deepcopy(d_gamma),deepcopy(d_vega),deepcopy(d_volga),deepcopy(d_vanna),deepcopy(d_zomma),deepcopy(d_speed),deepcopy(imp_vol)],index=descs)
s = strat(row,P0)
returns = np.log(s[1:]/s[:-1])
pls.append(s[-1]-s[0])
annualised_returns.append((s[-1]-P0)/P0)
vol_returns.append(np.sqrt(np.var(returns)))
sharpeRatios.append(sharpeRatio(returns))
if verbose :
print("Done")
if u%10 == 0:
print("Status {}% done".format(u/n*100))
pl_hist = plt.subplot(221)
ean_hist = plt.subplot(222)
vr_hist = plt.subplot(223)
sr_hist = plt.subplot(224)
plt.subplots_adjust(left=0, right=2)
plt.subplots_adjust(bottom=0, top=3)
pl_hist.hist(pls,color='g')
ean_hist.hist(annualised_returns,color='g')
vr_hist.hist(vol_returns, color='g')
sr_hist.hist(sharpeRatios, color='g')
pl_hist.set_title("Distribution of P & L")
ean_hist.set_title("Distribution of annualised returns")
vr_hist.set_title("Distribution of volatility returns")
sr_hist.set_title("Distribution of sharpe ratios")
plt.show()
print("Generated with {} paths".format(n))
def compute_myriad(df,P0,n,with_seeds=False,verbose=False):
annualised_returns_s1 = []
annualised_returns_s2 = []
if with_seeds:
for i in range(len(seeds)) :
s1 = strategy1(df.loc[i],P0)
s2 = strategy2(df.loc[i],P0)
annualised_returns_s1.append((s1[-1]-P0)/P0)
annualised_returns_s2.append((s2[-1]-P0)/P0)
for u in range(n) :
s = int(571232 * np.random.random())
np.random.seed(s)
init()
generate_path(np.random.normal(0,np.sqrt(dt),period),False)
row = pd.DataFrame([deepcopy(price_process),deepcopy(fund_value),deepcopy(d_delta),deepcopy(d_gamma),deepcopy(d_vega),deepcopy(d_volga),deepcopy(d_vanna),deepcopy(d_zomma),deepcopy(d_speed),deepcopy(imp_vol)],index=descs)
s1 = strategy1(row,P0)
s2 = strategy2(row,P0)
annualised_returns_s1.append((s1[-1]-P0)/P0)
annualised_returns_s2.append((s2[-1]-P0)/P0)
if u%10 == 0:
print("Status {}% done".format(u/n*100))
print("Processed {} paths".format(n))
return annualised_returns_s1,annualised_returns_s2
def accelerated(dWt,P0):
strike_reference = S_0
s1 = np.zeros(period)
s2 = np.zeros(period)
s1[0] = P0
s2[0] = P0
invested = 0
strike_ref = 1.05*price_process[0]
call_strike = put_strike = price_process[0]
invested_put = invested_call = 0
j = 50
for i in range(1,period):
q = 1 + int(4*i/period)
TTM = q * 0.25 - i * dt
price_process[i] = price_process[i-1] + dSt(price_process[i-1],dWt[i],i-1)
if TTM == 0.25:
payoff = sizes[q-2] * (price_process[i] - max(P1(strike_reference) - price_process[i],0) + max(P2(strike_reference) - price_process[i],0) - max(price_process[i] - C1(strike_reference),0))
strike_reference = price_process[i]
strat_value[i] = price_process[i] - P(price_process[i],P1(strike_reference),TTM,False) + P(price_process[i],P2(strike_reference),TTM,False) - C(price_process[i],C1(strike_reference),TTM,False)
if TTM == 0.25 :
sizes[q-1] = payoff / strat_value[i]
d_delta[i] += sizes[q-1] - sizes[q-2]
d_delta[i] += pf_delta(price_process[i],TTM,q,strike_reference)
# S1
s1[i] = s1[i-1] + invested * (price_process[i]-price_process[i-1])
if q*365-1 == i :
if strike_ref >= price_process[i]:
invested = s1[i-1]/price_process[i]
else :
invested = - s1[i-1]/price_process[i]
if (q-1)*365 + j == i :
invested = 0
# S2
s2[i] = s2[i-1]
if invested_put :
s2[i] += invested_put * (P(price_process[i],put_strike,TTM,False) - P(price_process[i-1],put_strike,TTM-dt,False))
if invested_call :
s2[i] += invested_call * (C(price_process[i],call_strike,TTM,False) - C(price_process[i-1],call_strike,TTM-dt,False))
if q*365-1 == i :
if strike_ref >= price_process[i]:
call_strike = price_process[i]
price_call = C(price_process[i],call_strike,0.25+dt,False)
invested_call = s2[i-1]/price_call
s2[i] -= price_call
else :
put_strike = price_process[i]
price_put = P(price_process[i],put_strike,0.25+dt,False)
invested_put = s2[i-1]/price_put
s2[i] -= price_put
if (q-1)*365 + j == i :
invested_call,invested_put = 0,0
if TTM == 0.25 :
strike_ref = price_process[i]
return ((s1[-1]-P0)/P0,(s2[-1]-P0)/P0)
def use_acceleration(n,P0):
annualised_returns_s1 = []
annualised_returns_s2 = []
for u in range(n) :
s = int(571232 * np.random.random())
np.random.seed(s)
init()
r1,r2 = accelerated(np.random.normal(0,np.sqrt(dt),period),P0)
annualised_returns_s1.append(r1)
annualised_returns_s2.append(r2)
if u%10 == 0:
print("Status {}% done".format(u/n*100))
return annualised_returns_s1,annualised_returns_s2
%%time
ar1,ar2 = compute_myriad(df,100,50,True)
Status 0.0% done Status 20.0% done Status 40.0% done Status 60.0% done Status 80.0% done Generated with 50 paths CPU times: user 10min 55s, sys: 850 ms, total: 10min 56s Wall time: 10min 56s
%%time
ar1_1,ar2_1 = compute_myriad(df,100,150)
Status 0.0% done Status 6.666666666666667% done Status 13.333333333333334% done Status 20.0% done Status 26.666666666666668% done Status 33.33333333333333% done Status 40.0% done Status 46.666666666666664% done Status 53.333333333333336% done Status 60.0% done Status 66.66666666666666% done Status 73.33333333333333% done Status 80.0% done Status 86.66666666666667% done Status 93.33333333333333% done Processed 150 paths CPU times: user 33min 28s, sys: 2.9 s, total: 33min 31s Wall time: 33min 35s
%%time
ar1_2,ar2_2 = use_acceleration(100,100)
Status 0.0% done Status 10.0% done Status 20.0% done Status 30.0% done Status 40.0% done Status 50.0% done Status 60.0% done Status 70.0% done Status 80.0% done Status 90.0% done CPU times: user 4min, sys: 99.7 ms, total: 4min Wall time: 4min
%%time
ar1_3,ar2_3 = use_acceleration(200,100)
Status 0.0% done Status 5.0% done Status 10.0% done Status 15.0% done Status 20.0% done Status 25.0% done Status 30.0% done Status 35.0% done Status 40.0% done Status 45.0% done Status 50.0% done Status 55.00000000000001% done Status 60.0% done Status 65.0% done Status 70.0% done Status 75.0% done Status 80.0% done Status 85.0% done Status 90.0% done Status 95.0% done CPU times: user 6min 11s, sys: 279 ms, total: 6min 11s Wall time: 6min 11s
ar1c = 100 * np.array(ar1 + ar1_1 + ar1_2 + ar1_3)
print("Mean is {} and Std is {}".format(ar1c.mean(),ar1c.std()))
fig = plt.figure()
sns.distplot(ar1c,bins=int(180/5), color = 'darkblue')
#plt.title("Distribution of annualised returns for strategy 1")
fig.set_figheight(3.5)
fig.set_figwidth(4.7747)
plt.savefig("s1.pgf")
plt.show()
Mean is 3.6638387384498468 and Std is 8.220800573877483
/home/kkameleon/.local/lib/python3.10/site-packages/seaborn/distributions.py:2619: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms). warnings.warn(msg, FutureWarning)
ar2c = 100 * np.array(ar2 + ar2_1 + ar2_2+ ar2_3)
print("Mean is {} and Std is {}".format(ar2c.mean(),ar2c.std()))
fig = plt.figure()
ax = sns.distplot(ar2c,kde=True,bins=int(180/5), color = 'darkblue')
#plt.title("Distribution of annualised returns for strategy 2")
#ax.set_ylabels('Density')
#ax.set_yticklabels([int(i/60) for i in range(8)])
fig.set_figheight(3.5)
fig.set_figwidth(4.7747)
plt.savefig("s2.pgf")
plt.show()
Mean is 75.53903406946337 and Std is 134.3000155548209
/home/kkameleon/.local/lib/python3.10/site-packages/seaborn/distributions.py:2619: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms). warnings.warn(msg, FutureWarning)