Opciones con trampilla
Una opción con barrera es un call o put con un interruptor. Una opción knock-out deja de existir si el precio toca la barrera; una knock-in solo existe si la toca. La barrera puede estar por encima del inicio (up) o por debajo (down). Como el comprador renuncia a parte del pago, estas opciones son más baratas que las simples, de ahí su popularidad, también en productos estructurados. Como las asiáticas, dependen de toda la trayectoria, y la herramienta natural es la simulación.
import numpy as np
paths = np.array([[100.0, 105.0, 112.0, 108.0], # stays below 115
[100.0, 98.0, 103.0, 110.0], # stays below 115
[100.0, 120.0, 95.0, 90.0]]) # touches 115 on day 1, then falls
hit_up = paths.max(axis=1) >= 115.0 # the maximum of each path against the barrier
print(hit_up) # [False False True]
print(paths.min(axis=1) <= 96.0) # a down barrier at 96: [False False True]# hit_barrier for both directions, with an error for anything else
import numpy as np
def hit_barrier(paths, barrier, direction):
if direction == "up":
return paths.max(axis=1) >= barrier
if direction == "down":
return paths.min(axis=1) <= barrier
raise ValueError("direction must be 'up' or 'down'")
paths = np.array([[100.0, 110.0, 105.0], [100.0, 109.9, 105.0]])
print(hit_barrier(paths, 110.0, "up")) # [ True False]: touching the barrier exactly counts
try:
hit_barrier(paths, 110.0, "sideways")
except ValueError as error:
print("rejected:", error)Knock-out, knock-in y la suma
En cada trayectoria calcula el pago del call simple max(S_T - K, 0) y la marca de barrera. El call knock-out conserva el pago donde la barrera *no* se tocó; el knock-in lo conserva donde sí. Cada trayectoria es una cosa u otra, así que *in + out = call simple*, trayectoria por trayectoria, una identidad útil: valora la más fácil y obtén la otra gratis, y úsala para probar tu código. np.where(hit, a, b) elige entre arrays elemento a elemento.
import numpy as np
paths = np.array([[100.0, 105.0, 112.0, 108.0],
[100.0, 98.0, 103.0, 110.0],
[100.0, 120.0, 95.0, 130.0]])
strike, barrier = 100.0, 115.0
vanilla = np.maximum(paths[:, -1] - strike, 0.0)
hit = paths.max(axis=1) >= barrier
knock_out = np.where(hit, 0.0, vanilla)
knock_in = np.where(hit, vanilla, 0.0)
print(vanilla, knock_out, knock_in) # [ 8. 10. 30.] [ 8. 10. 0.] [ 0. 0. 30.]
print(np.allclose(knock_in + knock_out, vanilla)) # TrueValoración y sesgo de observación discreta
El precio es el pago medio descontado, como antes. Pero cuidado con la comprobación de la barrera: una simulación mira el precio solo en las fechas de su malla, mientras que una barrera continua real también se cruza *entre* ellas. Con pocos pasos, las trayectorias simuladas cruzan a menudo la barrera sin verse, de modo que un call knock-out sale demasiado caro y un knock-in demasiado barato. Más pasos reducen el sesgo, y también las correcciones que desplazan la barrera en torno a 0.5826 * sigma * sqrt(dt).
import numpy as np
def simulate_paths(s0, rate, sigma, t, steps, n_paths, rng):
dt = t / steps
z = rng.standard_normal((n_paths, steps))
moves = (rate - 0.5 * sigma ** 2) * dt + sigma * np.sqrt(dt) * z
return s0 * np.exp(np.hstack([np.zeros((n_paths, 1)), np.cumsum(moves, axis=1)]))
def knock_out_price(paths, strike, barrier, rate, t):
vanilla = np.maximum(paths[:, -1] - strike, 0.0)
payoff = np.where(paths.max(axis=1) >= barrier, 0.0, vanilla)
return float(np.exp(-rate * t) * payoff.mean())
rng = np.random.default_rng(1)
for steps in (12, 52, 252):
paths = simulate_paths(100.0, 0.05, 0.25, 1.0, steps, 40000, rng)
print(steps, round(knock_out_price(paths, 100.0, 130.0, 0.05, 1.0), 3)) # the price falls as monitoring gets finer- Una barrera se toca si el máximo (up) o el mínimo (down) de la trayectoria la alcanza en cualquier fecha; la knock-out muere al tocarla, la knock-in nace al tocarla.
- Por trayectoria, knock-in + knock-out = el pago del call simple;
np.where(hit, a, b)elige; las opciones con barrera son más baratas que las simples. - Simular en una malla pierde los cruces entre fechas, así que las knock-out parecen demasiado caras: use más pasos o desplace la barrera; cuidado con el riesgo de salto cerca de la barrera.
A continuación: las variables antitéticas, una forma de lograr la misma precisión Monte Carlo con menos simulaciones.
Lab: qué se califica
- Detectar el toque con
hit_barrier25 pts - Matar la opción con
knock_out_call_payoff25 pts - Darle vida con
knock_in_call_payoff25 pts - Convertir los pagos en precio con
barrier_price25 pts