Initial commit — GeoOptions Intelligence Cockpit v2.0
Stack: FastAPI + React/TypeScript + SQLite + GPT-4o Features: Radar géopolitique, Marchés, Régime Macro, Journal de Bord MTM, Rapport IA, Super Contexte (base de raisonnement évolutive), Boucle feedback IA. Deploy: Docker + docker-compose + nginx pour openfin.open-squared.tech Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
126
backend/services/options_pricer.py
Normal file
126
backend/services/options_pricer.py
Normal file
@@ -0,0 +1,126 @@
|
||||
import numpy as np
|
||||
from scipy.stats import norm
|
||||
from typing import Dict, Any, List, Optional
|
||||
from datetime import datetime, timedelta
|
||||
import math
|
||||
|
||||
|
||||
def black_scholes(S: float, K: float, T: float, r: float, sigma: float, option_type: str = "call") -> Dict[str, float]:
|
||||
"""Black-Scholes pricing + Greeks."""
|
||||
S = float(S or 100.0)
|
||||
K = float(K or S)
|
||||
T = float(T or 0.001)
|
||||
sigma = float(sigma or 0.25)
|
||||
if T <= 0 or sigma <= 0:
|
||||
intrinsic = max(0, S - K) if option_type == "call" else max(0, K - S)
|
||||
return {"price": intrinsic, "delta": 0, "gamma": 0, "theta": 0, "vega": 0, "rho": 0}
|
||||
|
||||
d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))
|
||||
d2 = d1 - sigma * math.sqrt(T)
|
||||
|
||||
if option_type == "call":
|
||||
price = S * norm.cdf(d1) - K * math.exp(-r * T) * norm.cdf(d2)
|
||||
delta = norm.cdf(d1)
|
||||
rho = K * T * math.exp(-r * T) * norm.cdf(d2) / 100
|
||||
else:
|
||||
price = K * math.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
|
||||
delta = norm.cdf(d1) - 1
|
||||
rho = -K * T * math.exp(-r * T) * norm.cdf(-d2) / 100
|
||||
|
||||
gamma = norm.pdf(d1) / (S * sigma * math.sqrt(T))
|
||||
theta = (-(S * norm.pdf(d1) * sigma) / (2 * math.sqrt(T)) - r * K * math.exp(-r * T) * norm.cdf(d2 if option_type == "call" else -d2)) / 365
|
||||
vega = S * norm.pdf(d1) * math.sqrt(T) / 100
|
||||
|
||||
return {
|
||||
"price": round(price, 4),
|
||||
"delta": round(delta, 4),
|
||||
"gamma": round(gamma, 6),
|
||||
"theta": round(theta, 4),
|
||||
"vega": round(vega, 4),
|
||||
"rho": round(rho, 4),
|
||||
}
|
||||
|
||||
|
||||
def compute_pnl_curve(
|
||||
S: float, K: float, T: float, r: float, sigma: float,
|
||||
option_type: str, quantity: int, premium_paid: float
|
||||
) -> List[Dict[str, float]]:
|
||||
"""P&L at expiry across a range of underlying prices."""
|
||||
prices = np.linspace(S * 0.5, S * 1.5, 100)
|
||||
curve = []
|
||||
for price in prices:
|
||||
if option_type == "call":
|
||||
intrinsic = max(0, price - K)
|
||||
else:
|
||||
intrinsic = max(0, K - price)
|
||||
pnl = (intrinsic - premium_paid) * quantity * 100
|
||||
curve.append({"underlying": round(float(price), 2), "pnl": round(float(pnl), 2)})
|
||||
return curve
|
||||
|
||||
|
||||
def bull_call_spread(S: float, K_low: float, K_high: float, T: float, r: float, sigma: float) -> Dict[str, Any]:
|
||||
long_call = black_scholes(S, K_low, T, r, sigma, "call")
|
||||
short_call = black_scholes(S, K_high, T, r, sigma, "call")
|
||||
net_debit = long_call["price"] - short_call["price"]
|
||||
max_gain = (K_high - K_low) - net_debit
|
||||
return {
|
||||
"strategy": "Bull Call Spread",
|
||||
"net_debit": round(net_debit, 4),
|
||||
"max_loss": round(net_debit * 100, 2),
|
||||
"max_gain": round(max_gain * 100, 2),
|
||||
"breakeven": round(K_low + net_debit, 2),
|
||||
"legs": [
|
||||
{"type": "long call", "strike": K_low, "premium": long_call["price"]},
|
||||
{"type": "short call", "strike": K_high, "premium": short_call["price"]},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def bear_put_spread(S: float, K_high: float, K_low: float, T: float, r: float, sigma: float) -> Dict[str, Any]:
|
||||
long_put = black_scholes(S, K_high, T, r, sigma, "put")
|
||||
short_put = black_scholes(S, K_low, T, r, sigma, "put")
|
||||
net_debit = long_put["price"] - short_put["price"]
|
||||
max_gain = (K_high - K_low) - net_debit
|
||||
return {
|
||||
"strategy": "Bear Put Spread",
|
||||
"net_debit": round(net_debit, 4),
|
||||
"max_loss": round(net_debit * 100, 2),
|
||||
"max_gain": round(max_gain * 100, 2),
|
||||
"breakeven": round(K_high - net_debit, 2),
|
||||
"legs": [
|
||||
{"type": "long put", "strike": K_high, "premium": long_put["price"]},
|
||||
{"type": "short put", "strike": K_low, "premium": short_put["price"]},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def long_straddle(S: float, K: float, T: float, r: float, sigma: float) -> Dict[str, Any]:
|
||||
call = black_scholes(S, K, T, r, sigma, "call")
|
||||
put = black_scholes(S, K, T, r, sigma, "put")
|
||||
total_premium = call["price"] + put["price"]
|
||||
return {
|
||||
"strategy": "Long Straddle",
|
||||
"net_debit": round(total_premium, 4),
|
||||
"max_loss": round(total_premium * 100, 2),
|
||||
"max_gain": None,
|
||||
"breakevens": [round(K - total_premium, 2), round(K + total_premium, 2)],
|
||||
"legs": [
|
||||
{"type": "long call", "strike": K, "premium": call["price"]},
|
||||
{"type": "long put", "strike": K, "premium": put["price"]},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def implied_vol_surface(S: float, strikes_pct: List[float], expiries_days: List[int], r: float, base_sigma: float) -> List[Dict]:
|
||||
"""Generate a simplified IV surface (skew + term structure)."""
|
||||
surface = []
|
||||
for days in expiries_days:
|
||||
T = days / 365
|
||||
for pct in strikes_pct:
|
||||
K = S * pct
|
||||
moneyness = math.log(K / S)
|
||||
skew_adj = -0.3 * moneyness # typical negative skew
|
||||
term_adj = 0.02 * math.sqrt(30 / max(days, 1))
|
||||
iv = max(0.05, base_sigma + skew_adj + term_adj)
|
||||
surface.append({"expiry_days": days, "strike_pct": pct, "strike": round(K, 2), "iv": round(iv, 4)})
|
||||
return surface
|
||||
Reference in New Issue
Block a user