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,278 @@
1
+ """
2
+ datasources/sources/web_scraper_source.py — 公开财务数据网络爬虫
3
+ ================================================================
4
+ 爬取完全公开的金融数据网站,无需账号或 API key。
5
+ 所有请求遵守 robots.txt 和频率限制。
6
+
7
+ 覆盖:
8
+ - Macrotrends — 历史财务数据(收入/利润/现金流/估值)
9
+ - Wisesheets — 免费财务摘要
10
+ - 东方财富 — A股公告/研报摘要
11
+ - 同花顺 — A股财务数据
12
+ - Yahoo Finance — 基本面摘要(HTML解析)
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ import re
19
+ import time
20
+ import urllib.parse
21
+ import urllib.request
22
+ from typing import Any, Dict, List, Optional
23
+
24
+ from ..base import BaseDataSource, FundamentalsResult, HistoryResult, QuoteResult
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ _DEFAULT_HEADERS = {
29
+ "User-Agent": (
30
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
31
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
32
+ "Chrome/120.0.0.0 Safari/537.36"
33
+ ),
34
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
35
+ "Accept": "text/html,application/xhtml+xml,application/json,*/*;q=0.8",
36
+ }
37
+
38
+
39
+ def _fetch_html(url: str, timeout: int = 12, encoding: str = "utf-8") -> Optional[str]:
40
+ try:
41
+ req = urllib.request.Request(url, headers=_DEFAULT_HEADERS)
42
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
43
+ raw = resp.read()
44
+ try:
45
+ return raw.decode(encoding)
46
+ except UnicodeDecodeError:
47
+ return raw.decode("gbk", errors="replace")
48
+ except Exception as e:
49
+ logger.debug(f"[web_scraper] fetch {url[:80]} 失败: {e}")
50
+ return None
51
+
52
+
53
+ def _fetch_json(url: str, timeout: int = 12, extra_headers: dict = None) -> Optional[Any]:
54
+ import json
55
+ try:
56
+ headers = {**_DEFAULT_HEADERS, **(extra_headers or {})}
57
+ req = urllib.request.Request(url, headers=headers)
58
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
59
+ return json.loads(resp.read())
60
+ except Exception as e:
61
+ logger.debug(f"[web_scraper] json {url[:80]} 失败: {e}")
62
+ return None
63
+
64
+
65
+ # ─── 东方财富 A股公告爬虫 ────────────────────────────────────────────────────
66
+
67
+ def scrape_eastmoney_announcements(symbol: str, count: int = 10) -> List[Dict]:
68
+ """
69
+ 爬取东方财富 A股公告列表(免费公开页面)。
70
+ symbol: 6位A股代码(如 '600519')
71
+ """
72
+ s = symbol.replace(".SS", "").replace(".SZ", "").zfill(6)
73
+ url = (
74
+ f"https://np-anotice-stock.eastmoney.com/api/security/ann?"
75
+ f"sr=-1&page_size={count}&page_index=1&ann_type=A&client_source=web"
76
+ f"&stock_list={s}&f_node=0&s_node=0"
77
+ )
78
+ data = _fetch_json(url, extra_headers={"Referer": "https://www.eastmoney.com/"})
79
+ if not data or "data" not in data:
80
+ return []
81
+ anns = data["data"].get("list", [])
82
+ result = []
83
+ for a in anns[:count]:
84
+ result.append({
85
+ "title": a.get("notice_title", ""),
86
+ "date": a.get("notice_date", ""),
87
+ "type": a.get("notice_type", ""),
88
+ "url": f"https://data.eastmoney.com/notices/detail/{s}/{a.get('art_code','')}.html",
89
+ "source": "eastmoney",
90
+ })
91
+ return result
92
+
93
+
94
+ def scrape_eastmoney_news(symbol: str, count: int = 10) -> List[Dict]:
95
+ """爬取东方财富个股新闻。"""
96
+ s = symbol.replace(".SS", "").replace(".SZ", "").zfill(6)
97
+ url = (
98
+ f"https://newsapi.eastmoney.com/kuaixun/v1/getlist_101_ajaxResult_50_{s},,50,1.html"
99
+ )
100
+ html = _fetch_html(url)
101
+ if not html:
102
+ return []
103
+ # 尝试从 JSON 响应中解析
104
+ try:
105
+ import json
106
+ data = json.loads(html)
107
+ items = data.get("LiveList", [])[:count]
108
+ return [{
109
+ "title": i.get("title", ""),
110
+ "time": i.get("showtime", ""),
111
+ "source": i.get("medianame", ""),
112
+ "url": i.get("url", ""),
113
+ } for i in items]
114
+ except Exception:
115
+ pass
116
+ return []
117
+
118
+
119
+ # ─── Macrotrends 历史财务数据爬虫 ────────────────────────────────────────────
120
+
121
+ def scrape_macrotrends_revenue(symbol: str) -> Optional[List[Dict]]:
122
+ """
123
+ 从 Macrotrends 爬取年度营收历史(免费公开页面,无需登录)。
124
+ 注意:依赖页面结构,如网站改版可能失效。
125
+ """
126
+ slug_map = {"AAPL": "apple", "MSFT": "microsoft", "GOOGL": "alphabet",
127
+ "AMZN": "amazon", "META": "meta-platforms", "TSLA": "tesla",
128
+ "NVDA": "nvidia", "JPM": "jpmorgan-chase", "V": "visa"}
129
+ slug = slug_map.get(symbol.upper())
130
+ if not slug:
131
+ slug = symbol.lower()
132
+
133
+ url = f"https://www.macrotrends.net/stocks/charts/{symbol.upper()}/{slug}/revenue"
134
+ html = _fetch_html(url)
135
+ if not html:
136
+ return None
137
+
138
+ # 提取嵌入的 JSON 数据
139
+ match = re.search(r'var originalData\s*=\s*(\[.*?\]);', html, re.DOTALL)
140
+ if not match:
141
+ return None
142
+ try:
143
+ import json
144
+ rows = json.loads(match.group(1))
145
+ return [
146
+ {"date": r.get("date", ""), "revenue_usd": r.get("v1", 0)}
147
+ for r in rows
148
+ ][-20:] # 最近20个数据点
149
+ except Exception:
150
+ return None
151
+
152
+
153
+ # ─── SSE/SZSE 交易所公开数据爬虫 ─────────────────────────────────────────────
154
+
155
+ def scrape_sse_financials(symbol: str) -> Optional[Dict]:
156
+ """
157
+ 爬取上交所公开的公司财务摘要(适用于 6 开头的A股)。
158
+ """
159
+ s = symbol.replace(".SS", "").zfill(6)
160
+ if not s.startswith(("6", "9")):
161
+ return None
162
+
163
+ url = (
164
+ f"https://query.sse.com.cn/commonSoaQuery.do?"
165
+ f"isPagination=false&sqlId=COMMON_SSE_CP_GPLB_GPXG_JBXX_L&stockCode={s}"
166
+ )
167
+ data = _fetch_json(url, extra_headers={
168
+ "Referer": "https://www.sse.com.cn/",
169
+ "Host": "query.sse.com.cn",
170
+ })
171
+ if not data or "result" not in data:
172
+ return None
173
+ r = data["result"]
174
+ if isinstance(r, list) and r:
175
+ r = r[0]
176
+ if not isinstance(r, dict):
177
+ return None
178
+ return {
179
+ "symbol": s,
180
+ "name": r.get("FULL_NAME", ""),
181
+ "industry": r.get("INDUSTRY", ""),
182
+ "listing_date": r.get("LISTING_DATE", ""),
183
+ "total_shares": r.get("TOTAL_SHARES", ""),
184
+ "source": "sse",
185
+ }
186
+
187
+
188
+ def scrape_szse_financials(symbol: str) -> Optional[Dict]:
189
+ """爬取深交所公开的公司基本信息(适用于 0/3 开头的A股)。"""
190
+ s = symbol.replace(".SZ", "").zfill(6)
191
+ if not s.startswith(("0", "3", "2")):
192
+ return None
193
+
194
+ url = (
195
+ "https://www.szse.cn/api/report/show/nature/detail?"
196
+ "id=&"
197
+ )
198
+ # 深交所 API
199
+ data = _fetch_json(
200
+ "https://www.szse.cn/api/market/smstock/list?random=0.1&tab2PAGENUM=1"
201
+ "&tab2PAGENO=1&tab2COUNT=1&tab2SORTTAB=tab2&tab2SORTKEY=",
202
+ extra_headers={"Referer": "https://www.szse.cn/"},
203
+ )
204
+ return None # 深交所API结构复杂,返回None让上层fallback
205
+
206
+
207
+ # ─── 统一爬虫接口 ─────────────────────────────────────────────────────────────
208
+
209
+ class WebScraperSource(BaseDataSource):
210
+ """
211
+ 公开网页数据爬虫 — 汇总多个免费公开金融数据网站。
212
+ 实现 BaseDataSource 接口以便接入路由器;quote/history 委托给主数据源,
213
+ fundamentals() 提供爬取的补充数据(公告、交易所基本信息等)。
214
+ """
215
+
216
+ name = "web_scraper"
217
+ markets = ["a_share", "us"]
218
+ requires_key = False
219
+
220
+ def is_configured(self) -> bool:
221
+ return True
222
+
223
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
224
+ return None # 不提供实时行情,由 akshare/yfinance 负责
225
+
226
+ def fundamentals(self, symbol: str) -> Optional[FundamentalsResult]:
227
+ """从交易所公开页面补充基本信息(仅 A 股)。"""
228
+ s = symbol.replace(".SS", "").replace(".SZ", "").zfill(6)
229
+ info = None
230
+ if s.startswith(("6", "9")):
231
+ info = scrape_sse_financials(s)
232
+ elif s.startswith(("0", "3")):
233
+ info = scrape_szse_financials(s)
234
+ if not info:
235
+ return None
236
+ return FundamentalsResult(
237
+ symbol = symbol,
238
+ source = self.name,
239
+ )
240
+
241
+ # ── 扩展方法(供上层直接调用) ─────────────────────────────────────────────
242
+
243
+ def get_announcements(self, symbol: str, count: int = 10) -> List[Dict]:
244
+ """A股公告列表(东方财富)。"""
245
+ return scrape_eastmoney_announcements(symbol, count)
246
+
247
+ def get_news(self, symbol: str, count: int = 10) -> List[Dict]:
248
+ """个股新闻。"""
249
+ return scrape_eastmoney_news(symbol, count)
250
+
251
+ def get_historical_revenue(self, symbol: str) -> Optional[List[Dict]]:
252
+ """美股历史营收(Macrotrends)。"""
253
+ return scrape_macrotrends_revenue(symbol)
254
+
255
+ def get_exchange_info(self, symbol: str) -> Optional[Dict]:
256
+ """A股交易所公开信息。"""
257
+ s = symbol.replace(".SS", "").replace(".SZ", "").zfill(6)
258
+ if s.startswith(("6", "9")):
259
+ return scrape_sse_financials(s)
260
+ elif s.startswith(("0", "3")):
261
+ return scrape_szse_financials(s)
262
+ return None
263
+
264
+ def bulk_scrape(self, symbols: List[str], data_type: str = "announcements") -> Dict[str, Any]:
265
+ """批量爬取,带速率限制(每次请求间隔 1 秒)。"""
266
+ results = {}
267
+ for sym in symbols:
268
+ try:
269
+ if data_type == "announcements":
270
+ results[sym] = self.get_announcements(sym)
271
+ elif data_type == "news":
272
+ results[sym] = self.get_news(sym)
273
+ elif data_type == "revenue":
274
+ results[sym] = self.get_historical_revenue(sym)
275
+ time.sleep(1.0)
276
+ except Exception as e:
277
+ results[sym] = {"error": str(e)}
278
+ return results
@@ -0,0 +1,206 @@
1
+ """
2
+ datasources/sources/world_bank_source.py — 世界银行开放数据
3
+ ============================================================
4
+ 完全免费,无需 API key。数据来源:https://api.worldbank.org/v2/
5
+ 覆盖:GDP/人均GDP/通胀/贸易/外债/人口/能源等 16,000+ 指标,200+ 国家。
6
+
7
+ 常用指标:
8
+ NY.GDP.MKTP.CD — GDP(当前美元)
9
+ NY.GDP.PCAP.CD — 人均GDP
10
+ FP.CPI.TOTL.ZG — 通货膨胀率(CPI 年增长率)
11
+ NE.TRD.GNFS.ZS — 贸易占GDP比重
12
+ SL.UEM.TOTL.ZS — 失业率
13
+ SP.POP.TOTL — 总人口
14
+ BX.KLT.DINV.CD.WD — 外商直接投资(净流入)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ import urllib.request
21
+ from typing import Any, Dict, List, Optional
22
+
23
+ from ..base import BaseDataSource, HistoryResult, QuoteResult
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ _BASE = "https://api.worldbank.org/v2"
28
+
29
+ # 常用指标别名
30
+ INDICATOR_ALIASES: Dict[str, str] = {
31
+ "GDP": "NY.GDP.MKTP.CD",
32
+ "GDPPC": "NY.GDP.PCAP.CD",
33
+ "GDPGROWTH": "NY.GDP.MKTP.KD.ZG",
34
+ "CPI": "FP.CPI.TOTL.ZG",
35
+ "INFLATION": "FP.CPI.TOTL.ZG",
36
+ "UNRATE": "SL.UEM.TOTL.ZS",
37
+ "POPULATION":"SP.POP.TOTL",
38
+ "TRADE": "NE.TRD.GNFS.ZS",
39
+ "FDI": "BX.KLT.DINV.CD.WD",
40
+ "DEBT": "GC.DOD.TOTL.GD.ZS",
41
+ "EXPORTS": "NE.EXP.GNFS.ZS",
42
+ "IMPORTS": "NE.IMP.GNFS.ZS",
43
+ }
44
+
45
+ # 国家代码别名
46
+ COUNTRY_ALIASES: Dict[str, str] = {
47
+ "CHINA": "CN", "CN": "CN", "CHN": "CN",
48
+ "US": "US", "USA": "US", "AMERICA": "US",
49
+ "JAPAN": "JP", "JP": "JP",
50
+ "GERMANY": "DE", "DE": "DE",
51
+ "UK": "GB", "GB": "GB",
52
+ "INDIA": "IN", "IN": "IN",
53
+ "BRAZIL": "BR",
54
+ "WORLD": "WLD", "GLOBAL": "WLD",
55
+ "G7": "G7", "G20": "G20",
56
+ }
57
+
58
+
59
+ def _fetch(url: str, timeout: int = 15) -> Optional[List]:
60
+ import json
61
+ try:
62
+ req = urllib.request.Request(
63
+ url + "&format=json&per_page=100",
64
+ headers={"User-Agent": "aria-code/1.0"}
65
+ )
66
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
67
+ data = json.loads(resp.read())
68
+ if isinstance(data, list) and len(data) >= 2:
69
+ return data[1]
70
+ return None
71
+ except Exception as e:
72
+ logger.debug(f"[world_bank] fetch 失败: {e}")
73
+ return None
74
+
75
+
76
+ class WorldBankSource(BaseDataSource):
77
+ """世界银行开放数据 — 宏观经济与发展指标。"""
78
+
79
+ name = "world_bank"
80
+ markets = ["macro", "us", "cn"]
81
+ requires_key = False
82
+
83
+ def is_configured(self) -> bool:
84
+ return True
85
+
86
+ def supports(self, symbol: str) -> bool:
87
+ s = symbol.upper()
88
+ return (
89
+ "." in s and len(s) > 5 or # looks like WB indicator (NY.GDP.*)
90
+ s in INDICATOR_ALIASES
91
+ )
92
+
93
+ def _resolve_indicator(self, indicator: str) -> str:
94
+ return INDICATOR_ALIASES.get(indicator.upper(), indicator)
95
+
96
+ def _resolve_country(self, country: str) -> str:
97
+ return COUNTRY_ALIASES.get(country.upper(), country.upper())
98
+
99
+ def get_indicator(
100
+ self,
101
+ indicator: str,
102
+ country: str = "WLD",
103
+ start_year: int = 2000,
104
+ end_year: int = 2024,
105
+ ) -> Optional[HistoryResult]:
106
+ """
107
+ 获取指定国家/地区的经济指标历史序列。
108
+
109
+ indicator: 世界银行指标代码或别名(GDP/CPI/UNRATE 等)
110
+ country: ISO 2位代码或别名(CN/US/JP/WORLD 等)
111
+ """
112
+ try:
113
+ import pandas as pd
114
+ ind = self._resolve_indicator(indicator)
115
+ cty = self._resolve_country(country)
116
+ url = (f"{_BASE}/country/{cty}/indicator/{ind}"
117
+ f"?date={start_year}:{end_year}")
118
+ rows_raw = _fetch(url)
119
+ if not rows_raw:
120
+ return None
121
+ rows = []
122
+ for r in rows_raw:
123
+ if r.get("value") is not None:
124
+ rows.append({
125
+ "date": f"{r['date']}-12-31",
126
+ "close": float(r["value"]),
127
+ "country": r.get("country", {}).get("value", cty),
128
+ })
129
+ if not rows:
130
+ return None
131
+ df = pd.DataFrame(rows)
132
+ df["date"] = pd.to_datetime(df["date"])
133
+ df = df.set_index("date").sort_index()
134
+ return HistoryResult(symbol=f"{country}:{indicator}", data=df,
135
+ source=self.name, interval="1y")
136
+ except Exception as e:
137
+ logger.debug(f"[world_bank] get_indicator 失败: {e}")
138
+ return None
139
+
140
+ def compare_countries(
141
+ self,
142
+ indicator: str,
143
+ countries: List[str],
144
+ start_year: int = 2010,
145
+ ) -> Optional[Dict[str, Any]]:
146
+ """
147
+ 多国横向对比同一指标。
148
+ 返回: {country: [(year, value), ...], ...}
149
+ """
150
+ ind = self._resolve_indicator(indicator)
151
+ cty_codes = ";".join(self._resolve_country(c) for c in countries)
152
+ url = (f"{_BASE}/country/{cty_codes}/indicator/{ind}"
153
+ f"?date={start_year}:2024")
154
+ rows_raw = _fetch(url)
155
+ if not rows_raw:
156
+ return None
157
+
158
+ result: Dict[str, list] = {}
159
+ for r in rows_raw:
160
+ if r.get("value") is None:
161
+ continue
162
+ cty_name = r.get("country", {}).get("value", "?")
163
+ yr = r.get("date", "")
164
+ val = float(r["value"])
165
+ result.setdefault(cty_name, []).append((yr, val))
166
+
167
+ for k in result:
168
+ result[k].sort(key=lambda x: x[0])
169
+ return result
170
+
171
+ def search_indicators(self, query: str, limit: int = 10) -> List[Dict]:
172
+ """关键词搜索世界银行指标。"""
173
+ import json
174
+ import urllib.parse
175
+ q = urllib.parse.quote(query)
176
+ url = f"{_BASE}/indicator?format=json&per_page={limit}&mrv=1"
177
+ try:
178
+ req = urllib.request.Request(
179
+ f"{_BASE}/indicator?format=json&per_page={limit}&source=2",
180
+ headers={"User-Agent": "aria-code/1.0"}
181
+ )
182
+ with urllib.request.urlopen(req, timeout=10) as resp:
183
+ data = json.loads(resp.read())
184
+ if isinstance(data, list) and len(data) >= 2:
185
+ return [
186
+ {"id": i["id"], "name": i["name"],
187
+ "source": i.get("source", {}).get("value", "")}
188
+ for i in data[1] if query.lower() in i.get("name", "").lower()
189
+ ][:limit]
190
+ except Exception as e:
191
+ logger.debug(f"[world_bank] search 失败: {e}")
192
+ return []
193
+
194
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
195
+ # World Bank indicators aren't real-time quotes
196
+ return None
197
+
198
+ def history(self, symbol: str, days: int = 365 * 5, interval: str = "1y") -> Optional[HistoryResult]:
199
+ # Parse "CN:GDP" or just "GDP" (default to WLD)
200
+ if ":" in symbol:
201
+ country, indicator = symbol.split(":", 1)
202
+ else:
203
+ country, indicator = "WLD", symbol
204
+ years = max(1, days // 365)
205
+ start = 2024 - years
206
+ return self.get_indicator(indicator, country, start_year=start)
@@ -0,0 +1,152 @@
1
+ """
2
+ datasources/sources/yfinance_source.py — yfinance 美股/港股/加密 数据源
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import Optional
9
+
10
+ from ..base import BaseDataSource, FundamentalsResult, HistoryResult, QuoteResult, _detect_market
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class YFinanceSource(BaseDataSource):
16
+
17
+ name = "yfinance"
18
+ markets = ["us", "hk", "crypto"]
19
+ requires_key = False
20
+
21
+ def _to_yf_symbol(self, symbol: str) -> str:
22
+ s = symbol.upper()
23
+ if "/" in s: # BTC/USDT → BTC-USD
24
+ base, quote = s.split("/", 1)
25
+ return f"{base}-{'USD' if 'USDT' in quote else quote}"
26
+ return s
27
+
28
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
29
+ try:
30
+ import yfinance as yf
31
+ yf_sym = self._to_yf_symbol(symbol)
32
+ ticker = yf.Ticker(yf_sym)
33
+
34
+ price = None
35
+ prev = None
36
+ info = {}
37
+
38
+ # Attempt 1: fast_info (lighter, often succeeds when info is rate-limited)
39
+ try:
40
+ fi = ticker.fast_info
41
+ price = getattr(fi, "last_price", None) or getattr(fi, "previous_close", None)
42
+ prev = getattr(fi, "previous_close", None) or price
43
+ except Exception:
44
+ pass
45
+
46
+ # Attempt 2: full info dict
47
+ if not price:
48
+ try:
49
+ info = ticker.info or {}
50
+ price = (info.get("currentPrice") or info.get("regularMarketPrice")
51
+ or info.get("previousClose"))
52
+ prev = info.get("previousClose") or price
53
+ except Exception:
54
+ pass
55
+
56
+ # Attempt 3: last close from recent history
57
+ if not price:
58
+ try:
59
+ hist = ticker.history(period="5d", interval="1d", auto_adjust=True)
60
+ if hist is not None and not hist.empty:
61
+ close_col = next((c for c in hist.columns if c.lower() == "close"), None)
62
+ if close_col:
63
+ price = float(hist[close_col].iloc[-1])
64
+ prev = float(hist[close_col].iloc[-2]) if len(hist) > 1 else price
65
+ except Exception:
66
+ pass
67
+
68
+ if not price:
69
+ return None
70
+
71
+ # Fetch info lazily if not already fetched
72
+ if not info:
73
+ try:
74
+ info = ticker.info or {}
75
+ except Exception:
76
+ pass
77
+
78
+ chg = price - prev if prev else 0.0
79
+ chg_p = (chg / prev * 100) if prev else 0.0
80
+ market = _detect_market(symbol)
81
+ currency = "HKD" if market == "hk" else "USD"
82
+
83
+ return QuoteResult(
84
+ symbol = symbol,
85
+ name = info.get("shortName") or info.get("longName") or symbol,
86
+ price = float(price),
87
+ change = float(chg),
88
+ change_pct = float(chg_p),
89
+ volume = float(info.get("volume") or info.get("regularMarketVolume") or 0),
90
+ market_cap = float(info.get("marketCap") or 0),
91
+ pe_ttm = float(info.get("trailingPE") or 0),
92
+ pb = float(info.get("priceToBook") or 0),
93
+ high_52w = float(info.get("fiftyTwoWeekHigh") or 0),
94
+ low_52w = float(info.get("fiftyTwoWeekLow") or 0),
95
+ currency = currency,
96
+ market = market,
97
+ source = self.name,
98
+ )
99
+ except Exception as e:
100
+ logger.debug(f"[yfinance] quote {symbol} 失败: {e}")
101
+ return None
102
+
103
+ def history(self, symbol: str, days: int = 90, interval: str = "1d") -> Optional[HistoryResult]:
104
+ try:
105
+ import yfinance as yf
106
+ ticker = yf.Ticker(self._to_yf_symbol(symbol))
107
+ period = f"{days}d" if days <= 730 else "2y"
108
+ df = ticker.history(period=period, interval=interval, auto_adjust=True)
109
+ if df is None or df.empty:
110
+ return None
111
+ df.columns = [c.lower() for c in df.columns]
112
+ return HistoryResult(symbol=symbol, data=df, source=self.name, interval=interval)
113
+ except Exception as e:
114
+ logger.debug(f"[yfinance] history {symbol} 失败: {e}")
115
+ return None
116
+
117
+ def fundamentals(self, symbol: str) -> Optional[FundamentalsResult]:
118
+ try:
119
+ import yfinance as yf
120
+ info = yf.Ticker(self._to_yf_symbol(symbol)).info or {}
121
+ if not info:
122
+ return None
123
+
124
+ def _f(key: str, mult: float = 1.0) -> Optional[float]:
125
+ v = info.get(key)
126
+ if v is None or v != v: # None or NaN
127
+ return None
128
+ fv = float(v) * mult
129
+ return fv if fv != 0.0 else None
130
+
131
+ result = FundamentalsResult(
132
+ symbol = symbol,
133
+ pe_ttm = _f("trailingPE"),
134
+ pb = _f("priceToBook"),
135
+ roe = _f("returnOnEquity", 100),
136
+ revenue_growth = _f("revenueGrowth", 100),
137
+ net_profit_growth = _f("earningsGrowth", 100),
138
+ # trailingAnnualDividendYield is consistently a fraction (e.g. 0.0035 = 0.35%)
139
+ # dividendYield is unreliable (sometimes already pct, sometimes fraction)
140
+ dividend_yield = _f("trailingAnnualDividendYield", 100),
141
+ total_mv = _f("marketCap"),
142
+ source = self.name,
143
+ )
144
+ # Return None only if every field is None (data completely missing)
145
+ has_any = any(
146
+ getattr(result, f) is not None
147
+ for f in ("pe_ttm", "pb", "roe", "revenue_growth", "total_mv")
148
+ )
149
+ return result if has_any else None
150
+ except Exception as e:
151
+ logger.debug(f"[yfinance] fundamentals {symbol} 失败: {e}")
152
+ return None