Eine Aktie, die nur steigen oder fallen kann
Black-Scholes ist eine Formel, ein Binomialbaum ein Verfahren, und er kann bewerten, was die Formel nicht kann. Man zerlegt die Restlaufzeit in kleine Schritte. In jedem Schritt steigt die Aktie um den Faktor u oder fällt um den Faktor d, mehr nicht. Nach n Schritten gibt es n + 1 mögliche Preise. Die Auszahlungen sind bei Verfall bekannt, und jeder frühere Knoten ist der abgezinste Mittelwert der beiden Knoten, zu denen er führt; so füllt man den Baum rückwärts bis zur Gegenwart.
# One step: a stock at 100 goes to 110 or 90. What is a call with strike 100 worth?
import math
spot, strike, rate, dt = 100, 100, 0.05, 1.0
u, d = 1.10, 0.90
p = (math.exp(rate * dt) - d) / (u - d) # the probability that makes the stock grow at the rate
up_value, down_value = max(spot * u - strike, 0), max(spot * d - strike, 0)
price = math.exp(-rate * dt) * (p * up_value + (1 - p) * down_value)
print(round(p, 4), round(up_value, 4), down_value) # 0.7564 10.0 0
print(round(price, 4)) # 7.1947Die Wahrscheinlichkeit ist keine Prognose
Die Zahl p wird so gewählt, dass die Aktie im Mittel mit dem risikolosen Zins wächst: p * u + (1 - p) * d = exp(rate * dt), also p = (exp(rate * dt) - d) / (u - d). Das ist die risikoneutrale Wahrscheinlichkeit: ein Hilfsmittel, das ein Replikationsportfolio seinen abgezinsten Erwartungswert wert macht, keine Meinung darüber, wohin die Aktie läuft. Um die beobachtete Volatilität zu treffen, nutzt der Cox-Ross-Rubinstein-Baum (CRR) u = exp(sigma * sqrt(dt)) und d = 1 / u; ein Aufwärts- gefolgt von einem Abwärtsschritt führt zum selben Preis, der Baum rekombiniert: n Schritte ergeben nur n + 1 Endknoten.
# The CRR parameters and the prices at expiry of a 3-step tree
import math
def crr_parameters(rate, sigma, t, steps):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
return u, d, p
def terminal_prices(spot, u, d, steps):
return [spot * u ** j * d ** (steps - j) for j in range(steps + 1)]
u, d, p = crr_parameters(0.05, 0.2, 1, 3)
print(round(u, 4), round(d, 4), round(p, 4)) # 1.1224 0.8909 0.5438
print([round(price, 2) for price in terminal_prices(100, u, d, 3)])Rückwärts rechnen
Bei Verfall ist jeder Knoten nur die Auszahlung, max(S - K, 0) für einen Call. Eine Spalte früher ist jeder Wert exp(-rate * dt) * (p * value_up + (1 - p) * value_down), und die Spalte verliert einen Knoten. Nach n Durchläufen bleibt ein einziger Wert, der heutige Preis. In Python ist eine Spalte eine Liste, ein Durchlauf eine List Comprehension über Nachbarpaare. Mit wachsender Schrittzahl konvergiert der Preis gegen den Black-Scholes-Preis, der Baum liefert, was die Formel sagt.
# The tree price of a European call approaches the Black-Scholes price as steps grow
import math
def norm_cdf(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
def bs_call(spot, strike, rate, sigma, t):
d1 = (math.log(spot / strike) + (rate + 0.5 * sigma ** 2) * t) / (sigma * math.sqrt(t))
return spot * norm_cdf(d1) - strike * math.exp(-rate * t) * norm_cdf(d1 - sigma * math.sqrt(t))
def european_call_tree(spot, strike, rate, sigma, t, steps):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
disc = math.exp(-rate * dt)
values = [max(spot * u ** j * d ** (steps - j) - strike, 0) for j in range(steps + 1)]
for _ in range(steps):
values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(len(values) - 1)]
return values[0]
print("Black-Scholes", round(bs_call(100, 100, 0.05, 0.2, 1), 4))
for steps in (1, 5, 25, 100, 400):
print(steps, round(european_call_tree(100, 100, 0.05, 0.2, 1, steps), 4))Vorzeitige Ausübung
Die eigentliche Stärke des Baums ist die Option, die die Formel nicht bewerten kann: Eine amerikanische Option kann jederzeit ausgeübt werden. An jedem Knoten vergleicht der Inhaber zwei Werte, den abgezinsten Erwartungswert des Haltens und die Auszahlung bei sofortiger Ausübung, und nimmt den größeren. Dieses eine zusätzliche max je Knoten ist die ganze Änderung des Algorithmus. Bei einem Call auf eine Aktie ohne Dividende lohnt vorzeitige Ausübung nie, bei einem Put kann sie sich lohnen: Ein tief im Geld liegender Put ist heute als Bargeld mehr wert, daher kostet der amerikanische Put mehr als der europäische.
# An American put against a European put: the early-exercise premium
import math
def tree(spot, strike, rate, sigma, t, steps, american):
dt = t / steps
u = math.exp(sigma * math.sqrt(dt))
d = 1 / u
p = (math.exp(rate * dt) - d) / (u - d)
disc = math.exp(-rate * dt)
values = [max(strike - spot * u ** j * d ** (steps - j), 0) for j in range(steps + 1)]
for i in range(steps - 1, -1, -1):
values = [disc * (p * values[j + 1] + (1 - p) * values[j]) for j in range(i + 1)]
if american:
values = [max(values[j], strike - spot * u ** j * d ** (i - j)) for j in range(i + 1)]
return values[0]
for spot in (80, 100, 120):
european = tree(spot, 100, 0.05, 0.2, 1, 200, False)
american = tree(spot, 100, 0.05, 0.2, 1, 200, True)
print(spot, round(european, 4), round(american, 4), "premium", round(american - european, 4))n Schritte kosten etwa n**2 / 2 Knotenaktualisierungen, und die Konvergenz ist nicht glatt (der Fehler schwankt, je nachdem wo der Strike zwischen den Knoten liegt); Bäume prüft man daher mit mehreren Schrittzahlen.- In jedem Schritt steigt die Aktie um
uoder fällt umd; die CRR-Wahlu = exp(sigma * sqrt(dt)),d = 1 / urekombiniert den Baum,nSchritte haben alson + 1Endknoten. - Rückwärts bewerten: Auszahlungen bei Verfall, dann
exp(-rate * dt) * (p * up + (1 - p) * down)je Knoten;pist die risikoneutrale Wahrscheinlichkeit, keine Prognose. - Viele Schritte konvergieren gegen Black-Scholes; eine amerikanische Option fügt je Knoten ein
max(Fortführung, Ausübung)hinzu, dafür gibt es Bäume.
Als Nächstes: Finite-Differenzen-Verfahren, bewerten, indem man die partielle Differentialgleichung der Option auf einem Gitter löst.
Lab: Was bewertet wird
- Mit
crr_parametersdie Auf- und Abwärtsbewegung festlegen25 Pkt. - Mit
terminal_pricesdie Preise bei Verfall auflisten25 Pkt. - Mit
european_call_treeden Baum rückwärts durchlaufen25 Pkt. - Mit
american_put_treevorzeitige Ausübung erlauben25 Pkt.