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,145 @@
1
+ """
2
+ agents/engineering/tester.py — Tester & Self-Healing Agent
3
+ =========================================================
4
+ Engineering agent responsible for running fast pre-flight compilation checks,
5
+ sandbox dry-runs, traceback diagnosis, and surgical patch self-healing.
6
+ Delivers concise, high-signal results without terminal log flooding.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ import pathlib
13
+ import sys
14
+ from typing import Any, Callable, Dict, List, Optional
15
+
16
+ from ..base import BaseAgent, AgentResult
17
+ from aria_code.runtime.self_healing import SelfHealingEngine, SelfHealingResult
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class TesterAgent(BaseAgent):
23
+ __test__ = False
24
+ name = "tester"
25
+ description = "测试与自愈智能体 — 负责代码静态预检、沙箱试跑与自动诊断修复"
26
+
27
+ def __init__(
28
+ self,
29
+ llm_provider=None,
30
+ data_router=None,
31
+ on_token: Optional[Callable[[str], None]] = None,
32
+ on_thought: Optional[Callable[[str], None]] = None,
33
+ on_tool_start: Optional[Callable[[str, Dict], None]] = None,
34
+ on_tool_end: Optional[Callable[[str, Any], None]] = None,
35
+ lang: str = "zh",
36
+ max_retries: int = 3,
37
+ ) -> None:
38
+ super().__init__(
39
+ llm_provider=llm_provider,
40
+ data_router=data_router,
41
+ on_token=on_token,
42
+ on_thought=on_thought,
43
+ on_tool_start=on_tool_start,
44
+ on_tool_end=on_tool_end,
45
+ lang=lang,
46
+ )
47
+ self.engine = SelfHealingEngine(python_executable=sys.executable, max_retries=max_retries)
48
+
49
+ async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
50
+ """
51
+ Run test and self-healing on the target script.
52
+ """
53
+ script_path_str = data.get("script_path") or data.get("file_path") or ""
54
+ if not script_path_str:
55
+ # Look in generated folder
56
+ gen_path = pathlib.Path.cwd() / "generated" / f"strategy_{symbol.replace('.', '_').replace('-', '_').lower()}.py"
57
+ if gen_path.exists():
58
+ script_path_str = str(gen_path)
59
+ else:
60
+ return AgentResult(
61
+ agent=self.name,
62
+ symbol=symbol,
63
+ analysis=f"❌ 未找到待测试的代码文件: {symbol}",
64
+ confidence=0.0,
65
+ signal="HOLD",
66
+ error="Missing script_path",
67
+ )
68
+
69
+ target_file = pathlib.Path(script_path_str)
70
+ if not target_file.exists():
71
+ return AgentResult(
72
+ agent=self.name,
73
+ symbol=symbol,
74
+ analysis=f"❌ 目标文件不存在: {target_file}",
75
+ confidence=0.0,
76
+ signal="HOLD",
77
+ error=f"File not found: {target_file}",
78
+ )
79
+
80
+ # Run Self-Healing Engine
81
+ heal_result: SelfHealingResult = await self.engine.execute_and_heal(target_file)
82
+
83
+ if heal_result.success:
84
+ m = heal_result.metrics
85
+ ann_ret = m.get("annual_return_pct", m.get("total_return_pct", 0.0))
86
+ sharpe = m.get("sharpe_ratio", 0.0)
87
+ max_dd = m.get("max_drawdown_pct", 0.0)
88
+ win_rate = m.get("win_rate_pct", 0.0)
89
+ trades = m.get("total_trades", 0)
90
+
91
+ patch_msg = ""
92
+ if heal_result.patches_applied:
93
+ patch_msg = f"\n• 🛠️ 自动诊断与自愈修复: 已应用 {len(heal_result.patches_applied)} 处差量补丁 (未整文件重写)"
94
+
95
+ summary = (
96
+ f"### 🧪 回测执行验证成功\n\n"
97
+ f"| 关键回测指标 | 数值 |\n"
98
+ f"| :--- | :--- |\n"
99
+ f"| **年化收益率 (Annual Return)** | `{ann_ret}%` |\n"
100
+ f"| **夏普比率 (Sharpe Ratio)** | `{sharpe}` |\n"
101
+ f"| **最大回撤 (Max Drawdown)** | `{max_dd}%` |\n"
102
+ f"| **策略胜率 (Win Rate)** | `{win_rate}%` ({trades} 笔交易) |\n\n"
103
+ f"**产物交付路径**:\n"
104
+ f"- 策略脚本: `{target_file}`\n"
105
+ + "\n".join(f"- 输出产物: `{p}`" for p in heal_result.artifact_paths if p != str(target_file))
106
+ + patch_msg
107
+ )
108
+
109
+ signal = "STRONG_BUY" if sharpe > 1.5 and max_dd < 15.0 else ("BUY" if sharpe > 0.8 else "HOLD")
110
+
111
+ return AgentResult(
112
+ agent=self.name,
113
+ symbol=symbol,
114
+ analysis=summary,
115
+ confidence=0.92,
116
+ signal=signal,
117
+ key_points=[
118
+ f"Annual Return: {ann_ret}%",
119
+ f"Sharpe Ratio: {sharpe}",
120
+ f"Max Drawdown: {max_dd}%",
121
+ f"Win Rate: {win_rate}%",
122
+ ],
123
+ data_used={
124
+ "metrics": m,
125
+ "artifact_paths": heal_result.artifact_paths,
126
+ "patches_applied": heal_result.patches_applied,
127
+ },
128
+ )
129
+ else:
130
+ return AgentResult(
131
+ agent=self.name,
132
+ symbol=symbol,
133
+ analysis=f"❌ 测试自愈失败: {heal_result.error}",
134
+ confidence=0.0,
135
+ signal="HOLD",
136
+ error=heal_result.error,
137
+ data_used={"patches_applied": heal_result.patches_applied},
138
+ )
139
+
140
+
141
+ class TesterSelfHealingAgent(TesterAgent):
142
+ """Alias for TesterAgent."""
143
+ __test__ = False
144
+ name = "debugger"
145
+ description = "调试与自愈智能体 — 负责捕捉 Traceback 并精准修补代码"
@@ -0,0 +1,21 @@
1
+ from .technical import TechnicalAgent
2
+ from .macro import MacroAgent
3
+ from .fundamental import FundamentalAgent
4
+ from .risk import RiskAgent
5
+ from .synthesis import SynthesisAgent
6
+ from .strategist import StrategistAgent
7
+ from .corporate_finance import CorporateFinanceAgent
8
+ from .cashflow_burn import CashflowBurnRateAgent
9
+ from .stripe_revenue import StripeRevenueAgent
10
+
11
+ __all__ = [
12
+ "TechnicalAgent",
13
+ "MacroAgent",
14
+ "FundamentalAgent",
15
+ "RiskAgent",
16
+ "SynthesisAgent",
17
+ "StrategistAgent",
18
+ "CorporateFinanceAgent",
19
+ "CashflowBurnRateAgent",
20
+ "StripeRevenueAgent",
21
+ ]
@@ -0,0 +1,119 @@
1
+ """
2
+ agents/financial/cashflow_burn.py — Cash Flow, Burn Rate & Runway Analysis Agent
3
+ ==============================================================================
4
+ Monitors enterprise cash flow sustainability:
5
+ 1. Gross Burn vs. Net Burn Rate
6
+ 2. Cash Runway (Months of survival under zero revenue or current operations)
7
+ 3. Accounts Receivable Aging Breakdown & Bad Debt Risk
8
+ 4. Working Capital Liquidity Stress Testing
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ from typing import Any, Callable, Dict, List, Optional
15
+
16
+ from ..base import BaseAgent, AgentResult
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class CashflowBurnRateAgent(BaseAgent):
22
+ name = "cashflow_burn"
23
+ description = "企业现金流与烧钱率智能体 — 负责现金跑道测算、月度烧钱率与应收账龄风险审计"
24
+
25
+ def __init__(
26
+ self,
27
+ llm_provider=None,
28
+ data_router=None,
29
+ on_token: Optional[Callable[[str], None]] = None,
30
+ on_thought: Optional[Callable[[str], None]] = None,
31
+ on_tool_start: Optional[Callable[[str, Dict], None]] = None,
32
+ on_tool_end: Optional[Callable[[str, Any], None]] = None,
33
+ lang: str = "zh",
34
+ ) -> None:
35
+ super().__init__(
36
+ llm_provider=llm_provider,
37
+ data_router=data_router,
38
+ on_token=on_token,
39
+ on_thought=on_thought,
40
+ on_tool_start=on_tool_start,
41
+ on_tool_end=on_tool_end,
42
+ lang=lang,
43
+ )
44
+
45
+ async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
46
+ """
47
+ Analyze cash flow, burn rate, and runway.
48
+ """
49
+ company_name = symbol or data.get("company_name", "Enterprise")
50
+ cash = float(data.get("cash", data.get("cash_and_equivalents", 5000000.0)))
51
+ monthly_inflows = float(data.get("monthly_inflows", data.get("revenue_monthly", 800000.0)))
52
+ monthly_outflows = float(data.get("monthly_outflows", data.get("expenses_monthly", 1000000.0)))
53
+ ar_aging = data.get("ar_aging", {
54
+ "within_30_days": 1200000.0,
55
+ "days_31_60": 500000.0,
56
+ "days_61_90": 200000.0,
57
+ "over_90_days": 100000.0,
58
+ })
59
+
60
+ gross_burn = monthly_outflows
61
+ net_burn = monthly_outflows - monthly_inflows
62
+
63
+ if net_burn > 0:
64
+ runway_months = cash / net_burn
65
+ status = "CONCERN" if runway_months < 12.0 else ("SEVERE" if runway_months < 6.0 else "GOOD")
66
+ else:
67
+ runway_months = 999.0
68
+ status = "GOOD"
69
+
70
+ total_ar = sum(ar_aging.values())
71
+ overdue_ar = ar_aging.get("days_61_90", 0.0) + ar_aging.get("over_90_days", 0.0)
72
+ overdue_pct = (overdue_ar / max(1.0, total_ar)) * 100.0
73
+
74
+ runway_str = f"{runway_months:.1f} 个月" if runway_months < 100.0 else "正向自由现金流 (无需消耗现金储备)"
75
+ burn_str = f"¥{net_burn:,.2f}/月" if net_burn > 0 else f"正向结余 ¥{abs(net_burn):,.2f}/月"
76
+
77
+ analysis_text = (
78
+ f"### 💰 企业现金流与跑道 (Runway) 分析报告: {company_name}\n\n"
79
+ f"• **当前现金储备**: `¥{cash:,.2f}`\n"
80
+ f"• **月度净消耗 (Net Burn)**: `{burn_str}` (月支出 `¥{gross_burn:,.2f}`)\n"
81
+ f"• **现金可持续跑道**: `{runway_str}`\n"
82
+ f"• **应收账款总额**: `¥{total_ar:,.2f}` (逾期 >60天占比: `{overdue_pct:.1f}%`)\n\n"
83
+ f"**📊 账龄结构 (AR Aging)**:\n"
84
+ f"- 30天以内 (正常): `¥{ar_aging.get('within_30_days', 0):,.2f}`\n"
85
+ f"- 31-60天: `¥{ar_aging.get('days_31_60', 0):,.2f}`\n"
86
+ f"- 61-90天 (预警): `¥{ar_aging.get('days_61_90', 0):,.2f}`\n"
87
+ f"- >90天 (高坏账风险): `¥{ar_aging.get('over_90_days', 0):,.2f}`\n\n"
88
+ f"**💡 资金管理对策**:\n"
89
+ )
90
+
91
+ if net_burn > 0 and runway_months < 12.0:
92
+ analysis_text += f"- ⚠️ 现金跑道短于 12 个月,需严控固定支出或推进催收逾期款项 ¥{overdue_ar:,.2f}。\n"
93
+ else:
94
+ analysis_text += "- ✓ 现金储备处于安全水位,可按计划支持业务拓展。\n"
95
+
96
+ if overdue_pct > 15.0:
97
+ analysis_text += f"- ⚠️ 逾期账款占比达 {overdue_pct:.1f}%,需启动法务/催收介入以防死账坏账。"
98
+
99
+ return AgentResult(
100
+ agent=self.name,
101
+ symbol=company_name,
102
+ analysis=analysis_text,
103
+ confidence=0.93,
104
+ signal=status,
105
+ key_points=[
106
+ f"Cash Reserve: ¥{cash:,.2f}",
107
+ f"Net Burn Rate: {burn_str}",
108
+ f"Runway: {runway_str}",
109
+ f"Overdue AR (>60d): ¥{overdue_ar:,.2f} ({overdue_pct:.1f}%)",
110
+ ],
111
+ data_used={
112
+ "cash": cash,
113
+ "gross_burn": gross_burn,
114
+ "net_burn": net_burn,
115
+ "runway_months": runway_months,
116
+ "ar_aging": ar_aging,
117
+ },
118
+ provenance=["cashflow-runway-engine", "ar-aging-risk-matrix"],
119
+ )
@@ -0,0 +1,279 @@
1
+ """
2
+ agents/financial/catalyst.py — 催化剂检测 Agent
3
+ ================================================
4
+ 识别近期/即将到来的价格催化剂:财报日、股息除权、
5
+ 分析师评级变化、大宗交易/大股东增减持。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ from datetime import datetime, timedelta, timezone
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ from ..base import BaseAgent, AgentResult
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class CatalystAgent(BaseAgent):
20
+
21
+ name = "catalyst"
22
+ description = "催化剂检测 — 财报日、股息除权、分析师评级变化"
23
+
24
+ _SYSTEM = (
25
+ "You are an event-driven equity analyst. Analyze the upcoming and recent "
26
+ "catalysts for a stock: earnings dates, ex-dividend dates, analyst rating "
27
+ "changes, and insider activity. Assess whether these catalysts are "
28
+ "likely to be POSITIVE (price-driving), NEUTRAL, or NEGATIVE. "
29
+ "Focus on timing: catalysts within 14 days are high-impact. "
30
+ "Conclude with: POSITIVE / NEUTRAL / NEGATIVE"
31
+ )
32
+
33
+ async def fetch_data(self, symbol: str) -> Dict[str, Any]:
34
+ data = await super().fetch_data(symbol)
35
+ catalysts: Dict[str, Any] = {}
36
+
37
+ try:
38
+ import yfinance as yf
39
+ ticker = yf.Ticker(symbol)
40
+
41
+ # 1. Earnings calendar
42
+ try:
43
+ cal = ticker.calendar
44
+ if cal is not None:
45
+ if hasattr(cal, "to_dict"):
46
+ cal = cal.to_dict()
47
+ if isinstance(cal, dict):
48
+ for k in ("Earnings Date", "earnings_date", "earningsDate"):
49
+ v = cal.get(k)
50
+ if v:
51
+ catalysts["earnings_date"] = str(v[0] if isinstance(v, list) else v)
52
+ break
53
+ except Exception as e:
54
+ logger.debug("[catalyst] calendar fetch %s: %s", symbol, e)
55
+
56
+ # 2. Recent analyst recommendations
57
+ try:
58
+ recs = ticker.recommendations
59
+ if recs is not None and not recs.empty:
60
+ recs = recs.sort_index(ascending=False)
61
+ recent_recs = recs.head(5)
62
+ rec_list = []
63
+ for ts, row in recent_recs.iterrows():
64
+ rec_list.append({
65
+ "date": str(ts)[:10],
66
+ "firm": str(row.get("Firm", row.get("firm", ""))),
67
+ "grade": str(row.get("To Grade", row.get("toGrade", row.get("action", "")))),
68
+ "prev": str(row.get("From Grade", row.get("fromGrade", ""))),
69
+ "action": str(row.get("Action", row.get("action", ""))),
70
+ })
71
+ catalysts["recommendations"] = rec_list
72
+ except Exception as e:
73
+ logger.debug("[catalyst] recs fetch %s: %s", symbol, e)
74
+
75
+ # 3. Upcoming dividend
76
+ try:
77
+ info = ticker.info or {}
78
+ ex_div = info.get("exDividendDate")
79
+ div_rate = info.get("dividendRate") or info.get("trailingAnnualDividendRate")
80
+ if ex_div:
81
+ from datetime import date
82
+ ex_date = datetime.fromtimestamp(ex_div, tz=timezone.utc).date()
83
+ days_to_exdiv = (ex_date - date.today()).days
84
+ catalysts["ex_dividend"] = {
85
+ "date": str(ex_date),
86
+ "days_away": days_to_exdiv,
87
+ "annual_rate": div_rate,
88
+ }
89
+ except Exception as e:
90
+ logger.debug("[catalyst] dividend fetch %s: %s", symbol, e)
91
+
92
+ # 4. Short interest (proxy for contrarian catalyst)
93
+ try:
94
+ info = ticker.info or {}
95
+ short_ratio = info.get("shortRatio")
96
+ short_pct = info.get("shortPercentOfFloat")
97
+ if short_ratio or short_pct:
98
+ catalysts["short_interest"] = {
99
+ "ratio": short_ratio,
100
+ "pct_float": short_pct,
101
+ }
102
+ except Exception as e:
103
+ logger.debug("[catalyst] short interest %s: %s", symbol, e)
104
+
105
+ except Exception as e:
106
+ logger.debug("[catalyst] yfinance init %s: %s", symbol, e)
107
+
108
+ data["catalysts"] = catalysts
109
+ return data
110
+
111
+ async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
112
+ catalysts = data.get("catalysts", {})
113
+ quote = data.get("quote", {})
114
+ price = quote.get("price", 0)
115
+
116
+ if not catalysts:
117
+ return AgentResult(
118
+ agent=self.name, symbol=symbol,
119
+ analysis=f"{symbol}: 未获取到催化剂数据。",
120
+ confidence=0.3, signal="HOLD",
121
+ key_points=["无近期催化剂数据"],
122
+ )
123
+
124
+ catalyst_block = _format_catalysts(catalysts)
125
+ urgency = _assess_urgency(catalysts)
126
+
127
+ prompt = (
128
+ f"Stock: {symbol} Price: {price}\n\n"
129
+ f"Catalysts:\n{catalyst_block}\n\n"
130
+ "Evaluate:\n"
131
+ "1. Most impactful upcoming catalyst and timing\n"
132
+ "2. Analyst sentiment trend (upgrades vs downgrades)\n"
133
+ "3. Event-driven trade setup (if any)\n"
134
+ "4. Risk of negative surprise\n"
135
+ "5. Conclude: POSITIVE / NEUTRAL / NEGATIVE"
136
+ )
137
+
138
+ analysis = await self._call_llm(self._SYSTEM, prompt, max_tokens=450)
139
+ if not analysis:
140
+ analysis = _template_analysis(symbol, catalysts, urgency)
141
+
142
+ signal, confidence = _derive_signal(analysis, catalysts, urgency)
143
+ key_points = _build_key_points(catalysts, urgency)
144
+
145
+ return AgentResult(
146
+ agent=self.name, symbol=symbol,
147
+ analysis=analysis,
148
+ confidence=confidence,
149
+ signal=signal,
150
+ key_points=key_points,
151
+ data_used=catalysts,
152
+ )
153
+
154
+
155
+ # ── Helpers ───────────────────────────────────────────────────────────────────
156
+
157
+ def _format_catalysts(c: Dict) -> str:
158
+ lines = []
159
+
160
+ if "earnings_date" in c:
161
+ ed = c["earnings_date"]
162
+ try:
163
+ ed_dt = datetime.fromisoformat(str(ed).split()[0])
164
+ days = (ed_dt.date() - datetime.today().date()).days
165
+ timing = f"({days}天后)" if days >= 0 else f"({-days}天前)"
166
+ except Exception:
167
+ timing = ""
168
+ lines.append(f"财报日: {ed}{timing}")
169
+
170
+ if "ex_dividend" in c:
171
+ d = c["ex_dividend"]
172
+ days_away = d.get("days_away", 999)
173
+ rate = d.get("annual_rate", "")
174
+ rate_str = f" 年化股息 {rate:.2f}" if rate else ""
175
+ lines.append(f"除息日: {d.get('date','')}({days_away}天后){rate_str}")
176
+
177
+ if "recommendations" in c:
178
+ for r in c["recommendations"][:3]:
179
+ action = r.get("action", "").upper()
180
+ firm = r.get("firm", "")
181
+ grade = r.get("grade", "")
182
+ prev = r.get("prev", "")
183
+ change = f"{prev} → {grade}" if prev and prev != grade else grade
184
+ lines.append(f"分析师评级: [{r.get('date','')}] {firm} {action} {change}")
185
+
186
+ if "short_interest" in c:
187
+ si = c["short_interest"]
188
+ ratio = si.get("ratio")
189
+ pct = si.get("pct_float")
190
+ parts = []
191
+ if ratio: parts.append(f"空头比率 {ratio:.1f}x")
192
+ if pct: parts.append(f"流通股空仓 {pct*100:.1f}%")
193
+ if parts: lines.append(f"空头数据: {', '.join(parts)}")
194
+
195
+ return "\n".join(lines) or "无催化剂数据"
196
+
197
+
198
+ def _assess_urgency(c: Dict) -> str:
199
+ if "earnings_date" in c:
200
+ try:
201
+ ed_dt = datetime.fromisoformat(str(c["earnings_date"]).split()[0])
202
+ days = (ed_dt.date() - datetime.today().date()).days
203
+ if 0 <= days <= 7:
204
+ return "high"
205
+ if 0 <= days <= 14:
206
+ return "medium"
207
+ except Exception:
208
+ pass
209
+ exdiv = c.get("ex_dividend", {})
210
+ if 0 <= exdiv.get("days_away", 999) <= 5:
211
+ return "high"
212
+ return "low"
213
+
214
+
215
+ def _derive_signal(analysis: str, c: Dict, urgency: str) -> tuple[str, float]:
216
+ text = analysis.upper()
217
+ recs = c.get("recommendations", [])
218
+ upgrades = sum(1 for r in recs if "UPGRAD" in r.get("action", "").upper())
219
+ downgrades = sum(1 for r in recs if "DOWNGRAD" in r.get("action", "").upper())
220
+
221
+ if "POSITIVE" in text:
222
+ conf = 0.65 if urgency == "high" else 0.55
223
+ return "BUY", conf
224
+ if "NEGATIVE" in text:
225
+ conf = 0.65 if urgency == "high" else 0.55
226
+ return "SELL", conf
227
+
228
+ if upgrades > downgrades:
229
+ return "BUY", 0.5
230
+ if downgrades > upgrades:
231
+ return "SELL", 0.5
232
+ return "HOLD", 0.4
233
+
234
+
235
+ def _build_key_points(c: Dict, urgency: str) -> List[str]:
236
+ points = []
237
+ if "earnings_date" in c:
238
+ try:
239
+ ed_dt = datetime.fromisoformat(str(c["earnings_date"]).split()[0])
240
+ days = (ed_dt.date() - datetime.today().date()).days
241
+ points.append(f"财报日在 {days} 天后" if days >= 0 else f"财报 {-days} 天前已公布")
242
+ except Exception:
243
+ points.append(f"财报日: {c['earnings_date']}")
244
+ if "ex_dividend" in c:
245
+ d = c["ex_dividend"]
246
+ points.append(f"除息日 {d.get('date','')}({d.get('days_away','')}天)")
247
+ recs = c.get("recommendations", [])
248
+ if recs:
249
+ latest = recs[0]
250
+ points.append(f"最新评级: {latest.get('firm','')} {latest.get('grade','')}")
251
+ if urgency == "high":
252
+ points.append("⚡ 高优先级催化剂(7天内)")
253
+ return points[:5]
254
+
255
+
256
+ def _template_analysis(symbol: str, c: Dict, urgency: str) -> str:
257
+ parts = []
258
+ if "earnings_date" in c:
259
+ try:
260
+ ed_dt = datetime.fromisoformat(str(c["earnings_date"]).split()[0])
261
+ days = (ed_dt.date() - datetime.today().date()).days
262
+ parts.append(f"财报日在 {days} 天{'后' if days >= 0 else '前'}")
263
+ except Exception:
264
+ parts.append(f"财报日: {c['earnings_date']}")
265
+ recs = c.get("recommendations", [])
266
+ upgrades = sum(1 for r in recs if "UPGRAD" in r.get("action", "").upper())
267
+ downgrades = sum(1 for r in recs if "DOWNGRAD" in r.get("action", "").upper())
268
+ if upgrades or downgrades:
269
+ parts.append(f"近期评级: {upgrades}次升级 / {downgrades}次降级")
270
+ if "ex_dividend" in c:
271
+ d = c["ex_dividend"]
272
+ parts.append(f"除息日: {d.get('date','')}({d.get('days_away','')}天)")
273
+
274
+ sentiment = "POSITIVE" if upgrades > downgrades else ("NEGATIVE" if downgrades > upgrades else "NEUTRAL")
275
+ return (
276
+ f"{symbol} 催化剂摘要:\n"
277
+ + "\n".join(f" • {p}" for p in parts)
278
+ + f"\n结论:{sentiment}"
279
+ )