aria-code 0.167.0__py3-none-win_amd64.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 (601) hide show
  1. aria_bots/__init__.py +7 -0
  2. aria_bots/channels/README.md +97 -0
  3. aria_bots/channels/__init__.py +1 -0
  4. aria_bots/channels/approvals.py +263 -0
  5. aria_bots/channels/conversation.py +280 -0
  6. aria_bots/channels/digest.py +186 -0
  7. aria_bots/channels/registry.py +95 -0
  8. aria_bots/engine.py +144 -0
  9. aria_bots/feishu_bot.py +1385 -0
  10. aria_bots/logistics/__init__.py +1 -0
  11. aria_bots/logistics/carriers.py +253 -0
  12. aria_bots/logistics/freight.py +154 -0
  13. aria_bots/logistics/inventory.py +242 -0
  14. aria_bots/logistics/mcp_server.py +156 -0
  15. aria_bots/logistics/records.py +119 -0
  16. aria_bots/logistics/tenancy.py +152 -0
  17. aria_bots/market.py +91 -0
  18. aria_bots/relay_client.py +326 -0
  19. aria_bots/telegram_bot.py +299 -0
  20. aria_code/__init__.py +111 -0
  21. aria_code/_bin/aria-native.exe +0 -0
  22. aria_code/_bin/aria-runtime.exe +0 -0
  23. aria_code/_version.py +20 -0
  24. aria_code/account_memory.py +296 -0
  25. aria_code/account_session.py +166 -0
  26. aria_code/adk_apps/__init__.py +1 -0
  27. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  28. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  29. aria_code/adk_apps/aria_research/__init__.py +1 -0
  30. aria_code/adk_apps/aria_research/agent.py +31 -0
  31. aria_code/agents/__init__.py +49 -0
  32. aria_code/agents/base.py +329 -0
  33. aria_code/agents/code_review.py +200 -0
  34. aria_code/agents/deep/__init__.py +37 -0
  35. aria_code/agents/deep/calibration_loop.py +144 -0
  36. aria_code/agents/deep/critic.py +125 -0
  37. aria_code/agents/deep/deepen.py +193 -0
  38. aria_code/agents/deep/models.py +149 -0
  39. aria_code/agents/deep/pipeline.py +164 -0
  40. aria_code/agents/deep/quant_fusion.py +192 -0
  41. aria_code/agents/deep/themes.py +95 -0
  42. aria_code/agents/deep/tiers.py +106 -0
  43. aria_code/agents/engineering/__init__.py +14 -0
  44. aria_code/agents/engineering/coder.py +222 -0
  45. aria_code/agents/engineering/tester.py +145 -0
  46. aria_code/agents/financial/__init__.py +21 -0
  47. aria_code/agents/financial/cashflow_burn.py +119 -0
  48. aria_code/agents/financial/catalyst.py +279 -0
  49. aria_code/agents/financial/corporate_finance.py +175 -0
  50. aria_code/agents/financial/debate.py +224 -0
  51. aria_code/agents/financial/earnings.py +315 -0
  52. aria_code/agents/financial/fundamental.py +178 -0
  53. aria_code/agents/financial/macro.py +111 -0
  54. aria_code/agents/financial/news.py +231 -0
  55. aria_code/agents/financial/risk.py +146 -0
  56. aria_code/agents/financial/sector.py +279 -0
  57. aria_code/agents/financial/strategist.py +121 -0
  58. aria_code/agents/financial/stripe_revenue.py +127 -0
  59. aria_code/agents/financial/synthesis.py +274 -0
  60. aria_code/agents/financial/technical.py +277 -0
  61. aria_code/agents/orchestrator.py +44 -0
  62. aria_code/agents/portfolio_agent.py +392 -0
  63. aria_code/agents/realty/__init__.py +62 -0
  64. aria_code/agents/realty/asset_diagnosis.py +152 -0
  65. aria_code/agents/realty/business_match.py +166 -0
  66. aria_code/agents/realty/cashflow_verify.py +209 -0
  67. aria_code/agents/realty/contract_rules.py +210 -0
  68. aria_code/agents/realty/energy_anomaly.py +189 -0
  69. aria_code/agents/realty/exit_settlement.py +208 -0
  70. aria_code/agents/realty/fulfillment_risk.py +206 -0
  71. aria_code/agents/realty/ops_optimize.py +160 -0
  72. aria_code/agents/realty/revenue_share.py +215 -0
  73. aria_code/agents/registry.py +168 -0
  74. aria_code/agents/signal_scheme.py +111 -0
  75. aria_code/agents/sports/__init__.py +0 -0
  76. aria_code/agents/sports/football_agent.py +197 -0
  77. aria_code/agents/supervisor.py +113 -0
  78. aria_code/agents/team.py +500 -0
  79. aria_code/agents/video/__init__.py +16 -0
  80. aria_code/agents/video/director.py +376 -0
  81. aria_code/agents/video/qa_agent.py +143 -0
  82. aria_code/agents/video/script_doctor.py +112 -0
  83. aria_code/agents/warehouse/__init__.py +25 -0
  84. aria_code/agents/warehouse/contracts.py +26 -0
  85. aria_code/agents/warehouse/dashboard.py +155 -0
  86. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  87. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  88. aria_code/agents/warehouse/inventory_health.py +24 -0
  89. aria_code/agents/warehouse/local_run.py +71 -0
  90. aria_code/agents/warehouse/local_snapshot.py +132 -0
  91. aria_code/agents/warehouse/logistics_cost.py +84 -0
  92. aria_code/agents/warehouse/logistics_sync.py +21 -0
  93. aria_code/agents/warehouse/navigation.schema.json +110 -0
  94. aria_code/agents/warehouse/task_agent.py +172 -0
  95. aria_code/agents/warehouse/workflow.py +31 -0
  96. aria_code/aliyun_data_client.py +4 -0
  97. aria_code/apps/README.md +12 -0
  98. aria_code/apps/__init__.py +2 -0
  99. aria_code/apps/channels/README.md +9 -0
  100. aria_code/apps/channels/__init__.py +17 -0
  101. aria_code/apps/channels/approvals.py +11 -0
  102. aria_code/apps/channels/conversation.py +11 -0
  103. aria_code/apps/channels/digest.py +11 -0
  104. aria_code/apps/channels/intake.py +92 -0
  105. aria_code/apps/channels/registry.py +11 -0
  106. aria_code/apps/channels/tradingview.py +94 -0
  107. aria_code/apps/cli/README.md +13 -0
  108. aria_code/apps/cli/__init__.py +2 -0
  109. aria_code/apps/cli/_optional.py +120 -0
  110. aria_code/apps/cli/app_server.py +656 -0
  111. aria_code/apps/cli/auto_update.py +125 -0
  112. aria_code/apps/cli/bootstrap.py +206 -0
  113. aria_code/apps/cli/broker_render.py +206 -0
  114. aria_code/apps/cli/chat_turn.py +1327 -0
  115. aria_code/apps/cli/cloud_models.py +231 -0
  116. aria_code/apps/cli/codegen_paths.py +29 -0
  117. aria_code/apps/cli/commands/__init__.py +34 -0
  118. aria_code/apps/cli/commands/_business_render.py +78 -0
  119. aria_code/apps/cli/commands/_ui.py +71 -0
  120. aria_code/apps/cli/commands/analysis_cmds.py +378 -0
  121. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  122. aria_code/apps/cli/commands/auth_cmds.py +215 -0
  123. aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
  124. aria_code/apps/cli/commands/broker_cmds.py +1236 -0
  125. aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
  126. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  127. aria_code/apps/cli/commands/catalog.py +189 -0
  128. aria_code/apps/cli/commands/core_cmds.py +2748 -0
  129. aria_code/apps/cli/commands/data_cmds.py +521 -0
  130. aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
  131. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  132. aria_code/apps/cli/commands/engineering_cmds.py +399 -0
  133. aria_code/apps/cli/commands/file_cmds.py +312 -0
  134. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  135. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  136. aria_code/apps/cli/commands/market.py +544 -0
  137. aria_code/apps/cli/commands/market_cmds.py +1419 -0
  138. aria_code/apps/cli/commands/market_context.py +432 -0
  139. aria_code/apps/cli/commands/market_render.py +7 -0
  140. aria_code/apps/cli/commands/model_cmds.py +2374 -0
  141. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  142. aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
  143. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  144. aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
  145. aria_code/apps/cli/commands/report.py +476 -0
  146. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  147. aria_code/apps/cli/commands/session_cmds.py +236 -0
  148. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  149. aria_code/apps/cli/commands/team.py +723 -0
  150. aria_code/apps/cli/commands/team_render.py +8 -0
  151. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  152. aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
  153. aria_code/apps/cli/commands/workflow_cmds.py +629 -0
  154. aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
  155. aria_code/apps/cli/config_paths.py +73 -0
  156. aria_code/apps/cli/config_store.py +158 -0
  157. aria_code/apps/cli/constants.py +88 -0
  158. aria_code/apps/cli/context.py +34 -0
  159. aria_code/apps/cli/conversation_policy.py +53 -0
  160. aria_code/apps/cli/deterministic.py +200 -0
  161. aria_code/apps/cli/direct.py +48 -0
  162. aria_code/apps/cli/earnings_view.py +172 -0
  163. aria_code/apps/cli/endpoint_migration.py +64 -0
  164. aria_code/apps/cli/exec_events.py +255 -0
  165. aria_code/apps/cli/file_references.py +155 -0
  166. aria_code/apps/cli/finance_service_catalog.py +111 -0
  167. aria_code/apps/cli/football_reports.py +231 -0
  168. aria_code/apps/cli/github_app_auth.py +135 -0
  169. aria_code/apps/cli/google_login.py +239 -0
  170. aria_code/apps/cli/handlers/__init__.py +11 -0
  171. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  172. aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
  173. aria_code/apps/cli/handlers/market_handlers.py +2570 -0
  174. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  175. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  176. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  177. aria_code/apps/cli/headless.py +352 -0
  178. aria_code/apps/cli/helpers.py +326 -0
  179. aria_code/apps/cli/hooks.py +180 -0
  180. aria_code/apps/cli/i18n.py +310 -0
  181. aria_code/apps/cli/intent.py +136 -0
  182. aria_code/apps/cli/intent_router.py +305 -0
  183. aria_code/apps/cli/intent_shadow.py +72 -0
  184. aria_code/apps/cli/intent_signals.py +347 -0
  185. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  186. aria_code/apps/cli/local_tool_schemas.py +550 -0
  187. aria_code/apps/cli/main.py +39 -0
  188. aria_code/apps/cli/market_hours.py +100 -0
  189. aria_code/apps/cli/market_metadata.py +135 -0
  190. aria_code/apps/cli/market_universe.py +487 -0
  191. aria_code/apps/cli/message_processing.py +310 -0
  192. aria_code/apps/cli/mixin_binding.py +32 -0
  193. aria_code/apps/cli/model_catalog.py +652 -0
  194. aria_code/apps/cli/model_probe.py +169 -0
  195. aria_code/apps/cli/model_retirement.py +65 -0
  196. aria_code/apps/cli/native_branding.py +12 -0
  197. aria_code/apps/cli/native_bridge.py +118 -0
  198. aria_code/apps/cli/native_entry.py +58 -0
  199. aria_code/apps/cli/pdf_report.py +977 -0
  200. aria_code/apps/cli/plan_mode.py +139 -0
  201. aria_code/apps/cli/plotly_html.py +15 -0
  202. aria_code/apps/cli/prediction_feedback.py +202 -0
  203. aria_code/apps/cli/preflight.py +502 -0
  204. aria_code/apps/cli/project_aria.py +60 -0
  205. aria_code/apps/cli/prompt_assembly.py +78 -0
  206. aria_code/apps/cli/prompts/__init__.py +0 -0
  207. aria_code/apps/cli/prompts/coding.py +71 -0
  208. aria_code/apps/cli/prompts/select.py +193 -0
  209. aria_code/apps/cli/prompts/system_prompts.py +562 -0
  210. aria_code/apps/cli/prompts/ui.py +436 -0
  211. aria_code/apps/cli/provider_endpoints.py +237 -0
  212. aria_code/apps/cli/provider_keys.py +73 -0
  213. aria_code/apps/cli/providers/__init__.py +1 -0
  214. aria_code/apps/cli/providers/base.py +775 -0
  215. aria_code/apps/cli/providers/chat_routing.py +139 -0
  216. aria_code/apps/cli/providers/collaboration.py +98 -0
  217. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  218. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  219. aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
  220. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  221. aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
  222. aria_code/apps/cli/providers/runtime_bridge.py +586 -0
  223. aria_code/apps/cli/providers/vertexai_stream.py +592 -0
  224. aria_code/apps/cli/release_download.py +159 -0
  225. aria_code/apps/cli/review_runner.py +111 -0
  226. aria_code/apps/cli/runtime_consumer.py +769 -0
  227. aria_code/apps/cli/sandbox.py +32 -0
  228. aria_code/apps/cli/session_export.py +120 -0
  229. aria_code/apps/cli/session_jsonl.py +208 -0
  230. aria_code/apps/cli/session_refresh.py +123 -0
  231. aria_code/apps/cli/session_store.py +125 -0
  232. aria_code/apps/cli/skills_catalog.py +273 -0
  233. aria_code/apps/cli/task_isolation.py +211 -0
  234. aria_code/apps/cli/terminal_chrome.py +412 -0
  235. aria_code/apps/cli/todo_tracker.py +190 -0
  236. aria_code/apps/cli/tool_executor.py +932 -0
  237. aria_code/apps/cli/tool_registry.py +34 -0
  238. aria_code/apps/cli/tool_scope.py +113 -0
  239. aria_code/apps/cli/tools/__init__.py +40 -0
  240. aria_code/apps/cli/tools/context.py +46 -0
  241. aria_code/apps/cli/tools/file_tools.py +181 -0
  242. aria_code/apps/cli/tools/market_tools.py +626 -0
  243. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  244. aria_code/apps/cli/tools/system_tools.py +656 -0
  245. aria_code/apps/cli/tools/write_tools.py +961 -0
  246. aria_code/apps/cli/tradingview_bridge.py +529 -0
  247. aria_code/apps/cli/transactions.py +269 -0
  248. aria_code/apps/cli/turn_planning.py +137 -0
  249. aria_code/apps/cli/update_check.py +257 -0
  250. aria_code/apps/cli/updater.py +373 -0
  251. aria_code/apps/cli/utils/__init__.py +0 -0
  252. aria_code/apps/cli/utils/market_detect.py +1663 -0
  253. aria_code/apps/cli/web_login.py +136 -0
  254. aria_code/apps/cli/workflow_runner.py +127 -0
  255. aria_code/apps/cli/workspace_route.py +121 -0
  256. aria_code/apps/daemon/README.md +14 -0
  257. aria_code/apps/vscode/README.md +115 -0
  258. aria_code/apps/vscode/package.json +70 -0
  259. aria_code/aria_cli.py +5244 -0
  260. aria_code/aria_daemon.py +1372 -0
  261. aria_code/aria_feishu_bot.py +16 -0
  262. aria_code/aria_mcp_server.py +61 -0
  263. aria_code/aria_relay_client.py +4 -0
  264. aria_code/aria_telegram_bot.py +11 -0
  265. aria_code/ariarc.py +385 -0
  266. aria_code/artifacts.py +581 -0
  267. aria_code/backtest_engine.py +4 -0
  268. aria_code/backtest_report.py +4 -0
  269. aria_code/brokers/__init__.py +78 -0
  270. aria_code/brokers/automation.py +193 -0
  271. aria_code/brokers/base.py +231 -0
  272. aria_code/brokers/capabilities.py +264 -0
  273. aria_code/brokers/cn/__init__.py +10 -0
  274. aria_code/brokers/cn/easytrader_broker.py +193 -0
  275. aria_code/brokers/cn/futu_broker.py +194 -0
  276. aria_code/brokers/cn/longbridge_broker.py +190 -0
  277. aria_code/brokers/cn/tiger_broker.py +196 -0
  278. aria_code/brokers/cn/xtquant_broker.py +225 -0
  279. aria_code/brokers/config.py +414 -0
  280. aria_code/brokers/intl/__init__.py +5 -0
  281. aria_code/brokers/intl/alpaca_broker.py +183 -0
  282. aria_code/brokers/intl/ibkr_broker.py +215 -0
  283. aria_code/brokers/intl/trading212_broker.py +183 -0
  284. aria_code/brokers/intl/webull_broker.py +156 -0
  285. aria_code/brokers/paper_broker.py +266 -0
  286. aria_code/brokers/planning.py +296 -0
  287. aria_code/brokers/registry.py +213 -0
  288. aria_code/brokers/trading.py +267 -0
  289. aria_code/canva_client.py +4 -0
  290. aria_code/change_store.py +127 -0
  291. aria_code/clients/__init__.py +0 -0
  292. aria_code/clients/aliyun_data_client.py +660 -0
  293. aria_code/clients/aria_relay_client.py +16 -0
  294. aria_code/clients/canva_client.py +284 -0
  295. aria_code/clients/figma_client.py +100 -0
  296. aria_code/clients/football_data_client.py +1671 -0
  297. aria_code/clients/kling_video_client.py +182 -0
  298. aria_code/clients/market_data_client.py +1985 -0
  299. aria_code/clients/openai_image_client.py +242 -0
  300. aria_code/clients/runway_video_client.py +157 -0
  301. aria_code/clients/warehouse_erp_client.py +105 -0
  302. aria_code/cloud_memory.py +190 -0
  303. aria_code/command_safety.py +19 -0
  304. aria_code/computer_use_server.py +276 -0
  305. aria_code/computer_use_tools.py +4 -0
  306. aria_code/dashboard_generator.py +579 -0
  307. aria_code/data_analysis_tools.py +4 -0
  308. aria_code/data_cleaner.py +483 -0
  309. aria_code/data_service.py +513 -0
  310. aria_code/datasources/__init__.py +23 -0
  311. aria_code/datasources/base.py +166 -0
  312. aria_code/datasources/router.py +253 -0
  313. aria_code/datasources/sec_statements.py +217 -0
  314. aria_code/datasources/sources/__init__.py +15 -0
  315. aria_code/datasources/sources/akshare_source.py +269 -0
  316. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  317. aria_code/datasources/sources/edgar_source.py +219 -0
  318. aria_code/datasources/sources/finnhub_source.py +198 -0
  319. aria_code/datasources/sources/fred_source.py +223 -0
  320. aria_code/datasources/sources/tushare_source.py +143 -0
  321. aria_code/datasources/sources/web_scraper_source.py +278 -0
  322. aria_code/datasources/sources/world_bank_source.py +206 -0
  323. aria_code/datasources/sources/yfinance_source.py +152 -0
  324. aria_code/doctor.py +860 -0
  325. aria_code/domain/__init__.py +0 -0
  326. aria_code/domain/_finance_core/__init__.py +5 -0
  327. aria_code/domain/_finance_core/backtest_engine.py +325 -0
  328. aria_code/domain/_finance_core/performance_stats.py +101 -0
  329. aria_code/domain/backtest_engine.py +75 -0
  330. aria_code/domain/backtest_report.py +488 -0
  331. aria_code/domain/finance_formulas.py +389 -0
  332. aria_code/domain/performance_stats.py +6 -0
  333. aria_code/domain/portfolio_ledger.py +307 -0
  334. aria_code/domain/strategy_vault.py +421 -0
  335. aria_code/evals/__init__.py +37 -0
  336. aria_code/evals/behavior.py +113 -0
  337. aria_code/evals/gap.py +93 -0
  338. aria_code/evals/harness.py +949 -0
  339. aria_code/evals/review_probe.py +132 -0
  340. aria_code/evals/runner.py +425 -0
  341. aria_code/evals/scorecard.py +322 -0
  342. aria_code/evals/summary.py +125 -0
  343. aria_code/external_agent_runner.py +115 -0
  344. aria_code/figma_client.py +4 -0
  345. aria_code/file_analysis_tools.py +4 -0
  346. aria_code/finance_formulas.py +4 -0
  347. aria_code/football_data_client.py +4 -0
  348. aria_code/image_gen_tools.py +4 -0
  349. aria_code/intent_classifier.py +459 -0
  350. aria_code/kling_video_client.py +4 -0
  351. aria_code/licensing.py +151 -0
  352. aria_code/local_finance_tools.py +4 -0
  353. aria_code/local_image_provider.py +4 -0
  354. aria_code/local_llm_provider.py +4 -0
  355. aria_code/macro_tools.py +4 -0
  356. aria_code/markdown_pdf.py +289 -0
  357. aria_code/market_data_client.py +4 -0
  358. aria_code/mcp_client.py +744 -0
  359. aria_code/memory_manager.py +273 -0
  360. aria_code/model_capability.py +512 -0
  361. aria_code/notification_tools.py +4 -0
  362. aria_code/openai_image_client.py +4 -0
  363. aria_code/packages/__init__.py +26 -0
  364. aria_code/packages/adk_bridge/__init__.py +11 -0
  365. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  366. aria_code/packages/adk_bridge/market_tools.py +125 -0
  367. aria_code/packages/aria_agents/__init__.py +5 -0
  368. aria_code/packages/aria_agents/manifest.py +78 -0
  369. aria_code/packages/aria_core/__init__.py +34 -0
  370. aria_code/packages/aria_core/architecture.py +269 -0
  371. aria_code/packages/aria_core/export.py +147 -0
  372. aria_code/packages/aria_core/manifest.py +65 -0
  373. aria_code/packages/aria_core/paths.py +57 -0
  374. aria_code/packages/aria_core/secure_file.py +68 -0
  375. aria_code/packages/aria_infra/__init__.py +15 -0
  376. aria_code/packages/aria_infra/arthera.py +58 -0
  377. aria_code/packages/aria_infra/doctor.py +246 -0
  378. aria_code/packages/aria_infra/product.py +40 -0
  379. aria_code/packages/aria_mcp/__init__.py +25 -0
  380. aria_code/packages/aria_mcp/bridge.py +232 -0
  381. aria_code/packages/aria_mcp/circuit.py +82 -0
  382. aria_code/packages/aria_mcp/config.py +162 -0
  383. aria_code/packages/aria_mcp/installer.py +95 -0
  384. aria_code/packages/aria_mcp/server.py +1705 -0
  385. aria_code/packages/aria_mcp/tools.py +81 -0
  386. aria_code/packages/aria_sdk/__init__.py +19 -0
  387. aria_code/packages/aria_sdk/client.py +393 -0
  388. aria_code/packages/aria_sdk/providers.py +70 -0
  389. aria_code/packages/aria_sdk/streaming.py +75 -0
  390. aria_code/packages/aria_sdk/types.py +86 -0
  391. aria_code/packages/aria_services/__init__.py +92 -0
  392. aria_code/packages/aria_services/cache.py +60 -0
  393. aria_code/packages/aria_services/context.py +356 -0
  394. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  395. aria_code/packages/aria_services/data.py +11 -0
  396. aria_code/packages/aria_services/provider_health.py +304 -0
  397. aria_code/packages/aria_services/references.py +247 -0
  398. aria_code/packages/aria_services/registry.py +249 -0
  399. aria_code/packages/aria_services/research_protocol.py +137 -0
  400. aria_code/packages/aria_services/research_quality.py +186 -0
  401. aria_code/packages/aria_services/settings.py +160 -0
  402. aria_code/packages/aria_services/usage.py +151 -0
  403. aria_code/packages/aria_skills/__init__.py +46 -0
  404. aria_code/packages/aria_skills/catalog.py +149 -0
  405. aria_code/packages/aria_skills/loader.py +455 -0
  406. aria_code/packages/aria_skills/registry.py +84 -0
  407. aria_code/packages/aria_tools/__init__.py +5 -0
  408. aria_code/packages/aria_tools/financial/__init__.py +5 -0
  409. aria_code/packages/aria_tools/financial/_paths.py +92 -0
  410. aria_code/packages/aria_tools/registry.py +129 -0
  411. aria_code/packages/quant_engine/__init__.py +33 -0
  412. aria_code/packages/quant_engine/_namespace.py +24 -0
  413. aria_code/packages/quant_engine/backtest/__init__.py +10 -0
  414. aria_code/packages/quant_engine/backtest/core.py +53 -0
  415. aria_code/packages/quant_engine/backtest/engine.py +102 -0
  416. aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
  417. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  418. aria_code/packages/quant_engine/sports/__init__.py +76 -0
  419. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  420. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  421. aria_code/packages/quant_engine/sports/elo.py +300 -0
  422. aria_code/packages/quant_engine/sports/form.py +188 -0
  423. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  424. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  425. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  426. aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
  427. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  428. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  429. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  430. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  431. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  432. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  433. aria_code/packs/__init__.py +85 -0
  434. aria_code/packs/base.py +168 -0
  435. aria_code/packs/finance/__init__.py +250 -0
  436. aria_code/packs/healthcare/__init__.py +40 -0
  437. aria_code/packs/logistics/__init__.py +171 -0
  438. aria_code/packs/payments/__init__.py +139 -0
  439. aria_code/packs/realty/__init__.py +190 -0
  440. aria_code/packs/registry.py +202 -0
  441. aria_code/packs/rules.py +90 -0
  442. aria_code/plan_utils.py +194 -0
  443. aria_code/plugin_loader.py +328 -0
  444. aria_code/portfolio_ledger.py +4 -0
  445. aria_code/preview_server.py +449 -0
  446. aria_code/privacy/__init__.py +5 -0
  447. aria_code/privacy/feedback.py +123 -0
  448. aria_code/project_review.py +278 -0
  449. aria_code/project_review_api.py +235 -0
  450. aria_code/project_review_server.py +18 -0
  451. aria_code/project_tools.py +4 -0
  452. aria_code/providers/__init__.py +30 -0
  453. aria_code/providers/google_video_provider.py +256 -0
  454. aria_code/providers/llm/__init__.py +19 -0
  455. aria_code/providers/llm/anthropic.py +184 -0
  456. aria_code/providers/llm/arthera_gateway.py +90 -0
  457. aria_code/providers/llm/autoconfig.py +213 -0
  458. aria_code/providers/llm/base.py +139 -0
  459. aria_code/providers/llm/custom.py +158 -0
  460. aria_code/providers/llm/ollama.py +137 -0
  461. aria_code/providers/llm/openai_compat.py +350 -0
  462. aria_code/providers/llm/registry.py +457 -0
  463. aria_code/providers/local_image_provider.py +202 -0
  464. aria_code/providers/local_llm_provider.py +549 -0
  465. aria_code/providers/ollama_models.py +79 -0
  466. aria_code/providers/openai_params.py +32 -0
  467. aria_code/providers/tool_messages.py +100 -0
  468. aria_code/realty_data_tools.py +4 -0
  469. aria_code/report_exporters.py +150 -0
  470. aria_code/report_generator.py +1764 -0
  471. aria_code/review_service.py +497 -0
  472. aria_code/runtime/__init__.py +176 -0
  473. aria_code/runtime/acceptance.py +632 -0
  474. aria_code/runtime/agent_loop.py +1851 -0
  475. aria_code/runtime/agent_profiles.py +75 -0
  476. aria_code/runtime/approval.py +71 -0
  477. aria_code/runtime/baseline.py +219 -0
  478. aria_code/runtime/budget.py +232 -0
  479. aria_code/runtime/checkpoints.py +481 -0
  480. aria_code/runtime/consult.py +65 -0
  481. aria_code/runtime/contract.py +282 -0
  482. aria_code/runtime/delivery.py +432 -0
  483. aria_code/runtime/events.py +113 -0
  484. aria_code/runtime/gateway.py +167 -0
  485. aria_code/runtime/lsp.py +484 -0
  486. aria_code/runtime/native_index.py +153 -0
  487. aria_code/runtime/orchestrator.py +55 -0
  488. aria_code/runtime/processes.py +343 -0
  489. aria_code/runtime/project_graph.py +614 -0
  490. aria_code/runtime/repo_map.py +712 -0
  491. aria_code/runtime/research_bundle.py +105 -0
  492. aria_code/runtime/review.py +269 -0
  493. aria_code/runtime/run_state.py +127 -0
  494. aria_code/runtime/run_store.py +522 -0
  495. aria_code/runtime/scoped_runs.py +113 -0
  496. aria_code/runtime/self_healing.py +422 -0
  497. aria_code/runtime/semantic_diff.py +171 -0
  498. aria_code/runtime/subagent.py +721 -0
  499. aria_code/runtime/symbol_edit.py +117 -0
  500. aria_code/runtime/task_brief.py +46 -0
  501. aria_code/runtime/task_context.py +145 -0
  502. aria_code/runtime/task_ledger.py +108 -0
  503. aria_code/runtime/task_orchestrator.py +90 -0
  504. aria_code/runtime/task_outcome.py +33 -0
  505. aria_code/runtime/task_verification.py +266 -0
  506. aria_code/runtime/task_worktree.py +448 -0
  507. aria_code/runtime/tool_executor.py +329 -0
  508. aria_code/runtime/tool_policy.py +107 -0
  509. aria_code/runtime/transactions.py +216 -0
  510. aria_code/runtime/windows_job.py +115 -0
  511. aria_code/runtime/workflows.py +222 -0
  512. aria_code/runtime/worktrees.py +215 -0
  513. aria_code/runway_video_client.py +4 -0
  514. aria_code/safety/__init__.py +27 -0
  515. aria_code/safety/client_isolation.py +178 -0
  516. aria_code/safety/permissions.py +339 -0
  517. aria_code/safety/risk.py +699 -0
  518. aria_code/safety/sandbox.py +148 -0
  519. aria_code/safety/service.py +102 -0
  520. aria_code/service_endpoints.py +14 -0
  521. aria_code/setup_wizard.py +684 -0
  522. aria_code/spreadsheet_tools.py +4 -0
  523. aria_code/strategy_vault.py +4 -0
  524. aria_code/tools/__init__.py +0 -0
  525. aria_code/tools/artifact_tools.py +252 -0
  526. aria_code/tools/broker_tools.py +92 -0
  527. aria_code/tools/build_quant_engine.py +97 -0
  528. aria_code/tools/code_audit_tools.py +192 -0
  529. aria_code/tools/computer_use_tools.py +509 -0
  530. aria_code/tools/data_analysis_tools.py +831 -0
  531. aria_code/tools/enterprise_finance_tools.py +202 -0
  532. aria_code/tools/extended_tools.py +240 -0
  533. aria_code/tools/file_analysis_tools.py +880 -0
  534. aria_code/tools/image_gen_tools.py +114 -0
  535. aria_code/tools/local_finance_tools.py +3488 -0
  536. aria_code/tools/logistics_carriers.py +11 -0
  537. aria_code/tools/logistics_inventory.py +11 -0
  538. aria_code/tools/logistics_io.py +11 -0
  539. aria_code/tools/logistics_tenancy.py +11 -0
  540. aria_code/tools/logistics_tools.py +11 -0
  541. aria_code/tools/macro_tools.py +412 -0
  542. aria_code/tools/notification_tools.py +249 -0
  543. aria_code/tools/project_tools.py +525 -0
  544. aria_code/tools/realty_data_tools.py +662 -0
  545. aria_code/tools/spreadsheet_tools.py +399 -0
  546. aria_code/tools/stripe_tools.py +115 -0
  547. aria_code/tools/video_tools.py +500 -0
  548. aria_code/training/__init__.py +1 -0
  549. aria_code/training/compare.py +117 -0
  550. aria_code/training/prelude_commands.py +111 -0
  551. aria_code/training/prelude_eval.py +390 -0
  552. aria_code/training/prelude_real.py +183 -0
  553. aria_code/training/prelude_train.py +248 -0
  554. aria_code/training/sft_dataset.py +230 -0
  555. aria_code/training/trajectory_dataset.py +263 -0
  556. aria_code/ui/__init__.py +105 -0
  557. aria_code/ui/banner.py +404 -0
  558. aria_code/ui/completer.py +571 -0
  559. aria_code/ui/console.py +285 -0
  560. aria_code/ui/input_box.py +505 -0
  561. aria_code/ui/mentions.py +206 -0
  562. aria_code/ui/picker.py +248 -0
  563. aria_code/ui/render/__init__.py +11 -0
  564. aria_code/ui/render/actions.py +297 -0
  565. aria_code/ui/render/finance.py +1732 -0
  566. aria_code/ui/render/market.py +253 -0
  567. aria_code/ui/render/output.py +954 -0
  568. aria_code/ui/render/responsive.py +75 -0
  569. aria_code/ui/render/team.py +367 -0
  570. aria_code/ui/robot.py +239 -0
  571. aria_code/ui/startup_dashboard.py +110 -0
  572. aria_code/video_analysis.py +147 -0
  573. aria_code/video_editor.py +518 -0
  574. aria_code/video_workflow/__init__.py +8 -0
  575. aria_code/video_workflow/__main__.py +61 -0
  576. aria_code/video_workflow/engines/__init__.py +18 -0
  577. aria_code/video_workflow/engines/base.py +69 -0
  578. aria_code/video_workflow/engines/custom_model.py +204 -0
  579. aria_code/video_workflow/engines/google_veo.py +94 -0
  580. aria_code/video_workflow/engines/local_engine.py +71 -0
  581. aria_code/video_workflow/engines/registry.py +98 -0
  582. aria_code/video_workflow/google_models.py +278 -0
  583. aria_code/video_workflow/music/__init__.py +5 -0
  584. aria_code/video_workflow/music/foley_service.py +132 -0
  585. aria_code/video_workflow/music/service.py +212 -0
  586. aria_code/video_workflow/pipeline.py +620 -0
  587. aria_code/video_workflow/plan.py +202 -0
  588. aria_code/video_workflow/session.py +297 -0
  589. aria_code/workspace/__init__.py +6 -0
  590. aria_code/workspace/files.py +219 -0
  591. aria_code/workspace/verify.py +167 -0
  592. aria_code/workspace_changes.py +199 -0
  593. aria_code/workspace_service.py +196 -0
  594. aria_code/workspace_tools.py +150 -0
  595. aria_code-0.167.0.dist-info/METADATA +330 -0
  596. aria_code-0.167.0.dist-info/RECORD +601 -0
  597. aria_code-0.167.0.dist-info/WHEEL +5 -0
  598. aria_code-0.167.0.dist-info/entry_points.txt +5 -0
  599. aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
  600. aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
  601. aria_code-0.167.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,831 @@
1
+ """
2
+ data_analysis_tools.py — 数据分析增强层
3
+ ==========================================
4
+ 提供:
5
+ - Excel 导出 (多 Sheet + 内嵌图表)
6
+ - DuckDB SQL 接口(本地内存数据仓库)
7
+ - 价格预警系统 (~/.arthera/alerts.json)
8
+ - 多资产组合回测 (相关性矩阵 + 权重收益)
9
+ - 相关性热力图数据
10
+ - 自定义因子表达式 DSL
11
+
12
+ 依赖(可选):
13
+ pip install openpyxl duckdb pandas numpy yfinance
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ import logging
20
+ import os
21
+ from datetime import datetime, timedelta
22
+ from pathlib import Path
23
+ from typing import Any, Dict, List, Optional, Tuple
24
+ from aria_code.packages.aria_core.paths import aria_home
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ # ── Optional imports ──────────────────────────────────────────────────────────
29
+
30
+ try:
31
+ import pandas as pd
32
+ _HAS_PD = True
33
+ except ImportError:
34
+ _HAS_PD = False
35
+
36
+ try:
37
+ import numpy as np
38
+ _HAS_NP = True
39
+ except ImportError:
40
+ _HAS_NP = False
41
+
42
+ try:
43
+ import yfinance as yf
44
+ _HAS_YF = True
45
+ except ImportError:
46
+ _HAS_YF = False
47
+
48
+ try:
49
+ import duckdb
50
+ _HAS_DUCK = True
51
+ except ImportError:
52
+ _HAS_DUCK = False
53
+
54
+ try:
55
+ import openpyxl
56
+ from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
57
+ from openpyxl.chart import LineChart, Reference
58
+ from openpyxl.utils import get_column_letter
59
+ _HAS_OPENPYXL = True
60
+ except ImportError:
61
+ _HAS_OPENPYXL = False
62
+
63
+ ALERTS_PATH = aria_home() / "alerts.json"
64
+ EXPORT_DIR = aria_home() / "exports"
65
+
66
+
67
+ # ── Excel 导出 ────────────────────────────────────────────────────────────────
68
+
69
+ def export_to_excel(params: dict) -> dict:
70
+ """
71
+ 将数据导出到格式化 Excel 文件(多 Sheet)。
72
+
73
+ 参数:
74
+ data: dict,键=Sheet名,值=list[dict] 行数据
75
+ filename: 输出文件名(默认 aria_export_<timestamp>.xlsx)
76
+ add_chart: 是否为含 '价格'/'close'/'收盘' 列的 Sheet 添加折线图
77
+ """
78
+ if not _HAS_OPENPYXL:
79
+ return {"success": False, "error": "openpyxl 未安装,请运行: pip install openpyxl"}
80
+ if not _HAS_PD:
81
+ return {"success": False, "error": "pandas 未安装"}
82
+
83
+ data = params.get("data", {})
84
+ if not data:
85
+ return {"success": False, "error": "data 参数不能为空"}
86
+
87
+ EXPORT_DIR.mkdir(parents=True, exist_ok=True)
88
+ ts = datetime.now().strftime("%Y%m%d_%H%M%S")
89
+ filename = params.get("filename") or f"aria_export_{ts}.xlsx"
90
+ if not filename.endswith(".xlsx"):
91
+ filename += ".xlsx"
92
+ out_path = EXPORT_DIR / filename
93
+ add_chart = bool(params.get("add_chart", True))
94
+
95
+ wb = openpyxl.Workbook()
96
+ wb.remove(wb.active) # remove default empty sheet
97
+
98
+ # Style constants
99
+ header_fill = PatternFill("solid", fgColor="1F4E79")
100
+ header_font = Font(color="FFFFFF", bold=True, size=10)
101
+ alt_fill = PatternFill("solid", fgColor="EBF5FB")
102
+ center_align = Alignment(horizontal="center", vertical="center")
103
+ thin_border = Border(
104
+ bottom=Side(style="thin", color="BDC3C7"),
105
+ )
106
+
107
+ for sheet_name, rows in data.items():
108
+ if not rows:
109
+ continue
110
+ ws = wb.create_sheet(title=str(sheet_name)[:31]) # Excel max 31 chars
111
+ df = pd.DataFrame(rows)
112
+ cols = list(df.columns)
113
+
114
+ # Header row
115
+ for ci, col in enumerate(cols, 1):
116
+ cell = ws.cell(row=1, column=ci, value=str(col))
117
+ cell.fill = header_fill
118
+ cell.font = header_font
119
+ cell.alignment = center_align
120
+
121
+ # Data rows
122
+ price_col_idx = None
123
+ for ri, row_dict in enumerate(rows, 2):
124
+ for ci, col in enumerate(cols, 1):
125
+ val = row_dict.get(col)
126
+ cell = ws.cell(row=ri, column=ci, value=val)
127
+ if ri % 2 == 0:
128
+ cell.fill = alt_fill
129
+ cell.border = thin_border
130
+ # Auto-detect price column
131
+ col_lower = col.lower()
132
+ if price_col_idx is None and any(k in col_lower for k in
133
+ ["价格","close","收盘","price","last","最新"]):
134
+ price_col_idx = ci
135
+
136
+ # Auto-column width
137
+ for ci, col in enumerate(cols, 1):
138
+ max_len = max(len(str(col)),
139
+ *(len(str(r.get(col,""))) for r in rows[:50]))
140
+ ws.column_dimensions[get_column_letter(ci)].width = min(max_len + 3, 40)
141
+
142
+ # Freeze header
143
+ ws.freeze_panes = "A2"
144
+
145
+ # Add line chart if price column detected
146
+ if add_chart and price_col_idx and len(rows) >= 3:
147
+ chart = LineChart()
148
+ chart.title = f"{sheet_name} 价格走势"
149
+ chart.style = 10
150
+ chart.y_axis.title = "价格"
151
+ chart.x_axis.title = "时间"
152
+ data_ref = Reference(ws, min_col=price_col_idx,
153
+ min_row=1, max_row=len(rows) + 1)
154
+ chart.add_data(data_ref, titles_from_data=True)
155
+ chart.width = 20
156
+ chart.height = 12
157
+ ws.add_chart(chart, f"A{len(rows) + 4}")
158
+
159
+ # Summary sheet
160
+ ws_sum = wb.create_sheet(title="📊 汇总", index=0)
161
+ ws_sum["A1"] = "Aria Code 数据导出报告"
162
+ ws_sum["A1"].font = Font(bold=True, size=14, color="1F4E79")
163
+ ws_sum["A3"] = "导出时间:"
164
+ ws_sum["B3"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
165
+ ws_sum["A4"] = "Sheet 数量:"
166
+ ws_sum["B4"] = len(data)
167
+ ws_sum["A5"] = "总行数:"
168
+ ws_sum["B5"] = sum(len(v) for v in data.values())
169
+ ws_sum["A7"] = "包含 Sheet:"
170
+ for i, sname in enumerate(data.keys(), 8):
171
+ ws_sum[f"A{i}"] = f" • {sname}"
172
+ ws_sum[f"B{i}"] = f"{len(data[sname])} 行"
173
+
174
+ wb.save(str(out_path))
175
+ return {
176
+ "success": True,
177
+ "path": str(out_path),
178
+ "sheets": list(data.keys()),
179
+ "total_rows": sum(len(v) for v in data.values()),
180
+ "filename": filename,
181
+ }
182
+
183
+
184
+ # ── DuckDB SQL 接口 ───────────────────────────────────────────────────────────
185
+
186
+ # Persistent in-memory DuckDB connection for the session
187
+ _duck_conn: Optional[Any] = None
188
+ _duck_tables: Dict[str, bool] = {}
189
+
190
+
191
+ def _get_duck_conn():
192
+ global _duck_conn
193
+ if _duck_conn is None:
194
+ _duck_conn = duckdb.connect(":memory:")
195
+ return _duck_conn
196
+
197
+
198
+ def sql_query(params: dict) -> dict:
199
+ """
200
+ 在内存 DuckDB 中执行 SQL 查询。
201
+
202
+ 参数:
203
+ query: SQL 语句
204
+ load: 可选,dict {table_name: list[dict]} — 在查询前加载数据
205
+ limit: 结果行数限制(默认 500)
206
+ """
207
+ if not _HAS_DUCK:
208
+ return {"success": False,
209
+ "error": "duckdb 未安装,请运行: pip install duckdb"}
210
+ if not _HAS_PD:
211
+ return {"success": False, "error": "pandas 未安装"}
212
+
213
+ query = str(params.get("query", "")).strip()
214
+ if not query:
215
+ return {"success": False, "error": "query 不能为空"}
216
+
217
+ conn = _get_duck_conn()
218
+ limit = int(params.get("limit", 500))
219
+ load = params.get("load", {})
220
+
221
+ # Load tables
222
+ for tname, rows in (load or {}).items():
223
+ if rows:
224
+ df = pd.DataFrame(rows)
225
+ conn.register(tname, df)
226
+ _duck_tables[tname] = True
227
+
228
+ # Safety check: block destructive operations
229
+ q_upper = query.upper().lstrip()
230
+ if any(q_upper.startswith(kw) for kw in ("DROP ", "DELETE ", "TRUNCATE ")):
231
+ return {"success": False,
232
+ "error": "安全限制:不允许 DROP/DELETE/TRUNCATE 操作"}
233
+
234
+ # Auto-add LIMIT if SELECT without one
235
+ if q_upper.startswith("SELECT") and "LIMIT" not in q_upper:
236
+ query = f"{query.rstrip(';')} LIMIT {limit}"
237
+
238
+ try:
239
+ result = conn.execute(query).fetchdf()
240
+ records = result.to_dict("records")
241
+ cols = list(result.columns)
242
+ return {
243
+ "success": True,
244
+ "columns": cols,
245
+ "rows": records,
246
+ "row_count": len(records),
247
+ "tables_loaded": list(_duck_tables.keys()),
248
+ }
249
+ except Exception as e:
250
+ return {"success": False, "error": str(e), "query": query}
251
+
252
+
253
+ def sql_list_tables(params: dict = None) -> dict:
254
+ """列出内存 DuckDB 中已加载的表。"""
255
+ if not _HAS_DUCK:
256
+ return {"success": False, "error": "duckdb 未安装"}
257
+ conn = _get_duck_conn()
258
+ try:
259
+ df = conn.execute("SHOW TABLES").fetchdf()
260
+ tables = df["name"].tolist() if "name" in df.columns else []
261
+ return {"success": True, "tables": tables}
262
+ except Exception as e:
263
+ return {"success": False, "error": str(e)}
264
+
265
+
266
+ # ── 价格预警系统 ──────────────────────────────────────────────────────────────
267
+
268
+ def _load_alerts() -> List[dict]:
269
+ if ALERTS_PATH.exists():
270
+ try:
271
+ return json.loads(ALERTS_PATH.read_text(encoding="utf-8"))
272
+ except Exception:
273
+ pass
274
+ return []
275
+
276
+
277
+ def _save_alerts(alerts: list) -> None:
278
+ ALERTS_PATH.parent.mkdir(parents=True, exist_ok=True)
279
+ ALERTS_PATH.write_text(json.dumps(alerts, ensure_ascii=False, indent=2),
280
+ encoding="utf-8")
281
+
282
+
283
+ def add_price_alert(params: dict) -> dict:
284
+ """
285
+ 添加价格预警。
286
+
287
+ 参数:
288
+ symbol: 标的代码(如 "AAPL" / "600519")
289
+ condition: 触发条件 ("gt" / "lt" / "cross_up" / "cross_down")
290
+ price: 触发价格
291
+ note: 备注(可选)
292
+ """
293
+ symbol = str(params.get("symbol", "")).upper().strip()
294
+ condition = str(params.get("condition", "gt")).lower()
295
+ price = float(params.get("price", 0))
296
+ note = str(params.get("note", ""))
297
+
298
+ if not symbol:
299
+ return {"success": False, "error": "symbol 不能为空"}
300
+ if condition not in ("gt", "lt", "cross_up", "cross_down"):
301
+ return {"success": False,
302
+ "error": "condition 必须是 gt/lt/cross_up/cross_down"}
303
+ if price <= 0:
304
+ return {"success": False, "error": "price 必须 > 0"}
305
+
306
+ alerts = _load_alerts()
307
+ alert_id = f"{symbol}_{condition}_{price}_{int(datetime.now().timestamp())}"
308
+ new_alert = {
309
+ "id": alert_id,
310
+ "symbol": symbol,
311
+ "condition": condition,
312
+ "price": price,
313
+ "note": note,
314
+ "created_at": datetime.now().isoformat(),
315
+ "triggered": False,
316
+ }
317
+ alerts.append(new_alert)
318
+ _save_alerts(alerts)
319
+
320
+ cond_label = {"gt": ">", "lt": "<",
321
+ "cross_up": "向上突破", "cross_down": "向下跌破"}[condition]
322
+ return {
323
+ "success": True,
324
+ "alert_id": alert_id,
325
+ "message": f"已设置预警:{symbol} 价格 {cond_label} {price}",
326
+ "total_alerts": len(alerts),
327
+ }
328
+
329
+
330
+ def list_price_alerts(params: dict = None) -> dict:
331
+ """列出所有价格预警。"""
332
+ alerts = _load_alerts()
333
+ active = [a for a in alerts if not a.get("triggered")]
334
+ done = [a for a in alerts if a.get("triggered")]
335
+ return {
336
+ "success": True,
337
+ "active_alerts": active,
338
+ "triggered_alerts": done,
339
+ "total": len(alerts),
340
+ }
341
+
342
+
343
+ def delete_price_alert(params: dict) -> dict:
344
+ """删除指定 ID 的价格预警。"""
345
+ alert_id = str(params.get("alert_id", ""))
346
+ if not alert_id:
347
+ return {"success": False, "error": "alert_id 不能为空"}
348
+ alerts = _load_alerts()
349
+ before = len(alerts)
350
+ alerts = [a for a in alerts if a.get("id") != alert_id]
351
+ if len(alerts) == before:
352
+ return {"success": False, "error": f"未找到预警 {alert_id}"}
353
+ _save_alerts(alerts)
354
+ return {"success": True, "deleted_id": alert_id, "remaining": len(alerts)}
355
+
356
+
357
+ def check_alerts(params: dict = None) -> dict:
358
+ """
359
+ 检查当前所有未触发预警的状态(需联网获取实时价格)。
360
+ 返回已触发的预警列表。
361
+ """
362
+ if not _HAS_YF:
363
+ return {"success": False, "error": "yfinance 未安装,无法获取实时价格"}
364
+
365
+ alerts = _load_alerts()
366
+ active = [a for a in alerts if not a.get("triggered")]
367
+ if not active:
368
+ return {"success": True, "triggered": [], "message": "无活跃预警"}
369
+
370
+ # Batch price fetch
371
+ symbols = list(set(a.get("symbol", "") for a in active if a.get("symbol")))
372
+ prices: Dict[str, float] = {}
373
+ try:
374
+ tickers = yf.Tickers(" ".join(symbols))
375
+ for sym in symbols:
376
+ try:
377
+ info = tickers.tickers[sym].fast_info
378
+ px = getattr(info, "last_price", None) or \
379
+ getattr(info, "regularMarketPrice", None)
380
+ if px:
381
+ prices[sym] = float(px)
382
+ except Exception:
383
+ pass
384
+ except Exception as e:
385
+ logger.debug("Alert price fetch failed: %s", e)
386
+
387
+ triggered_now = []
388
+ for alert in active:
389
+ sym = alert.get("symbol")
390
+ cond = alert.get("condition")
391
+ tgt = alert.get("price")
392
+ if not sym or not cond or tgt is None:
393
+ continue
394
+ cur = prices.get(sym)
395
+ if cur is None:
396
+ continue
397
+ hit = False
398
+ if cond == "gt" and cur > tgt:
399
+ hit = True
400
+ elif cond == "lt" and cur < tgt:
401
+ hit = True
402
+ elif cond == "cross_up" and cur >= tgt:
403
+ hit = True
404
+ elif cond == "cross_down" and cur <= tgt:
405
+ hit = True
406
+ if hit:
407
+ alert["triggered"] = True
408
+ alert["triggered_at"] = datetime.now().isoformat()
409
+ alert["triggered_price"] = cur
410
+ triggered_now.append(alert)
411
+
412
+ _save_alerts(alerts)
413
+
414
+ # Push notifications for newly triggered alerts
415
+ if triggered_now:
416
+ try:
417
+ from notification_tools import send_alert_notification
418
+ for _alrt in triggered_now:
419
+ send_alert_notification(_alrt)
420
+ except Exception as _ne:
421
+ logger.debug("Notification dispatch failed: %s", _ne)
422
+
423
+ return {
424
+ "success": True,
425
+ "triggered": triggered_now,
426
+ "checked": len(active),
427
+ "prices": prices,
428
+ }
429
+
430
+
431
+ # ── 相关性矩阵 ────────────────────────────────────────────────────────────────
432
+
433
+ def calc_correlation_matrix(params: dict) -> dict:
434
+ """
435
+ 计算多资产收益率相关性矩阵。
436
+
437
+ 参数:
438
+ symbols: list[str],如 ["AAPL","MSFT","TSLA","SPY"]
439
+ period: 历史区间 ("1y" / "2y" / "6mo",默认 "1y")
440
+ interval: 频率 ("1d" / "1wk",默认 "1d")
441
+ """
442
+ if not _HAS_YF:
443
+ return {"success": False, "error": "yfinance 未安装"}
444
+ if not _HAS_PD or not _HAS_NP:
445
+ return {"success": False, "error": "pandas / numpy 未安装"}
446
+
447
+ symbols = params.get("symbols", [])
448
+ period = str(params.get("period", "1y"))
449
+ interval = str(params.get("interval", "1d"))
450
+
451
+ if len(symbols) < 2:
452
+ return {"success": False, "error": "至少需要 2 个标的"}
453
+ if len(symbols) > 20:
454
+ symbols = symbols[:20]
455
+
456
+ try:
457
+ raw = yf.download(symbols, period=period, interval=interval,
458
+ progress=False, auto_adjust=True)
459
+ if raw.empty:
460
+ return {"success": False, "error": "下载数据为空"}
461
+
462
+ if isinstance(raw.columns, pd.MultiIndex):
463
+ closes = raw["Close"]
464
+ else:
465
+ closes = raw
466
+
467
+ closes = closes.dropna(how="all", axis=1)
468
+ rets = closes.pct_change().dropna()
469
+ corr = rets.corr()
470
+
471
+ # Convert to serializable format
472
+ corr_dict = {}
473
+ for sym in corr.columns:
474
+ corr_dict[sym] = {
475
+ other: round(float(corr.loc[sym, other]), 4)
476
+ for other in corr.columns
477
+ }
478
+
479
+ # Summary stats
480
+ stats = {}
481
+ for sym in closes.columns:
482
+ s = closes[sym].dropna()
483
+ r = rets[sym].dropna() if sym in rets.columns else pd.Series(dtype=float)
484
+ if s.empty:
485
+ continue
486
+ stats[sym] = {
487
+ "return_total": round((s.iloc[-1]/s.iloc[0] - 1)*100, 2),
488
+ "volatility": round(_annual_vol(r) * 100, 2) if len(r) > 1 else None,
489
+ "sharpe": _calc_sharpe(r),
490
+ "max_drawdown": round(_max_drawdown(s) * 100, 2),
491
+ }
492
+
493
+ return {
494
+ "success": True,
495
+ "symbols": list(closes.columns),
496
+ "period": period,
497
+ "interval": interval,
498
+ "corr_matrix": corr_dict,
499
+ "stats": stats,
500
+ "data_points": len(rets),
501
+ }
502
+ except Exception as e:
503
+ return {"success": False, "error": str(e)}
504
+
505
+
506
+ # ── 多资产组合回测 ─────────────────────────────────────────────────────────────
507
+
508
+ def portfolio_backtest(params: dict) -> dict:
509
+ """
510
+ 多资产组合历史回测。
511
+
512
+ 参数:
513
+ symbols: list[str],如 ["AAPL","MSFT","GOOG"]
514
+ weights: list[float],权重(自动归一化);为空则等权
515
+ period: "1y" / "2y" / "3y" / "5y"(默认 "2y")
516
+ benchmark: 基准标的(默认 "SPY")
517
+ rebalance: 再平衡频率 ("monthly" / "quarterly" / "none",默认 "monthly")
518
+ """
519
+ if not _HAS_YF or not _HAS_PD or not _HAS_NP:
520
+ return {"success": False, "error": "需要 yfinance + pandas + numpy"}
521
+
522
+ symbols = params.get("symbols", [])
523
+ weights_r = params.get("weights", [])
524
+ period = str(params.get("period", "2y"))
525
+ benchmark = str(params.get("benchmark", "SPY"))
526
+ rebalance = str(params.get("rebalance", "monthly"))
527
+
528
+ if not symbols:
529
+ return {"success": False, "error": "symbols 不能为空"}
530
+ if len(symbols) > 15:
531
+ symbols = symbols[:15]
532
+
533
+ # Normalize weights
534
+ if weights_r and len(weights_r) == len(symbols):
535
+ ws = [float(w) for w in weights_r]
536
+ total = sum(ws)
537
+ weights = [w / total for w in ws]
538
+ else:
539
+ weights = [1 / len(symbols)] * len(symbols)
540
+
541
+ all_syms = list(dict.fromkeys(symbols + [benchmark]))
542
+ try:
543
+ raw = yf.download(all_syms, period=period, progress=False, auto_adjust=True)
544
+ if raw.empty:
545
+ return {"success": False, "error": "数据下载失败"}
546
+
547
+ closes = raw["Close"] if isinstance(raw.columns, pd.MultiIndex) else raw
548
+ closes = closes.dropna(how="all", axis=1)
549
+
550
+ # Align symbols with available data
551
+ avail = [s for s in symbols if s in closes.columns]
552
+ if not avail:
553
+ return {"success": False, "error": "所有标的数据均不可用"}
554
+ if len(avail) < len(symbols):
555
+ missing = [s for s in symbols if s not in closes.columns]
556
+ weights = [weights[i] for i, s in enumerate(symbols) if s in closes.columns]
557
+ wt_total = sum(weights)
558
+ weights = [w / wt_total for w in weights]
559
+ symbols = avail
560
+
561
+ port_data = closes[symbols].ffill().dropna()
562
+ if benchmark in closes.columns:
563
+ bench_data = closes[benchmark].ffill().dropna()
564
+ else:
565
+ bench_data = None
566
+
567
+ # Rebalance
568
+ if rebalance == "monthly":
569
+ freq = "MS"
570
+ elif rebalance == "quarterly":
571
+ freq = "QS"
572
+ else:
573
+ freq = None
574
+
575
+ # Compute portfolio returns
576
+ if freq and len(port_data) > 0:
577
+ port_rets = port_data.pct_change().dropna()
578
+ # Monthly rebalancing: compound weighted returns per period
579
+ port_monthly_rets = (port_rets + 1).resample(freq).prod() - 1
580
+ port_rets_w = (port_monthly_rets[symbols] * weights).sum(axis=1)
581
+ # Expand back to daily for metrics
582
+ port_cum = (1 + port_rets_w).cumprod()
583
+ else:
584
+ port_rets = port_data.pct_change().dropna()
585
+ port_rets_w = (port_rets[symbols] * weights).sum(axis=1)
586
+ port_cum = (1 + port_rets_w).cumprod()
587
+
588
+ total_return = float(port_cum.iloc[-1] - 1) * 100
589
+ annual_vol = _annual_vol(port_rets_w) * 100
590
+ sharpe = _calc_sharpe(port_rets_w)
591
+ max_dd = round(_max_drawdown(port_cum) * 100, 2)
592
+
593
+ # Benchmark
594
+ bench_stats = None
595
+ if bench_data is not None:
596
+ bench_rets = bench_data.pct_change().dropna()
597
+ b_cum = (1 + bench_rets).cumprod()
598
+ bench_stats = {
599
+ "symbol": benchmark,
600
+ "total_return": round((float(b_cum.iloc[-1]) - 1)*100, 2),
601
+ "volatility": round(_annual_vol(bench_rets) * 100, 2),
602
+ "sharpe": _calc_sharpe(bench_rets),
603
+ "max_drawdown": round(_max_drawdown(b_cum) * 100, 2),
604
+ }
605
+
606
+ # Allocation breakdown
607
+ allocation = [{"symbol": s, "weight_pct": round(w*100, 1),
608
+ "weight_abs": w} for s, w in zip(symbols, weights)]
609
+
610
+ # Performance curve (monthly points)
611
+ if freq:
612
+ curve_pts = [{"date": str(d.date()), "value": round(float(v), 4)}
613
+ for d, v in port_cum.items()]
614
+ else:
615
+ curve_pts = [{"date": str(d.date()), "value": round(float(v), 4)}
616
+ for d, v in port_cum.iloc[::5].items()] # every 5 days
617
+
618
+ return {
619
+ "success": True,
620
+ "symbols": symbols,
621
+ "weights": weights,
622
+ "allocation": allocation,
623
+ "period": period,
624
+ "rebalance": rebalance,
625
+ "portfolio": {
626
+ "total_return_pct": round(total_return, 2),
627
+ "annual_vol_pct": round(annual_vol, 2),
628
+ "sharpe_ratio": sharpe,
629
+ "max_drawdown_pct": max_dd,
630
+ "calmar_ratio": round(total_return / abs(max_dd), 2) if max_dd != 0 else None,
631
+ },
632
+ "benchmark": bench_stats,
633
+ "curve": curve_pts[-60:], # last 60 points for chart
634
+ "data_points": len(port_rets_w),
635
+ }
636
+ except Exception as e:
637
+ logger.exception("Portfolio backtest error")
638
+ return {"success": False, "error": str(e)}
639
+
640
+
641
+ # ── 自定义因子 DSL ─────────────────────────────────────────────────────────────
642
+
643
+ def eval_custom_factor(params: dict) -> dict:
644
+ """
645
+ 计算自定义因子表达式。
646
+
647
+ 参数:
648
+ symbol: 标的代码
649
+ period: 历史区间(默认 "1y")
650
+ expr: 因子表达式字符串(Python 数学语法,可使用变量)
651
+ 可用变量:close, open, high, low, volume, returns
652
+ 可用函数:sma(n), ema(n), std(n), rsi(n), atr(n)
653
+ 示例:
654
+ "(close - sma(20)) / std(20)" → Z-Score
655
+ "sma(5) / sma(20) - 1" → 短期动量
656
+ "volume / sma(20, volume)" → 量比
657
+ """
658
+ if not _HAS_YF or not _HAS_PD or not _HAS_NP:
659
+ return {"success": False, "error": "需要 yfinance + pandas + numpy"}
660
+
661
+ symbol = str(params.get("symbol", "")).upper()
662
+ period = str(params.get("period", "1y"))
663
+ expr = str(params.get("expr", "")).strip()
664
+
665
+ if not symbol or not expr:
666
+ return {"success": False, "error": "symbol 和 expr 均为必填"}
667
+
668
+ # Security: block imports and exec
669
+ forbidden = ["import", "__", "exec(", "eval(", "open(", "os.", "sys."]
670
+ for tok in forbidden:
671
+ if tok in expr:
672
+ return {"success": False, "error": f"表达式包含禁止关键字: {tok}"}
673
+
674
+ try:
675
+ tkr = yf.Ticker(symbol)
676
+ hist = tkr.history(period=period, auto_adjust=True)
677
+ if hist.empty:
678
+ return {"success": False, "error": f"无法获取 {symbol} 历史数据"}
679
+
680
+ close = hist["Close"].astype(float)
681
+ open_ = hist["Open"].astype(float)
682
+ high = hist["High"].astype(float)
683
+ low = hist["Low"].astype(float)
684
+ volume = hist["Volume"].astype(float)
685
+ returns = close.pct_change()
686
+
687
+ def sma(n, series=None):
688
+ s = series if series is not None else close
689
+ return s.rolling(n).mean()
690
+
691
+ def ema(n, series=None):
692
+ s = series if series is not None else close
693
+ return s.ewm(span=n, adjust=False).mean()
694
+
695
+ def std(n, series=None):
696
+ s = series if series is not None else close
697
+ return s.rolling(n).std()
698
+
699
+ def rsi(n=14):
700
+ delta = close.diff()
701
+ gain = delta.clip(lower=0).rolling(n).mean()
702
+ loss = (-delta.clip(upper=0)).rolling(n).mean()
703
+ rs = gain / loss
704
+ return 100 - (100 / (1 + rs))
705
+
706
+ def atr(n=14):
707
+ tr = pd.concat([
708
+ high - low,
709
+ (high - close.shift()).abs(),
710
+ (low - close.shift()).abs(),
711
+ ], axis=1).max(axis=1)
712
+ return tr.rolling(n).mean()
713
+
714
+ ns = dict(
715
+ close=close, open=open_, high=high, low=low,
716
+ volume=volume, returns=returns,
717
+ sma=sma, ema=ema, std=std, rsi=rsi, atr=atr,
718
+ pd=pd, np=np, abs=abs, min=min, max=max,
719
+ )
720
+
721
+ result_series = eval(expr, {"__builtins__": {}}, ns) # noqa: S307
722
+ if hasattr(result_series, "dropna"):
723
+ result_series = result_series.dropna()
724
+
725
+ latest_val = float(result_series.iloc[-1]) if len(result_series) > 0 else None
726
+
727
+ # Last 20 values for chart
728
+ tail = result_series.tail(20)
729
+ series_pts = [{"date": str(d.date()), "value": round(float(v), 6)}
730
+ for d, v in tail.items()]
731
+
732
+ return {
733
+ "success": True,
734
+ "symbol": symbol,
735
+ "expr": expr,
736
+ "latest": latest_val,
737
+ "series": series_pts,
738
+ "data_len": len(result_series),
739
+ }
740
+ except Exception as e:
741
+ return {"success": False, "error": f"表达式计算失败: {e}"}
742
+
743
+
744
+ # ── 通用数据导入 ───────────────────────────────────────────────────────────────
745
+
746
+ def load_csv_data(params: dict) -> dict:
747
+ """
748
+ 从 CSV 文件加载数据到内存 DuckDB 表。
749
+
750
+ 参数:
751
+ path: CSV 文件路径
752
+ table_name: 目标表名(默认由文件名推导)
753
+ encoding: 编码(默认 utf-8)
754
+ """
755
+ if not _HAS_PD:
756
+ return {"success": False, "error": "pandas 未安装"}
757
+
758
+ path = Path(str(params.get("path", ""))).expanduser()
759
+ if not path.exists():
760
+ return {"success": False, "error": f"文件不存在: {path}"}
761
+ if path.suffix.lower() not in (".csv", ".tsv", ".txt"):
762
+ return {"success": False, "error": "仅支持 CSV/TSV 文件"}
763
+
764
+ enc = params.get("encoding", "utf-8")
765
+ tname = str(params.get("table_name", path.stem))
766
+
767
+ try:
768
+ df = pd.read_csv(str(path), encoding=enc)
769
+ if _HAS_DUCK:
770
+ conn = _get_duck_conn()
771
+ conn.register(tname, df)
772
+ _duck_tables[tname] = True
773
+
774
+ return {
775
+ "success": True,
776
+ "table_name": tname,
777
+ "rows": len(df),
778
+ "columns": list(df.columns),
779
+ "sample": df.head(3).to_dict("records"),
780
+ "path": str(path),
781
+ }
782
+ except Exception as e:
783
+ return {"success": False, "error": str(e)}
784
+
785
+
786
+ # ── Private Helpers ───────────────────────────────────────────────────────────
787
+
788
+ def _periods_per_year(rets) -> float:
789
+ """Observations a year in this return series, read from its index.
790
+
791
+ Monthly-rebalanced portfolio returns are monthly, so scaling them by
792
+ sqrt(252) overstated volatility and Sharpe about 4.6x. An index that is not
793
+ dates falls back to 252 (daily), the old assumption.
794
+ """
795
+ from aria_code.domain import performance_stats as stats
796
+
797
+ try:
798
+ idx = rets.index
799
+ # N dated returns span N - 1 intervals.
800
+ found = stats.periods_per_year_from_dates(len(rets) - 1, idx[0], idx[-1])
801
+ except Exception:
802
+ found = None
803
+ return found if found else float(stats.FALLBACK_PERIODS_PER_YEAR)
804
+
805
+
806
+ def _annual_vol(rets) -> float:
807
+ return float(rets.std() * np.sqrt(_periods_per_year(rets)))
808
+
809
+
810
+ def _calc_sharpe(rets, rf_daily: float = 0.0) -> Optional[float]:
811
+ if not _HAS_NP or not hasattr(rets, "__len__") or len(rets) < 2:
812
+ return None
813
+ try:
814
+ excess = rets - rf_daily
815
+ if float(excess.std()) == 0:
816
+ return None
817
+ return round(float(excess.mean() / excess.std() * np.sqrt(_periods_per_year(rets))), 3)
818
+ except Exception:
819
+ return None
820
+
821
+
822
+ def _max_drawdown(cum_series) -> float:
823
+ if not _HAS_NP or not _HAS_PD:
824
+ return 0.0
825
+ try:
826
+ s = pd.Series(cum_series)
827
+ roll_max = s.cummax()
828
+ drawdown = (s - roll_max) / roll_max
829
+ return float(drawdown.min())
830
+ except Exception:
831
+ return 0.0