aria-code 4.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. agents/__init__.py +32 -0
  2. agents/base.py +190 -0
  3. agents/deep/__init__.py +37 -0
  4. agents/deep/calibration_loop.py +144 -0
  5. agents/deep/critic.py +125 -0
  6. agents/deep/deepen.py +193 -0
  7. agents/deep/models.py +149 -0
  8. agents/deep/pipeline.py +164 -0
  9. agents/deep/quant_fusion.py +192 -0
  10. agents/deep/themes.py +95 -0
  11. agents/deep/tiers.py +106 -0
  12. agents/financial/__init__.py +10 -0
  13. agents/financial/catalyst.py +279 -0
  14. agents/financial/debate.py +145 -0
  15. agents/financial/earnings.py +303 -0
  16. agents/financial/fundamental.py +159 -0
  17. agents/financial/macro.py +99 -0
  18. agents/financial/news.py +207 -0
  19. agents/financial/risk.py +132 -0
  20. agents/financial/sector.py +279 -0
  21. agents/financial/synthesis.py +274 -0
  22. agents/financial/technical.py +258 -0
  23. agents/portfolio_agent.py +333 -0
  24. agents/realty/__init__.py +62 -0
  25. agents/realty/asset_diagnosis.py +150 -0
  26. agents/realty/business_match.py +165 -0
  27. agents/realty/cashflow_verify.py +208 -0
  28. agents/realty/contract_rules.py +209 -0
  29. agents/realty/energy_anomaly.py +188 -0
  30. agents/realty/exit_settlement.py +207 -0
  31. agents/realty/fulfillment_risk.py +205 -0
  32. agents/realty/ops_optimize.py +159 -0
  33. agents/realty/revenue_share.py +214 -0
  34. agents/registry.py +144 -0
  35. agents/sports/__init__.py +0 -0
  36. agents/sports/football_agent.py +169 -0
  37. agents/team.py +289 -0
  38. aliyun_data_client.py +660 -0
  39. apps/README.md +12 -0
  40. apps/__init__.py +2 -0
  41. apps/channels/README.md +15 -0
  42. apps/cli/README.md +13 -0
  43. apps/cli/__init__.py +2 -0
  44. apps/cli/bootstrap.py +99 -0
  45. apps/cli/codegen_paths.py +29 -0
  46. apps/cli/commands/__init__.py +16 -0
  47. apps/cli/commands/analysis_cmds.py +288 -0
  48. apps/cli/commands/backtest_cmds.py +1887 -0
  49. apps/cli/commands/broker_cmds.py +1154 -0
  50. apps/cli/commands/business_workflow_cmds.py +289 -0
  51. apps/cli/commands/catalog.py +84 -0
  52. apps/cli/commands/data_cmds.py +405 -0
  53. apps/cli/commands/diagnostic_cmds.py +179 -0
  54. apps/cli/commands/diagnostic_ops_cmds.py +696 -0
  55. apps/cli/commands/finance_render.py +12 -0
  56. apps/cli/commands/market.py +399 -0
  57. apps/cli/commands/market_cmds.py +1276 -0
  58. apps/cli/commands/market_context.py +425 -0
  59. apps/cli/commands/market_render.py +7 -0
  60. apps/cli/commands/model_cmds.py +1579 -0
  61. apps/cli/commands/ops_cmds.py +668 -0
  62. apps/cli/commands/portfolio_cmds.py +962 -0
  63. apps/cli/commands/report.py +377 -0
  64. apps/cli/commands/scaffold_templates.py +617 -0
  65. apps/cli/commands/session_cmds.py +179 -0
  66. apps/cli/commands/session_ux_cmds.py +280 -0
  67. apps/cli/commands/team.py +588 -0
  68. apps/cli/commands/team_render.py +8 -0
  69. apps/cli/commands/ui_cmds.py +358 -0
  70. apps/cli/commands/workflow_cmds.py +279 -0
  71. apps/cli/commands/workspace_cmds.py +1414 -0
  72. apps/cli/config_paths.py +70 -0
  73. apps/cli/config_store.py +61 -0
  74. apps/cli/deterministic.py +122 -0
  75. apps/cli/direct.py +48 -0
  76. apps/cli/github_app_auth.py +135 -0
  77. apps/cli/handlers/__init__.py +11 -0
  78. apps/cli/handlers/broker_handlers.py +122 -0
  79. apps/cli/handlers/chart_handlers.py +1309 -0
  80. apps/cli/handlers/market_handlers.py +2509 -0
  81. apps/cli/handlers/realty_handlers.py +114 -0
  82. apps/cli/handlers/strategy_advice.py +82 -0
  83. apps/cli/hooks.py +180 -0
  84. apps/cli/i18n.py +284 -0
  85. apps/cli/intent.py +136 -0
  86. apps/cli/intent_router.py +217 -0
  87. apps/cli/lifecycle_hooks.py +48 -0
  88. apps/cli/main.py +29 -0
  89. apps/cli/market_metadata.py +135 -0
  90. apps/cli/market_universe.py +265 -0
  91. apps/cli/message_processing.py +257 -0
  92. apps/cli/plan_mode.py +139 -0
  93. apps/cli/plotly_html.py +15 -0
  94. apps/cli/prediction_feedback.py +202 -0
  95. apps/cli/preflight.py +497 -0
  96. apps/cli/project_aria.py +60 -0
  97. apps/cli/prompts/__init__.py +0 -0
  98. apps/cli/prompts/coding.py +658 -0
  99. apps/cli/prompts/system_prompts.py +531 -0
  100. apps/cli/prompts/ui.py +434 -0
  101. apps/cli/providers/__init__.py +1 -0
  102. apps/cli/providers/base.py +271 -0
  103. apps/cli/providers/chat_routing.py +80 -0
  104. apps/cli/providers/llm/__init__.py +1 -0
  105. apps/cli/providers/llm/ollama_stream.py +1170 -0
  106. apps/cli/providers/llm/sse_stream.py +216 -0
  107. apps/cli/providers/runtime_bridge.py +185 -0
  108. apps/cli/runtime_consumer.py +489 -0
  109. apps/cli/session_export.py +87 -0
  110. apps/cli/session_jsonl.py +207 -0
  111. apps/cli/session_store.py +112 -0
  112. apps/cli/todo_tracker.py +190 -0
  113. apps/cli/tools/__init__.py +40 -0
  114. apps/cli/tools/context.py +46 -0
  115. apps/cli/tools/file_tools.py +112 -0
  116. apps/cli/tools/market_tools.py +549 -0
  117. apps/cli/tools/notebook_tools.py +111 -0
  118. apps/cli/tools/system_tools.py +669 -0
  119. apps/cli/tools/write_tools.py +715 -0
  120. apps/cli/tradingview_bridge.py +434 -0
  121. apps/cli/update_check.py +152 -0
  122. apps/cli/utils/__init__.py +0 -0
  123. apps/cli/utils/market_detect.py +1578 -0
  124. apps/daemon/README.md +14 -0
  125. apps/vscode/README.md +115 -0
  126. apps/vscode/package.json +70 -0
  127. aria_cli.py +11636 -0
  128. aria_code-4.1.3.dist-info/METADATA +952 -0
  129. aria_code-4.1.3.dist-info/RECORD +284 -0
  130. aria_code-4.1.3.dist-info/WHEEL +5 -0
  131. aria_code-4.1.3.dist-info/entry_points.txt +2 -0
  132. aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
  133. aria_code-4.1.3.dist-info/top_level.txt +50 -0
  134. aria_daemon.py +1295 -0
  135. aria_feishu_bot.py +1359 -0
  136. aria_relay_client.py +182 -0
  137. aria_relay_server.py +405 -0
  138. aria_telegram_bot.py +202 -0
  139. ariarc.py +328 -0
  140. artifacts.py +491 -0
  141. backtest_report.py +472 -0
  142. brokers/__init__.py +72 -0
  143. brokers/base.py +207 -0
  144. brokers/capabilities.py +264 -0
  145. brokers/cn/__init__.py +10 -0
  146. brokers/cn/easytrader_broker.py +193 -0
  147. brokers/cn/futu_broker.py +194 -0
  148. brokers/cn/longbridge_broker.py +190 -0
  149. brokers/cn/tiger_broker.py +196 -0
  150. brokers/cn/xtquant_broker.py +175 -0
  151. brokers/config.py +364 -0
  152. brokers/intl/__init__.py +5 -0
  153. brokers/intl/alpaca_broker.py +183 -0
  154. brokers/intl/ibkr_broker.py +215 -0
  155. brokers/intl/webull_broker.py +156 -0
  156. brokers/paper_broker.py +259 -0
  157. brokers/planning.py +296 -0
  158. brokers/registry.py +181 -0
  159. brokers/trading.py +237 -0
  160. change_store.py +127 -0
  161. command_safety.py +19 -0
  162. computer_use_tools.py +504 -0
  163. dashboard_generator.py +578 -0
  164. data_analysis_tools.py +808 -0
  165. data_cleaner.py +483 -0
  166. data_service.py +481 -0
  167. datasources/__init__.py +23 -0
  168. datasources/base.py +166 -0
  169. datasources/router.py +221 -0
  170. datasources/sources/__init__.py +15 -0
  171. datasources/sources/akshare_source.py +269 -0
  172. datasources/sources/alpha_vantage_source.py +202 -0
  173. datasources/sources/edgar_source.py +218 -0
  174. datasources/sources/finnhub_source.py +197 -0
  175. datasources/sources/fred_source.py +219 -0
  176. datasources/sources/tushare_source.py +141 -0
  177. datasources/sources/web_scraper_source.py +278 -0
  178. datasources/sources/world_bank_source.py +205 -0
  179. datasources/sources/yfinance_source.py +152 -0
  180. demo_player.py +204 -0
  181. doctor.py +508 -0
  182. file_analysis_tools.py +734 -0
  183. finance_formulas.py +389 -0
  184. football_data_client.py +1670 -0
  185. intent_classifier.py +358 -0
  186. local_finance_tools.py +3221 -0
  187. local_llm_provider.py +552 -0
  188. macro_tools.py +368 -0
  189. market_data_client.py +1899 -0
  190. mcp_client.py +506 -0
  191. memory_manager.py +245 -0
  192. model_capability.py +416 -0
  193. notification_tools.py +248 -0
  194. packages/__init__.py +23 -0
  195. packages/aria_agents/__init__.py +5 -0
  196. packages/aria_agents/manifest.py +69 -0
  197. packages/aria_core/__init__.py +34 -0
  198. packages/aria_core/architecture.py +192 -0
  199. packages/aria_core/export.py +124 -0
  200. packages/aria_core/manifest.py +65 -0
  201. packages/aria_infra/__init__.py +15 -0
  202. packages/aria_infra/arthera.py +52 -0
  203. packages/aria_infra/doctor.py +246 -0
  204. packages/aria_infra/product.py +37 -0
  205. packages/aria_mcp/__init__.py +25 -0
  206. packages/aria_mcp/bridge.py +38 -0
  207. packages/aria_mcp/config.py +97 -0
  208. packages/aria_mcp/tools.py +61 -0
  209. packages/aria_sdk/__init__.py +19 -0
  210. packages/aria_sdk/client.py +396 -0
  211. packages/aria_sdk/providers.py +70 -0
  212. packages/aria_sdk/streaming.py +73 -0
  213. packages/aria_sdk/types.py +86 -0
  214. packages/aria_services/__init__.py +55 -0
  215. packages/aria_services/context.py +258 -0
  216. packages/aria_services/data.py +11 -0
  217. packages/aria_services/provider_health.py +189 -0
  218. packages/aria_services/registry.py +213 -0
  219. packages/aria_services/usage.py +138 -0
  220. packages/aria_skills/__init__.py +5 -0
  221. packages/aria_skills/registry.py +59 -0
  222. packages/aria_tools/__init__.py +5 -0
  223. packages/aria_tools/registry.py +128 -0
  224. packages/quant_engine/__init__.py +6 -0
  225. packages/quant_engine/sports/__init__.py +72 -0
  226. packages/quant_engine/sports/calibrator.py +353 -0
  227. packages/quant_engine/sports/dixon_coles.py +234 -0
  228. packages/quant_engine/sports/elo.py +299 -0
  229. packages/quant_engine/sports/form.py +188 -0
  230. packages/quant_engine/sports/h2h.py +195 -0
  231. packages/quant_engine/sports/ml_model.py +354 -0
  232. packages/quant_engine/sports/predictor.py +311 -0
  233. packages/quant_engine/sports/tracker.py +664 -0
  234. packages/quant_engine/stochastic/__init__.py +27 -0
  235. packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  236. packages/quant_engine/stochastic/ito_calculus.py +477 -0
  237. packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  238. packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  239. packages/quant_engine/stochastic/options_pricing.py +573 -0
  240. packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  241. plan_utils.py +194 -0
  242. plugin_loader.py +328 -0
  243. portfolio_ledger.py +262 -0
  244. privacy/__init__.py +5 -0
  245. privacy/feedback.py +123 -0
  246. project_tools.py +525 -0
  247. providers/__init__.py +30 -0
  248. providers/llm/__init__.py +19 -0
  249. providers/llm/anthropic.py +184 -0
  250. providers/llm/base.py +139 -0
  251. providers/llm/ollama.py +128 -0
  252. providers/llm/openai_compat.py +282 -0
  253. providers/llm/registry.py +358 -0
  254. realty_data_tools.py +659 -0
  255. report_generator.py +1314 -0
  256. runtime/__init__.py +103 -0
  257. runtime/agent_loop.py +1183 -0
  258. runtime/approval.py +51 -0
  259. runtime/events.py +102 -0
  260. runtime/gateway.py +128 -0
  261. runtime/lsp.py +346 -0
  262. runtime/subagent.py +258 -0
  263. runtime/tool_executor.py +104 -0
  264. runtime/tool_policy.py +106 -0
  265. safety/__init__.py +21 -0
  266. safety/permissions.py +275 -0
  267. setup_wizard.py +653 -0
  268. strategy_vault.py +420 -0
  269. ui/__init__.py +100 -0
  270. ui/banner.py +310 -0
  271. ui/completer.py +391 -0
  272. ui/console.py +271 -0
  273. ui/image_render.py +243 -0
  274. ui/input_box.py +376 -0
  275. ui/picker.py +195 -0
  276. ui/render/__init__.py +11 -0
  277. ui/render/finance.py +1480 -0
  278. ui/render/market.py +225 -0
  279. ui/render/output.py +681 -0
  280. ui/render/team.py +346 -0
  281. ui/robot.py +235 -0
  282. workspace/__init__.py +6 -0
  283. workspace/files.py +170 -0
  284. workspace/verify.py +113 -0
@@ -0,0 +1,573 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Options Pricing & Greeks — Citadel / SIG 风格期权定价引擎
4
+ =========================================================
5
+ 实现:
6
+ Black-Scholes 解析定价(欧式)
7
+ Greeks: Delta / Gamma / Theta / Vega / Rho / Vanna / Volga
8
+ 隐含波动率反推(Brent 法 + Newton-Raphson)
9
+ 二叉树(CRR)— 美式期权提前行权
10
+ 波动率曲面:SVI 参数化 + 隐波面插值
11
+ Put-Call Parity 套利检验
12
+ Greeks 聚合(组合层面 Delta / Gamma 汇总)
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import math
19
+ from dataclasses import dataclass, field
20
+ from typing import Dict, List, Literal, Optional, Tuple
21
+
22
+ import numpy as np
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ try:
27
+ from scipy.optimize import brentq, minimize
28
+ from scipy.stats import norm as sp_norm
29
+ _SCIPY = True
30
+ _norm_cdf = sp_norm.cdf
31
+ _norm_pdf = sp_norm.pdf
32
+ except ImportError:
33
+ _SCIPY = False
34
+ # 纯 Python 备用(精度稍低)
35
+ def _norm_cdf(x: float) -> float:
36
+ return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))
37
+ def _norm_pdf(x: float) -> float:
38
+ return math.exp(-0.5 * x * x) / math.sqrt(2.0 * math.pi)
39
+
40
+
41
+ # ── 数据类 ─────────────────────────────────────────────────────────────────────
42
+
43
+ @dataclass
44
+ class OptionSpec:
45
+ """期权规格"""
46
+ S: float # 标的现价
47
+ K: float # 行权价
48
+ T: float # 到期时间(年,如 0.25 = 3 个月)
49
+ r: float # 无风险利率(年化,如 0.05)
50
+ sigma: float # 波动率(年化,如 0.20)
51
+ option_type: str # "call" | "put"
52
+ q: float = 0.0 # 股息收益率(年化)
53
+
54
+ @property
55
+ def is_call(self) -> bool:
56
+ return self.option_type.lower() in ("call", "c")
57
+
58
+ @property
59
+ def moneyness(self) -> float:
60
+ """ln(F/K),正值 = 实值"""
61
+ F = self.S * math.exp((self.r - self.q) * self.T)
62
+ return math.log(F / self.K) if self.K > 0 else 0.0
63
+
64
+
65
+ @dataclass
66
+ class BSResult:
67
+ """Black-Scholes 定价结果"""
68
+ price: float
69
+ delta: float
70
+ gamma: float
71
+ theta: float # 每日 theta(除以 365)
72
+ vega: float # 每 1% vol 的价格变化
73
+ rho: float # 每 1% 利率变化的价格变化
74
+ vanna: float # ∂Delta/∂σ
75
+ volga: float # ∂Vega/∂σ = ∂²V/∂σ²
76
+ d1: float
77
+ d2: float
78
+
79
+ def greeks_summary(self) -> str:
80
+ return (
81
+ f" 价格: {self.price:.4f}\n"
82
+ f" Delta: {self.delta:+.4f} Gamma: {self.gamma:.4f}\n"
83
+ f" Theta: {self.theta:+.4f}/日 Vega: {self.vega:.4f}/1%波动\n"
84
+ f" Rho: {self.rho:+.4f}/1%利率 Vanna: {self.vanna:.4f} Volga: {self.volga:.4f}"
85
+ )
86
+
87
+
88
+ @dataclass
89
+ class ImpliedVolResult:
90
+ """隐含波动率求解结果"""
91
+ iv: float
92
+ converged: bool
93
+ iterations: int
94
+ price_error: float # |model_price - market_price|
95
+
96
+
97
+ @dataclass
98
+ class VolSurface:
99
+ """波动率曲面(按到期 × 行权价网格)"""
100
+ expiries: np.ndarray # 到期时间(年),形如 [0.08, 0.25, 0.5, 1.0]
101
+ strikes: np.ndarray # 行权价
102
+ ivs: np.ndarray # 形状 (len(expiries), len(strikes)),隐含波动率
103
+ S: float # 建立时标的现价
104
+ svi_params: Optional[Dict] = None # SVI 参数(若已拟合)
105
+
106
+ def get_iv(self, T: float, K: float) -> float:
107
+ """双线性插值查询隐波"""
108
+ t_idx = np.searchsorted(self.expiries, T).clip(1, len(self.expiries) - 1)
109
+ k_idx = np.searchsorted(self.strikes, K).clip(1, len(self.strikes) - 1)
110
+ # 简单双线性插值
111
+ t0, t1 = self.expiries[t_idx - 1], self.expiries[t_idx]
112
+ k0, k1 = self.strikes[k_idx - 1], self.strikes[k_idx]
113
+ wt = (T - t0) / (t1 - t0 + 1e-10)
114
+ wk = (K - k0) / (k1 - k0 + 1e-10)
115
+ iv00 = self.ivs[t_idx - 1, k_idx - 1]
116
+ iv01 = self.ivs[t_idx - 1, k_idx ]
117
+ iv10 = self.ivs[t_idx, k_idx - 1]
118
+ iv11 = self.ivs[t_idx, k_idx ]
119
+ return float((1 - wt) * ((1 - wk) * iv00 + wk * iv01) + wt * ((1 - wk) * iv10 + wk * iv11))
120
+
121
+
122
+ @dataclass
123
+ class PortfolioGreeks:
124
+ """组合 Greeks 汇总"""
125
+ delta: float = 0.0 # 总 delta(以标的股数计)
126
+ gamma: float = 0.0 # 总 gamma
127
+ theta: float = 0.0 # 总每日 theta($)
128
+ vega: float = 0.0 # 总 vega(per 1% vol, $)
129
+ rho: float = 0.0 # 总 rho
130
+
131
+ def delta_dollars(self, S: float) -> float:
132
+ """Delta 美元敞口"""
133
+ return self.delta * S
134
+
135
+ def one_pct_move_pnl(self, S: float) -> float:
136
+ """标的价格上涨 1% 的 P&L 估计(一阶 + 二阶)"""
137
+ dS = S * 0.01
138
+ return self.delta * dS + 0.5 * self.gamma * dS ** 2
139
+
140
+ def summary(self, S: float) -> str:
141
+ lines = [
142
+ "组合 Greeks 汇总",
143
+ f" Delta: {self.delta:+.2f} (${self.delta_dollars(S):+,.0f})",
144
+ f" Gamma: {self.gamma:+.4f}",
145
+ f" Theta: {self.theta:+.2f} $/日",
146
+ f" Vega: {self.vega:+.2f} $/1%",
147
+ f" Rho: {self.rho:+.2f} $/1%",
148
+ f" 标的↑1%: 预估 ${self.one_pct_move_pnl(S):+,.0f}",
149
+ ]
150
+ return "\n".join(lines)
151
+
152
+
153
+ # ── Black-Scholes 解析定价 ─────────────────────────────────────────────────────
154
+
155
+ def black_scholes(opt: OptionSpec) -> BSResult:
156
+ """
157
+ Black-Scholes-Merton 解析定价(带股息)
158
+
159
+ 对 T ≤ 0 或 σ ≤ 0 做安全处理。
160
+ """
161
+ S, K, T, r, sigma, q = opt.S, opt.K, opt.T, opt.r, opt.sigma, opt.q
162
+
163
+ if T <= 0:
164
+ # 已到期
165
+ intrinsic = max(S - K, 0.0) if opt.is_call else max(K - S, 0.0)
166
+ return BSResult(price=intrinsic, delta=(1.0 if opt.is_call and S >= K else 0.0),
167
+ gamma=0.0, theta=0.0, vega=0.0, rho=0.0,
168
+ vanna=0.0, volga=0.0, d1=0.0, d2=0.0)
169
+
170
+ if sigma <= 0:
171
+ sigma = 1e-6
172
+
173
+ sqrt_T = math.sqrt(T)
174
+ d1 = (math.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * sqrt_T)
175
+ d2 = d1 - sigma * sqrt_T
176
+
177
+ Nd1 = float(_norm_cdf(d1))
178
+ Nd2 = float(_norm_cdf(d2))
179
+ nd1 = float(_norm_pdf(d1)) # φ(d1)
180
+ Nmd1 = 1.0 - Nd1
181
+ Nmd2 = 1.0 - Nd2
182
+
183
+ disc = math.exp(-r * T)
184
+ disc_q = math.exp(-q * T)
185
+ F = S * disc_q / disc # 前向价格(简化)
186
+ F_full = S * math.exp((r - q) * T)
187
+
188
+ if opt.is_call:
189
+ price = S * disc_q * Nd1 - K * disc * Nd2
190
+ delta = disc_q * Nd1
191
+ rho = K * T * disc * Nd2 / 100 # 每 1% 利率变化
192
+ else:
193
+ price = K * disc * Nmd2 - S * disc_q * Nmd1
194
+ delta = -disc_q * Nmd1
195
+ rho = -K * T * disc * Nmd2 / 100
196
+
197
+ gamma = disc_q * nd1 / (S * sigma * sqrt_T)
198
+ vega = S * disc_q * nd1 * sqrt_T / 100 # 每 1% vol
199
+ theta = (
200
+ -(S * sigma * disc_q * nd1) / (2 * sqrt_T)
201
+ + (q * S * disc_q * (Nd1 if opt.is_call else Nmd1) * (-1 if opt.is_call else 1))
202
+ - (r * K * disc * (Nd2 if opt.is_call else Nmd2) * (1 if opt.is_call else -1))
203
+ ) / 365 # 日 theta
204
+
205
+ # 高阶:Vanna = ∂Delta/∂σ = -(d2/σ) * γ * S (前向近似)
206
+ vanna = -disc_q * nd1 * d2 / sigma
207
+
208
+ # Volga = ∂Vega/∂σ = Vega * d1 * d2 / σ
209
+ volga = (vega * 100) * d1 * d2 / sigma / 100 # 保持 /1% 单位
210
+
211
+ return BSResult(
212
+ price=float(price), delta=float(delta), gamma=float(gamma),
213
+ theta=float(theta), vega=float(vega), rho=float(rho),
214
+ vanna=float(vanna), volga=float(volga), d1=d1, d2=d2,
215
+ )
216
+
217
+
218
+ # ── 隐含波动率反推 ─────────────────────────────────────────────────────────────
219
+
220
+ def implied_volatility(
221
+ market_price: float,
222
+ opt: OptionSpec,
223
+ method: str = "brent", # "brent" | "newton"
224
+ tol: float = 1e-6,
225
+ max_iter: int = 200,
226
+ ) -> ImpliedVolResult:
227
+ """
228
+ 反推隐含波动率
229
+
230
+ Args:
231
+ market_price: 市场观察期权价格
232
+ opt: OptionSpec(sigma 字段将被忽略)
233
+ method: "brent"(稳健)或 "newton"(快速)
234
+ """
235
+ # 内在价值检查
236
+ intrinsic = max(opt.S - opt.K, 0.0) if opt.is_call else max(opt.K - opt.S, 0.0)
237
+ disc = math.exp(-opt.r * opt.T)
238
+ intrinsic_pv = intrinsic * disc
239
+
240
+ if market_price < intrinsic_pv - tol:
241
+ return ImpliedVolResult(iv=float("nan"), converged=False, iterations=0,
242
+ price_error=market_price - intrinsic_pv)
243
+
244
+ def price_at_vol(sigma: float) -> float:
245
+ spec = OptionSpec(S=opt.S, K=opt.K, T=opt.T, r=opt.r, sigma=sigma,
246
+ option_type=opt.option_type, q=opt.q)
247
+ return black_scholes(spec).price - market_price
248
+
249
+ if method == "brent" and _SCIPY:
250
+ try:
251
+ iv = brentq(price_at_vol, 1e-6, 10.0, xtol=tol, maxiter=max_iter)
252
+ err = abs(price_at_vol(iv) + market_price - market_price)
253
+ return ImpliedVolResult(iv=float(iv), converged=True, iterations=0, price_error=err)
254
+ except ValueError:
255
+ pass # 区间无根,回退 Newton
256
+
257
+ # Newton-Raphson(以 vega 为导数)
258
+ sigma = 0.30 # 初始猜测
259
+ for i in range(max_iter):
260
+ spec = OptionSpec(S=opt.S, K=opt.K, T=opt.T, r=opt.r, sigma=sigma,
261
+ option_type=opt.option_type, q=opt.q)
262
+ res = black_scholes(spec)
263
+ diff = res.price - market_price
264
+ # vega = ∂price/∂σ(注意 vega 是 /1%,转回 /1 unit)
265
+ vega_1 = res.vega * 100
266
+ if abs(vega_1) < 1e-10:
267
+ break
268
+ sigma -= diff / vega_1
269
+ sigma = max(1e-6, min(sigma, 10.0))
270
+ if abs(diff) < tol:
271
+ return ImpliedVolResult(iv=float(sigma), converged=True,
272
+ iterations=i + 1, price_error=abs(diff))
273
+
274
+ return ImpliedVolResult(iv=float(sigma), converged=False,
275
+ iterations=max_iter, price_error=abs(price_at_vol(sigma) + market_price - market_price))
276
+
277
+
278
+ # ── 美式期权 — CRR 二叉树 ──────────────────────────────────────────────────────
279
+
280
+ def binomial_american(
281
+ opt: OptionSpec,
282
+ n_steps: int = 200,
283
+ ) -> float:
284
+ """
285
+ Cox-Ross-Rubinstein 二叉树定价美式期权
286
+
287
+ Args:
288
+ opt: OptionSpec
289
+ n_steps: 树的步数(越多越精确,通常 100~500 即可)
290
+
291
+ Returns:
292
+ 美式期权价格
293
+ """
294
+ S, K, T, r, sigma, q = opt.S, opt.K, opt.T, opt.r, opt.sigma, opt.q
295
+ dt = T / n_steps
296
+ u = math.exp(sigma * math.sqrt(dt))
297
+ d = 1.0 / u
298
+ disc = math.exp(-r * dt)
299
+ p_up = (math.exp((r - q) * dt) - d) / (u - d)
300
+ p_dn = 1.0 - p_up
301
+
302
+ # 终端节点价值
303
+ prices = np.array([S * (u ** (n_steps - 2 * j)) for j in range(n_steps + 1)])
304
+ if opt.is_call:
305
+ values = np.maximum(prices - K, 0.0)
306
+ else:
307
+ values = np.maximum(K - prices, 0.0)
308
+
309
+ # 向根节点折回,允许提前行权
310
+ for i in range(n_steps - 1, -1, -1):
311
+ prices = prices[:-1] / u # 节点标的价格
312
+ values = disc * (p_up * values[:-1] + p_dn * values[1:])
313
+ if opt.is_call:
314
+ intrinsic = np.maximum(prices - K, 0.0)
315
+ else:
316
+ intrinsic = np.maximum(K - prices, 0.0)
317
+ values = np.maximum(values, intrinsic)
318
+
319
+ return float(values[0])
320
+
321
+
322
+ # ── SVI 波动率微笑参数化 ────────────────────────────────────────────────────────
323
+
324
+ class SVISmile:
325
+ """
326
+ Gatheral SVI(Stochastic Volatility Inspired)参数化波动率微笑
327
+
328
+ 总方差:w(k) = a + b*(ρ*(k-m) + √((k-m)²+σ²))
329
+ 其中 k = ln(K/F)(对数货币性)
330
+
331
+ 参数:
332
+ a > 0:整体波动率水平
333
+ b > 0:翅膀陡峭度
334
+ ρ ∈ (-1,1):偏斜(负值 = 左偏)
335
+ m ∈ ℝ:顶点偏移
336
+ σ > 0:顶点曲率(ATM 平滑度)
337
+ """
338
+
339
+ def __init__(
340
+ self,
341
+ a: float = 0.04,
342
+ b: float = 0.10,
343
+ rho: float = -0.30,
344
+ m: float = 0.0,
345
+ sigma: float = 0.10,
346
+ ):
347
+ self.a = a
348
+ self.b = b
349
+ self.rho = rho
350
+ self.m = m
351
+ self.sigma = sigma
352
+
353
+ def total_variance(self, k: float) -> float:
354
+ """w(k) = a + b*(ρ*(k-m) + √((k-m)²+σ²))"""
355
+ km = k - self.m
356
+ return self.a + self.b * (self.rho * km + math.sqrt(km ** 2 + self.sigma ** 2))
357
+
358
+ def iv(self, k: float, T: float) -> float:
359
+ """隐含波动率 = √(w(k)/T)"""
360
+ w = self.total_variance(k)
361
+ if w < 0 or T <= 0:
362
+ return float("nan")
363
+ return math.sqrt(w / T)
364
+
365
+ def fit(
366
+ self,
367
+ log_moneyness: np.ndarray,
368
+ market_ivs: np.ndarray,
369
+ T: float,
370
+ ) -> "SVISmile":
371
+ """
372
+ 最小二乘拟合 SVI 参数
373
+
374
+ Args:
375
+ log_moneyness: k = ln(K/F) 数组
376
+ market_ivs: 对应隐含波动率(年化)
377
+ T: 到期时间(年)
378
+
379
+ Returns:
380
+ 拟合后的新 SVISmile 实例
381
+ """
382
+ if not _SCIPY:
383
+ return self # 无 scipy 时不做拟合
384
+
385
+ market_total_var = market_ivs ** 2 * T
386
+
387
+ def objective(params: np.ndarray) -> float:
388
+ a, b, rho, m, sigma = params
389
+ if b < 0 or sigma < 1e-6 or abs(rho) >= 1 or a < 0:
390
+ return 1e10
391
+ model_var = np.array([
392
+ a + b * (rho * (k - m) + math.sqrt((k - m) ** 2 + sigma ** 2))
393
+ for k in log_moneyness
394
+ ])
395
+ return float(np.mean((model_var - market_total_var) ** 2))
396
+
397
+ x0 = [self.a, self.b, self.rho, self.m, self.sigma]
398
+ bounds = [(1e-6, 2.0), (1e-6, 2.0), (-0.999, 0.999), (-2.0, 2.0), (1e-4, 2.0)]
399
+ res = minimize(objective, x0, method="L-BFGS-B", bounds=bounds,
400
+ options={"maxiter": 500, "ftol": 1e-12})
401
+ if res.success:
402
+ a, b, rho, m, sigma = res.x
403
+ return SVISmile(a=a, b=b, rho=rho, m=m, sigma=sigma)
404
+ return self
405
+
406
+
407
+ # ── Put-Call Parity 套利检验 ─────────────────────────────────────────────────
408
+
409
+ def parity_arbitrage(
410
+ call_price: float,
411
+ put_price: float,
412
+ S: float, K: float, T: float, r: float,
413
+ q: float = 0.0,
414
+ threshold_bps: float = 5.0,
415
+ ) -> Dict:
416
+ """
417
+ 检验 Put-Call Parity 是否成立
418
+
419
+ C - P = S·e^(-qT) - K·e^(-rT)
420
+
421
+ Returns:
422
+ dict 含: parity_lhs, parity_rhs, deviation_bps, is_arbitrage
423
+ """
424
+ lhs = call_price - put_price
425
+ rhs = S * math.exp(-q * T) - K * math.exp(-r * T)
426
+ dev = lhs - rhs
427
+ dev_bps = abs(dev / S) * 1e4
428
+
429
+ return {
430
+ "parity_lhs": lhs,
431
+ "parity_rhs": rhs,
432
+ "deviation": dev,
433
+ "deviation_bps": dev_bps,
434
+ "is_arbitrage": dev_bps > threshold_bps,
435
+ "direction": "buy_put_sell_call" if dev > 0 else "buy_call_sell_put",
436
+ }
437
+
438
+
439
+ # ── 组合 Greeks 聚合 ──────────────────────────────────────────────────────────
440
+
441
+ class OptionsPortfolio:
442
+ """
443
+ 期权组合 Greeks 聚合器
444
+
445
+ 支持多腿期权组合(Straddle / Strangle / Butterfly 等)。
446
+ """
447
+
448
+ def __init__(self):
449
+ self._legs: List[Tuple[OptionSpec, float]] = [] # (spec, quantity)
450
+
451
+ def add_leg(self, opt: OptionSpec, qty: float, multiplier: float = 100.0):
452
+ """
453
+ 添加一条腿
454
+
455
+ Args:
456
+ opt: 期权规格
457
+ qty: 手数(正 = 买入, 负 = 卖出)
458
+ multiplier: 合约乘数(股票期权通常 100)
459
+ """
460
+ self._legs.append((opt, qty * multiplier))
461
+
462
+ def greeks(self) -> PortfolioGreeks:
463
+ """汇总所有腿的 Greeks"""
464
+ pg = PortfolioGreeks()
465
+ for opt, qty in self._legs:
466
+ bs = black_scholes(opt)
467
+ pg.delta += bs.delta * qty
468
+ pg.gamma += bs.gamma * qty
469
+ pg.theta += bs.theta * qty * opt.S # 转为 $ per day
470
+ pg.vega += bs.vega * qty * opt.S # 转为 $ per 1% vol
471
+ pg.rho += bs.rho * qty * opt.S
472
+ return pg
473
+
474
+ def total_value(self) -> float:
475
+ """组合当前市值(未乘以名义价值,只是 BS 价格×手数)"""
476
+ return sum(black_scholes(opt).price * qty for opt, qty in self._legs)
477
+
478
+ def scenario_pnl(
479
+ self,
480
+ spot_moves: np.ndarray, # 相对移动,如 np.linspace(-0.20, 0.20, 41)
481
+ vol_shocks: np.ndarray, # 波动率绝对变化,如 np.linspace(-0.05, 0.05, 11)
482
+ ) -> "np.ndarray":
483
+ """
484
+ Greeks-based 情景 P&L 矩阵
485
+
486
+ Returns:
487
+ (len(spot_moves), len(vol_shocks)) 的 P&L 矩阵
488
+ """
489
+ S0 = self._legs[0][0].S if self._legs else 1.0
490
+ pnl_matrix = np.zeros((len(spot_moves), len(vol_shocks)))
491
+ base_val = self.total_value()
492
+
493
+ for i, ds in enumerate(spot_moves):
494
+ for j, dv in enumerate(vol_shocks):
495
+ port = OptionsPortfolio()
496
+ for opt, qty_total in self._legs:
497
+ new_opt = OptionSpec(
498
+ S=opt.S * (1 + ds),
499
+ K=opt.K,
500
+ T=opt.T,
501
+ r=opt.r,
502
+ sigma=max(opt.sigma + dv, 1e-4),
503
+ option_type=opt.option_type,
504
+ q=opt.q,
505
+ )
506
+ # 每手数量已 embed 在 qty_total,避免重乘 multiplier
507
+ port._legs.append((new_opt, qty_total))
508
+ pnl_matrix[i, j] = port.total_value() - base_val
509
+
510
+ return pnl_matrix
511
+
512
+
513
+ # ── 便利函数 ──────────────────────────────────────────────────────────────────
514
+
515
+ def bs_price(
516
+ S: float, K: float, T: float, r: float, sigma: float,
517
+ option_type: str = "call", q: float = 0.0
518
+ ) -> float:
519
+ """简洁 B-S 定价接口"""
520
+ return black_scholes(OptionSpec(S=S, K=K, T=T, r=r, sigma=sigma,
521
+ option_type=option_type, q=q)).price
522
+
523
+
524
+ def delta_hedge_ratio(
525
+ S: float, K: float, T: float, r: float, sigma: float,
526
+ option_type: str = "call", q: float = 0.0,
527
+ ) -> float:
528
+ """计算 Delta 对冲比率(每合约需要对冲的股数)"""
529
+ return black_scholes(OptionSpec(S=S, K=K, T=T, r=r, sigma=sigma,
530
+ option_type=option_type, q=q)).delta
531
+
532
+
533
+ def iv_surface(
534
+ S: float,
535
+ calls: "pd.DataFrame", # columns: expiry(年), strike, price # noqa: F821
536
+ r: float = 0.05,
537
+ q: float = 0.0,
538
+ ) -> VolSurface:
539
+ """
540
+ 从期权报价构建隐含波动率曲面
541
+
542
+ Args:
543
+ S: 标的现价
544
+ calls: DataFrame 含 expiry, strike, price 列
545
+ r: 无风险利率
546
+ q: 股息收益率
547
+
548
+ Returns:
549
+ VolSurface 对象
550
+ """
551
+ import pandas as pd
552
+
553
+ expiries = sorted(calls["expiry"].unique())
554
+ strikes = sorted(calls["strike"].unique())
555
+ ivs = np.full((len(expiries), len(strikes)), np.nan)
556
+
557
+ for i, T in enumerate(expiries):
558
+ for j, K in enumerate(strikes):
559
+ row = calls[(calls["expiry"] == T) & (calls["strike"] == K)]
560
+ if row.empty:
561
+ continue
562
+ mkt_price = float(row["price"].iloc[0])
563
+ opt = OptionSpec(S=S, K=K, T=T, r=r, sigma=0.30, option_type="call", q=q)
564
+ iv_res = implied_volatility(mkt_price, opt)
565
+ if iv_res.converged:
566
+ ivs[i, j] = iv_res.iv
567
+
568
+ return VolSurface(
569
+ expiries=np.array(expiries),
570
+ strikes=np.array(strikes),
571
+ ivs=ivs,
572
+ S=S,
573
+ )
@@ -0,0 +1,90 @@
1
+ """
2
+ Stochastic Processes for Interest Rates and Mean-Reverting Assets
3
+ 包含均值回归过程:OU, CIR, Vasicek, Hull-White
4
+ """
5
+
6
+ import numpy as np
7
+ from typing import Optional, Tuple
8
+
9
+ class OrnsteinUhlenbeck:
10
+ """
11
+ Ornstein-Uhlenbeck Process (均值回归过程)
12
+ dX_t = kappa * (theta - X_t) * dt + sigma * dW_t
13
+
14
+ 常用于建模:
15
+ - 利率 (Vasicek Model)
16
+ - 波动率 (Heston 模型中的方差回归)
17
+ - 配对交易中的价差 (Spread)
18
+ """
19
+ def __init__(self, kappa: float, theta: float, sigma: float, x0: float):
20
+ self.kappa = kappa
21
+ self.theta = theta
22
+ self.sigma = sigma
23
+ self.x0 = x0
24
+
25
+ def simulate(self, T: float, n_steps: int, n_paths: int = 1, seed: Optional[int] = None) -> np.ndarray:
26
+ if seed is not None: np.random.seed(seed)
27
+ dt = T / n_steps
28
+ paths = np.zeros((n_paths, n_steps + 1))
29
+ paths[:, 0] = self.x0
30
+
31
+ for t in range(n_steps):
32
+ dW = np.random.normal(0, np.sqrt(dt), n_paths)
33
+ paths[:, t+1] = paths[:, t] + self.kappa * (self.theta - paths[:, t]) * dt + self.sigma * dW
34
+
35
+ return paths
36
+
37
+ class CIRProcess:
38
+ """
39
+ Cox-Ingersoll-Ross Process (平方根均值回归过程)
40
+ dX_t = kappa * (theta - X_t) * dt + sigma * sqrt(X_t) * dW_t
41
+
42
+ 优点:保证 X_t 始终非负(若 2*kappa*theta > sigma^2 则永远不触碰0)
43
+ """
44
+ def __init__(self, kappa: float, theta: float, sigma: float, x0: float):
45
+ self.kappa = kappa
46
+ self.theta = theta
47
+ self.sigma = sigma
48
+ self.x0 = x0
49
+
50
+ def simulate(self, T: float, n_steps: int, n_paths: int = 1, seed: Optional[int] = None) -> np.ndarray:
51
+ if seed is not None: np.random.seed(seed)
52
+ dt = T / n_steps
53
+ paths = np.zeros((n_paths, n_steps + 1))
54
+ paths[:, 0] = self.x0
55
+
56
+ for t in range(n_steps):
57
+ dW = np.random.normal(0, np.sqrt(dt), n_paths)
58
+ # 使用 Full Truncation 保证数值稳定性
59
+ x_plus = np.maximum(paths[:, t], 0)
60
+ paths[:, t+1] = paths[:, t] + self.kappa * (self.theta - x_plus) * dt + self.sigma * np.sqrt(x_plus) * dW
61
+
62
+ return paths
63
+
64
+ class VasicekModel(OrnsteinUhlenbeck):
65
+ """Vasicek 利率模型 (即 OU 过程用于利率)"""
66
+ pass
67
+
68
+ class HullWhiteModel:
69
+ """
70
+ Hull-White Model (带时变参数的均值回归)
71
+ dr_t = (theta(t) - a * r_t) * dt + sigma * dW_t
72
+ """
73
+ def __init__(self, a: float, sigma: float, r0: float):
74
+ self.a = a
75
+ self.sigma = sigma
76
+ self.r0 = r0
77
+
78
+ def simulate(self, theta_func, T: float, n_steps: int, n_paths: int = 1, seed: Optional[int] = None) -> np.ndarray:
79
+ if seed is not None: np.random.seed(seed)
80
+ dt = T / n_steps
81
+ t_grid = np.linspace(0, T, n_steps + 1)
82
+ paths = np.zeros((n_paths, n_steps + 1))
83
+ paths[:, 0] = self.r0
84
+
85
+ for t in range(n_steps):
86
+ dW = np.random.normal(0, np.sqrt(dt), n_paths)
87
+ theta_t = theta_func(t_grid[t])
88
+ paths[:, t+1] = paths[:, t] + (theta_t - self.a * paths[:, t]) * dt + self.sigma * dW
89
+
90
+ return paths