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,513 @@
1
+ """
2
+ data_service.py — unified market data facade
3
+ ============================================
4
+
5
+ This module sits above MarketDataClient and datasources.DataRouter. It returns
6
+ one normalized shape for quotes, history, fundamentals, and technical signals
7
+ so report/backtest code does not need to know provider-specific schemas.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import time
13
+ from dataclasses import asdict, dataclass, field, is_dataclass
14
+ from datetime import datetime, timezone
15
+ from typing import Any, Dict, List, Optional, Tuple
16
+
17
+ try:
18
+ from packages.aria_services.cache import DistributedCacheManager
19
+ except ImportError:
20
+ DistributedCacheManager = None
21
+
22
+ from aria_code.packages.aria_services.provider_health import (
23
+ GLOBAL_PROVIDER_HEALTH,
24
+ ProviderHealthRegistry,
25
+ ProviderIssue,
26
+ classify_provider_error,
27
+ )
28
+
29
+
30
+ def _dedupe(values: List[Any]) -> List[str]:
31
+ return list(dict.fromkeys(str(v) for v in values if v not in (None, "", [], {})))
32
+
33
+
34
+ def _is_present(value: Any) -> bool:
35
+ return value not in (None, "", [], {})
36
+
37
+
38
+ def _to_dict(value: Any) -> Dict[str, Any]:
39
+ if value is None:
40
+ return {}
41
+ if isinstance(value, dict):
42
+ return dict(value)
43
+ if is_dataclass(value):
44
+ return asdict(value)
45
+ if hasattr(value, "to_dict"):
46
+ try:
47
+ return dict(value.to_dict())
48
+ except Exception:
49
+ pass
50
+ if hasattr(value, "__dict__"):
51
+ return dict(value.__dict__)
52
+ return {}
53
+
54
+
55
+ def _provider_chain(data: Dict[str, Any], fallback: str) -> List[str]:
56
+ chain = data.get("provider_chain")
57
+ if isinstance(chain, list):
58
+ return _dedupe(chain)
59
+ return _dedupe([data.get("provider"), data.get("source"), fallback])
60
+
61
+
62
+ def _provider_from_call(method: str, data: Dict[str, Any] | None = None) -> str:
63
+ data = data or {}
64
+ provider = data.get("provider") or data.get("source")
65
+ if provider:
66
+ return str(provider)
67
+ return "market_data_client" if method in {"quote", "history", "fundamentals", "technical_indicators"} else method
68
+
69
+
70
+ def _data_with_success(data: Dict[str, Any], success: bool) -> Dict[str, Any]:
71
+ """Return payload with its embedded success flag matching DataServiceResult."""
72
+ if not data:
73
+ return {}
74
+ out = dict(data)
75
+ out["success"] = bool(success)
76
+ return out
77
+
78
+
79
+ def _utc_now() -> datetime:
80
+ return datetime.now(timezone.utc)
81
+
82
+
83
+ def _utc_timestamp() -> str:
84
+ return _utc_now().isoformat(timespec="seconds").replace("+00:00", "Z")
85
+
86
+
87
+ def _parse_timestamp(value: Any) -> Optional[datetime]:
88
+ if not value:
89
+ return None
90
+ if isinstance(value, datetime):
91
+ return value if value.tzinfo else value.replace(tzinfo=timezone.utc)
92
+ text = str(value).strip()
93
+ if not text:
94
+ return None
95
+ if text.endswith("Z"):
96
+ text = text[:-1] + "+00:00"
97
+ try:
98
+ dt = datetime.fromisoformat(text)
99
+ return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)
100
+ except ValueError:
101
+ return None
102
+
103
+
104
+ @dataclass
105
+ class DataServiceResult:
106
+ kind: str
107
+ symbol: str
108
+ success: bool
109
+ data: Dict[str, Any] = field(default_factory=dict)
110
+ provider_chain: List[str] = field(default_factory=list)
111
+ warnings: List[str] = field(default_factory=list)
112
+ errors: List[str] = field(default_factory=list)
113
+ missing_fields: List[str] = field(default_factory=list)
114
+ source: str = ""
115
+ stale: bool = False
116
+ quality: Dict[str, Any] = field(default_factory=dict)
117
+ timestamp: str = field(default_factory=_utc_timestamp)
118
+
119
+
120
+ @dataclass
121
+ class DataBundle:
122
+ symbol: str
123
+ quote: Dict[str, Any] = field(default_factory=dict)
124
+ history: Dict[str, Any] = field(default_factory=dict)
125
+ fundamentals: Dict[str, Any] = field(default_factory=dict)
126
+ technical: Dict[str, Any] = field(default_factory=dict)
127
+ provider_chain: List[str] = field(default_factory=list)
128
+ warnings: List[str] = field(default_factory=list)
129
+ errors: List[str] = field(default_factory=list)
130
+ missing_fields: List[str] = field(default_factory=list)
131
+ quality: Dict[str, Any] = field(default_factory=dict)
132
+ status: str = "data_unavailable"
133
+ timestamp: str = field(default_factory=_utc_timestamp)
134
+
135
+
136
+ class DataService:
137
+ """Unified data entrypoint with cache, fallback, provenance and validation."""
138
+
139
+ def __init__(
140
+ self,
141
+ market_client: Any = None,
142
+ router: Any = None,
143
+ ttl_seconds: int = 60,
144
+ max_quote_age_seconds: int = 900,
145
+ provider_health: ProviderHealthRegistry | None = None,
146
+ ):
147
+ if market_client is None:
148
+ from market_data_client import MarketDataClient
149
+ market_client = MarketDataClient()
150
+ self._router_disabled = router is False
151
+ if self._router_disabled:
152
+ router = None
153
+ if router is None:
154
+ if not self._router_disabled:
155
+ try:
156
+ from datasources.router import get_router
157
+ router = get_router()
158
+ except Exception:
159
+ router = None
160
+ self.market_client = market_client
161
+ self.router = router
162
+ self.ttl_seconds = ttl_seconds
163
+ self.max_quote_age_seconds = max_quote_age_seconds
164
+ self.provider_health = provider_health or GLOBAL_PROVIDER_HEALTH
165
+ import os
166
+ redis_url = os.environ.get("REDIS_URL", None)
167
+ if DistributedCacheManager:
168
+ self._cache_manager = DistributedCacheManager(redis_url=redis_url)
169
+ else:
170
+ self._cache_manager = None
171
+ self._cache: Dict[Tuple[Any, ...], Tuple[float, DataServiceResult]] = {}
172
+ def quote(self, symbol: str) -> DataServiceResult:
173
+ return self._cached(("quote", symbol), lambda: self._quote_uncached(symbol))
174
+
175
+ def history(self, symbol: str, days: int = 370, interval: str = "1d") -> DataServiceResult:
176
+ return self._cached(("history", symbol, days, interval), lambda: self._history_uncached(symbol, days, interval))
177
+
178
+ def fundamentals(self, symbol: str) -> DataServiceResult:
179
+ return self._cached(("fundamentals", symbol), lambda: self._fundamentals_uncached(symbol))
180
+
181
+ def technical_indicators(self, symbol: str, days: int = 120) -> DataServiceResult:
182
+ return self._cached(("technical", symbol, days), lambda: self._technical_uncached(symbol, days))
183
+
184
+ def bundle(self, symbol: str, history_days: int = 370, technical_days: int = 120) -> DataBundle:
185
+ quote = self.quote(symbol)
186
+ history = self.history(symbol, days=history_days)
187
+ fundamentals = self.fundamentals(symbol)
188
+ technical = self.technical_indicators(symbol, days=technical_days)
189
+
190
+ provider_chain = _dedupe(
191
+ quote.provider_chain + history.provider_chain + fundamentals.provider_chain + technical.provider_chain
192
+ )
193
+ warnings = quote.warnings + history.warnings + fundamentals.warnings + technical.warnings
194
+ errors = quote.errors + history.errors + fundamentals.errors + technical.errors
195
+ missing_fields = self._bundle_missing_fields(quote, history, fundamentals, technical)
196
+ stale = quote.stale or history.stale or technical.stale
197
+
198
+ core_success = quote.success and history.success and technical.success
199
+ any_success = quote.success or history.success or fundamentals.success or technical.success
200
+ status = "complete" if core_success and not missing_fields else "partial" if any_success else "data_unavailable"
201
+ if stale and status == "complete":
202
+ status = "stale"
203
+ return DataBundle(
204
+ symbol=symbol,
205
+ quote=quote.data,
206
+ history=history.data,
207
+ fundamentals=fundamentals.data,
208
+ technical=technical.data,
209
+ provider_chain=provider_chain,
210
+ warnings=warnings[:10],
211
+ errors=errors[:10],
212
+ missing_fields=missing_fields,
213
+ quality={
214
+ "status": status,
215
+ "stale": stale,
216
+ "providers": provider_chain,
217
+ "provider_health": self.provider_health.snapshot(),
218
+ "missing_fields": missing_fields,
219
+ "warnings": warnings[:10],
220
+ "errors": errors[:10],
221
+ },
222
+ status=status,
223
+ )
224
+
225
+ def _cached(self, key: Tuple[Any, ...], factory: Any) -> DataServiceResult:
226
+ # Convert tuple key to string for Redis
227
+ str_key = "|".join(str(k) for k in key)
228
+
229
+ if self._cache_manager:
230
+ import json
231
+ cached = self._cache_manager.get(str_key)
232
+ if cached:
233
+ # If cached is dict (from redis json), we might need to cast it back to DataServiceResult
234
+ # For simplicity, if we rely on pickle or raw dict, we assume the factory returns raw or dataclass
235
+ # Since DataServiceResult is a dataclass, we could recreate it or just return it.
236
+ if isinstance(cached, dict):
237
+ return DataServiceResult(**cached)
238
+ return cached
239
+
240
+ result = factory()
241
+ # To cache it in redis, convert dataclass to dict if necessary
242
+ val_to_cache = result.__dict__ if hasattr(result, '__dict__') else result
243
+ self._cache_manager.set(str_key, val_to_cache, self.ttl_seconds)
244
+ return result
245
+ else:
246
+ now = time.time()
247
+ cached = self._cache.get(key)
248
+ if cached and now - cached[0] <= self.ttl_seconds:
249
+ return cached[1]
250
+ result = factory()
251
+ self._cache[key] = (now, result)
252
+ return result
253
+
254
+ def _quote_uncached(self, symbol: str) -> DataServiceResult:
255
+ warnings: List[str] = []
256
+ data = self._call_market("quote", symbol, warnings)
257
+ if not self._valid_quote(data):
258
+ primary = data
259
+ fallback = self._call_router("quote", symbol, warnings)
260
+ data = fallback or primary
261
+ return self._result("quote", symbol, data, warnings, required=["price"], validator=self._valid_quote)
262
+
263
+ def _history_uncached(self, symbol: str, days: int, interval: str) -> DataServiceResult:
264
+ warnings: List[str] = []
265
+ data = self._call_market("history", symbol, warnings, days=days, interval=interval)
266
+ if not self._valid_history(data):
267
+ data = self._call_router("history", symbol, warnings, days=days, interval=interval)
268
+ return self._result("history", symbol, data, warnings, required=["data"], validator=self._valid_history)
269
+
270
+ def _fundamentals_uncached(self, symbol: str) -> DataServiceResult:
271
+ warnings: List[str] = []
272
+ data = self._call_market("fundamentals", symbol, warnings)
273
+ if not self._valid_payload(data):
274
+ data = self._call_router("fundamentals", symbol, warnings)
275
+ return self._result("fundamentals", symbol, data, warnings, required=[])
276
+
277
+ def _technical_uncached(self, symbol: str, days: int) -> DataServiceResult:
278
+ warnings: List[str] = []
279
+ data = self._call_market("technical_indicators", symbol, warnings, days=days)
280
+
281
+ # Yahoo Finance v8 fallback — for US symbols when primary TA source is rate-limited
282
+ # or returns insufficient data (newly-listed stocks, ETFs without MDC coverage)
283
+ _sym = symbol.upper().strip()
284
+ _is_us = not (_sym.endswith((".SZ", ".SS", ".SH", ".HK")) or _sym.isdigit())
285
+ _needs_fallback = _is_us and (
286
+ not data or
287
+ data.get("success") is False or
288
+ (data.get("success") and data.get("rsi") is None)
289
+ )
290
+ if _needs_fallback:
291
+ try:
292
+ import json as _jv8
293
+ import urllib.request as _uv8
294
+ import statistics as _sv8
295
+ _url = (
296
+ f"https://query1.finance.yahoo.com/v8/finance/chart/{_sym}"
297
+ "?interval=1d&range=6mo"
298
+ )
299
+ _req = _uv8.Request(_url, headers={
300
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
301
+ "Accept": "application/json",
302
+ })
303
+ with _uv8.urlopen(_req, timeout=10) as _r:
304
+ _raw = _jv8.loads(_r.read())
305
+ _res = _raw["chart"]["result"][0]
306
+ _q = _res["indicators"]["quote"][0]
307
+ _c = [x for x in _q.get("close", []) if x is not None]
308
+ _v = [x for x in _q.get("volume", []) if x is not None]
309
+ if len(_c) >= 14:
310
+ def _ema(p, n):
311
+ k, r = 2/(n+1), [p[0]]
312
+ for x in p[1:]: r.append(x*k + r[-1]*(1-k))
313
+ return r
314
+ _d = [_c[i]-_c[i-1] for i in range(1, len(_c))]
315
+ _g = [max(x,0) for x in _d]; _l = [max(-x,0) for x in _d]
316
+ _ag = sum(_g[:14])/14; _al = sum(_l[:14])/14
317
+ for i in range(14, len(_g)):
318
+ _ag = (_ag*13+_g[i])/14; _al = (_al*13+_l[i])/14
319
+ _rsi = (100 - 100/(1+_ag/_al)) if _al else 100.0
320
+ n = len(_c)
321
+ _ma20 = sum(_c[-20:])/min(20, n)
322
+ _ma60 = sum(_c[-60:])/min(60, n) if n >= 14 else _ma20
323
+ _std = _sv8.stdev(_c[-min(20,n):]) if n >= 2 else 0
324
+ _v8_data: Dict[str, Any] = {
325
+ "success": True,
326
+ "price": round(_c[-1], 2),
327
+ "rsi": round(_rsi, 2),
328
+ "ma20": round(_ma20, 2),
329
+ "ma60": round(_ma60, 2),
330
+ "bb_upper": round(_ma20 + 2*_std, 2),
331
+ "bb_mid": round(_ma20, 2),
332
+ "bb_lower": round(_ma20 - 2*_std, 2),
333
+ "provider": "yahoo_v8",
334
+ "history_bars": n,
335
+ }
336
+ if n >= 26:
337
+ _e12 = _ema(_c, 12); _e26 = _ema(_c, 26)
338
+ _md = [a-b for a,b in zip(_e12, _e26)]
339
+ _sg = _ema(_md, 9)
340
+ _v8_data["macd"] = round(_md[-1], 4)
341
+ _v8_data["macd_signal"] = round(_sg[-1], 4)
342
+ _v8_data["macd_hist"] = round(_md[-1]-_sg[-1], 4)
343
+ if _v:
344
+ _v8_data["volume"] = int(_v[-1])
345
+ data = _v8_data
346
+ warnings.append(f"yahoo_v8 fallback ({n} bars)")
347
+ elif len(_c) > 0:
348
+ # Too few bars for TA — still surface current price and bar count
349
+ data = {
350
+ "success": False,
351
+ "price": round(_c[-1], 2),
352
+ "history_bars": len(_c),
353
+ "provider": "yahoo_v8",
354
+ }
355
+ if _v:
356
+ data["volume"] = int(_v[-1])
357
+ _bar_str = f"{len(_c)} 个交易日"
358
+ warnings.append(f"数据不足(仅 {_bar_str})— 新上市标的,TA 指标需至少 14 日历史")
359
+ except Exception:
360
+ pass
361
+
362
+ return self._result("technical", symbol, data, warnings, required=["rsi", "macd", "ma20"])
363
+
364
+ def _call_market(self, method: str, symbol: str, warnings: List[str], **kwargs: Any) -> Dict[str, Any]:
365
+ try:
366
+ fn = getattr(self.market_client, method)
367
+ data = _to_dict(fn(symbol, **kwargs))
368
+ provider = _provider_from_call(method, data)
369
+ if data.get("success") is False:
370
+ issue = classify_provider_error(provider, data.get("error") or "unavailable")
371
+ self.provider_health.mark_issue(issue)
372
+ warnings.append(self._format_issue(method, issue))
373
+ elif data:
374
+ self.provider_health.mark_success(provider)
375
+ return data
376
+ except Exception as exc:
377
+ issue = classify_provider_error("market_data_client", exc)
378
+ self.provider_health.mark_issue(issue)
379
+ warnings.append(self._format_issue(method, issue))
380
+ return {}
381
+
382
+ def _call_router(self, method: str, symbol: str, warnings: List[str], **kwargs: Any) -> Dict[str, Any]:
383
+ if self._router_disabled:
384
+ return {}
385
+ if self.router is None:
386
+ issue = classify_provider_error("data_router", "router unavailable")
387
+ self.provider_health.mark_issue(issue)
388
+ warnings.append(self._format_issue(method, issue))
389
+ return {}
390
+ try:
391
+ fn = getattr(self.router, method)
392
+ data = _to_dict(fn(symbol, **kwargs))
393
+ if data:
394
+ data.setdefault("success", True)
395
+ data.setdefault("provider", data.get("source") or f"data_router.{method}")
396
+ data.setdefault("provider_chain", _dedupe([data.get("provider"), data.get("source")]))
397
+ self.provider_health.mark_success(str(data.get("provider")))
398
+ return data
399
+ except Exception as exc:
400
+ issue = classify_provider_error("data_router", exc)
401
+ self.provider_health.mark_issue(issue)
402
+ warnings.append(self._format_issue(method, issue))
403
+ return {}
404
+
405
+ @staticmethod
406
+ def _format_issue(method: str, issue: ProviderIssue) -> str:
407
+ return f"{issue.provider}.{method}: {issue.category} — {issue.message}"
408
+
409
+ def _result(
410
+ self,
411
+ kind: str,
412
+ symbol: str,
413
+ data: Dict[str, Any],
414
+ warnings: List[str],
415
+ required: List[str],
416
+ validator: Any = None,
417
+ ) -> DataServiceResult:
418
+ validator = validator or self._valid_payload
419
+ success = validator(data)
420
+ missing = [key for key in required if not _is_present(data.get(key))]
421
+ if kind == "quote" and "price" in required and not self._valid_quote(data):
422
+ if "price" not in missing:
423
+ missing.append("price")
424
+ if data.get("success") is False:
425
+ success = False
426
+ payload = _data_with_success(data, success)
427
+ source = str(data.get("provider") or data.get("source") or "")
428
+ payload_ts = data.get("timestamp") or data.get("asof") or data.get("as_of")
429
+ timestamp = str(payload_ts or _utc_timestamp())
430
+ stale = self._is_stale(kind, payload_ts)
431
+ errors = []
432
+ if data.get("error"):
433
+ errors.append(str(data.get("error")))
434
+ status = "ok" if success and not missing and not stale else (
435
+ "stale" if success and stale else "partial" if success else "unavailable"
436
+ )
437
+ return DataServiceResult(
438
+ kind=kind,
439
+ symbol=symbol,
440
+ success=success,
441
+ data=payload if payload else {},
442
+ provider_chain=_provider_chain(data, kind) if data else [],
443
+ warnings=warnings[:5],
444
+ errors=errors[:5],
445
+ missing_fields=missing,
446
+ source=source,
447
+ stale=stale,
448
+ quality={
449
+ "status": status,
450
+ "stale": stale,
451
+ "source": source,
452
+ "providers": _provider_chain(data, kind) if data else [],
453
+ "provider_health": self.provider_health.snapshot(),
454
+ "missing_fields": missing,
455
+ "warnings": warnings[:5],
456
+ "errors": errors[:5],
457
+ },
458
+ timestamp=timestamp,
459
+ )
460
+
461
+ def _is_stale(self, kind: str, timestamp: Any) -> bool:
462
+ if kind != "quote":
463
+ return False
464
+ dt = _parse_timestamp(timestamp)
465
+ if dt is None:
466
+ return False
467
+ return (_utc_now() - dt).total_seconds() > self.max_quote_age_seconds
468
+
469
+ @staticmethod
470
+ def _valid_payload(data: Dict[str, Any]) -> bool:
471
+ return bool(data) and data.get("success") is not False
472
+
473
+ @staticmethod
474
+ def _valid_quote(data: Dict[str, Any]) -> bool:
475
+ if not data or data.get("success") is False:
476
+ return False
477
+ price = data.get("price")
478
+ try:
479
+ return price is not None and float(price) > 0
480
+ except (TypeError, ValueError):
481
+ return False
482
+
483
+ @staticmethod
484
+ def _valid_history(data: Dict[str, Any]) -> bool:
485
+ if not data or data.get("success") is False:
486
+ return False
487
+ rows = data.get("data")
488
+ try:
489
+ return rows is not None and len(rows) > 0
490
+ except TypeError:
491
+ return False
492
+
493
+ @staticmethod
494
+ def _bundle_missing_fields(
495
+ quote: DataServiceResult,
496
+ history: DataServiceResult,
497
+ fundamentals: DataServiceResult,
498
+ technical: DataServiceResult,
499
+ ) -> List[str]:
500
+ missing: List[str] = []
501
+ if not quote.success or not _is_present(quote.data.get("price")):
502
+ missing.append("price")
503
+ if not history.success:
504
+ missing.append("history")
505
+ for key in ("pe_ratio", "pe_ttm", "pb_ratio", "pb", "roe"):
506
+ if _is_present(fundamentals.data.get(key)):
507
+ break
508
+ else:
509
+ missing.append("fundamentals")
510
+ for key in ("rsi", "macd", "ma20"):
511
+ if not _is_present(technical.data.get(key)):
512
+ missing.append(key)
513
+ return _dedupe(missing)
@@ -0,0 +1,23 @@
1
+ """
2
+ datasources/ — Aria Code 统一市场数据层
3
+ ========================================
4
+ 用户在 ~/.aria/datasources.yaml 配置数据源优先级,
5
+ 路由器按序尝试,首个成功的结果直接返回。
6
+
7
+ 支持市场:
8
+ A股 → akshare / tushare / eastmoney
9
+ 美股 → yfinance / polygon / finnhub / alphavantage
10
+ 加密 → ccxt (binance/okx) / yfinance
11
+ 基本面 → tushare / yfinance
12
+
13
+ 快速使用:
14
+ from datasources.router import DataRouter
15
+ router = DataRouter()
16
+ print(router.quote("AAPL"))
17
+ print(router.quote("000001")) # A股
18
+ print(router.quote("BTC/USDT")) # 加密
19
+ """
20
+
21
+ from .router import DataRouter, get_router
22
+
23
+ __all__ = ["DataRouter", "get_router"]