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,135 @@
1
+ """Auditable freight analysis from supplied waybills or an existing local database."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import csv
6
+ import json
7
+ import math
8
+ import pathlib
9
+ import sqlite3
10
+ from collections import defaultdict
11
+ from contextlib import closing
12
+ from typing import Any, Dict, List
13
+
14
+
15
+ def _number(record: dict[str, Any], field: str, row: int, *, required: bool = False) -> float | None:
16
+ value = record.get(field)
17
+ if value is None or value == "":
18
+ if required:
19
+ raise ValueError(f"Row {row}: {field} is required")
20
+ return None
21
+ if isinstance(value, bool):
22
+ raise ValueError(f"Row {row}: {field} must be a number")
23
+ try:
24
+ number = float(value)
25
+ except (TypeError, ValueError) as exc:
26
+ raise ValueError(f"Row {row}: {field} must be a number") from exc
27
+ if not math.isfinite(number) or number < 0:
28
+ raise ValueError(f"Row {row}: {field} must be finite and non-negative")
29
+ return number
30
+
31
+
32
+ def _on_time(value: Any, row: int) -> bool | None:
33
+ if value is None or value == "":
34
+ return None
35
+ if isinstance(value, bool):
36
+ return value
37
+ if value in (0, 1):
38
+ return bool(value)
39
+ if isinstance(value, str):
40
+ normalized = value.strip().lower()
41
+ if normalized in {"true", "yes", "1"}:
42
+ return True
43
+ if normalized in {"false", "no", "0"}:
44
+ return False
45
+ raise ValueError(f"Row {row}: is_on_time must be a boolean")
46
+
47
+
48
+ def tool_analyze_logistics_data(params: Dict[str, Any]) -> Dict[str, Any]:
49
+ """Analyze actual records. Empty input never produces representative data."""
50
+ try:
51
+ file_path = params.get("file_path")
52
+ records = params.get("waybills")
53
+ if file_path and records is not None:
54
+ raise ValueError("Provide either file_path or waybills, not both")
55
+ if file_path:
56
+ path = pathlib.Path(file_path).expanduser().resolve()
57
+ if path.suffix.lower() == ".json":
58
+ content = json.loads(path.read_text(encoding="utf-8"))
59
+ records = content if isinstance(content, list) else content.get("waybills") if isinstance(content, dict) else None
60
+ elif path.suffix.lower() == ".csv":
61
+ with path.open(encoding="utf-8-sig", newline="") as handle:
62
+ records = list(csv.DictReader(handle))
63
+ else:
64
+ raise ValueError("Only CSV and JSON waybill files are supported")
65
+ source = str(path)
66
+ data_source = f"file:{path.name}"
67
+ elif records is None:
68
+ path = pathlib.Path.home() / ".aria" / "erp_warehouse.db"
69
+ if not path.is_file():
70
+ raise ValueError("No waybills supplied and no local logistics database found")
71
+ with closing(sqlite3.connect(f"{path.as_uri()}?mode=ro", uri=True)) as connection:
72
+ connection.row_factory = sqlite3.Row
73
+ records = [dict(row) for row in connection.execute("SELECT * FROM logistics_waybills")]
74
+ source = str(path)
75
+ data_source = "local logistics database"
76
+ else:
77
+ source = "provided_waybills"
78
+ data_source = "caller-supplied records"
79
+
80
+ if not isinstance(records, list) or not records:
81
+ raise ValueError("No waybill records available for analysis")
82
+
83
+ spend = 0.0
84
+ known_delivery = 0
85
+ on_time = 0
86
+ carriers: dict[str, dict[str, Any]] = defaultdict(lambda: {"count": 0, "total_spend": 0.0, "known_delivery_count": 0, "on_time_count": 0})
87
+ anomalies: list[dict[str, Any]] = []
88
+ for row, record in enumerate(records, 1):
89
+ if not isinstance(record, dict):
90
+ raise ValueError(f"Row {row}: waybill must be an object")
91
+ cost = _number(record, "total_cost", row, required=True)
92
+ assert cost is not None
93
+ actual = _number(record, "actual_weight_kg", row)
94
+ billed = _number(record, "billed_weight_kg", row)
95
+ timely = _on_time(record.get("is_on_time"), row)
96
+ carrier = str(record.get("carrier") or "Unknown")
97
+ spend += cost
98
+ carriers[carrier]["count"] += 1
99
+ carriers[carrier]["total_spend"] += cost
100
+ if timely is not None:
101
+ known_delivery += 1
102
+ on_time += int(timely)
103
+ carriers[carrier]["known_delivery_count"] += 1
104
+ carriers[carrier]["on_time_count"] += int(timely)
105
+ if actual is not None and billed is not None and actual > 0 and billed > actual * 1.2:
106
+ anomalies.append({"waybill_no": record.get("waybill_no"), "carrier": carrier, "actual_weight_kg": actual, "billed_weight_kg": billed, "reason": "Billed weight exceeds actual weight by more than 20%; verify dimensional weight before disputing"})
107
+
108
+ metrics = []
109
+ for carrier, item in sorted(carriers.items()):
110
+ item["total_spend"] = round(item["total_spend"], 2)
111
+ item["on_time_rate"] = round(item["on_time_count"] / item["known_delivery_count"] * 100, 2) if item["known_delivery_count"] else None
112
+ metrics.append({"carrier": carrier, **item})
113
+ rate = round(on_time / known_delivery * 100, 2) if known_delivery else None
114
+ result = {"total_waybills": len(records), "total_freight_spend": round(spend, 2), "overall_on_time_rate": rate, "known_delivery_count": known_delivery, "carrier_metrics": metrics, "billing_anomalies": anomalies, "data_source": data_source}
115
+ rate_label = f"{rate}%" if rate is not None else "不可得(无准时状态记录)"
116
+ return {"success": True, "data": result, "source": source, "summary": f"已审计 {len(records)} 单运单(来源:{data_source}),运费总计 ¥{spend:,.2f},已知准时交付率 {rate_label},发现 {len(anomalies)} 笔需核实的计费重量异常。"}
117
+ except (OSError, sqlite3.Error, ValueError, TypeError, json.JSONDecodeError) as exc:
118
+ return {"success": False, "error": str(exc)}
119
+
120
+
121
+ def register_logistics_tools(tools_dict: Dict[str, Any], schemas_list: List[Dict[str, Any]]) -> int:
122
+ """Register the waybill analyzer as a local tool."""
123
+ tools_dict["analyze_logistics_data"] = (tool_analyze_logistics_data, "Analyze supplied freight waybills and billing anomalies")
124
+ schemas_list.append({
125
+ "name": "analyze_logistics_data",
126
+ "description": "Analyze actual waybills from supplied records, a CSV/JSON file, or an existing local database",
127
+ "parameters": {
128
+ "type": "object",
129
+ "properties": {
130
+ "file_path": {"type": "string", "description": "CSV or JSON waybill file path"},
131
+ "waybills": {"type": "array", "description": "Waybill records with total_cost"},
132
+ },
133
+ },
134
+ })
135
+ return 1
@@ -0,0 +1,369 @@
1
+ """
2
+ macro_tools.py — 宏观经济数据层
3
+ =================================
4
+ 来源:
5
+ - FRED (美联储 St. Louis) — GDP, CPI, 联邦基金利率, 失业率, M2
6
+ - AKShare — 中国 CPI/PPI/PMI/GDP/LPR/社融
7
+ - yfinance — 全球央行政策利率(备用)
8
+
9
+ 全部函数返回 {"success": bool, "data": [...], ...} 格式,
10
+ 与 local_finance_tools 保持一致。
11
+
12
+ 依赖安装(可选):
13
+ pip install requests # FRED REST API
14
+ pip install akshare # 中国数据
15
+ """
16
+
17
+ from __future__ import annotations
18
+
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
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 requests as _req
32
+ _HAS_REQUESTS = True
33
+ except ImportError:
34
+ _HAS_REQUESTS = False
35
+
36
+ try:
37
+ import akshare as ak
38
+ _HAS_AK = True
39
+ except ImportError:
40
+ _HAS_AK = False
41
+
42
+ try:
43
+ import pandas as pd
44
+ _HAS_PD = True
45
+ except ImportError:
46
+ _HAS_PD = False
47
+
48
+ # ── FRED API helper ──────────────────────────────────────────────────────────
49
+
50
+ FRED_BASE = "https://fred.stlouisfed.org/graph/fredgraph.csv"
51
+ FRED_API_BASE = "https://api.stlouisfed.org/fred"
52
+
53
+ def _get_fred_key() -> str:
54
+ """Read FRED API key from env or ~/.arthera/providers.json."""
55
+ key = os.getenv("FRED_API_KEY", "")
56
+ if not key:
57
+ try:
58
+ import json
59
+ p = aria_home() / "providers.json"
60
+ if p.exists():
61
+ d = json.loads(p.read_text())
62
+ key = d.get("fred", {}).get("api_key", "") or d.get("fred_api_key", "")
63
+ except Exception:
64
+ pass
65
+ return key
66
+
67
+
68
+ def _fred_series(series_id: str, limit: int = 24, units: str = "") -> List[Dict]:
69
+ """Fetch a FRED data series. Returns list of {date, value} dicts.
70
+
71
+ ``units`` maps to FRED's units transformation, e.g. "pc1" = percent change
72
+ from a year ago (used for YoY rates like CPI inflation, so we don't show
73
+ the raw index level mislabelled as a percentage).
74
+ """
75
+ if not _HAS_REQUESTS:
76
+ return []
77
+ key = _get_fred_key()
78
+ try:
79
+ params: Dict[str, Any] = {
80
+ "series_id": series_id,
81
+ "limit": limit,
82
+ "sort_order": "desc",
83
+ "file_type": "json",
84
+ }
85
+ if units:
86
+ params["units"] = units
87
+ if key:
88
+ params["api_key"] = key
89
+ url = f"{FRED_API_BASE}/series/observations"
90
+ r = _req.get(url, params=params, timeout=8)
91
+ r.raise_for_status()
92
+ obs = r.json().get("observations", [])
93
+ return [
94
+ {"date": o["date"], "value": float(o["value"]) if o["value"] != "." else None}
95
+ for o in reversed(obs)
96
+ ]
97
+ else:
98
+ # Public CSV endpoint (no key needed, slower). fredgraph supports
99
+ # transformation codes via the `transformation` query param.
100
+ obs_url = f"https://fred.stlouisfed.org/graph/fredgraph.csv?id={series_id}"
101
+ if units:
102
+ obs_url += f"&transformation={units}"
103
+ r = _req.get(obs_url, timeout=10)
104
+ r.raise_for_status()
105
+ lines = r.text.strip().split("\n")[1:] # skip header
106
+ result = []
107
+ for line in lines[-limit:]:
108
+ parts = line.split(",")
109
+ if len(parts) == 2:
110
+ try:
111
+ result.append({"date": parts[0], "value": float(parts[1])})
112
+ except ValueError:
113
+ pass
114
+ return result
115
+ except Exception as e:
116
+ logger.debug("FRED fetch %s failed: %s", series_id, e)
117
+ return []
118
+
119
+
120
+ # ── US Macro ──────────────────────────────────────────────────────────────────
121
+
122
+ _FRED_SERIES_MAP = {
123
+ "gdp": ("GDP", "GDP 实际值 (十亿美元, 季度)", "B"),
124
+ "gdp_growth": ("A191RL1Q225SBEA", "GDP 同比增速 (%)", "%"),
125
+ "cpi": ("CPIAUCSL", "CPI 城市消费者价格指数", "idx"),
126
+ "cpi_yoy": ("CPIAUCSL", "CPI 同比 (%)", "%", "pc1"), # pc1 = YoY %
127
+ "core_cpi_yoy": ("CPILFESL", "核心 CPI 同比 (%)", "%", "pc1"),
128
+ "core_cpi": ("CPILFESL", "核心 CPI (剔除食品能源)", "idx"),
129
+ "pce": ("PCEPI", "PCE 通胀指数", "idx"),
130
+ "fed_rate": ("FEDFUNDS", "联邦基金利率 (%)", "%"),
131
+ "unemployment": ("UNRATE", "美国失业率 (%)", "%"),
132
+ "m2": ("M2SL", "M2 货币供给 (十亿美元)", "B"),
133
+ "10y_yield": ("DGS10", "10年期国债收益率 (%)", "%"),
134
+ "2y_yield": ("DGS2", "2年期国债收益率 (%)", "%"),
135
+ "vix": ("VIXCLS", "VIX 恐慌指数", ""),
136
+ "retail_sales": ("RSAFS", "零售销售额 (百万美元, 季调)", "M"),
137
+ "industrial": ("INDPRO", "工业产出指数", "idx"),
138
+ "housing": ("HOUST", "新屋开工 (千套, 季调年化)", "K"),
139
+ "ppi": ("PPIACO", "PPI 生产者价格指数", "idx"),
140
+ }
141
+
142
+
143
+ def get_us_macro(indicator: str = "all", periods: int = 12) -> dict:
144
+ """
145
+ 获取美国宏观经济数据。
146
+
147
+ indicator: "gdp" | "cpi" | "fed_rate" | "unemployment" | "m2" |
148
+ "10y_yield" | "retail_sales" | "all"
149
+ periods: 返回最近 N 期数据
150
+ """
151
+ if indicator == "all":
152
+ keys = ["gdp_growth", "cpi_yoy", "fed_rate", "unemployment", "10y_yield",
153
+ "2y_yield", "m2", "ppi"]
154
+ else:
155
+ keys = [indicator] if indicator in _FRED_SERIES_MAP else []
156
+ if not keys:
157
+ return {"success": False, "error": f"未知指标: {indicator}。可选: {', '.join(_FRED_SERIES_MAP)}"}
158
+
159
+ results = {}
160
+ for key in keys:
161
+ _spec = _FRED_SERIES_MAP[key]
162
+ series_id, label, unit = _spec[0], _spec[1], _spec[2]
163
+ _units = _spec[3] if len(_spec) > 3 else ""
164
+ # pc1 (YoY) needs ~13 months of monthly data to compute the first point
165
+ _lim = max(periods, 14) if _units == "pc1" else periods
166
+ data = _fred_series(series_id, limit=_lim, units=_units)
167
+ if data:
168
+ latest = data[-1]
169
+ prev = data[-2] if len(data) >= 2 else None
170
+ change = None
171
+ if latest["value"] is not None and prev and prev["value"] is not None:
172
+ change = round(latest["value"] - prev["value"], 3)
173
+ results[key] = {
174
+ "label": label,
175
+ "unit": unit,
176
+ "latest": latest,
177
+ "prev": prev,
178
+ "change": change,
179
+ "series": data[-6:], # last 6 periods for sparkline
180
+ }
181
+
182
+ if not results:
183
+ return {"success": False, "error": "FRED 数据获取失败(无 API Key 时使用公共 CSV 端点,速度较慢)"}
184
+
185
+ # Yield curve shape
186
+ if "10y_yield" in results and "2y_yield" in results:
187
+ v10 = results["10y_yield"]["latest"]["value"] or 0
188
+ v2 = results["2y_yield"]["latest"]["value"] or 0
189
+ spread = round(v10 - v2, 3)
190
+ results["_yield_curve"] = {
191
+ "spread_10y_2y": spread,
192
+ "shape": "正常" if spread > 0.2 else "倒挂" if spread < 0 else "平坦",
193
+ }
194
+
195
+ return {"success": True, "country": "US", "indicator": indicator,
196
+ "data": results, "provider": "FRED"}
197
+
198
+
199
+ # ── China Macro ───────────────────────────────────────────────────────────────
200
+
201
+ def get_cn_macro(indicator: str = "all") -> dict:
202
+ """
203
+ 获取中国宏观经济数据 (via akshare)。
204
+
205
+ indicator: "cpi" | "ppi" | "pmi" | "gdp" | "lpr" | "m2" | "all"
206
+ """
207
+ if not _HAS_AK:
208
+ return {"success": False, "error": "akshare 未安装,请运行: pip install akshare"}
209
+
210
+ results = {}
211
+
212
+ def _fetch(fn, key, label):
213
+ try:
214
+ df = fn()
215
+ if df is None or (hasattr(df, "empty") and df.empty):
216
+ return
217
+ # Keep last 12 rows
218
+ df = df.tail(12)
219
+ records = df.to_dict("records") if _HAS_PD else []
220
+ latest = records[-1] if records else {}
221
+ results[key] = {"label": label, "latest": latest, "series": records}
222
+ except Exception as e:
223
+ logger.debug("akshare %s failed: %s", key, e)
224
+
225
+ if indicator in ("cpi", "all"):
226
+ _fetch(ak.macro_china_cpi_yearly, "cpi", "中国 CPI 同比 (%)")
227
+
228
+ if indicator in ("ppi", "all"):
229
+ _fetch(ak.macro_china_ppi_yearly, "ppi", "中国 PPI 同比 (%)")
230
+
231
+ if indicator in ("pmi", "all"):
232
+ _fetch(ak.macro_china_pmi_yearly, "pmi_manufacturing",
233
+ "制造业 PMI")
234
+ try:
235
+ df_s = ak.macro_china_non_man_pmi()
236
+ if df_s is not None and not df_s.empty:
237
+ records_s = df_s.tail(12).to_dict("records")
238
+ results["pmi_service"] = {
239
+ "label": "非制造业 PMI",
240
+ "latest": records_s[-1] if records_s else {},
241
+ "series": records_s,
242
+ }
243
+ except Exception:
244
+ pass
245
+
246
+ if indicator in ("gdp", "all"):
247
+ _fetch(ak.macro_china_gdp_yearly, "gdp", "中国 GDP 同比增速 (%)")
248
+
249
+ if indicator in ("lpr", "all"):
250
+ try:
251
+ df_lpr = ak.macro_china_lpr()
252
+ if df_lpr is not None and not df_lpr.empty:
253
+ records_l = df_lpr.tail(6).to_dict("records")
254
+ results["lpr"] = {"label": "LPR 利率", "series": records_l,
255
+ "latest": records_l[-1] if records_l else {}}
256
+ except Exception as e:
257
+ logger.debug("LPR fetch failed: %s", e)
258
+
259
+ if indicator in ("m2", "all"):
260
+ _fetch(ak.macro_china_money_supply, "m2", "中国 M2 同比增速 (%)")
261
+
262
+ if not results:
263
+ return {"success": False, "error": "未能获取任何中国宏观数据"}
264
+
265
+ return {"success": True, "country": "CN", "indicator": indicator,
266
+ "data": results, "provider": "akshare"}
267
+
268
+
269
+ # ── Economic Calendar ─────────────────────────────────────────────────────────
270
+
271
+ def get_economic_calendar(days_ahead: int = 7) -> dict:
272
+ """
273
+ 获取未来 N 天的重大经济事件日历。
274
+ 数据来源:投资道/东方财富 (akshare),备用: Finnhub
275
+ """
276
+ results = []
277
+
278
+ # Try akshare economic calendar (东方财富)
279
+ if _HAS_AK:
280
+ try:
281
+ df = ak.news_economic_baidu(date=datetime.now().strftime("%Y%m%d"))
282
+ if df is not None and not df.empty:
283
+ cols = [c for c in ["time","event","actual","forecast","previous","importance"]
284
+ if c in df.columns]
285
+ results = df[cols].head(30).to_dict("records") if cols else []
286
+ except Exception as e:
287
+ logger.debug("akshare calendar failed: %s", e)
288
+
289
+ # Fallback: Finnhub economic calendar
290
+ if not results and _HAS_REQUESTS:
291
+ try:
292
+ key = _get_finnhub_key()
293
+ if key:
294
+ from_dt = datetime.now().strftime("%Y-%m-%d")
295
+ to_dt = (datetime.now() + timedelta(days=days_ahead)).strftime("%Y-%m-%d")
296
+ r = _req.get(
297
+ "https://finnhub.io/api/v1/calendar/economic",
298
+ params={"from": from_dt, "to": to_dt, "token": key},
299
+ timeout=8,
300
+ )
301
+ r.raise_for_status()
302
+ results = r.json().get("economicCalendar", [])
303
+ except Exception as e:
304
+ logger.debug("Finnhub calendar failed: %s", e)
305
+
306
+ if not results:
307
+ # Static upcoming FOMC/PBOC dates as fallback
308
+ results = [
309
+ {"event": "FOMC Meeting", "importance": "HIGH",
310
+ "note": "具体日期请查阅 federalreserve.gov/monetarypolicy"},
311
+ {"event": "PBOC LPR Announcement", "importance": "HIGH",
312
+ "note": "每月 20 日前后公布"},
313
+ {"event": "US CPI Release", "importance": "HIGH",
314
+ "note": "每月第二周(周二或周三)"},
315
+ {"event": "China PMI Release", "importance": "MEDIUM",
316
+ "note": "每月最后一天(官方)/ 次月第一工作日(财新)"},
317
+ ]
318
+
319
+ return {"success": True, "events": results, "days_ahead": days_ahead,
320
+ "provider": "akshare/finnhub"}
321
+
322
+
323
+ def _get_finnhub_key() -> str:
324
+ """Read Finnhub key from env or providers.json."""
325
+ k = os.getenv("FINNHUB_API_KEY", "")
326
+ if not k:
327
+ try:
328
+ import json
329
+ p = aria_home() / "providers.json"
330
+ if p.exists():
331
+ d = json.loads(p.read_text())
332
+ k = d.get("finnhub", {}).get("api_key", "") or d.get("finnhub_api_key", "")
333
+ except Exception:
334
+ pass
335
+ return k
336
+
337
+
338
+ # ── Central Bank Policy Rates ─────────────────────────────────────────────────
339
+
340
+ def get_central_bank_rates() -> dict:
341
+ """主要央行政策利率快照。"""
342
+ CB_TICKERS = {
343
+ "美联储 (Fed Funds)": "FEDFUNDS",
344
+ "欧央行 (ECB Refi)": "ECBDFR",
345
+ "英央行 (BoE Rate)": "BOERUKM",
346
+ "日央行 (BoJ Rate)": "IRSTCI01JPM156N",
347
+ }
348
+ rates = {}
349
+ for name, series in CB_TICKERS.items():
350
+ data = _fred_series(series, limit=3)
351
+ if data:
352
+ latest = data[-1]
353
+ rates[name] = latest["value"]
354
+
355
+ # PBOC LPR via akshare
356
+ if _HAS_AK:
357
+ try:
358
+ df = ak.macro_china_lpr()
359
+ if df is not None and not df.empty:
360
+ row = df.iloc[-1]
361
+ rates["中国人民银行 LPR 1Y"] = float(row.get("1年期贷款市场报价利率", row.iloc[1]))
362
+ rates["中国人民银行 LPR 5Y"] = float(row.get("5年期贷款市场报价利率", row.iloc[2]))
363
+ except Exception:
364
+ pass
365
+
366
+ if not rates:
367
+ return {"success": False, "error": "无法获取央行利率数据"}
368
+
369
+ return {"success": True, "rates": rates, "provider": "FRED+akshare"}