Una fórmula para una opción
En 1973 Black y Scholes mostraron que, si una acción sigue el modelo lognormal con volatilidad constante, una opción se puede replicar negociando la acción y un bono, de modo que su precio no depende de lo probable que los inversores crean una subida. El precio de un call europeo es S * N(d1) - K * exp(-rT) * N(d2): la acción que recibes si la opción acaba en el dinero, ponderada por un número tipo probabilidad, menos el strike descontado que pagas, ponderado por la probabilidad neutral al riesgo N(d2) de ejercer. El put se deduce de la paridad.
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2))) # N(x): the probability that a standard normal is below x
print(round(norm_cdf(0.0), 4), round(norm_cdf(1.96), 4)) # 0.5 0.975
def d1_d2(spot, strike, rate, sigma, t):
d1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
return d1, d1 - sigma * math.sqrt(t)
d1, d2 = d1_d2(100, 100, 0.05, 0.2, 1)
print(round(d1, 4), round(d2, 4)) # 0.35 0.15El call y el put
N es la función de distribución normal estándar, que math.erf de Python da como 0.5 * (1 + erf(x / sqrt(2))). d1 y d2 combinan la posición ln(S/K), la deriva y la volatilidad durante la vida de la opción. Con ellos: call = S * N(d1) - K * exp(-rT) * N(d2) y put = K * exp(-rT) * N(-d2) - S * N(-d1). Para spot 100, strike 100, tipo del 5 %, volatilidad del 20 % y un año, el call cuesta 10,45 y el put 5,57, y la diferencia 4,88 es exactamente el valor de paridad S - K * exp(-rT).
def bs_call(spot, strike, rate, sigma, t):
d1, d2 = d1_d2(spot, strike, rate, sigma, t)
return spot * norm_cdf(d1) - strike * math.exp(-rate * t) * norm_cdf(d2)
def bs_put(spot, strike, rate, sigma, t):
d1, d2 = d1_d2(spot, strike, rate, sigma, t)
return strike * math.exp(-rate * t) * norm_cdf(-d2) - spot * norm_cdf(-d1)
call, put = bs_call(100, 100, 0.05, 0.2, 1), bs_put(100, 100, 0.05, 0.2, 1)
print(round(call, 4), round(put, 4)) # 10.4506 5.5735
print(round(call - put, 4), round(100 - 100 * math.exp(-0.05), 4)) # 4.8771 4.8771 put-call parity holdsQué mueve el precio
Cinco parámetros determinan el precio. Un strike mayor abarata el call (80: 24,59; 100: 10,45; 120: 3,25). Una volatilidad mayor encarece ambas opciones, ya que más incertidumbre solo añade alza para el titular (10 %: 6,81; 20 %: 10,45; 40 %: 18,02). Un plazo mayor hasta el vencimiento también sube el precio (3 meses: 4,62; 1 año: 10,45; 4 años: 25,21). Observa que el rendimiento esperado de la acción no aparece en absoluto en la fórmula: solo hay que estimar la volatilidad, por eso los precios de opciones cotizados se expresan a menudo como una volatilidad.
for strike in (80, 100, 120):
print("strike", strike, round(bs_call(100, strike, 0.05, 0.2, 1), 4)) # 24.5888, 10.4506, 3.2475
for sigma in (0.1, 0.2, 0.4):
print("sigma", sigma, round(bs_call(100, 100, 0.05, sigma, 1), 4)) # 6.805, 10.4506, 18.023
for t in (0.25, 1, 4):
print("t", t, round(bs_call(100, 100, 0.05, 0.2, t), 4)) # 4.615, 10.4506, 25.2133# Time value: the call is worth more than its intrinsic value (what it would pay if it expired today)
intrinsic = max(100 - 100 * math.exp(-0.05), 0)
print(round(intrinsic, 4), round(bs_call(100, 100, 0.05, 0.2, 1) - intrinsic, 4)) # 4.8771 5.5735 intrinsic value and time valueN(x) = 0.5 * (1 + erf(x / sqrt(2))), yd1 = (ln(S/K) + (r + sigma²/2) T) / (sigma sqrt(T)),d2 = d1 - sigma sqrt(T).- Call =
S N(d1) - K e^(-rT) N(d2); put =K e^(-rT) N(-d2) - S N(-d1); su diferencia es el valor de paridadS - K e^(-rT). - Los precios suben con la volatilidad y el plazo y bajan con el strike del call; el rendimiento esperado de la acción no está, solo hace falta la volatilidad.
A continuación: la delta, cuánto se mueve el precio de la opción cuando la acción se mueve un dólar.
Lab: qué se califica
- Construir la probabilidad acumulada de la campana con
norm_cdf25 pts - Calcular las dos cantidades de Black–Scholes con
d1_d225 pts - Valorar un call con
bs_call25 pts - Valorar un put con
bs_put25 pts