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,269 @@
1
+ """
2
+ datasources/sources/akshare_source.py — Akshare A股数据源
3
+ ==========================================================
4
+ 免费,无需 API key,覆盖 A股实时行情、历史数据、北向资金等。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from datetime import date, timedelta
11
+ from typing import Any, Dict, Optional
12
+
13
+ from ..base import BaseDataSource, FundamentalsResult, HistoryResult, QuoteResult
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class AkshareSource(BaseDataSource):
19
+
20
+ name = "akshare"
21
+ markets = ["a_share"]
22
+ requires_key = False
23
+
24
+ def _normalize_code(self, symbol: str) -> str:
25
+ """'sh600519' / '600519' / '600519.SH' → '600519'"""
26
+ s = symbol.upper().replace("SH", "").replace("SZ", "").replace("BJ", "")
27
+ s = s.replace(".", "").strip()
28
+ if s.startswith("6") or s.startswith("0") or s.startswith("3") or s.startswith("8"):
29
+ return s[:6]
30
+ return s
31
+
32
+ def _yf_symbol(self, code: str) -> str:
33
+ """600519 → 600519.SS,000858 → 000858.SZ,300xxx → 300xxx.SZ"""
34
+ if code.startswith("6") or code.startswith("9"):
35
+ return f"{code}.SS"
36
+ return f"{code}.SZ"
37
+
38
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
39
+ """
40
+ 三级降级:
41
+ 1. akshare stock_zh_a_spot_em (全市场快照)
42
+ 2. akshare stock_individual_info_em (单股)
43
+ 3. yfinance .SS/.SZ (国际用户兜底)
44
+ 4. 最近交易日收盘价 from history
45
+ """
46
+ import warnings
47
+ warnings.filterwarnings("ignore")
48
+ code = self._normalize_code(symbol)
49
+
50
+ # ── 方法1: 全市场快照 ────────────────────────────────────────────────
51
+ try:
52
+ import akshare as ak
53
+ df = ak.stock_zh_a_spot_em()
54
+ row = df[df["代码"] == code]
55
+ if not row.empty:
56
+ r = row.iloc[0]
57
+ price = float(r.get("最新价", 0) or 0)
58
+ if price > 0:
59
+ return QuoteResult(
60
+ symbol = symbol,
61
+ name = str(r.get("名称", "")),
62
+ price = price,
63
+ change = float(r.get("涨跌额", 0) or 0),
64
+ change_pct = float(r.get("涨跌幅", 0) or 0),
65
+ volume = float(r.get("成交量", 0) or 0),
66
+ market_cap = float(r.get("总市值", 0) or 0),
67
+ pe_ttm = float(r.get("市盈率-动态", 0) or 0),
68
+ pb = float(r.get("市净率", 0) or 0),
69
+ high_52w = float(r.get("52周最高", 0) or 0),
70
+ low_52w = float(r.get("52周最低", 0) or 0),
71
+ currency = "CNY",
72
+ market = "a_share",
73
+ source = self.name,
74
+ )
75
+ except Exception:
76
+ pass
77
+
78
+ # ── 方法2: 单股信息(EastMoney 单接口) ────────────────────────────
79
+ try:
80
+ import akshare as ak
81
+ df2 = ak.stock_individual_info_em(symbol=code)
82
+ if df2 is not None and not df2.empty:
83
+ info = dict(zip(df2["item"], df2["value"]))
84
+ price = float(str(info.get("最新", info.get("收盘", 0))).replace(",", "") or 0)
85
+ prev = float(str(info.get("昨收", price)).replace(",", "") or price)
86
+ if price > 0:
87
+ return QuoteResult(
88
+ symbol = symbol,
89
+ name = str(info.get("股票简称", "")),
90
+ price = price,
91
+ change = round(price - prev, 4),
92
+ change_pct = round((price - prev) / prev * 100, 2) if prev else 0,
93
+ currency = "CNY",
94
+ market = "a_share",
95
+ source = self.name,
96
+ )
97
+ except Exception:
98
+ pass
99
+
100
+ # ── 方法3: yfinance .SS/.SZ(海外访问兜底) ─────────────────────────
101
+ try:
102
+ import yfinance as yf
103
+ yf_sym = self._yf_symbol(code)
104
+ ticker = yf.Ticker(yf_sym)
105
+
106
+ price = None
107
+ prev = None
108
+ info = {}
109
+ try:
110
+ fi = ticker.fast_info
111
+ price = getattr(fi, "last_price", None) or getattr(fi, "previous_close", None)
112
+ prev = getattr(fi, "previous_close", None) or price
113
+ except Exception:
114
+ pass
115
+ if not price:
116
+ try:
117
+ info = ticker.info or {}
118
+ price = info.get("currentPrice") or info.get("regularMarketPrice") or info.get("previousClose")
119
+ prev = info.get("previousClose") or price
120
+ except Exception:
121
+ pass
122
+ if price and float(price) > 0:
123
+ if not info:
124
+ try:
125
+ info = ticker.info or {}
126
+ except Exception:
127
+ pass
128
+ chg = float(price) - float(prev) if prev else 0
129
+ chg_p = chg / float(prev) * 100 if prev else 0
130
+ return QuoteResult(
131
+ symbol = symbol,
132
+ name = info.get("shortName") or info.get("longName") or symbol,
133
+ price = float(price),
134
+ change = chg,
135
+ change_pct = chg_p,
136
+ volume = float(info.get("volume") or 0),
137
+ market_cap = float(info.get("marketCap") or 0),
138
+ pe_ttm = float(info.get("trailingPE") or 0),
139
+ pb = float(info.get("priceToBook") or 0),
140
+ currency = "CNY",
141
+ market = "a_share",
142
+ source = f"{self.name}+yfinance",
143
+ )
144
+ except Exception:
145
+ pass
146
+
147
+ # ── 方法4: 最近历史收盘价 ────────────────────────────────────────────
148
+ try:
149
+ h = self.history(symbol, days=5)
150
+ if h and h.data is not None and not h.data.empty:
151
+ df = h.data
152
+ # Flexible column detection (akshare returns Chinese column names)
153
+ close_col = next(
154
+ (c for c in df.columns if "收盘" in str(c) or "close" in str(c).lower()),
155
+ df.columns[-1]
156
+ )
157
+ price = float(df[close_col].iloc[-1])
158
+ prev = float(df[close_col].iloc[-2]) if len(df) > 1 else price
159
+ if price > 0:
160
+ return QuoteResult(
161
+ symbol = symbol,
162
+ price = price,
163
+ change = round(price - prev, 4),
164
+ change_pct = round((price - prev) / prev * 100, 2) if prev else 0,
165
+ currency = "CNY",
166
+ market = "a_share",
167
+ source = f"{self.name}(last_close)",
168
+ )
169
+ except Exception as e:
170
+ logger.debug(f"[akshare] quote history fallback {symbol} 失败: {e}")
171
+
172
+ logger.debug(f"[akshare] quote {symbol}: 所有方法均失败")
173
+ return None
174
+
175
+ def history(self, symbol: str, days: int = 90, interval: str = "1d") -> Optional[HistoryResult]:
176
+ # ── 方法1: akshare stock_zh_a_hist ──────────────────────────────────
177
+ try:
178
+ import akshare as ak
179
+ code = self._normalize_code(symbol)
180
+ end = date.today().strftime("%Y%m%d")
181
+ start = (date.today() - timedelta(days=days + 10)).strftime("%Y%m%d")
182
+ df = ak.stock_zh_a_hist(
183
+ symbol=code, period="daily",
184
+ start_date=start, end_date=end, adjust="qfq"
185
+ )
186
+ if df is not None and not df.empty:
187
+ return HistoryResult(symbol=symbol, data=df, source=self.name, interval=interval)
188
+ except Exception as e:
189
+ logger.debug(f"[akshare] history(akshare) {symbol} 失败: {e}")
190
+
191
+ # ── 方法2: yfinance .SS/.SZ (海外兜底) ──────────────────────────────
192
+ try:
193
+ import yfinance as yf
194
+ import pandas as pd
195
+ code = self._normalize_code(symbol)
196
+ yf_sym = self._yf_symbol(code)
197
+ ticker = yf.Ticker(yf_sym)
198
+ period = f"{days}d" if days <= 730 else "2y"
199
+ df = ticker.history(period=period, auto_adjust=True)
200
+ if df is not None and not df.empty:
201
+ df.index = pd.to_datetime(df.index)
202
+ return HistoryResult(symbol=symbol, data=df, source=f"{self.name}+yfinance", interval=interval)
203
+ except Exception as e:
204
+ logger.debug(f"[akshare] history(yfinance) {symbol} 失败: {e}")
205
+
206
+ return None
207
+
208
+ def fundamentals(self, symbol: str) -> Optional[FundamentalsResult]:
209
+ """A股基本面:优先 akshare,降级 yfinance .SS/.SZ"""
210
+ # ── 方法1: akshare 市场快照字段 ──────────────────────────────────────
211
+ try:
212
+ import akshare as ak
213
+ import math
214
+ code = self._normalize_code(symbol)
215
+ df = ak.stock_zh_a_spot_em()
216
+ row = df[df["代码"] == code]
217
+ if not row.empty:
218
+ r = row.iloc[0]
219
+ def _ak(key: str) -> Optional[float]:
220
+ v = r.get(key)
221
+ if v is None:
222
+ return None
223
+ try:
224
+ fv = float(v)
225
+ return None if (math.isnan(fv) or fv == 0) else fv
226
+ except (TypeError, ValueError):
227
+ return None
228
+ pe = _ak("市盈率-动态")
229
+ pb = _ak("市净率")
230
+ mv = _ak("总市值")
231
+ if pe or pb:
232
+ return FundamentalsResult(
233
+ symbol=symbol, pe_ttm=pe, pb=pb,
234
+ total_mv=mv, source=self.name,
235
+ )
236
+ except Exception:
237
+ pass
238
+
239
+ # ── 方法2: yfinance .SS/.SZ ───────────────────────────────────────────
240
+ try:
241
+ from .yfinance_source import YFinanceSource
242
+ code = self._normalize_code(symbol)
243
+ yf_sym = self._yf_symbol(code)
244
+ fund = YFinanceSource().fundamentals(yf_sym)
245
+ if fund:
246
+ fund.symbol = symbol
247
+ fund.source = f"{self.name}+yfinance"
248
+ return fund
249
+ except Exception as e:
250
+ logger.debug(f"[akshare] fundamentals yfinance fallback {symbol} 失败: {e}")
251
+
252
+ return None
253
+
254
+ def northbound_flow(self) -> Optional[Dict]:
255
+ """北向资金净流入(akshare 特有)"""
256
+ try:
257
+ import akshare as ak
258
+ df = ak.stock_hsgt_north_net_flow_in_em(symbol="北向资金")
259
+ if df is None or df.empty:
260
+ return None
261
+ latest = df.iloc[-1]
262
+ return {
263
+ "date": str(latest.get("日期", "")),
264
+ "net_flow": float(latest.get("当日净流入", 0) or 0),
265
+ "source": self.name,
266
+ }
267
+ except Exception as e:
268
+ logger.debug(f"[akshare] northbound_flow 失败: {e}")
269
+ return None
@@ -0,0 +1,203 @@
1
+ """
2
+ datasources/sources/alpha_vantage_source.py — Alpha Vantage 免费层数据
3
+ ======================================================================
4
+ 免费 API key 从 https://www.alphavantage.co/support/#api-key 申请(秒得)。
5
+ 免费限制: 25 请求/天,5 请求/分钟。
6
+
7
+ 功能: 美股/ETF 行情、技术指标、外汇汇率、大宗商品、基本面数据。
8
+
9
+ 配置: ALPHA_VANTAGE_KEY 环境变量 或 ~/.aria/.env
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import os
16
+ import time
17
+ import urllib.request
18
+ from datetime import date, timedelta
19
+ from pathlib import Path
20
+ from typing import Dict, Optional
21
+
22
+ from ..base import BaseDataSource, FundamentalsResult, HistoryResult, QuoteResult
23
+ from aria_code.packages.aria_core.paths import aria_home
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ _BASE = "https://www.alphavantage.co/query"
28
+
29
+
30
+ def _load_key() -> str:
31
+ key = os.getenv("ALPHA_VANTAGE_KEY", "") or os.getenv("ALPHAVANTAGE_KEY", "")
32
+ if not key:
33
+ for p in [Path.home() / ".aria" / ".env", aria_home() / ".env"]:
34
+ if p.exists():
35
+ for line in p.read_text(encoding="utf-8").splitlines():
36
+ if line.startswith(("ALPHA_VANTAGE_KEY=", "ALPHAVANTAGE_KEY=")):
37
+ key = line.split("=", 1)[1].strip()
38
+ break
39
+ if key:
40
+ break
41
+ if not key:
42
+ # providers.json 中存储的键名是 "alphavantage"(无下划线)
43
+ try:
44
+ import json as _json
45
+ _p = aria_home() / "providers.json"
46
+ if _p.exists():
47
+ _raw = _json.loads(_p.read_text(encoding="utf-8"))
48
+ key = (
49
+ _raw.get("data", {}).get("alphavantage", {}).get("api_key", "")
50
+ or _raw.get("data", {}).get("alpha_vantage", {}).get("api_key", "")
51
+ )
52
+ except Exception:
53
+ pass
54
+ return key
55
+
56
+
57
+ def _fetch(params: dict, timeout: int = 15) -> Optional[dict]:
58
+ import json
59
+ qs = "&".join(f"{k}={v}" for k, v in params.items())
60
+ url = f"{_BASE}?{qs}"
61
+ try:
62
+ req = urllib.request.Request(url, headers={"User-Agent": "aria-code/1.0"})
63
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
64
+ return json.loads(resp.read())
65
+ except Exception as e:
66
+ logger.debug(f"[alpha_vantage] fetch 失败: {e}")
67
+ return None
68
+
69
+
70
+ class AlphaVantageSource(BaseDataSource):
71
+ """
72
+ Alpha Vantage 数据源。
73
+ 免费 key 足够日常使用(25次/天);付费 key 无频率限制。
74
+ """
75
+
76
+ name = "alpha_vantage"
77
+ markets = ["us", "hk", "forex", "commodity"]
78
+ requires_key = True
79
+
80
+ def __init__(self, config=None):
81
+ super().__init__(config)
82
+ self._key = _load_key()
83
+
84
+ def is_configured(self) -> bool:
85
+ return bool(self._key)
86
+
87
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
88
+ data = _fetch({"function": "GLOBAL_QUOTE", "symbol": symbol, "apikey": self._key})
89
+ if not data:
90
+ return None
91
+ q = data.get("Global Quote", {})
92
+ if not q or "05. price" not in q:
93
+ return None
94
+ price = float(q.get("05. price", 0))
95
+ change = float(q.get("09. change", 0))
96
+ pct = float(q.get("10. change percent", "0%").replace("%", ""))
97
+ vol = float(q.get("06. volume", 0))
98
+ return QuoteResult(
99
+ symbol = symbol,
100
+ price = price,
101
+ change = change,
102
+ change_pct = pct,
103
+ volume = vol,
104
+ market = "us",
105
+ source = self.name,
106
+ timestamp = q.get("07. latest trading day", ""),
107
+ )
108
+
109
+ def history(
110
+ self,
111
+ symbol: str,
112
+ days: int = 365,
113
+ interval: str = "1d",
114
+ ) -> Optional[HistoryResult]:
115
+ try:
116
+ import pandas as pd
117
+ size = "compact" if days <= 100 else "full"
118
+ func = "TIME_SERIES_DAILY_ADJUSTED"
119
+ data = _fetch({"function": func, "symbol": symbol,
120
+ "outputsize": size, "apikey": self._key})
121
+ if not data:
122
+ return None
123
+ ts = data.get("Time Series (Daily)", {})
124
+ if not ts:
125
+ return None
126
+ cutoff = (date.today() - timedelta(days=days)).isoformat()
127
+ rows = []
128
+ for d, v in ts.items():
129
+ if d < cutoff:
130
+ continue
131
+ rows.append({
132
+ "date": d,
133
+ "open": float(v.get("1. open", 0)),
134
+ "high": float(v.get("2. high", 0)),
135
+ "low": float(v.get("3. low", 0)),
136
+ "close": float(v.get("5. adjusted close", v.get("4. close", 0))),
137
+ "volume": float(v.get("6. volume", 0)),
138
+ })
139
+ if not rows:
140
+ return None
141
+ df = pd.DataFrame(rows)
142
+ df["date"] = pd.to_datetime(df["date"])
143
+ df = df.set_index("date").sort_index()
144
+ return HistoryResult(symbol=symbol, data=df, source=self.name, interval="1d")
145
+ except Exception as e:
146
+ logger.debug(f"[alpha_vantage] history {symbol} 失败: {e}")
147
+ return None
148
+
149
+ def fundamentals(self, symbol: str) -> Optional[FundamentalsResult]:
150
+ data = _fetch({"function": "OVERVIEW", "symbol": symbol, "apikey": self._key})
151
+ if not data or not data.get("Symbol"):
152
+ return None
153
+ def _f(k, mult: float = 1.0):
154
+ v = data.get(k, "None")
155
+ try:
156
+ fv = float(v) if v not in ("None", "-", "", "0") else None
157
+ return fv * mult if fv is not None else None
158
+ except ValueError:
159
+ return None
160
+ return FundamentalsResult(
161
+ symbol = symbol,
162
+ pe_ttm = _f("TrailingPE"),
163
+ pb = _f("PriceToBookRatio"),
164
+ roe = _f("ReturnOnEquityTTM", 100),
165
+ revenue_growth = _f("RevenueGrowthQtrlyYOY"),
166
+ dividend_yield = _f("DividendYield", 100),
167
+ source = self.name,
168
+ )
169
+
170
+ def get_forex(self, from_currency: str, to_currency: str) -> Optional[Dict]:
171
+ """实时外汇汇率。"""
172
+ data = _fetch({"function": "CURRENCY_EXCHANGE_RATE",
173
+ "from_currency": from_currency,
174
+ "to_currency": to_currency,
175
+ "apikey": self._key})
176
+ if not data:
177
+ return None
178
+ r = data.get("Realtime Currency Exchange Rate", {})
179
+ return {
180
+ "from": from_currency, "to": to_currency,
181
+ "rate": float(r.get("5. Exchange Rate", 0)),
182
+ "time": r.get("6. Last Refreshed", ""),
183
+ }
184
+
185
+ def get_commodity(self, symbol: str = "WTI") -> Optional[HistoryResult]:
186
+ """大宗商品历史价格(WTI/BRENT/GOLD/COPPER 等)。"""
187
+ _MAP = {"WTI": "WTI", "BRENT": "BRENT", "GOLD": "GOLD",
188
+ "COPPER": "COPPER", "ALUMINUM": "ALUMINUM", "WHEAT": "WHEAT"}
189
+ func = _MAP.get(symbol.upper(), symbol.upper())
190
+ data = _fetch({"function": func, "interval": "monthly", "apikey": self._key})
191
+ if not data:
192
+ return None
193
+ try:
194
+ import pandas as pd
195
+ rows = [{"date": r["date"], "close": float(r["value"])}
196
+ for r in data.get("data", []) if r.get("value") not in (".", None)]
197
+ df = pd.DataFrame(rows)
198
+ df["date"] = pd.to_datetime(df["date"])
199
+ df = df.set_index("date").sort_index()
200
+ return HistoryResult(symbol=symbol, data=df, source=self.name, interval="1mo")
201
+ except Exception as e:
202
+ logger.debug(f"[alpha_vantage] commodity {symbol} 失败: {e}")
203
+ return None
@@ -0,0 +1,219 @@
1
+ """
2
+ datasources/sources/edgar_source.py — SEC EDGAR 美国上市公司财务数据
3
+ ====================================================================
4
+ 完全免费,无需 API key。数据来源:https://data.sec.gov (SEC EDGAR API)
5
+ 覆盖:10-K / 10-Q 财报、公司基本信息、财务报表、内幕交易披露。
6
+
7
+ 用法:
8
+ src = EDGARSource()
9
+ facts = src.get_company_facts("AAPL") # → 财务指标历史
10
+ filings = src.get_recent_filings("MSFT") # → 最近10-K/10-Q
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ import time
17
+ import urllib.request
18
+ from typing import Any, Dict, List, Optional
19
+
20
+ from ..base import BaseDataSource, FundamentalsResult, QuoteResult
21
+
22
+ import os as _os
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ _EDGAR_API = "https://data.sec.gov"
27
+ _EDGAR_WWW = "https://www.sec.gov"
28
+ _HEADERS = {
29
+ "User-Agent": _os.environ.get("EDGAR_USER_AGENT", "aria-code contact@example.com"), # SEC requires self-identification
30
+ "Accept-Encoding": "gzip, deflate",
31
+ }
32
+
33
+ # 主要美股 ticker → CIK 缓存(常用的直接查,避免每次API调用)
34
+ _TICKER_CIK_CACHE: Dict[str, str] = {}
35
+
36
+
37
+ def _fetch_json(url: str, timeout: int = 15) -> Optional[Dict]:
38
+ try:
39
+ import gzip
40
+ import json
41
+ req = urllib.request.Request(url, headers=_HEADERS)
42
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
43
+ raw = resp.read()
44
+ if resp.headers.get("Content-Encoding") == "gzip":
45
+ raw = gzip.decompress(raw)
46
+ return json.loads(raw)
47
+ except Exception as e:
48
+ logger.debug(f"[edgar] fetch {url} 失败: {e}")
49
+ return None
50
+
51
+
52
+ class EDGARSource(BaseDataSource):
53
+ """
54
+ SEC EDGAR 数据源 — 美国上市公司财报和披露文件。
55
+ """
56
+
57
+ name = "edgar"
58
+ markets = ["us"]
59
+ requires_key = False
60
+
61
+ def __init__(self, config=None):
62
+ super().__init__(config)
63
+ self._tickers: Optional[Dict] = None
64
+ self._cik_map: Dict[str, str] = {}
65
+
66
+ def is_configured(self) -> bool:
67
+ return True
68
+
69
+ def _load_ticker_map(self) -> None:
70
+ """加载 SEC 全量 ticker→CIK 映射(首次调用时下载一次)"""
71
+ if self._tickers is not None:
72
+ return
73
+ data = _fetch_json(f"{_EDGAR_WWW}/files/company_tickers.json")
74
+ if data:
75
+ for v in data.values():
76
+ ticker = v.get("ticker", "").upper()
77
+ cik = str(v.get("cik_str", "")).zfill(10)
78
+ if ticker:
79
+ self._cik_map[ticker] = cik
80
+ self._tickers = self._cik_map
81
+
82
+ def ticker_to_cik(self, symbol: str) -> Optional[str]:
83
+ self._load_ticker_map()
84
+ return self._cik_map.get(symbol.upper())
85
+
86
+ def get_company_facts(self, symbol: str) -> Optional[Dict[str, Any]]:
87
+ """
88
+ 获取公司全部财务事实(XBRL 格式)。
89
+ 返回包含 EPS/Revenue/NetIncome/Assets 等历史序列的字典。
90
+ """
91
+ cik = self.ticker_to_cik(symbol)
92
+ if not cik:
93
+ return {"error": f"未找到 {symbol} 的 CIK"}
94
+ data = _fetch_json(f"{_EDGAR_API}/api/xbrl/companyfacts/CIK{cik}.json")
95
+ if not data:
96
+ return None
97
+
98
+ us_gaap = data.get("facts", {}).get("us-gaap", {})
99
+ result = {"symbol": symbol, "cik": cik, "metrics": {}}
100
+ wanted = {
101
+ "Revenues": "revenue",
102
+ "RevenueFromContractWithCustomerExcludingAssessedTax": "revenue",
103
+ "NetIncomeLoss": "net_income",
104
+ "EarningsPerShareBasic": "eps_basic",
105
+ "EarningsPerShareDiluted": "eps_diluted",
106
+ "Assets": "total_assets",
107
+ "LiabilitiesAndStockholdersEquity": "total_equity",
108
+ "OperatingIncomeLoss": "operating_income",
109
+ "CommonStockSharesOutstanding": "shares_outstanding",
110
+ }
111
+ for gaap_key, alias in wanted.items():
112
+ if gaap_key in us_gaap:
113
+ units = us_gaap[gaap_key].get("units", {})
114
+ unit_key = "USD" if "USD" in units else ("shares" if "shares" in units else next(iter(units), None))
115
+ if unit_key and unit_key in units:
116
+ entries = [
117
+ {"end": e["end"], "val": e["val"], "form": e.get("form", "")}
118
+ for e in units[unit_key]
119
+ if e.get("form") in ("10-K", "10-Q", "20-F")
120
+ ]
121
+ if entries:
122
+ entries.sort(key=lambda x: x["end"], reverse=True)
123
+ result["metrics"][alias] = entries[:20]
124
+ return result
125
+
126
+ def get_recent_filings(self, symbol: str, form_types: List[str] = None) -> List[Dict]:
127
+ """获取最近提交的财务报告(10-K、10-Q、8-K 等)。"""
128
+ cik = self.ticker_to_cik(symbol)
129
+ if not cik:
130
+ return [{"error": f"未找到 {symbol} 的 CIK"}]
131
+ form_types = form_types or ["10-K", "10-Q", "8-K"]
132
+ data = _fetch_json(f"{_EDGAR_API}/submissions/CIK{cik}.json")
133
+ if not data:
134
+ return []
135
+
136
+ recent = data.get("filings", {}).get("recent", {})
137
+ forms = recent.get("form", [])
138
+ dates = recent.get("filingDate", [])
139
+ accnos = recent.get("accessionNumber", [])
140
+ docs = recent.get("primaryDocument", [])
141
+
142
+ results = []
143
+ for form, filing_date, accno, doc in zip(forms, dates, accnos, docs):
144
+ if form in form_types:
145
+ accno_clean = accno.replace("-", "")
146
+ url = f"https://www.sec.gov/Archives/edgar/data/{int(cik)}/{accno_clean}/{doc}"
147
+ results.append({
148
+ "form": form,
149
+ "date": filing_date,
150
+ "accession": accno,
151
+ "url": url,
152
+ "cik": cik,
153
+ })
154
+ if len(results) >= 20:
155
+ break
156
+ return results
157
+
158
+ def get_insider_trades(self, symbol: str, days: int = 90) -> List[Dict]:
159
+ """获取内幕交易披露(Form 4)。"""
160
+ cik = self.ticker_to_cik(symbol)
161
+ if not cik:
162
+ return []
163
+ data = _fetch_json(f"{_EDGAR_API}/submissions/CIK{cik}.json")
164
+ if not data:
165
+ return []
166
+
167
+ recent = data.get("filings", {}).get("recent", {})
168
+ forms = recent.get("form", [])
169
+ dates = recent.get("filingDate", [])
170
+ results = []
171
+ from datetime import date as _date, timedelta as _td
172
+ cutoff = (_date.today() - _td(days=days)).isoformat()
173
+
174
+ for form, filing_date in zip(forms, dates):
175
+ if form == "4" and filing_date >= cutoff:
176
+ results.append({"form": "4", "date": filing_date, "type": "insider"})
177
+ if len(results) >= 30:
178
+ break
179
+ return results
180
+
181
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
182
+ facts = self.get_company_facts(symbol)
183
+ if not facts or "metrics" in facts and not facts["metrics"]:
184
+ return None
185
+ m = facts.get("metrics", {})
186
+ rev = m.get("revenue", [{}])[0].get("val", 0) if m.get("revenue") else 0
187
+ return QuoteResult(
188
+ symbol = symbol,
189
+ name = f"EDGAR:{symbol}",
190
+ price = 0.0,
191
+ market = "us",
192
+ source = self.name,
193
+ extra = {"annual_revenue": rev},
194
+ )
195
+
196
+ def fundamentals(self, symbol: str) -> Optional[FundamentalsResult]:
197
+ facts = self.get_company_facts(symbol)
198
+ if not facts:
199
+ return None
200
+ m = facts.get("metrics", {})
201
+
202
+ def _latest(key: str) -> float:
203
+ entries = m.get(key, [])
204
+ return float(entries[0]["val"]) if entries else 0.0
205
+
206
+ net_income = _latest("net_income")
207
+ revenue = _latest("revenue")
208
+ rev_yoy = 0.0
209
+ if m.get("revenue") and len(m["revenue"]) >= 2:
210
+ cur = float(m["revenue"][0]["val"])
211
+ prev = float(m["revenue"][1]["val"])
212
+ if prev:
213
+ rev_yoy = (cur - prev) / abs(prev) * 100
214
+
215
+ return FundamentalsResult(
216
+ symbol = symbol,
217
+ revenue_growth = rev_yoy,
218
+ source = self.name,
219
+ )