Auszahlung auf den Durchschnitt
Ein Standard-Call hängt vom Preis an einem einzigen Tag ab, dem letzten. Eine asiatische Option hängt vom Durchschnittspreis über eine Reihe von Terminen ab: Ein asiatischer Call zahlt max(Durchschnitt - K, 0). Unternehmen nutzen sie, um Kosten abzusichern, die laufend anfallen, etwa einen monatlichen Ölkauf, und sie sind bei Verfall schwer zu manipulieren. Sie sind *pfadabhängig*, für den arithmetischen Durchschnitt gibt es also keine einfache Black-Scholes-Formel, und das übliche Werkzeug ist die Monte-Carlo-Simulation.
import numpy as np
# Two simulated paths: the starting price, then monthly prices; the last column is expiry
paths = np.array([[100.0, 90.0, 110.0, 130.0],
[100.0, 70.0, 80.0, 90.0]])
average = paths[:, 1:].mean(axis=1) # the monitoring dates only, not the starting price
print(average) # [110. 80.]
print(np.maximum(average - 100.0, 0.0)) # Asian call payoffs: [10. 0.]
print(np.maximum(paths[:, -1] - 100.0, 0.0)) # European call payoffs on the same paths: [30. 0.]Die Pfade simulieren
Die Pfade stammen aus der geometrischen Brownschen Bewegung mit risikoneutralem Drift: Der Zins r ersetzt mu. Man zieht eine Matrix standardnormaler Zahlen mit einer Zeile je Pfad und einer Spalte je Beobachtungstermin, wandelt sie in Log-Preisbewegungen um und bildet längs jeder Zeile die kumulative Summe. Ein Array enthält dann alle Pfade, und jeder weitere Schritt (Durchschnitte, Auszahlungen) ist eine vektorisierte Operation entlang der Zeilen, ohne Schleife über Pfade.
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))
log_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(log_moves, axis=1)]))
rng = np.random.default_rng(1)
paths = simulate_paths(100.0, 0.05, 0.2, 1.0, 12, 50000, rng) # monthly monitoring for a year
print(paths.shape) # (50000, 13)
print(round(float(paths[:, -1].mean()), 2), round(100 * np.exp(0.05), 2)) # the risk-neutral mean of the final priceDer Preis und sein Fehlerbalken
Der Preis der Option ist die abgezinste mittlere Auszahlung: exp(-r * T) * mean(payoffs). Ein Monte-Carlo-Preis ist eine Schätzung, geben Sie also stets den Standardfehler an, std(payoffs) / sqrt(Zahl der Pfade) (abgezinst), der zeigt, wie weit die Zahl von der Wahrheit entfernt sein kann: Viermal so viele Pfade halbieren ihn. Ein asiatischer Call ist billiger als ein europäischer auf dieselbe Aktie, weil die Mittelung den Preis glättet und die effektive Volatilität senkt.
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))
log_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(log_moves, axis=1)]))
def asian_price(paths, strike, rate, t):
payoff = np.maximum(paths[:, 1:].mean(axis=1) - strike, 0.0)
discount = np.exp(-rate * t)
return discount * payoff.mean(), discount * payoff.std(ddof=1) / np.sqrt(len(payoff))
rng = np.random.default_rng(1)
paths = simulate_paths(100.0, 0.05, 0.2, 1.0, 12, 100000, rng)
price, error = asian_price(paths, 100.0, 0.05, 1.0)
european = np.exp(-0.05) * np.maximum(paths[:, -1] - 100.0, 0.0).mean()
print(round(float(price), 3), "+/-", round(float(error), 3))
print(round(float(european), 3), "European call on the same paths")# Geometric and arithmetic averages of the same monitoring prices
import numpy as np
prices = np.array([90.0, 110.0, 130.0])
arithmetic = prices.mean()
geometric = np.exp(np.log(prices).mean())
print(round(float(arithmetic), 3), round(float(geometric), 3)) # 110.0 108.774: the geometric average is never larger- Eine asiatische Option zahlt auf den Durchschnitt der Beobachtungstermine:
max(Durchschnitt - K, 0); pfadabhängig, also keine einfache Formel, Monte Carlo ist das Werkzeug. - Simulieren Sie alle Pfade in einem Array (Zeilen = Pfade, Spalten = Termine) mit risikoneutralem Drift; Durchschnitte und Auszahlungen sind dann zeilenweise vektorisiert.
- Preis =
exp(-r * T) * mean(payoffs)mit Standardfehlerstd / sqrt(paths); die Mittelung macht ihn billiger als einen europäischen Call, die geometrische Version ist eine Kontrollvariable.
Als Nächstes: Barrier-Optionen, die entstehen oder verfallen, wenn der Preis ein Niveau berührt.
Lab: Was bewertet wird
- Mit
average_priceentlang jedes Pfads mitteln25 Pkt. - Mit
geometric_averageden geometrischen Durchschnitt probieren25 Pkt. - Mit
asian_call_payoffauf den Durchschnitt auszahlen25 Pkt. - Mit
asian_pricebewerten, samt Fehlerbalken25 Pkt.