Files
2026-07-13 12:36:27 +08:00

77 lines
2.4 KiB
Python

from __future__ import annotations
import math
import matplotlib.pyplot as plt
import pandas as pd
import yfinance as yf
_DARK_BG = "#0f172a"
_AXES_BG = "#1e293b"
_GRID_COLOR = "#334155"
_TEXT_COLOR = "#cbd5e1"
_TICK_COLOR = "#94a3b8"
def apply_dark_style(fig: plt.Figure, *axes) -> None:
fig.patch.set_facecolor(_DARK_BG)
for ax in axes:
ax.set_facecolor(_AXES_BG)
ax.tick_params(colors=_TICK_COLOR)
ax.xaxis.label.set_color(_TEXT_COLOR)
ax.yaxis.label.set_color(_TEXT_COLOR)
ax.title.set_color(_TEXT_COLOR)
for s in ax.spines.values():
s.set_color(_GRID_COLOR)
ax.grid(True, color=_GRID_COLOR, alpha=0.45)
lg = ax.get_legend()
if lg is not None:
lg.get_frame().set_facecolor(_AXES_BG)
lg.get_frame().set_edgecolor(_GRID_COLOR)
for t in lg.get_texts():
t.set_color(_TEXT_COLOR)
def close_series(ticker: str, start: str, end: str) -> pd.Series:
df = yf.download(ticker, start=start, end=end, auto_adjust=True, progress=False)
if df.empty:
raise RuntimeError(f"未获取到 {ticker} 数据")
close = df["Close"]
if isinstance(close, pd.DataFrame):
close = close.iloc[:, 0]
s = close.dropna()
s.name = ticker
return s
def annual_to_daily(rate: float, periods: int = 252) -> float:
return (1.0 + rate) ** (1.0 / periods)
def rsi(series: pd.Series, period: int = 14) -> pd.Series:
d = series.diff()
up = d.clip(lower=0).ewm(alpha=1.0 / period, adjust=False).mean()
dn = (-d.clip(upper=0)).ewm(alpha=1.0 / period, adjust=False).mean()
rs = up / dn.replace(0, math.nan)
return 100.0 - (100.0 / (1.0 + rs))
def norm_cdf(x: float) -> float:
return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))
def bs_price(spot: float, strike: float, t_years: float, rate: float, sigma: float, kind: str) -> float:
if t_years <= 0:
if kind == "put":
return max(strike - spot, 0.0)
return max(spot - strike, 0.0)
sigma = max(sigma, 1e-6)
if spot <= 0 or strike <= 0:
return 0.0
d1 = (math.log(spot / strike) + (rate + 0.5 * sigma * sigma) * t_years) / (sigma * math.sqrt(t_years))
d2 = d1 - sigma * math.sqrt(t_years)
if kind == "put":
return strike * math.exp(-rate * t_years) * norm_cdf(-d2) - spot * norm_cdf(-d1)
return spot * norm_cdf(d1) - strike * math.exp(-rate * t_years) * norm_cdf(d2)