aria-code 0.52.0__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 (478) hide show
  1. aria_code/__init__.py +42 -0
  2. aria_code/_version.py +20 -0
  3. aria_code/adk_apps/__init__.py +1 -0
  4. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  5. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  6. aria_code/adk_apps/aria_research/__init__.py +1 -0
  7. aria_code/adk_apps/aria_research/agent.py +31 -0
  8. aria_code/agents/__init__.py +34 -0
  9. aria_code/agents/base.py +318 -0
  10. aria_code/agents/code_review.py +200 -0
  11. aria_code/agents/deep/__init__.py +37 -0
  12. aria_code/agents/deep/calibration_loop.py +144 -0
  13. aria_code/agents/deep/critic.py +125 -0
  14. aria_code/agents/deep/deepen.py +193 -0
  15. aria_code/agents/deep/models.py +149 -0
  16. aria_code/agents/deep/pipeline.py +164 -0
  17. aria_code/agents/deep/quant_fusion.py +192 -0
  18. aria_code/agents/deep/themes.py +95 -0
  19. aria_code/agents/deep/tiers.py +106 -0
  20. aria_code/agents/engineering/__init__.py +14 -0
  21. aria_code/agents/engineering/coder.py +222 -0
  22. aria_code/agents/engineering/tester.py +145 -0
  23. aria_code/agents/financial/__init__.py +21 -0
  24. aria_code/agents/financial/cashflow_burn.py +119 -0
  25. aria_code/agents/financial/catalyst.py +279 -0
  26. aria_code/agents/financial/corporate_finance.py +175 -0
  27. aria_code/agents/financial/debate.py +224 -0
  28. aria_code/agents/financial/earnings.py +303 -0
  29. aria_code/agents/financial/fundamental.py +191 -0
  30. aria_code/agents/financial/macro.py +99 -0
  31. aria_code/agents/financial/news.py +207 -0
  32. aria_code/agents/financial/risk.py +165 -0
  33. aria_code/agents/financial/sector.py +279 -0
  34. aria_code/agents/financial/strategist.py +121 -0
  35. aria_code/agents/financial/stripe_revenue.py +127 -0
  36. aria_code/agents/financial/synthesis.py +274 -0
  37. aria_code/agents/financial/technical.py +258 -0
  38. aria_code/agents/orchestrator.py +44 -0
  39. aria_code/agents/portfolio_agent.py +363 -0
  40. aria_code/agents/realty/__init__.py +62 -0
  41. aria_code/agents/realty/asset_diagnosis.py +152 -0
  42. aria_code/agents/realty/business_match.py +166 -0
  43. aria_code/agents/realty/cashflow_verify.py +209 -0
  44. aria_code/agents/realty/contract_rules.py +210 -0
  45. aria_code/agents/realty/energy_anomaly.py +189 -0
  46. aria_code/agents/realty/exit_settlement.py +208 -0
  47. aria_code/agents/realty/fulfillment_risk.py +206 -0
  48. aria_code/agents/realty/ops_optimize.py +160 -0
  49. aria_code/agents/realty/revenue_share.py +215 -0
  50. aria_code/agents/registry.py +158 -0
  51. aria_code/agents/signal_scheme.py +111 -0
  52. aria_code/agents/sports/__init__.py +0 -0
  53. aria_code/agents/sports/football_agent.py +197 -0
  54. aria_code/agents/supervisor.py +113 -0
  55. aria_code/agents/team.py +492 -0
  56. aria_code/agents/warehouse/__init__.py +25 -0
  57. aria_code/agents/warehouse/contracts.py +26 -0
  58. aria_code/agents/warehouse/dashboard.py +155 -0
  59. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  60. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  61. aria_code/agents/warehouse/inventory_health.py +24 -0
  62. aria_code/agents/warehouse/local_run.py +71 -0
  63. aria_code/agents/warehouse/local_snapshot.py +132 -0
  64. aria_code/agents/warehouse/logistics_cost.py +84 -0
  65. aria_code/agents/warehouse/logistics_sync.py +21 -0
  66. aria_code/agents/warehouse/workflow.py +31 -0
  67. aria_code/aliyun_data_client.py +4 -0
  68. aria_code/apps/README.md +12 -0
  69. aria_code/apps/__init__.py +2 -0
  70. aria_code/apps/channels/README.md +15 -0
  71. aria_code/apps/channels/__init__.py +17 -0
  72. aria_code/apps/channels/intake.py +92 -0
  73. aria_code/apps/channels/registry.py +95 -0
  74. aria_code/apps/channels/tradingview.py +94 -0
  75. aria_code/apps/cli/README.md +13 -0
  76. aria_code/apps/cli/__init__.py +2 -0
  77. aria_code/apps/cli/_optional.py +120 -0
  78. aria_code/apps/cli/bootstrap.py +145 -0
  79. aria_code/apps/cli/broker_render.py +206 -0
  80. aria_code/apps/cli/codegen_paths.py +29 -0
  81. aria_code/apps/cli/commands/__init__.py +16 -0
  82. aria_code/apps/cli/commands/_business_render.py +78 -0
  83. aria_code/apps/cli/commands/_ui.py +71 -0
  84. aria_code/apps/cli/commands/analysis_cmds.py +369 -0
  85. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  86. aria_code/apps/cli/commands/auth_cmds.py +239 -0
  87. aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
  88. aria_code/apps/cli/commands/broker_cmds.py +1234 -0
  89. aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
  90. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  91. aria_code/apps/cli/commands/catalog.py +87 -0
  92. aria_code/apps/cli/commands/core_cmds.py +2520 -0
  93. aria_code/apps/cli/commands/data_cmds.py +457 -0
  94. aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
  95. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  96. aria_code/apps/cli/commands/file_cmds.py +312 -0
  97. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  98. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  99. aria_code/apps/cli/commands/market.py +434 -0
  100. aria_code/apps/cli/commands/market_cmds.py +1381 -0
  101. aria_code/apps/cli/commands/market_context.py +425 -0
  102. aria_code/apps/cli/commands/market_render.py +7 -0
  103. aria_code/apps/cli/commands/model_cmds.py +2153 -0
  104. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  105. aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
  106. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  107. aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
  108. aria_code/apps/cli/commands/report.py +437 -0
  109. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  110. aria_code/apps/cli/commands/session_cmds.py +236 -0
  111. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  112. aria_code/apps/cli/commands/team.py +669 -0
  113. aria_code/apps/cli/commands/team_render.py +8 -0
  114. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  115. aria_code/apps/cli/commands/warehouse_cmds.py +79 -0
  116. aria_code/apps/cli/commands/workflow_cmds.py +480 -0
  117. aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
  118. aria_code/apps/cli/config_paths.py +73 -0
  119. aria_code/apps/cli/config_store.py +78 -0
  120. aria_code/apps/cli/constants.py +88 -0
  121. aria_code/apps/cli/context.py +34 -0
  122. aria_code/apps/cli/deterministic.py +191 -0
  123. aria_code/apps/cli/direct.py +48 -0
  124. aria_code/apps/cli/finance_service_catalog.py +111 -0
  125. aria_code/apps/cli/football_reports.py +231 -0
  126. aria_code/apps/cli/github_app_auth.py +135 -0
  127. aria_code/apps/cli/google_login.py +196 -0
  128. aria_code/apps/cli/handlers/__init__.py +11 -0
  129. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  130. aria_code/apps/cli/handlers/chart_handlers.py +1309 -0
  131. aria_code/apps/cli/handlers/market_handlers.py +2513 -0
  132. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  133. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  134. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  135. aria_code/apps/cli/helpers.py +302 -0
  136. aria_code/apps/cli/hooks.py +180 -0
  137. aria_code/apps/cli/i18n.py +297 -0
  138. aria_code/apps/cli/intent.py +136 -0
  139. aria_code/apps/cli/intent_router.py +276 -0
  140. aria_code/apps/cli/intent_signals.py +344 -0
  141. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  142. aria_code/apps/cli/local_tool_schemas.py +536 -0
  143. aria_code/apps/cli/main.py +29 -0
  144. aria_code/apps/cli/market_metadata.py +135 -0
  145. aria_code/apps/cli/market_universe.py +487 -0
  146. aria_code/apps/cli/message_processing.py +310 -0
  147. aria_code/apps/cli/model_catalog.py +620 -0
  148. aria_code/apps/cli/pdf_report.py +977 -0
  149. aria_code/apps/cli/plan_mode.py +139 -0
  150. aria_code/apps/cli/plotly_html.py +15 -0
  151. aria_code/apps/cli/prediction_feedback.py +202 -0
  152. aria_code/apps/cli/preflight.py +502 -0
  153. aria_code/apps/cli/project_aria.py +60 -0
  154. aria_code/apps/cli/prompt_assembly.py +59 -0
  155. aria_code/apps/cli/prompts/__init__.py +0 -0
  156. aria_code/apps/cli/prompts/coding.py +53 -0
  157. aria_code/apps/cli/prompts/select.py +142 -0
  158. aria_code/apps/cli/prompts/system_prompts.py +552 -0
  159. aria_code/apps/cli/prompts/ui.py +436 -0
  160. aria_code/apps/cli/provider_endpoints.py +237 -0
  161. aria_code/apps/cli/provider_keys.py +73 -0
  162. aria_code/apps/cli/providers/__init__.py +1 -0
  163. aria_code/apps/cli/providers/base.py +521 -0
  164. aria_code/apps/cli/providers/chat_routing.py +128 -0
  165. aria_code/apps/cli/providers/collaboration.py +98 -0
  166. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  167. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  168. aria_code/apps/cli/providers/llm/ollama_stream.py +1594 -0
  169. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  170. aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
  171. aria_code/apps/cli/providers/runtime_bridge.py +353 -0
  172. aria_code/apps/cli/providers/vertexai_stream.py +316 -0
  173. aria_code/apps/cli/runtime_consumer.py +657 -0
  174. aria_code/apps/cli/sandbox.py +32 -0
  175. aria_code/apps/cli/session_export.py +120 -0
  176. aria_code/apps/cli/session_jsonl.py +208 -0
  177. aria_code/apps/cli/session_store.py +112 -0
  178. aria_code/apps/cli/skills_catalog.py +273 -0
  179. aria_code/apps/cli/todo_tracker.py +190 -0
  180. aria_code/apps/cli/tool_executor.py +823 -0
  181. aria_code/apps/cli/tool_registry.py +34 -0
  182. aria_code/apps/cli/tool_scope.py +106 -0
  183. aria_code/apps/cli/tools/__init__.py +40 -0
  184. aria_code/apps/cli/tools/context.py +46 -0
  185. aria_code/apps/cli/tools/file_tools.py +180 -0
  186. aria_code/apps/cli/tools/market_tools.py +620 -0
  187. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  188. aria_code/apps/cli/tools/system_tools.py +726 -0
  189. aria_code/apps/cli/tools/write_tools.py +845 -0
  190. aria_code/apps/cli/tradingview_bridge.py +529 -0
  191. aria_code/apps/cli/turn_planning.py +137 -0
  192. aria_code/apps/cli/update_check.py +153 -0
  193. aria_code/apps/cli/utils/__init__.py +0 -0
  194. aria_code/apps/cli/utils/market_detect.py +1597 -0
  195. aria_code/apps/daemon/README.md +14 -0
  196. aria_code/apps/vscode/README.md +115 -0
  197. aria_code/apps/vscode/package.json +70 -0
  198. aria_code/aria_cli.py +6743 -0
  199. aria_code/aria_daemon.py +1398 -0
  200. aria_code/aria_feishu_bot.py +1423 -0
  201. aria_code/aria_mcp_server.py +7 -0
  202. aria_code/aria_relay_client.py +4 -0
  203. aria_code/aria_relay_server.py +490 -0
  204. aria_code/aria_telegram_bot.py +202 -0
  205. aria_code/ariarc.py +385 -0
  206. aria_code/artifacts.py +581 -0
  207. aria_code/backtest_engine.py +4 -0
  208. aria_code/backtest_report.py +4 -0
  209. aria_code/brokers/__init__.py +78 -0
  210. aria_code/brokers/automation.py +193 -0
  211. aria_code/brokers/base.py +231 -0
  212. aria_code/brokers/capabilities.py +264 -0
  213. aria_code/brokers/cn/__init__.py +10 -0
  214. aria_code/brokers/cn/easytrader_broker.py +193 -0
  215. aria_code/brokers/cn/futu_broker.py +194 -0
  216. aria_code/brokers/cn/longbridge_broker.py +190 -0
  217. aria_code/brokers/cn/tiger_broker.py +196 -0
  218. aria_code/brokers/cn/xtquant_broker.py +225 -0
  219. aria_code/brokers/config.py +414 -0
  220. aria_code/brokers/intl/__init__.py +5 -0
  221. aria_code/brokers/intl/alpaca_broker.py +183 -0
  222. aria_code/brokers/intl/ibkr_broker.py +215 -0
  223. aria_code/brokers/intl/trading212_broker.py +183 -0
  224. aria_code/brokers/intl/webull_broker.py +156 -0
  225. aria_code/brokers/paper_broker.py +266 -0
  226. aria_code/brokers/planning.py +296 -0
  227. aria_code/brokers/registry.py +213 -0
  228. aria_code/brokers/trading.py +267 -0
  229. aria_code/canva_client.py +4 -0
  230. aria_code/change_store.py +127 -0
  231. aria_code/clients/__init__.py +0 -0
  232. aria_code/clients/aliyun_data_client.py +660 -0
  233. aria_code/clients/aria_relay_client.py +182 -0
  234. aria_code/clients/canva_client.py +284 -0
  235. aria_code/clients/figma_client.py +100 -0
  236. aria_code/clients/football_data_client.py +1671 -0
  237. aria_code/clients/kling_video_client.py +182 -0
  238. aria_code/clients/market_data_client.py +1976 -0
  239. aria_code/clients/openai_image_client.py +242 -0
  240. aria_code/clients/runway_video_client.py +157 -0
  241. aria_code/clients/warehouse_erp_client.py +105 -0
  242. aria_code/cloud_memory.py +181 -0
  243. aria_code/command_safety.py +19 -0
  244. aria_code/computer_use_tools.py +4 -0
  245. aria_code/dashboard_generator.py +579 -0
  246. aria_code/data_analysis_tools.py +4 -0
  247. aria_code/data_cleaner.py +483 -0
  248. aria_code/data_service.py +513 -0
  249. aria_code/datasources/__init__.py +23 -0
  250. aria_code/datasources/base.py +166 -0
  251. aria_code/datasources/router.py +253 -0
  252. aria_code/datasources/sources/__init__.py +15 -0
  253. aria_code/datasources/sources/akshare_source.py +269 -0
  254. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  255. aria_code/datasources/sources/edgar_source.py +219 -0
  256. aria_code/datasources/sources/finnhub_source.py +198 -0
  257. aria_code/datasources/sources/fred_source.py +223 -0
  258. aria_code/datasources/sources/tushare_source.py +143 -0
  259. aria_code/datasources/sources/web_scraper_source.py +278 -0
  260. aria_code/datasources/sources/world_bank_source.py +206 -0
  261. aria_code/datasources/sources/yfinance_source.py +152 -0
  262. aria_code/demo_player.py +207 -0
  263. aria_code/doctor.py +813 -0
  264. aria_code/domain/__init__.py +0 -0
  265. aria_code/domain/backtest_engine.py +354 -0
  266. aria_code/domain/backtest_report.py +472 -0
  267. aria_code/domain/finance_formulas.py +389 -0
  268. aria_code/domain/portfolio_ledger.py +307 -0
  269. aria_code/domain/strategy_vault.py +421 -0
  270. aria_code/evals/__init__.py +37 -0
  271. aria_code/evals/harness.py +598 -0
  272. aria_code/evals/runner.py +238 -0
  273. aria_code/external_agent_runner.py +115 -0
  274. aria_code/figma_client.py +4 -0
  275. aria_code/file_analysis_tools.py +4 -0
  276. aria_code/finance_formulas.py +4 -0
  277. aria_code/football_data_client.py +4 -0
  278. aria_code/image_gen_tools.py +4 -0
  279. aria_code/intent_classifier.py +386 -0
  280. aria_code/kling_video_client.py +4 -0
  281. aria_code/licensing.py +151 -0
  282. aria_code/local_finance_tools.py +4 -0
  283. aria_code/local_image_provider.py +4 -0
  284. aria_code/local_llm_provider.py +4 -0
  285. aria_code/macro_tools.py +4 -0
  286. aria_code/markdown_pdf.py +289 -0
  287. aria_code/market_data_client.py +4 -0
  288. aria_code/mcp_client.py +639 -0
  289. aria_code/memory_manager.py +246 -0
  290. aria_code/model_capability.py +486 -0
  291. aria_code/notification_tools.py +4 -0
  292. aria_code/openai_image_client.py +4 -0
  293. aria_code/packages/__init__.py +23 -0
  294. aria_code/packages/adk_bridge/__init__.py +11 -0
  295. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  296. aria_code/packages/adk_bridge/market_tools.py +125 -0
  297. aria_code/packages/aria_agents/__init__.py +5 -0
  298. aria_code/packages/aria_agents/manifest.py +78 -0
  299. aria_code/packages/aria_core/__init__.py +34 -0
  300. aria_code/packages/aria_core/architecture.py +269 -0
  301. aria_code/packages/aria_core/export.py +147 -0
  302. aria_code/packages/aria_core/manifest.py +65 -0
  303. aria_code/packages/aria_core/paths.py +57 -0
  304. aria_code/packages/aria_core/secure_file.py +68 -0
  305. aria_code/packages/aria_infra/__init__.py +15 -0
  306. aria_code/packages/aria_infra/arthera.py +52 -0
  307. aria_code/packages/aria_infra/doctor.py +246 -0
  308. aria_code/packages/aria_infra/product.py +40 -0
  309. aria_code/packages/aria_mcp/__init__.py +25 -0
  310. aria_code/packages/aria_mcp/bridge.py +201 -0
  311. aria_code/packages/aria_mcp/circuit.py +82 -0
  312. aria_code/packages/aria_mcp/config.py +111 -0
  313. aria_code/packages/aria_mcp/installer.py +95 -0
  314. aria_code/packages/aria_mcp/server.py +1550 -0
  315. aria_code/packages/aria_mcp/tools.py +81 -0
  316. aria_code/packages/aria_sdk/__init__.py +19 -0
  317. aria_code/packages/aria_sdk/client.py +396 -0
  318. aria_code/packages/aria_sdk/providers.py +70 -0
  319. aria_code/packages/aria_sdk/streaming.py +79 -0
  320. aria_code/packages/aria_sdk/types.py +86 -0
  321. aria_code/packages/aria_services/__init__.py +92 -0
  322. aria_code/packages/aria_services/cache.py +60 -0
  323. aria_code/packages/aria_services/context.py +356 -0
  324. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  325. aria_code/packages/aria_services/data.py +11 -0
  326. aria_code/packages/aria_services/provider_health.py +268 -0
  327. aria_code/packages/aria_services/references.py +247 -0
  328. aria_code/packages/aria_services/registry.py +249 -0
  329. aria_code/packages/aria_services/research_protocol.py +57 -0
  330. aria_code/packages/aria_services/research_quality.py +186 -0
  331. aria_code/packages/aria_services/settings.py +157 -0
  332. aria_code/packages/aria_services/usage.py +151 -0
  333. aria_code/packages/aria_skills/__init__.py +46 -0
  334. aria_code/packages/aria_skills/catalog.py +149 -0
  335. aria_code/packages/aria_skills/loader.py +455 -0
  336. aria_code/packages/aria_skills/registry.py +84 -0
  337. aria_code/packages/aria_tools/__init__.py +5 -0
  338. aria_code/packages/aria_tools/financial/__init__.py +8 -0
  339. aria_code/packages/aria_tools/financial/_paths.py +130 -0
  340. aria_code/packages/aria_tools/financial/compliance.py +31 -0
  341. aria_code/packages/aria_tools/financial/factors.py +33 -0
  342. aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
  343. aria_code/packages/aria_tools/financial/strategy.py +31 -0
  344. aria_code/packages/aria_tools/registry.py +128 -0
  345. aria_code/packages/quant_engine/__init__.py +34 -0
  346. aria_code/packages/quant_engine/_namespace.py +62 -0
  347. aria_code/packages/quant_engine/backtest/__init__.py +14 -0
  348. aria_code/packages/quant_engine/backtest/core.py +53 -0
  349. aria_code/packages/quant_engine/backtest/engine.py +96 -0
  350. aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
  351. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  352. aria_code/packages/quant_engine/sports/__init__.py +80 -0
  353. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  354. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  355. aria_code/packages/quant_engine/sports/elo.py +300 -0
  356. aria_code/packages/quant_engine/sports/form.py +188 -0
  357. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  358. aria_code/packages/quant_engine/sports/ml_model.py +377 -0
  359. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  360. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  361. aria_code/packages/quant_engine/stochastic/__init__.py +35 -0
  362. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  363. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  364. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  365. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  366. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  367. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  368. aria_code/packs/__init__.py +85 -0
  369. aria_code/packs/base.py +168 -0
  370. aria_code/packs/finance/__init__.py +250 -0
  371. aria_code/packs/logistics/__init__.py +169 -0
  372. aria_code/packs/payments/__init__.py +139 -0
  373. aria_code/packs/realty/__init__.py +190 -0
  374. aria_code/packs/registry.py +202 -0
  375. aria_code/packs/rules.py +90 -0
  376. aria_code/plan_utils.py +194 -0
  377. aria_code/plugin_loader.py +328 -0
  378. aria_code/portfolio_ledger.py +4 -0
  379. aria_code/preview_server.py +449 -0
  380. aria_code/privacy/__init__.py +5 -0
  381. aria_code/privacy/feedback.py +123 -0
  382. aria_code/project_review.py +278 -0
  383. aria_code/project_review_api.py +235 -0
  384. aria_code/project_review_server.py +18 -0
  385. aria_code/project_tools.py +4 -0
  386. aria_code/providers/__init__.py +30 -0
  387. aria_code/providers/llm/__init__.py +19 -0
  388. aria_code/providers/llm/anthropic.py +184 -0
  389. aria_code/providers/llm/autoconfig.py +213 -0
  390. aria_code/providers/llm/base.py +139 -0
  391. aria_code/providers/llm/custom.py +158 -0
  392. aria_code/providers/llm/ollama.py +129 -0
  393. aria_code/providers/llm/openai_compat.py +313 -0
  394. aria_code/providers/llm/registry.py +444 -0
  395. aria_code/providers/local_image_provider.py +202 -0
  396. aria_code/providers/local_llm_provider.py +589 -0
  397. aria_code/realty_data_tools.py +4 -0
  398. aria_code/report_exporters.py +150 -0
  399. aria_code/report_generator.py +1764 -0
  400. aria_code/runtime/__init__.py +169 -0
  401. aria_code/runtime/acceptance.py +481 -0
  402. aria_code/runtime/agent_loop.py +1520 -0
  403. aria_code/runtime/approval.py +57 -0
  404. aria_code/runtime/budget.py +232 -0
  405. aria_code/runtime/checkpoints.py +432 -0
  406. aria_code/runtime/crash_report.py +174 -0
  407. aria_code/runtime/events.py +113 -0
  408. aria_code/runtime/gateway.py +150 -0
  409. aria_code/runtime/lsp.py +484 -0
  410. aria_code/runtime/orchestrator.py +55 -0
  411. aria_code/runtime/repo_map.py +700 -0
  412. aria_code/runtime/run_state.py +127 -0
  413. aria_code/runtime/run_store.py +450 -0
  414. aria_code/runtime/self_healing.py +422 -0
  415. aria_code/runtime/subagent.py +547 -0
  416. aria_code/runtime/task_ledger.py +86 -0
  417. aria_code/runtime/task_orchestrator.py +90 -0
  418. aria_code/runtime/tool_executor.py +217 -0
  419. aria_code/runtime/tool_policy.py +107 -0
  420. aria_code/runtime/worktrees.py +215 -0
  421. aria_code/runway_video_client.py +4 -0
  422. aria_code/safety/__init__.py +23 -0
  423. aria_code/safety/permissions.py +306 -0
  424. aria_code/safety/service.py +102 -0
  425. aria_code/setup_wizard.py +673 -0
  426. aria_code/spreadsheet_tools.py +4 -0
  427. aria_code/strategy_vault.py +4 -0
  428. aria_code/tools/__init__.py +0 -0
  429. aria_code/tools/artifact_tools.py +252 -0
  430. aria_code/tools/broker_tools.py +92 -0
  431. aria_code/tools/build_quant_engine.py +97 -0
  432. aria_code/tools/code_audit_tools.py +192 -0
  433. aria_code/tools/computer_use_tools.py +504 -0
  434. aria_code/tools/data_analysis_tools.py +809 -0
  435. aria_code/tools/enterprise_finance_tools.py +156 -0
  436. aria_code/tools/extended_tools.py +240 -0
  437. aria_code/tools/file_analysis_tools.py +885 -0
  438. aria_code/tools/image_gen_tools.py +114 -0
  439. aria_code/tools/local_finance_tools.py +3417 -0
  440. aria_code/tools/logistics_tools.py +135 -0
  441. aria_code/tools/macro_tools.py +369 -0
  442. aria_code/tools/notification_tools.py +249 -0
  443. aria_code/tools/project_tools.py +525 -0
  444. aria_code/tools/realty_data_tools.py +659 -0
  445. aria_code/tools/spreadsheet_tools.py +388 -0
  446. aria_code/tools/stripe_tools.py +115 -0
  447. aria_code/ui/__init__.py +105 -0
  448. aria_code/ui/banner.py +508 -0
  449. aria_code/ui/completer.py +501 -0
  450. aria_code/ui/console.py +281 -0
  451. aria_code/ui/image_render.py +243 -0
  452. aria_code/ui/input_box.py +488 -0
  453. aria_code/ui/picker.py +201 -0
  454. aria_code/ui/render/__init__.py +11 -0
  455. aria_code/ui/render/finance.py +1705 -0
  456. aria_code/ui/render/market.py +225 -0
  457. aria_code/ui/render/output.py +775 -0
  458. aria_code/ui/render/responsive.py +75 -0
  459. aria_code/ui/render/team.py +354 -0
  460. aria_code/ui/robot.py +235 -0
  461. aria_code/ui/startup_dashboard.py +109 -0
  462. aria_code/utils/vscode_uri.py +37 -0
  463. aria_code/video_analysis.py +147 -0
  464. aria_code/video_editor.py +271 -0
  465. aria_code/workspace/__init__.py +6 -0
  466. aria_code/workspace/files.py +193 -0
  467. aria_code/workspace/verify.py +113 -0
  468. aria_code/workspace_changes.py +199 -0
  469. aria_code/workspace_context.py +233 -0
  470. aria_code/workspace_service.py +196 -0
  471. aria_code/workspace_tools.py +150 -0
  472. aria_code-0.52.0.dist-info/METADATA +237 -0
  473. aria_code-0.52.0.dist-info/RECORD +478 -0
  474. aria_code-0.52.0.dist-info/WHEEL +5 -0
  475. aria_code-0.52.0.dist-info/entry_points.txt +3 -0
  476. aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
  477. aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
  478. aria_code-0.52.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,377 @@
1
+ """
2
+ sports/ml_model.py — 足球 XGBoost 预测模型
3
+ =============================================
4
+ 从 tracker.py 积累的已结算预测记录中学习,
5
+ 与 Dixon-Coles 规则模型进行 A/B Brier Score 对比。
6
+
7
+ 触发逻辑(原始设计):
8
+ - 首次训练: ≥20 条已结算记录(Elo + 实际结果)
9
+ - 自动重训: 每新增 10 条记录触发一次
10
+ - 预测时: 优先使用 ML 模型,数据不足则 fallback → DC
11
+
12
+ ⚠️ 现状:本模块目前**未接入预测路径,这是有意的**。
13
+ predictor.py 只跑 Elo+DC+Form+H2H(见其 model_tag),从不调用
14
+ get_football_ml()。2026-08-19 复查时一度把它当成"死代码"准备删除,查了
15
+ 训练产物才发现真相——它不是忘了接线,是 A/B 输给了基线:
16
+
17
+ ~/.arthera/football_ml_report.json (trained_at 2026-06-15)
18
+ n_samples 25
19
+ cv_brier_mean 1.0446 ← 本模型
20
+ dc_brier 0.3251 ← Dixon-Coles 基线
21
+ improvement -0.7196 ← 负数 = 更差
22
+
23
+ Brier 越低越好,ML 比基线差约 3.2 倍。25 条样本喂 9 维特征的 XGBoost,
24
+ 1.04 的 CV Brier 正是过拟合的典型表现(3 分类 Brier 上界 2.0,随机猜测
25
+ 约 0.67,本模型比随机还差)。所以**不接入是正确决定**,不是遗漏。
26
+
27
+ 要重新评估的前提是样本量先上来(经验上 9 维特征至少需要几百条已结算记录,
28
+ 25 条差了一个数量级)。届时重跑 train() 看 improvement 是否转正,
29
+ 再决定要不要在 predictor.py 里接上。
30
+
31
+ 保留本文件的理由:删掉会一并丢失"ML 在此样本量下输给 DC"这个结论,
32
+ 下一个人只会重新实现一遍再重新发现一次。
33
+
34
+ 特征向量 (9维):
35
+ elo_diff, elo_home, elo_away,
36
+ lambda_home, lambda_away, lambda_ratio,
37
+ league_avg, elo_diff_abs_scaled, is_high_gap
38
+
39
+ 标签: 0=away, 1=draw, 2=home(XGBoost 多分类)
40
+
41
+ 持久化:
42
+ ~/.arthera/football_ml_model.pkl
43
+ ~/.arthera/football_ml_report.json
44
+ """
45
+
46
+ from __future__ import annotations
47
+
48
+ import json
49
+ import logging
50
+ import pickle
51
+ import time
52
+ from pathlib import Path
53
+ from typing import Dict, List, Optional, Tuple
54
+
55
+ import numpy as np
56
+ from aria_code.packages.aria_core.paths import aria_home
57
+
58
+ logger = logging.getLogger(__name__)
59
+
60
+ _MODEL_PATH = aria_home() / "football_ml_model.pkl"
61
+ _REPORT_PATH = aria_home() / "football_ml_report.json"
62
+ _MIN_TRAIN = 20
63
+ _RETRAIN_EVERY = 10
64
+
65
+ try:
66
+ from xgboost import XGBClassifier
67
+ _HAS_XGB = True
68
+ except ImportError:
69
+ try:
70
+ import lightgbm as lgb
71
+ _HAS_XGB = False
72
+ _HAS_LGB = True
73
+ except ImportError:
74
+ _HAS_XGB = False
75
+ _HAS_LGB = False
76
+
77
+ try:
78
+ from sklearn.preprocessing import StandardScaler
79
+ from sklearn.model_selection import cross_val_score, StratifiedKFold
80
+ _HAS_SK = True
81
+ except ImportError:
82
+ _HAS_SK = False
83
+
84
+
85
+ # ── 特征提取 ──────────────────────────────────────────────────────────────────
86
+
87
+ _FEATURE_NAMES = [
88
+ "elo_diff", "elo_home", "elo_away",
89
+ "lambda_home", "lambda_away", "lambda_ratio",
90
+ "league_avg", "elo_gap_scaled", "is_high_gap",
91
+ ]
92
+
93
+
94
+ def _extract_features(record: Dict) -> Optional[np.ndarray]:
95
+ """从一条预测记录提取特征向量,缺字段返回 None。"""
96
+ elo_h = record.get("home_elo")
97
+ elo_a = record.get("away_elo")
98
+ lh = record.get("lambda_home")
99
+ la = record.get("lambda_away")
100
+ avg = record.get("league_avg", 1.35)
101
+
102
+ if any(v is None for v in [elo_h, elo_a, lh, la]):
103
+ return None
104
+
105
+ elo_h, elo_a, lh, la, avg = float(elo_h), float(elo_a), float(lh), float(la), float(avg)
106
+ diff = elo_h - elo_a
107
+
108
+ return np.array([
109
+ diff, # Elo 差
110
+ elo_h, # 主队 Elo
111
+ elo_a, # 客队 Elo
112
+ lh, # 主队期望进球
113
+ la, # 客队期望进球
114
+ lh / (la + 1e-6), # λ 比值(反映实力差距)
115
+ avg, # 赛事场均进球
116
+ abs(diff) / 400.0, # 标准化 Elo 差(400=1个标准差)
117
+ 1.0 if abs(diff) > 200 else 0.0, # 悬殊场次标志
118
+ ], dtype=np.float32)
119
+
120
+
121
+ def _result_to_label(result: str) -> int:
122
+ """home=2, draw=1, away=0"""
123
+ return {"home": 2, "draw": 1, "away": 0}.get(result, -1)
124
+
125
+
126
+ # ── 训练器 ────────────────────────────────────────────────────────────────────
127
+
128
+ class FootballMLModel:
129
+ """
130
+ 足球 XGBoost/LightGBM 预测器。
131
+
132
+ 用法:
133
+ m = FootballMLModel.load_or_train()
134
+ if m.is_ready:
135
+ p = m.predict(record) # {"home_win": 0.72, "draw": 0.18, "away_win": 0.10}
136
+ """
137
+
138
+ def __init__(self):
139
+ self._model = None
140
+ self._scaler = None
141
+ self._report: Dict = {}
142
+ self._n_trained = 0
143
+
144
+ @property
145
+ def is_ready(self) -> bool:
146
+ return self._model is not None
147
+
148
+ # ── 训练 ──────────────────────────────────────────────────────────────────
149
+
150
+ def train(self, records: Optional[List[Dict]] = None) -> Dict:
151
+ """
152
+ 从 tracker 记录中训练。records 为 None 时自动从磁盘加载。
153
+ 返回训练报告 dict。
154
+ """
155
+ if not (_HAS_XGB or _HAS_LGB):
156
+ return {"error": "pip install xgboost 或 lightgbm 后重试"}
157
+ if not _HAS_SK:
158
+ return {"error": "pip install scikit-learn 后重试"}
159
+
160
+ if records is None:
161
+ records = _load_settled_records()
162
+
163
+ # 过滤出含完整特征的记录
164
+ X_rows, y_rows = [], []
165
+ for r in records:
166
+ label = _result_to_label(r.get("result", ""))
167
+ if label == -1:
168
+ continue
169
+ feat = _extract_features(r)
170
+ if feat is None:
171
+ continue
172
+ X_rows.append(feat)
173
+ y_rows.append(label)
174
+
175
+ n = len(X_rows)
176
+ if n < _MIN_TRAIN:
177
+ return {"status": "waiting", "n": n, "need": _MIN_TRAIN,
178
+ "message": f"需要 {_MIN_TRAIN} 条完整记录,当前 {n} 条"}
179
+
180
+ X = np.array(X_rows)
181
+ y = np.array(y_rows)
182
+
183
+ # 标准化
184
+ scaler = StandardScaler()
185
+ X_s = scaler.fit_transform(X)
186
+
187
+ # 模型
188
+ if _HAS_XGB:
189
+ model = XGBClassifier(
190
+ n_estimators=200, max_depth=4, learning_rate=0.05,
191
+ subsample=0.8, colsample_bytree=0.8,
192
+ reg_alpha=0.1, reg_lambda=0.5,
193
+ objective="multi:softprob", num_class=3,
194
+ eval_metric="mlogloss", use_label_encoder=False,
195
+ random_state=42, verbosity=0,
196
+ )
197
+ else:
198
+ import lightgbm as lgb
199
+ model = lgb.LGBMClassifier(
200
+ n_estimators=200, max_depth=4, learning_rate=0.05,
201
+ num_class=3, objective="multiclass",
202
+ feature_fraction=0.8, bagging_fraction=0.8,
203
+ reg_alpha=0.1, reg_lambda=0.5,
204
+ verbose=-1, random_state=42,
205
+ )
206
+
207
+ # 走步交叉验证(时序感知:按时间顺序分折)
208
+ cv_briers = _walk_forward_cv(model, X_s, y, n_splits=min(5, n // 4))
209
+
210
+ # 全量重训练
211
+ model.fit(X_s, y)
212
+
213
+ self._model = model
214
+ self._scaler = scaler
215
+ self._n_trained = n
216
+
217
+ # CV Brier vs DC Brier(走步验证,公平对比)
218
+ dc_brier = _dc_brier_from_records(records[:n])
219
+ cv_mean = float(np.mean(cv_briers)) if cv_briers else None
220
+ # improvement = DC - CV_ML(正值表示 ML 更准,使用 CV 避免训练集过拟合)
221
+ improvement = round(dc_brier - cv_mean, 4) if cv_mean is not None else None
222
+
223
+ lib = "XGBoost" if _HAS_XGB else "LightGBM"
224
+ self._report = {
225
+ "lib": lib,
226
+ "n_samples": int(n),
227
+ "cv_brier_mean": round(cv_mean, 4) if cv_mean is not None else None,
228
+ "cv_brier_std": round(float(np.std(cv_briers)), 4) if cv_briers else None,
229
+ "dc_brier": round(float(dc_brier), 4),
230
+ "improvement": improvement, # >0 = ML 更准(基于 CV,可信)
231
+ "trained_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
232
+ "feature_names": _FEATURE_NAMES,
233
+ }
234
+
235
+ _save_model(self._model, self._scaler, self._report)
236
+ logger.info(
237
+ f"[FootballML] {lib} 训练完成 n={n} "
238
+ f"CV Brier={self._report.get('cv_brier_mean')} "
239
+ f"DC Brier={dc_brier:.4f} 提升={self._report['improvement']:+.4f}"
240
+ )
241
+ return self._report
242
+
243
+ # ── 预测 ──────────────────────────────────────────────────────────────────
244
+
245
+ def predict(self, record: Dict) -> Optional[Dict[str, float]]:
246
+ """
247
+ 从预测记录(含 elo/lambda)输出 ML 概率。
248
+ 返回 None 表示特征不完整,调用方应 fallback 到 DC。
249
+ """
250
+ if not self.is_ready:
251
+ return None
252
+ feat = _extract_features(record)
253
+ if feat is None:
254
+ return None
255
+
256
+ feat_s = self._scaler.transform(feat.reshape(1, -1))
257
+ proba = self._model.predict_proba(feat_s)[0] # [away, draw, home]
258
+ return {
259
+ "away_win": round(float(proba[0]), 4),
260
+ "draw": round(float(proba[1]), 4),
261
+ "home_win": round(float(proba[2]), 4),
262
+ "model": "XGB+Elo+λ",
263
+ }
264
+
265
+ @property
266
+ def report(self) -> Dict:
267
+ return self._report
268
+
269
+ # ── 加载/保存 ─────────────────────────────────────────────────────────────
270
+
271
+ @classmethod
272
+ def load_or_train(cls, force_train: bool = False) -> "FootballMLModel":
273
+ """加载已存模型,若不存在或需重训则自动训练。"""
274
+ m = cls()
275
+ if _MODEL_PATH.exists() and not force_train:
276
+ try:
277
+ payload = pickle.loads(_MODEL_PATH.read_bytes())
278
+ m._model = payload["model"]
279
+ m._scaler = payload["scaler"]
280
+ m._report = payload.get("report", {})
281
+ m._n_trained = payload.get("n_trained", 0)
282
+
283
+ # 检查是否需要重训
284
+ records = _load_settled_records()
285
+ if len(records) >= m._n_trained + _RETRAIN_EVERY:
286
+ logger.info("[FootballML] 新增 ≥10 条记录,触发重训")
287
+ m.train(records)
288
+ return m
289
+ except Exception as e:
290
+ logger.warning(f"[FootballML] 加载失败: {e},重新训练")
291
+
292
+ m.train()
293
+ return m
294
+
295
+
296
+ # ── 工具函数 ──────────────────────────────────────────────────────────────────
297
+
298
+ def _load_settled_records() -> List[Dict]:
299
+ """从 tracker 加载已结算预测记录。"""
300
+ try:
301
+ from .tracker import _PRED_PATH, _load_json
302
+ records = _load_json(_PRED_PATH, [])
303
+ return [r for r in records if r.get("result") and r.get("brier_score") is not None]
304
+ except Exception:
305
+ return []
306
+
307
+
308
+ def _walk_forward_cv(model, X: np.ndarray, y: np.ndarray, n_splits: int = 5) -> List[float]:
309
+ """时序感知交叉验证,返回每折 Brier Score。"""
310
+ import copy
311
+ n = len(X)
312
+ fold_size = max(4, n // (n_splits + 1))
313
+ briers = []
314
+ for i in range(n_splits):
315
+ tr_end = (i + 1) * fold_size
316
+ te_end = tr_end + fold_size
317
+ if te_end > n:
318
+ break
319
+ y_tr = y[:tr_end]
320
+ # 跳过训练集类别不足的折(XGBoost 要求所有类别都出现)
321
+ if len(np.unique(y_tr)) < 3:
322
+ continue
323
+ try:
324
+ m_copy = copy.deepcopy(model)
325
+ m_copy.fit(X[:tr_end], y_tr)
326
+ proba = m_copy.predict_proba(X[tr_end:te_end])
327
+ if proba.shape[1] == 3:
328
+ briers.append(_brier_mc(proba, y[tr_end:te_end]))
329
+ except Exception:
330
+ continue
331
+ return briers
332
+
333
+
334
+ def _brier_mc(proba: np.ndarray, y: np.ndarray) -> float:
335
+ """多分类 Brier Score。"""
336
+ total = 0.0
337
+ n_classes = proba.shape[1]
338
+ for i, yi in enumerate(y):
339
+ for c in range(n_classes):
340
+ total += (proba[i, c] - (1.0 if yi == c else 0.0)) ** 2
341
+ return total / max(len(y), 1)
342
+
343
+
344
+ def _dc_brier_from_records(records: List[Dict]) -> float:
345
+ """用记录里已存的 brier_score(DC 模型)计算均值。"""
346
+ scores = [r["brier_score"] for r in records if r.get("brier_score") is not None]
347
+ return float(np.mean(scores)) if scores else 0.5
348
+
349
+
350
+ def _save_model(model, scaler, report: Dict) -> None:
351
+ try:
352
+ n = report.get("n_samples", 0)
353
+ payload = {"model": model, "scaler": scaler, "report": report, "n_trained": n}
354
+ _MODEL_PATH.parent.mkdir(parents=True, exist_ok=True)
355
+ _MODEL_PATH.write_bytes(pickle.dumps(payload))
356
+ # JSON 序列化:将 numpy 类型转换为 Python 原生类型
357
+ def _to_native(obj):
358
+ if isinstance(obj, (np.floating, np.float32, np.float64)): return float(obj)
359
+ if isinstance(obj, (np.integer,)): return int(obj)
360
+ if isinstance(obj, np.ndarray): return obj.tolist()
361
+ return obj
362
+ safe_report = json.loads(json.dumps(report, default=_to_native))
363
+ _REPORT_PATH.write_text(json.dumps(safe_report, ensure_ascii=False, indent=2), encoding="utf-8")
364
+ except Exception as e:
365
+ logger.warning(f"[FootballML] 保存失败: {e}")
366
+
367
+
368
+ # ── 单例 ─────────────────────────────────────────────────────────────────────
369
+
370
+ _instance: Optional[FootballMLModel] = None
371
+
372
+
373
+ def get_football_ml() -> FootballMLModel:
374
+ global _instance
375
+ if _instance is None:
376
+ _instance = FootballMLModel.load_or_train()
377
+ return _instance
@@ -0,0 +1,312 @@
1
+ """
2
+ sports/predictor.py — 统一足球比赛预测引擎 v2
3
+ ==============================================
4
+ 整合 Elo + Dixon-Coles(NB) + 近期状态 + H2H + 赛事情境 五个模块。
5
+
6
+ v2 改进:
7
+ 1. 负二项分布(大比分悬殊时自动启用,尾部更重)
8
+ 2. 动态 DC×Elo 混合权重(form 数据越充足 DC 权重越高)
9
+ 3. 赛事情境参数(必须赢/已出线保守/淘汰赛)
10
+ 4. 动态 WC 场均进球(从 tracker 实时获取)
11
+ 5. ρ 随赛果积累自动校准
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import math
17
+ from typing import Dict, List, Optional, Tuple
18
+
19
+ from .elo import EloRatingSystem, get_elo
20
+ from .dixon_coles import compute_match_probabilities, estimate_rho_from_results
21
+ from .form import analyze_form, parse_api_results
22
+ from .h2h import analyze_h2h, _neutral_h2h
23
+ from aria_code.packages.aria_core.paths import aria_home
24
+
25
+
26
+ # ── 联赛场均进球(每队每场,后备默认值)──────────────────────────────────────
27
+ _LEAGUE_AVG_GOALS: Dict[str, float] = {
28
+ "wc": 1.35, "euro": 1.20, "copa": 1.28,
29
+ "pl": 1.51, "bl1": 1.56, "sa": 1.33,
30
+ "pd": 1.34, "fl1": 1.43, "cl": 1.40,
31
+ "friendly": 1.45, "default": 1.35,
32
+ }
33
+
34
+ # ── 赛事情境因子 ──────────────────────────────────────────────────────────────
35
+ _CONTEXT: Dict[str, Dict[str, float]] = {
36
+ "normal": {"lmult_h": 1.00, "lmult_a": 1.00, "draw_boost": 0.00},
37
+ "must_win": {"lmult_h": 1.10, "lmult_a": 0.95, "draw_boost": -0.04},
38
+ "safe": {"lmult_h": 0.88, "lmult_a": 0.88, "draw_boost": 0.06},
39
+ "knockout": {"lmult_h": 1.00, "lmult_a": 1.00, "draw_boost": 0.12},
40
+ "knockout_attack": {"lmult_h": 1.08, "lmult_a": 1.00, "draw_boost": 0.05},
41
+ }
42
+
43
+
44
+ class FootballPredictor:
45
+ """
46
+ 增强型足球比赛预测引擎 v2。
47
+
48
+ 用法:
49
+ pred = FootballPredictor()
50
+ result = pred.predict("germany", "curacao", league="wc",
51
+ tournament_context="normal")
52
+ """
53
+
54
+ def __init__(self, elo_system: Optional[EloRatingSystem] = None):
55
+ self._elo = elo_system or get_elo()
56
+
57
+ def predict(
58
+ self,
59
+ home_team: str,
60
+ away_team: str,
61
+ league: str = "default",
62
+ neutral_venue: bool = True,
63
+ form_home: Optional[List[Dict]] = None,
64
+ form_away: Optional[List[Dict]] = None,
65
+ h2h_matches: Optional[List[Dict]] = None,
66
+ historical_results: Optional[List[Tuple[int, int]]] = None,
67
+ tournament_context: str = "normal",
68
+ league_avg_override: Optional[float] = None,
69
+ home_attack_override: Optional[float] = None,
70
+ away_attack_override: Optional[float] = None,
71
+ home_defense_override: Optional[float] = None,
72
+ away_defense_override: Optional[float] = None,
73
+ ) -> Dict:
74
+ """
75
+ 主预测函数。
76
+
77
+ tournament_context:
78
+ "normal" — 小组赛正常(默认)
79
+ "must_win" — 必须赢(全力进攻)
80
+ "safe" — 已出线、可保守
81
+ "knockout" — 淘汰赛(平局→加时)
82
+ "knockout_attack" — 淘汰赛落后方
83
+ """
84
+ # ── Step 0: 基础参数 ───────────────────────────────────────────────────
85
+ league_key = league.lower().replace("-", "").replace("_", "")
86
+ league_avg = league_avg_override or _LEAGUE_AVG_GOALS.get(
87
+ league_key, _LEAGUE_AVG_GOALS["default"]
88
+ )
89
+ ctx = _CONTEXT.get(tournament_context, _CONTEXT["normal"])
90
+
91
+ # ── Step 1: Elo → 攻防基础参数(二次曲线,更陡)─────────────────────
92
+ home_stats = self._elo.get_attack_defense(home_team, league_avg)
93
+ away_stats = self._elo.get_attack_defense(away_team, league_avg)
94
+
95
+ h_attack = home_attack_override or home_stats["attack"]
96
+ a_attack = away_attack_override or away_stats["attack"]
97
+ h_defense = home_defense_override or home_stats["defense"]
98
+ a_defense = away_defense_override or away_stats["defense"]
99
+
100
+ home_elo = home_stats["elo"]
101
+ away_elo = away_stats["elo"]
102
+ elo_diff = home_elo - away_elo
103
+
104
+ # ── Step 2: 近期状态调整 ───────────────────────────────────────────────
105
+ home_form = _neutral_form_dict()
106
+ away_form = _neutral_form_dict()
107
+ form_matches_h = 0
108
+ form_matches_a = 0
109
+
110
+ if form_home:
111
+ parsed_h = parse_api_results(form_home, home_team)
112
+ if parsed_h:
113
+ home_form = analyze_form(parsed_h)
114
+ form_matches_h = home_form.get("matches_analyzed", 0)
115
+ if form_away:
116
+ parsed_a = parse_api_results(form_away, away_team)
117
+ if parsed_a:
118
+ away_form = analyze_form(parsed_a)
119
+ form_matches_a = away_form.get("matches_analyzed", 0)
120
+
121
+ h_attack *= home_form["form_factor_attack"]
122
+ a_attack *= away_form["form_factor_attack"]
123
+ h_defense *= home_form["form_factor_defense"]
124
+ a_defense *= away_form["form_factor_defense"]
125
+
126
+ # ── Step 3: 主场优势 + 赛事情境 ───────────────────────────────────────
127
+ home_adv_mult = 1.0 if neutral_venue else 1.12
128
+
129
+ # ── Step 4: 期望进球 ───────────────────────────────────────────────────
130
+ lambda_home = h_attack * a_defense * home_adv_mult * league_avg * ctx["lmult_h"]
131
+ lambda_away = a_attack * h_defense * league_avg * ctx["lmult_a"]
132
+
133
+ # H2H 微调(±8% 期望进球)
134
+ h2h_result = _neutral_h2h(home_team, away_team)
135
+ if h2h_matches:
136
+ h2h_result = analyze_h2h(h2h_matches, home_team, away_team)
137
+ h2h_adv = h2h_result.get("h2h_advantage", 0.0)
138
+ lambda_home *= (1.0 + h2h_adv)
139
+ lambda_away *= (1.0 - h2h_adv)
140
+
141
+ # ── Step 4b: 自动校准修正 ──────────────────────────────────────────────
142
+ # 全局 λ 偏差(实际进球 / 预测 λ 的历史 EMA)
143
+ # 队伍专属进球偏差(≥3 场数据才生效)
144
+ try:
145
+ from .calibrator import get_calibrated_params, get_team_goal_bias
146
+ cal = get_calibrated_params()
147
+ lambda_home *= cal.get("lambda_home_bias", 1.0)
148
+ lambda_away *= cal.get("lambda_away_bias", 1.0)
149
+ lambda_home *= get_team_goal_bias(home_team)
150
+ lambda_away *= get_team_goal_bias(away_team)
151
+ except Exception:
152
+ pass
153
+
154
+ lambda_home = max(0.20, min(lambda_home, 8.0))
155
+ lambda_away = max(0.20, min(lambda_away, 8.0))
156
+
157
+ # ── Step 5: 动态 ρ 校准 ────────────────────────────────────────────────
158
+ rho = _load_calibrated_rho()
159
+ if historical_results and len(historical_results) >= 20:
160
+ rho = estimate_rho_from_results(historical_results)
161
+
162
+ # ── Step 6: Dixon-Coles(NB 自动启用于悬殊场次)──────────────────────
163
+ dc_result = compute_match_probabilities(
164
+ lambda_home, lambda_away, rho, elo_diff=elo_diff
165
+ )
166
+
167
+ # ── Step 7: Elo 概率混合(动态权重)──────────────────────────────────
168
+ elo_probs = self._elo.win_probability(home_team, away_team, neutral_venue)
169
+
170
+ # form 数据越充足,DC 权重越高;数据稀少时 Elo 权重更保守
171
+ avg_form_matches = (form_matches_h + form_matches_a) / 2.0
172
+ w_dc = min(0.78, 0.55 + avg_form_matches * 0.04)
173
+ w_elo = 1.0 - w_dc
174
+
175
+ mix_home = dc_result["home_win"] * w_dc + elo_probs["home_win"] * w_elo
176
+ mix_draw = dc_result["draw"] * w_dc + elo_probs["draw"] * w_elo
177
+ mix_away = dc_result["away_win"] * w_dc + elo_probs["away_win"] * w_elo
178
+
179
+ # 淘汰赛平局加成(反映加时/点球场景)
180
+ draw_boost = ctx["draw_boost"]
181
+ if draw_boost != 0:
182
+ mix_draw = max(0.02, mix_draw + draw_boost)
183
+
184
+ total = mix_home + mix_draw + mix_away
185
+ mix_home /= total
186
+ mix_draw /= total
187
+ mix_away /= total
188
+
189
+ # Raw (pre-temperature) probabilities — recorded for calibration so the
190
+ # temperature optimizer never compounds an already-applied shrink.
191
+ raw_home, raw_draw, raw_away = mix_home, mix_draw, mix_away
192
+
193
+ # ── Step 8: 概率温度校准(收敛过度自信的预测)────────────────────────
194
+ try:
195
+ from .calibrator import get_confidence_temp, _apply_temp
196
+ _temp = get_confidence_temp()
197
+ if _temp != 1.0:
198
+ mix_home, mix_draw, mix_away = _apply_temp(mix_home, mix_draw, mix_away, _temp)
199
+ except Exception:
200
+ pass
201
+
202
+ def impl_odds(p: float) -> float:
203
+ return round(1.0 / p, 2) if p > 0.01 else 99.0
204
+
205
+ use_nb = abs(elo_diff) > 150
206
+ model_tag = f"Elo+DC{'(NB)' if use_nb else ''}+Form+H2H"
207
+ if draw_boost:
208
+ model_tag += f"+{tournament_context}"
209
+
210
+ return {
211
+ "home_team": home_team,
212
+ "away_team": away_team,
213
+ "home_win": round(mix_home, 4),
214
+ "draw": round(mix_draw, 4),
215
+ "away_win": round(mix_away, 4),
216
+ "raw_home_win": round(raw_home, 4),
217
+ "raw_draw": round(raw_draw, 4),
218
+ "raw_away_win": round(raw_away, 4),
219
+ "btts": dc_result["btts"],
220
+ "over_2_5": dc_result["over_2_5"],
221
+ "lambda_home": round(lambda_home, 2),
222
+ "lambda_away": round(lambda_away, 2),
223
+ "league_avg_goals": round(league_avg, 2),
224
+ "top_scorelines": dc_result["top_scorelines"],
225
+ "implied_odds": {
226
+ "home": impl_odds(mix_home),
227
+ "draw": impl_odds(mix_draw),
228
+ "away": impl_odds(mix_away),
229
+ },
230
+ "home_elo": home_elo,
231
+ "away_elo": away_elo,
232
+ "elo_diff": round(elo_diff, 0),
233
+ "home_attack": round(h_attack, 3),
234
+ "away_attack": round(a_attack, 3),
235
+ "home_defense": round(h_defense, 3),
236
+ "away_defense": round(a_defense, 3),
237
+ "rho": rho,
238
+ "dc_home_win": dc_result["home_win"],
239
+ "dc_draw": dc_result["draw"],
240
+ "dc_away_win": dc_result["away_win"],
241
+ "elo_home_win": elo_probs["home_win"],
242
+ "elo_draw": elo_probs["draw"],
243
+ "elo_away_win": elo_probs["away_win"],
244
+ "home_form": home_form.get("form_string", "?????"),
245
+ "away_form": away_form.get("form_string", "?????"),
246
+ "home_momentum": home_form.get("momentum", "stable"),
247
+ "away_momentum": away_form.get("momentum", "stable"),
248
+ "h2h_summary": h2h_result.get("summary", ""),
249
+ "h2h_advantage": h2h_adv,
250
+ "w_dc": round(w_dc, 2),
251
+ "w_elo": round(w_elo, 2),
252
+ "use_nb": use_nb,
253
+ "tournament_context": tournament_context,
254
+ "model": model_tag,
255
+ }
256
+
257
+
258
+ def _neutral_form_dict() -> Dict:
259
+ return {
260
+ "form_factor_attack": 1.0,
261
+ "form_factor_defense": 1.0,
262
+ "form_string": "?????",
263
+ "momentum": "stable",
264
+ "matches_analyzed": 0,
265
+ }
266
+
267
+
268
+ def _load_calibrated_rho() -> float:
269
+ """从 tracker 读取已校准的 ρ 值,不可用则返回默认 -0.10。"""
270
+ try:
271
+ from pathlib import Path
272
+ import json
273
+ p = aria_home() / "wc_rho.json"
274
+ if p.exists():
275
+ d = json.loads(p.read_text())
276
+ return d.get("rho", -0.10)
277
+ except Exception:
278
+ pass
279
+ return -0.10
280
+
281
+
282
+ _predictor_instance: Optional[FootballPredictor] = None
283
+
284
+
285
+ def get_predictor() -> FootballPredictor:
286
+ global _predictor_instance
287
+ if _predictor_instance is None:
288
+ _predictor_instance = FootballPredictor()
289
+ return _predictor_instance
290
+
291
+
292
+ def quick_predict(
293
+ home_team: str,
294
+ away_team: str,
295
+ league: str = "wc",
296
+ neutral_venue: bool = True,
297
+ tournament_context: str = "normal",
298
+ league_avg_override: Optional[float] = None,
299
+ ) -> Dict:
300
+ """
301
+ 一行调用接口。
302
+
303
+ 示例:
304
+ from packages.quant_engine.sports.predictor import quick_predict
305
+ r = quick_predict("germany", "ivory coast", tournament_context="must_win")
306
+ print(f"德国赢: {r['home_win']*100:.1f}%")
307
+ """
308
+ return get_predictor().predict(
309
+ home_team, away_team, league, neutral_venue,
310
+ tournament_context=tournament_context,
311
+ league_avg_override=league_avg_override,
312
+ )