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,620 @@
1
+ """Market data and broker tools extracted from aria_cli.py.
2
+
3
+ Lazy-imports ``market_data_client`` and ``brokers`` so the module loads
4
+ even when those optional packages are absent. The import guards mirror
5
+ aria_cli.py lines 134-189 so the same failure modes apply.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import os
12
+ import sys
13
+ from datetime import datetime, timezone
14
+ from pathlib import Path
15
+
16
+ _ROOT = Path(__file__).parent.parent.parent.parent # aria-code/
17
+ if str(_ROOT) not in sys.path:
18
+ sys.path.insert(0, str(_ROOT))
19
+
20
+ from aria_code.apps.cli.market_metadata import enrich_market_quote
21
+ from aria_code.packages.aria_core.paths import aria_home
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ def _quote_time_metadata(quote: dict) -> dict[str, str]:
27
+ """Return provenance without calling an undated quote 'latest'."""
28
+ retrieved_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
29
+ for key in ("as_of", "timestamp", "quote_time", "last_trade_time", "date"):
30
+ value = quote.get(key)
31
+ if value not in (None, ""):
32
+ return {"as_of": str(value), "retrieved_at": retrieved_at,
33
+ "time_quality": "provider_timestamp"}
34
+ return {"retrieved_at": retrieved_at, "time_quality": "retrieval_time_only"}
35
+
36
+ # ── Optional: market data client ────────────────────────────────────────────
37
+ try:
38
+ from market_data_client import get_mdc as _get_mdc # noqa: E402
39
+ _HAS_MDC = True
40
+ except ImportError:
41
+ _HAS_MDC = False
42
+ _get_mdc = None # type: ignore[assignment]
43
+
44
+ # ── Unified data service (MDC primary + DataRouter fallback) ──────────────────
45
+ # DataService bridges MarketDataClient with the configured DataRouter, which
46
+ # honours ~/.aria/datasources.yaml (tushare / akshare / finnhub / fred / …).
47
+ # Lazily constructed so importing this module stays cheap.
48
+ _DATA_SERVICE = None
49
+
50
+
51
+ def _get_data_service():
52
+ global _DATA_SERVICE
53
+ if _DATA_SERVICE is None:
54
+ from data_service import DataService
55
+ _DATA_SERVICE = DataService()
56
+ return _DATA_SERVICE
57
+
58
+ # ── Optional: broker integration ────────────────────────────────────────────
59
+ try:
60
+ from brokers import ( # noqa: E402
61
+ get_registry as _get_broker_registry,
62
+ list_broker_configs as _list_broker_configs,
63
+ BROKERS_CONFIG_PATH as _BROKERS_CONFIG_PATH,
64
+ )
65
+ _HAS_BROKERS = True
66
+ except ImportError:
67
+ _HAS_BROKERS = False
68
+ def _get_broker_registry(): return None # type: ignore[return-value]
69
+ def _list_broker_configs(): return []
70
+ _BROKERS_CONFIG_PATH = None
71
+
72
+
73
+ def _get_finnhub_key() -> str:
74
+ """Read Finnhub API key from env or ~/.arthera/providers.json."""
75
+ val = os.getenv("FINNHUB_API_KEY", "")
76
+ if val:
77
+ return val
78
+ providers_file = aria_home() / "providers.json"
79
+ try:
80
+ if providers_file.exists():
81
+ raw = json.loads(providers_file.read_text(encoding="utf-8"))
82
+ for section in ("llm", "data"):
83
+ entry = raw.get(section, {}).get("finnhub", {})
84
+ if entry.get("api_key"):
85
+ return entry["api_key"]
86
+ except Exception:
87
+ pass
88
+ return ""
89
+
90
+
91
+ def _extract_symbol(params: dict) -> str:
92
+ """Robustly extract and clean a stock/crypto symbol from tool params."""
93
+ if not isinstance(params, dict):
94
+ return ""
95
+ val = params.get("symbol") or params.get("symbols") or params.get("ticker") or params.get("code") or params.get("stock")
96
+ if isinstance(val, (list, tuple)):
97
+ val = val[0] if val else ""
98
+ elif isinstance(val, dict):
99
+ val = val.get("symbol") or val.get("ticker") or val.get("code") or ""
100
+ s = str(val or "").strip()
101
+ if (s.startswith("[") and s.endswith("]")) or (s.startswith("(") and s.endswith(")")):
102
+ s = s[1:-1].strip()
103
+ return s.strip("'\"` ").upper()
104
+
105
+
106
+ def tool_get_market_data(params: dict) -> dict:
107
+ """Fetch real-time quote + technical indicators for any stock/ETF/crypto.
108
+
109
+ Supports A-shares (6-digit code), HK (.HK), US tickers, crypto.
110
+ Primary source: MarketDataClient. Fallback: Finnhub (US/global only).
111
+ """
112
+ symbol = _extract_symbol(params)
113
+ if not symbol:
114
+ return {"success": False, "error": "symbol is required"}
115
+ symbol_base = symbol.rsplit(".", 1)[0] if symbol.endswith((".SZ", ".SS", ".SH")) else symbol
116
+ is_ashare_symbol = (
117
+ symbol_base.isdigit() and len(symbol_base) == 6
118
+ ) or (
119
+ symbol_base.startswith(("SH", "SZ"))
120
+ and symbol_base[2:].isdigit()
121
+ and len(symbol_base[2:]) == 6
122
+ )
123
+
124
+ # ── 1. Quote ─────────────────────────────────────────────────────────────
125
+ quote: dict = {"success": False, "error": "market data client unavailable"}
126
+ if _HAS_MDC and _get_mdc is not None:
127
+ import time as _t
128
+ mdc = _get_mdc()
129
+ for _att in range(3):
130
+ try:
131
+ quote = mdc.quote(symbol)
132
+ if quote.get("success"):
133
+ break
134
+ _e = str(quote.get("error", "")).lower()
135
+ if ("rate" in _e or "429" in _e) and _att < 2:
136
+ _t.sleep(2 ** _att)
137
+ continue
138
+ break
139
+ except Exception as exc:
140
+ _es = str(exc).lower()
141
+ if ("rate" in _es or "429" in _es) and _att < 2:
142
+ _t.sleep(2 ** _att)
143
+ continue
144
+ _raw = str(exc)
145
+ if "Connection aborted" in _raw or "RemoteDisconnected" in _raw:
146
+ quote = {"success": False, "error": "网络连接中断,请稍后重试"}
147
+ elif "Connection refused" in _raw:
148
+ quote = {"success": False, "error": "连接被拒绝,数据服务暂时不可用"}
149
+ elif "timeout" in _raw.lower():
150
+ quote = {"success": False, "error": "连接超时,请稍后重试"}
151
+ else:
152
+ quote = {"success": False, "error": _raw}
153
+ break
154
+
155
+ # Finnhub fallback for US/global symbols
156
+ if not quote.get("success"):
157
+ _fh_key = _get_finnhub_key()
158
+ if _fh_key:
159
+ try:
160
+ import requests as _rq
161
+ _r = _rq.get(
162
+ f"https://finnhub.io/api/v1/quote?symbol={symbol}&token={_fh_key}",
163
+ timeout=6,
164
+ )
165
+ if _r.status_code == 200:
166
+ _fh = _r.json()
167
+ if _fh.get("c"):
168
+ quote = {
169
+ "success": True, "symbol": symbol,
170
+ "price": round(_fh["c"], 4),
171
+ "change_pct": round(float(_fh.get("dp") or 0), 4),
172
+ "high": round(_fh.get("h", 0), 4),
173
+ "low": round(_fh.get("l", 0), 4),
174
+ "currency": "USD", "provider": "finnhub",
175
+ }
176
+ except Exception:
177
+ pass
178
+
179
+ # Unified DataService fallback — reaches the user's configured DataRouter
180
+ # sources (tushare / akshare / finnhub / alpha_vantage / …) when both the
181
+ # native MDC chain and Finnhub failed. Additive: only runs on failure.
182
+ if not quote.get("success"):
183
+ try:
184
+ _res = _get_data_service().quote(symbol)
185
+ if _res.success and _res.data:
186
+ quote = dict(_res.data)
187
+ quote.setdefault("success", True)
188
+ except Exception:
189
+ pass
190
+
191
+ if not quote.get("success"):
192
+ return {
193
+ "success": False,
194
+ "symbol": symbol,
195
+ "market": "CN" if is_ashare_symbol else "GLOBAL",
196
+ "provider_chain": quote.get("provider_chain") or (
197
+ ["eastmoney", "akshare", "yfinance"] if is_ashare_symbol
198
+ else ["yfinance", "finnhub"]
199
+ ),
200
+ "error": quote.get("error") or "行情数据源暂时不可用,请稍后重试。",
201
+ }
202
+
203
+ quote = enrich_market_quote(symbol, quote)
204
+ result = {
205
+ "success": True,
206
+ "symbol": symbol,
207
+ "name": quote.get("name") or symbol,
208
+ "price": quote.get("price"),
209
+ "change_pct": quote.get("change_pct"),
210
+ "high": quote.get("high"),
211
+ "low": quote.get("low"),
212
+ "volume": quote.get("volume"),
213
+ "market_cap": quote.get("market_cap"),
214
+ "currency": quote.get("currency") or "USD",
215
+ "provider": quote.get("provider") or "market_data_client",
216
+ "provider_chain": quote.get("provider_chain") or [
217
+ quote.get("provider") or "market_data_client"
218
+ ],
219
+ }
220
+ result.update(_quote_time_metadata(quote))
221
+
222
+ # ── 2. Technical indicators ───────────────────────────────────────────────
223
+ ti: dict = {}
224
+ if _HAS_MDC and _get_mdc is not None:
225
+ try:
226
+ ti = mdc.technical_indicators(symbol, days=120) or {} # type: ignore[name-defined]
227
+ except Exception:
228
+ ti = {}
229
+
230
+ if (not ti.get("success") or ti.get("rsi") is None) and not is_ashare_symbol:
231
+ import yfinance as _yf
232
+ import numpy as _np
233
+ from datetime import date as _date, timedelta as _td
234
+ _yf_sym = symbol
235
+ if symbol.isdigit() and len(symbol) == 6:
236
+ _yf_sym = symbol + (".SS" if symbol.startswith("6") else ".SZ")
237
+
238
+ def _compute_ta(df) -> dict:
239
+ _c = df["Close"] if "Close" in df.columns else df.iloc[:, 0]
240
+ _v = df["Volume"] if "Volume" in df.columns else None
241
+ _d = _c.diff()
242
+ _g = _d.clip(lower=0).rolling(14).mean()
243
+ _l = (-_d.clip(upper=0)).rolling(14).mean()
244
+ _rsi_val = float((100 - 100 / (1 + _g / _l.replace(0, _np.nan))).iloc[-1])
245
+ _ema12 = _c.ewm(span=12).mean()
246
+ _ema26 = _c.ewm(span=26).mean()
247
+ _macd = _ema12 - _ema26
248
+ _mhist = float((_macd - _macd.ewm(span=9).mean()).iloc[-1])
249
+ _ma20 = _c.rolling(20).mean()
250
+ _std20 = _c.rolling(20).std()
251
+ _ma60 = _c.rolling(60).mean() if len(_c) >= 60 else _ma20
252
+ r = {
253
+ "success": True,
254
+ "rsi": round(_rsi_val, 2) if not _np.isnan(_rsi_val) else None,
255
+ "macd_hist": round(_mhist, 4),
256
+ "ma20": round(float(_ma20.iloc[-1]), 2),
257
+ "ma60": round(float(_ma60.iloc[-1]), 2),
258
+ "bb_upper": round(float((_ma20 + 2 * _std20).iloc[-1]), 2),
259
+ "bb_lower": round(float((_ma20 - 2 * _std20).iloc[-1]), 2),
260
+ }
261
+ if _v is not None and result.get("volume") is None:
262
+ _rv = _v.iloc[-1]
263
+ if not _np.isnan(_rv):
264
+ result["volume"] = int(_rv)
265
+ return r
266
+
267
+ _df_ta = None
268
+ try:
269
+ _df_ta = _yf.Ticker(_yf_sym).history(period="6mo", auto_adjust=True)
270
+ if _df_ta.empty:
271
+ _df_ta = None
272
+ except Exception:
273
+ _df_ta = None
274
+
275
+ if _df_ta is None or len(_df_ta) < 20:
276
+ try:
277
+ _start = (_date.today() - _td(days=185)).isoformat()
278
+ _df_ta = _yf.download(
279
+ _yf_sym, start=_start, auto_adjust=True, progress=False, timeout=15
280
+ )
281
+ if hasattr(_df_ta.columns, "levels") and len(_df_ta.columns.levels) > 1:
282
+ _df_ta.columns = _df_ta.columns.droplevel(1)
283
+ if _df_ta.empty:
284
+ _df_ta = None
285
+ except Exception:
286
+ _df_ta = None
287
+
288
+ if _df_ta is not None and len(_df_ta) >= 20:
289
+ try:
290
+ ti = _compute_ta(_df_ta)
291
+ except Exception:
292
+ pass
293
+
294
+ if ti.get("success"):
295
+ for _k in ("rsi", "macd_hist", "ma20", "ma60", "bb_upper", "bb_lower"):
296
+ if ti.get(_k) is not None:
297
+ result[_k] = ti[_k]
298
+
299
+ return result
300
+
301
+
302
+ def tool_get_market_history(params: dict) -> dict:
303
+ """Fetch OHLC price history for any stock/ETF/index/crypto.
304
+
305
+ Returns a *compact* summary (period stats + the most recent candles), NOT
306
+ the full series, so the model can reason about trends without flooding the
307
+ context window. Routes through the unified DataService: MarketDataClient
308
+ first (Tushare for A-shares when configured, then Eastmoney/Sina/AKShare,
309
+ yfinance for HK/US/global), then the configured DataRouter as fallback.
310
+
311
+ Use this instead of writing ad-hoc akshare/tushare scripts: it handles
312
+ symbol normalisation, source fallback, and is environment-independent.
313
+ """
314
+ symbol = _extract_symbol(params)
315
+ if not symbol:
316
+ return {"success": False, "error": "symbol is required"}
317
+ try:
318
+ days = int(params.get("days", 120) or 120)
319
+ except (TypeError, ValueError):
320
+ days = 120
321
+ days = max(5, min(days, 1000))
322
+ interval = str(params.get("interval", "1d") or "1d").lower()
323
+
324
+ # Prefer the unified DataService (MDC + DataRouter fallback, honours the
325
+ # user's datasources.yaml). Fall back to MDC directly if it can't load.
326
+ hist = None
327
+ chain = None
328
+ err = None
329
+ try:
330
+ res = _get_data_service().history(symbol, days=days, interval=interval)
331
+ chain = res.provider_chain
332
+ if res.errors:
333
+ err = res.errors[0]
334
+ if (res.data or {}).get("data"):
335
+ hist = res.data
336
+ except Exception as exc:
337
+ err = str(exc)
338
+ if _HAS_MDC and _get_mdc is not None:
339
+ try:
340
+ _h = _get_mdc().history(symbol, days=days, interval=interval)
341
+ if _h.get("data"):
342
+ hist = _h
343
+ chain = _h.get("provider_chain")
344
+ else:
345
+ err = _h.get("error") or err
346
+ except Exception as exc2:
347
+ err = str(exc2)
348
+
349
+ if not hist or not hist.get("data"):
350
+ return {
351
+ "success": False,
352
+ "symbol": symbol,
353
+ "provider_chain": chain or ["eastmoney", "sina", "akshare", "yfinance"],
354
+ "error": err or "历史行情数据源暂时不可用,请稍后重试。",
355
+ }
356
+
357
+ rows = hist["data"] # ascending by date: [{date,open,high,low,close,volume},...]
358
+ closes = [r["close"] for r in rows if r.get("close")]
359
+ if not closes:
360
+ return {"success": False, "symbol": symbol, "error": "history contained no usable closes"}
361
+
362
+ def _ma(n: int):
363
+ return round(sum(closes[-n:]) / n, 4) if len(closes) >= n else None
364
+
365
+ start_close = closes[0]
366
+ end_close = closes[-1]
367
+ period_high = max(r["high"] for r in rows if r.get("high"))
368
+ period_low = min(r["low"] for r in rows if r.get("low"))
369
+ vols = [r["volume"] for r in rows if r.get("volume")]
370
+
371
+ # Only the most recent candles travel back to the model — the rest is
372
+ # summarised. Keeps the payload small regardless of `days`.
373
+ recent_n = min(30, len(rows))
374
+ return {
375
+ "success": True,
376
+ "symbol": hist.get("symbol") or symbol,
377
+ "name": hist.get("name") or symbol,
378
+ "provider": hist.get("provider"),
379
+ "interval": interval,
380
+ "total_points": len(rows),
381
+ "summary": {
382
+ "start_date": rows[0].get("date"),
383
+ "end_date": rows[-1].get("date"),
384
+ "start_close": round(start_close, 4),
385
+ "end_close": round(end_close, 4),
386
+ "change_pct": round((end_close - start_close) / start_close * 100, 2) if start_close else None,
387
+ "period_high": round(period_high, 4),
388
+ "period_low": round(period_low, 4),
389
+ "avg_volume": int(sum(vols) / len(vols)) if vols else None,
390
+ "ma5": _ma(5),
391
+ "ma20": _ma(20),
392
+ "ma60": _ma(60),
393
+ },
394
+ "recent_candles": rows[-recent_n:],
395
+ }
396
+
397
+
398
+ def tool_broker_query(params: dict) -> dict:
399
+ """Query a connected broker account (read-only): balance, positions, orders."""
400
+ if not _HAS_BROKERS:
401
+ return {"success": False, "error": "brokers 模块未加载,请确认 brokers/ 目录存在"}
402
+
403
+ query = str(params.get("query", "positions")).lower()
404
+ bid = params.get("broker_id", "")
405
+
406
+ try:
407
+ reg = _get_broker_registry()
408
+ if bid:
409
+ broker = reg.get(bid)
410
+ if not broker:
411
+ broker = reg.connect(bid)
412
+ else:
413
+ broker = reg.active()
414
+ if not broker:
415
+ broker = reg.connect_default()
416
+ if not broker:
417
+ cfgs = _list_broker_configs()
418
+ if not cfgs:
419
+ return {
420
+ "success": False,
421
+ "error": (
422
+ "尚未配置任何券商。\n"
423
+ f"请编辑 {_BROKERS_CONFIG_PATH} 或使用 /broker add <type> 命令。"
424
+ ),
425
+ }
426
+ return {
427
+ "success": False,
428
+ "error": "没有已连接的券商。请先运行 /broker connect <id>。",
429
+ }
430
+
431
+ if "account" in query or "balance" in query or "资金" in query or "余额" in query:
432
+ acct = broker.account_info()
433
+ return {
434
+ "success": True, "query": "account",
435
+ "broker": broker.label, "broker_type": broker.broker_type,
436
+ "account_id": acct.masked_account, "currency": acct.currency,
437
+ "total_assets": acct.total_assets, "cash": acct.cash,
438
+ "market_value": acct.market_value, "frozen": acct.frozen,
439
+ "pnl_today": acct.pnl_today, "pnl_total": acct.pnl_total,
440
+ }
441
+
442
+ if "position" in query or "持仓" in query or "portfolio" in query:
443
+ positions = broker.positions()
444
+ return {
445
+ "success": True, "query": "positions",
446
+ "broker": broker.label, "count": len(positions),
447
+ "positions": [
448
+ {
449
+ "symbol": p.symbol, "name": p.name,
450
+ "quantity": p.quantity, "available": p.available_qty,
451
+ "cost": p.cost_price, "price": p.current_price,
452
+ "market_value": p.market_value,
453
+ "pnl": p.pnl, "pnl_pct": round(p.pnl_pct, 2),
454
+ "currency": p.currency,
455
+ }
456
+ for p in positions
457
+ ],
458
+ }
459
+
460
+ if "order" in query or "订单" in query or "委托" in query:
461
+ status = params.get("status", "all")
462
+ orders = broker.orders(status=status, limit=int(params.get("limit", 20)))
463
+ return {
464
+ "success": True, "query": "orders",
465
+ "broker": broker.label, "status_filter": status,
466
+ "count": len(orders),
467
+ "orders": [
468
+ {
469
+ "order_id": o.order_id, "symbol": o.symbol, "name": o.name,
470
+ "side": o.side, "type": o.order_type,
471
+ "quantity": o.quantity, "filled": o.filled_qty,
472
+ "price": o.price, "avg_price": o.avg_price,
473
+ "status": o.status, "time": o.created_at,
474
+ "currency": o.currency,
475
+ }
476
+ for o in orders
477
+ ],
478
+ }
479
+
480
+ # Default: positions
481
+ positions = broker.positions()
482
+ return {
483
+ "success": True, "query": "positions",
484
+ "broker": broker.label, "count": len(positions),
485
+ "positions": [
486
+ {
487
+ "symbol": p.symbol, "name": p.name,
488
+ "quantity": p.quantity, "market_value": p.market_value,
489
+ "pnl": p.pnl, "pnl_pct": round(p.pnl_pct, 2),
490
+ }
491
+ for p in positions
492
+ ],
493
+ }
494
+
495
+ except Exception as e:
496
+ return {"success": False, "error": str(e)}
497
+
498
+
499
+ def tool_broker_order(params: dict) -> dict:
500
+ """Propose or execute a previewed order through the trading service layer."""
501
+ if not _HAS_BROKERS:
502
+ return {"success": False, "error": "brokers 模块未加载"}
503
+
504
+ symbol = str(params.get("symbol", "")).strip().upper()
505
+ side = str(params.get("side", "")).lower()
506
+ qty = params.get("quantity") or params.get("qty")
507
+ price = params.get("price")
508
+ order_type = str(params.get("order_type", "limit")).lower()
509
+ confirmed = bool(params.get("confirmed", False))
510
+ preview_id = str(params.get("preview_id", "") or params.get("order_preview_id", "")).strip()
511
+ target_weight = params.get("target_weight")
512
+
513
+ try:
514
+ reg = _get_broker_registry()
515
+ broker = reg.active()
516
+ if not broker:
517
+ broker = reg.connect_default()
518
+ except Exception as _e:
519
+ logger.debug("broker lookup failed: %s", _e)
520
+ broker = None
521
+
522
+ if not broker:
523
+ return {"success": False, "error": "无已连接账户,请先 /broker connect"}
524
+
525
+ if confirmed:
526
+ if not preview_id:
527
+ return {
528
+ "success": False,
529
+ "confirmation_required": True,
530
+ "error": "confirmed=true 需要 preview_id;请先生成订单预览。",
531
+ }
532
+ try:
533
+ from brokers import execute_order_preview
534
+ return execute_order_preview(broker, preview_id, confirmed=True)
535
+ except Exception as e:
536
+ return {"success": False, "error": str(e)}
537
+
538
+ if not symbol:
539
+ return {"success": False, "error": "symbol 是必填项"}
540
+ if side not in ("buy", "sell"):
541
+ return {"success": False, "error": "side 必须是 'buy' 或 'sell'"}
542
+ if not qty:
543
+ return {"success": False, "error": "quantity 是必填项"}
544
+ try:
545
+ qty = float(qty)
546
+ if qty <= 0:
547
+ raise ValueError
548
+ except (ValueError, TypeError):
549
+ return {"success": False, "error": "quantity 必须是正数"}
550
+
551
+ try:
552
+ from brokers import OrderIntent, build_order_preview
553
+ preview = build_order_preview(
554
+ broker,
555
+ OrderIntent(
556
+ symbol=symbol,
557
+ side=side,
558
+ quantity=qty,
559
+ price=float(price) if price is not None else None,
560
+ order_type=order_type,
561
+ target_weight=float(target_weight) if target_weight is not None else None,
562
+ source=str(params.get("source", "order_preview") or "order_preview"),
563
+ user_message=str(params.get("user_message", "") or ""),
564
+ metadata={"tool": "broker_order"},
565
+ ),
566
+ )
567
+ except Exception as e:
568
+ return {"success": False, "error": str(e)}
569
+
570
+ plan_data = preview.get("order_plan")
571
+ plan_message = ""
572
+ risk = (plan_data or {}).get("risk", {})
573
+ if risk.get("violations"):
574
+ plan_message = "\n".join(f" - {v}" for v in risk.get("violations", []))
575
+ elif risk.get("warnings"):
576
+ plan_message = "\n".join(f" - {w}" for w in risk.get("warnings", []))
577
+ blockers = preview.get("execution_blockers") or []
578
+ if blockers:
579
+ plan_message = "\n".join(f" - {v}" for v in blockers)
580
+
581
+ if plan_data and plan_data.get("risk", {}).get("violations"):
582
+ return {
583
+ "success": False, "risk_rejected": True,
584
+ "order_plan": plan_data,
585
+ "preview_id": preview.get("preview_id"),
586
+ "trade_preview": preview,
587
+ "message": "订单计划未通过风控:\n" + plan_message,
588
+ }
589
+
590
+ _price_str = f"{float(price):.2f}" if price is not None else "市价"
591
+ _side_cn = "买入" if side == "buy" else "卖出"
592
+ _risk_note = ""
593
+ if plan_data:
594
+ if risk.get("warnings"):
595
+ _risk_note = "\n\n风控提示:\n" + plan_message
596
+ elif blockers:
597
+ _risk_note = "\n\n执行限制:\n" + plan_message
598
+ return {
599
+ "success": False,
600
+ "confirmation_required": True,
601
+ "preview_id": preview.get("preview_id"),
602
+ "trade_preview": preview,
603
+ "order_plan": plan_data,
604
+ "order_preview": {
605
+ "preview_id": preview.get("preview_id"),
606
+ "mode": preview.get("mode"),
607
+ "broker": preview.get("broker_label"),
608
+ "symbol": symbol, "side": side, "side_cn": _side_cn,
609
+ "qty": qty, "price": price, "price_display": _price_str,
610
+ "order_type": order_type,
611
+ "can_execute": preview.get("can_execute"),
612
+ },
613
+ "message": (
614
+ f"⚠️ 请确认以下订单预览 `{preview.get('preview_id')}`:\n"
615
+ f" 模式: {preview.get('mode')} 券商: {preview.get('broker_label')}\n"
616
+ f" {_side_cn} **{symbol}** 数量: {qty:,.4g} 价格: {_price_str}\n\n"
617
+ "确认时必须携带 preview_id;其他任何回复取消。"
618
+ f"{_risk_note}"
619
+ ),
620
+ }