aria-code 4.1.3__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 (284) hide show
  1. agents/__init__.py +32 -0
  2. agents/base.py +190 -0
  3. agents/deep/__init__.py +37 -0
  4. agents/deep/calibration_loop.py +144 -0
  5. agents/deep/critic.py +125 -0
  6. agents/deep/deepen.py +193 -0
  7. agents/deep/models.py +149 -0
  8. agents/deep/pipeline.py +164 -0
  9. agents/deep/quant_fusion.py +192 -0
  10. agents/deep/themes.py +95 -0
  11. agents/deep/tiers.py +106 -0
  12. agents/financial/__init__.py +10 -0
  13. agents/financial/catalyst.py +279 -0
  14. agents/financial/debate.py +145 -0
  15. agents/financial/earnings.py +303 -0
  16. agents/financial/fundamental.py +159 -0
  17. agents/financial/macro.py +99 -0
  18. agents/financial/news.py +207 -0
  19. agents/financial/risk.py +132 -0
  20. agents/financial/sector.py +279 -0
  21. agents/financial/synthesis.py +274 -0
  22. agents/financial/technical.py +258 -0
  23. agents/portfolio_agent.py +333 -0
  24. agents/realty/__init__.py +62 -0
  25. agents/realty/asset_diagnosis.py +150 -0
  26. agents/realty/business_match.py +165 -0
  27. agents/realty/cashflow_verify.py +208 -0
  28. agents/realty/contract_rules.py +209 -0
  29. agents/realty/energy_anomaly.py +188 -0
  30. agents/realty/exit_settlement.py +207 -0
  31. agents/realty/fulfillment_risk.py +205 -0
  32. agents/realty/ops_optimize.py +159 -0
  33. agents/realty/revenue_share.py +214 -0
  34. agents/registry.py +144 -0
  35. agents/sports/__init__.py +0 -0
  36. agents/sports/football_agent.py +169 -0
  37. agents/team.py +289 -0
  38. aliyun_data_client.py +660 -0
  39. apps/README.md +12 -0
  40. apps/__init__.py +2 -0
  41. apps/channels/README.md +15 -0
  42. apps/cli/README.md +13 -0
  43. apps/cli/__init__.py +2 -0
  44. apps/cli/bootstrap.py +99 -0
  45. apps/cli/codegen_paths.py +29 -0
  46. apps/cli/commands/__init__.py +16 -0
  47. apps/cli/commands/analysis_cmds.py +288 -0
  48. apps/cli/commands/backtest_cmds.py +1887 -0
  49. apps/cli/commands/broker_cmds.py +1154 -0
  50. apps/cli/commands/business_workflow_cmds.py +289 -0
  51. apps/cli/commands/catalog.py +84 -0
  52. apps/cli/commands/data_cmds.py +405 -0
  53. apps/cli/commands/diagnostic_cmds.py +179 -0
  54. apps/cli/commands/diagnostic_ops_cmds.py +696 -0
  55. apps/cli/commands/finance_render.py +12 -0
  56. apps/cli/commands/market.py +399 -0
  57. apps/cli/commands/market_cmds.py +1276 -0
  58. apps/cli/commands/market_context.py +425 -0
  59. apps/cli/commands/market_render.py +7 -0
  60. apps/cli/commands/model_cmds.py +1579 -0
  61. apps/cli/commands/ops_cmds.py +668 -0
  62. apps/cli/commands/portfolio_cmds.py +962 -0
  63. apps/cli/commands/report.py +377 -0
  64. apps/cli/commands/scaffold_templates.py +617 -0
  65. apps/cli/commands/session_cmds.py +179 -0
  66. apps/cli/commands/session_ux_cmds.py +280 -0
  67. apps/cli/commands/team.py +588 -0
  68. apps/cli/commands/team_render.py +8 -0
  69. apps/cli/commands/ui_cmds.py +358 -0
  70. apps/cli/commands/workflow_cmds.py +279 -0
  71. apps/cli/commands/workspace_cmds.py +1414 -0
  72. apps/cli/config_paths.py +70 -0
  73. apps/cli/config_store.py +61 -0
  74. apps/cli/deterministic.py +122 -0
  75. apps/cli/direct.py +48 -0
  76. apps/cli/github_app_auth.py +135 -0
  77. apps/cli/handlers/__init__.py +11 -0
  78. apps/cli/handlers/broker_handlers.py +122 -0
  79. apps/cli/handlers/chart_handlers.py +1309 -0
  80. apps/cli/handlers/market_handlers.py +2509 -0
  81. apps/cli/handlers/realty_handlers.py +114 -0
  82. apps/cli/handlers/strategy_advice.py +82 -0
  83. apps/cli/hooks.py +180 -0
  84. apps/cli/i18n.py +284 -0
  85. apps/cli/intent.py +136 -0
  86. apps/cli/intent_router.py +217 -0
  87. apps/cli/lifecycle_hooks.py +48 -0
  88. apps/cli/main.py +29 -0
  89. apps/cli/market_metadata.py +135 -0
  90. apps/cli/market_universe.py +265 -0
  91. apps/cli/message_processing.py +257 -0
  92. apps/cli/plan_mode.py +139 -0
  93. apps/cli/plotly_html.py +15 -0
  94. apps/cli/prediction_feedback.py +202 -0
  95. apps/cli/preflight.py +497 -0
  96. apps/cli/project_aria.py +60 -0
  97. apps/cli/prompts/__init__.py +0 -0
  98. apps/cli/prompts/coding.py +658 -0
  99. apps/cli/prompts/system_prompts.py +531 -0
  100. apps/cli/prompts/ui.py +434 -0
  101. apps/cli/providers/__init__.py +1 -0
  102. apps/cli/providers/base.py +271 -0
  103. apps/cli/providers/chat_routing.py +80 -0
  104. apps/cli/providers/llm/__init__.py +1 -0
  105. apps/cli/providers/llm/ollama_stream.py +1170 -0
  106. apps/cli/providers/llm/sse_stream.py +216 -0
  107. apps/cli/providers/runtime_bridge.py +185 -0
  108. apps/cli/runtime_consumer.py +489 -0
  109. apps/cli/session_export.py +87 -0
  110. apps/cli/session_jsonl.py +207 -0
  111. apps/cli/session_store.py +112 -0
  112. apps/cli/todo_tracker.py +190 -0
  113. apps/cli/tools/__init__.py +40 -0
  114. apps/cli/tools/context.py +46 -0
  115. apps/cli/tools/file_tools.py +112 -0
  116. apps/cli/tools/market_tools.py +549 -0
  117. apps/cli/tools/notebook_tools.py +111 -0
  118. apps/cli/tools/system_tools.py +669 -0
  119. apps/cli/tools/write_tools.py +715 -0
  120. apps/cli/tradingview_bridge.py +434 -0
  121. apps/cli/update_check.py +152 -0
  122. apps/cli/utils/__init__.py +0 -0
  123. apps/cli/utils/market_detect.py +1578 -0
  124. apps/daemon/README.md +14 -0
  125. apps/vscode/README.md +115 -0
  126. apps/vscode/package.json +70 -0
  127. aria_cli.py +11636 -0
  128. aria_code-4.1.3.dist-info/METADATA +952 -0
  129. aria_code-4.1.3.dist-info/RECORD +284 -0
  130. aria_code-4.1.3.dist-info/WHEEL +5 -0
  131. aria_code-4.1.3.dist-info/entry_points.txt +2 -0
  132. aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
  133. aria_code-4.1.3.dist-info/top_level.txt +50 -0
  134. aria_daemon.py +1295 -0
  135. aria_feishu_bot.py +1359 -0
  136. aria_relay_client.py +182 -0
  137. aria_relay_server.py +405 -0
  138. aria_telegram_bot.py +202 -0
  139. ariarc.py +328 -0
  140. artifacts.py +491 -0
  141. backtest_report.py +472 -0
  142. brokers/__init__.py +72 -0
  143. brokers/base.py +207 -0
  144. brokers/capabilities.py +264 -0
  145. brokers/cn/__init__.py +10 -0
  146. brokers/cn/easytrader_broker.py +193 -0
  147. brokers/cn/futu_broker.py +194 -0
  148. brokers/cn/longbridge_broker.py +190 -0
  149. brokers/cn/tiger_broker.py +196 -0
  150. brokers/cn/xtquant_broker.py +175 -0
  151. brokers/config.py +364 -0
  152. brokers/intl/__init__.py +5 -0
  153. brokers/intl/alpaca_broker.py +183 -0
  154. brokers/intl/ibkr_broker.py +215 -0
  155. brokers/intl/webull_broker.py +156 -0
  156. brokers/paper_broker.py +259 -0
  157. brokers/planning.py +296 -0
  158. brokers/registry.py +181 -0
  159. brokers/trading.py +237 -0
  160. change_store.py +127 -0
  161. command_safety.py +19 -0
  162. computer_use_tools.py +504 -0
  163. dashboard_generator.py +578 -0
  164. data_analysis_tools.py +808 -0
  165. data_cleaner.py +483 -0
  166. data_service.py +481 -0
  167. datasources/__init__.py +23 -0
  168. datasources/base.py +166 -0
  169. datasources/router.py +221 -0
  170. datasources/sources/__init__.py +15 -0
  171. datasources/sources/akshare_source.py +269 -0
  172. datasources/sources/alpha_vantage_source.py +202 -0
  173. datasources/sources/edgar_source.py +218 -0
  174. datasources/sources/finnhub_source.py +197 -0
  175. datasources/sources/fred_source.py +219 -0
  176. datasources/sources/tushare_source.py +141 -0
  177. datasources/sources/web_scraper_source.py +278 -0
  178. datasources/sources/world_bank_source.py +205 -0
  179. datasources/sources/yfinance_source.py +152 -0
  180. demo_player.py +204 -0
  181. doctor.py +508 -0
  182. file_analysis_tools.py +734 -0
  183. finance_formulas.py +389 -0
  184. football_data_client.py +1670 -0
  185. intent_classifier.py +358 -0
  186. local_finance_tools.py +3221 -0
  187. local_llm_provider.py +552 -0
  188. macro_tools.py +368 -0
  189. market_data_client.py +1899 -0
  190. mcp_client.py +506 -0
  191. memory_manager.py +245 -0
  192. model_capability.py +416 -0
  193. notification_tools.py +248 -0
  194. packages/__init__.py +23 -0
  195. packages/aria_agents/__init__.py +5 -0
  196. packages/aria_agents/manifest.py +69 -0
  197. packages/aria_core/__init__.py +34 -0
  198. packages/aria_core/architecture.py +192 -0
  199. packages/aria_core/export.py +124 -0
  200. packages/aria_core/manifest.py +65 -0
  201. packages/aria_infra/__init__.py +15 -0
  202. packages/aria_infra/arthera.py +52 -0
  203. packages/aria_infra/doctor.py +246 -0
  204. packages/aria_infra/product.py +37 -0
  205. packages/aria_mcp/__init__.py +25 -0
  206. packages/aria_mcp/bridge.py +38 -0
  207. packages/aria_mcp/config.py +97 -0
  208. packages/aria_mcp/tools.py +61 -0
  209. packages/aria_sdk/__init__.py +19 -0
  210. packages/aria_sdk/client.py +396 -0
  211. packages/aria_sdk/providers.py +70 -0
  212. packages/aria_sdk/streaming.py +73 -0
  213. packages/aria_sdk/types.py +86 -0
  214. packages/aria_services/__init__.py +55 -0
  215. packages/aria_services/context.py +258 -0
  216. packages/aria_services/data.py +11 -0
  217. packages/aria_services/provider_health.py +189 -0
  218. packages/aria_services/registry.py +213 -0
  219. packages/aria_services/usage.py +138 -0
  220. packages/aria_skills/__init__.py +5 -0
  221. packages/aria_skills/registry.py +59 -0
  222. packages/aria_tools/__init__.py +5 -0
  223. packages/aria_tools/registry.py +128 -0
  224. packages/quant_engine/__init__.py +6 -0
  225. packages/quant_engine/sports/__init__.py +72 -0
  226. packages/quant_engine/sports/calibrator.py +353 -0
  227. packages/quant_engine/sports/dixon_coles.py +234 -0
  228. packages/quant_engine/sports/elo.py +299 -0
  229. packages/quant_engine/sports/form.py +188 -0
  230. packages/quant_engine/sports/h2h.py +195 -0
  231. packages/quant_engine/sports/ml_model.py +354 -0
  232. packages/quant_engine/sports/predictor.py +311 -0
  233. packages/quant_engine/sports/tracker.py +664 -0
  234. packages/quant_engine/stochastic/__init__.py +27 -0
  235. packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  236. packages/quant_engine/stochastic/ito_calculus.py +477 -0
  237. packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  238. packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  239. packages/quant_engine/stochastic/options_pricing.py +573 -0
  240. packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  241. plan_utils.py +194 -0
  242. plugin_loader.py +328 -0
  243. portfolio_ledger.py +262 -0
  244. privacy/__init__.py +5 -0
  245. privacy/feedback.py +123 -0
  246. project_tools.py +525 -0
  247. providers/__init__.py +30 -0
  248. providers/llm/__init__.py +19 -0
  249. providers/llm/anthropic.py +184 -0
  250. providers/llm/base.py +139 -0
  251. providers/llm/ollama.py +128 -0
  252. providers/llm/openai_compat.py +282 -0
  253. providers/llm/registry.py +358 -0
  254. realty_data_tools.py +659 -0
  255. report_generator.py +1314 -0
  256. runtime/__init__.py +103 -0
  257. runtime/agent_loop.py +1183 -0
  258. runtime/approval.py +51 -0
  259. runtime/events.py +102 -0
  260. runtime/gateway.py +128 -0
  261. runtime/lsp.py +346 -0
  262. runtime/subagent.py +258 -0
  263. runtime/tool_executor.py +104 -0
  264. runtime/tool_policy.py +106 -0
  265. safety/__init__.py +21 -0
  266. safety/permissions.py +275 -0
  267. setup_wizard.py +653 -0
  268. strategy_vault.py +420 -0
  269. ui/__init__.py +100 -0
  270. ui/banner.py +310 -0
  271. ui/completer.py +391 -0
  272. ui/console.py +271 -0
  273. ui/image_render.py +243 -0
  274. ui/input_box.py +376 -0
  275. ui/picker.py +195 -0
  276. ui/render/__init__.py +11 -0
  277. ui/render/finance.py +1480 -0
  278. ui/render/market.py +225 -0
  279. ui/render/output.py +681 -0
  280. ui/render/team.py +346 -0
  281. ui/robot.py +235 -0
  282. workspace/__init__.py +6 -0
  283. workspace/files.py +170 -0
  284. workspace/verify.py +113 -0
@@ -0,0 +1,205 @@
1
+ """
2
+ datasources/sources/world_bank_source.py — 世界银行开放数据
3
+ ============================================================
4
+ 完全免费,无需 API key。数据来源:https://api.worldbank.org/v2/
5
+ 覆盖:GDP/人均GDP/通胀/贸易/外债/人口/能源等 16,000+ 指标,200+ 国家。
6
+
7
+ 常用指标:
8
+ NY.GDP.MKTP.CD — GDP(当前美元)
9
+ NY.GDP.PCAP.CD — 人均GDP
10
+ FP.CPI.TOTL.ZG — 通货膨胀率(CPI 年增长率)
11
+ NE.TRD.GNFS.ZS — 贸易占GDP比重
12
+ SL.UEM.TOTL.ZS — 失业率
13
+ SP.POP.TOTL — 总人口
14
+ BX.KLT.DINV.CD.WD — 外商直接投资(净流入)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ import urllib.request
21
+ from typing import Any, Dict, List, Optional
22
+
23
+ from ..base import BaseDataSource, HistoryResult, QuoteResult
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ _BASE = "https://api.worldbank.org/v2"
28
+
29
+ # 常用指标别名
30
+ INDICATOR_ALIASES: Dict[str, str] = {
31
+ "GDP": "NY.GDP.MKTP.CD",
32
+ "GDPPC": "NY.GDP.PCAP.CD",
33
+ "GDPGROWTH": "NY.GDP.MKTP.KD.ZG",
34
+ "CPI": "FP.CPI.TOTL.ZG",
35
+ "INFLATION": "FP.CPI.TOTL.ZG",
36
+ "UNRATE": "SL.UEM.TOTL.ZS",
37
+ "POPULATION":"SP.POP.TOTL",
38
+ "TRADE": "NE.TRD.GNFS.ZS",
39
+ "FDI": "BX.KLT.DINV.CD.WD",
40
+ "DEBT": "GC.DOD.TOTL.GD.ZS",
41
+ "EXPORTS": "NE.EXP.GNFS.ZS",
42
+ "IMPORTS": "NE.IMP.GNFS.ZS",
43
+ }
44
+
45
+ # 国家代码别名
46
+ COUNTRY_ALIASES: Dict[str, str] = {
47
+ "CHINA": "CN", "CN": "CN", "CHN": "CN",
48
+ "US": "US", "USA": "US", "AMERICA": "US",
49
+ "JAPAN": "JP", "JP": "JP",
50
+ "GERMANY": "DE", "DE": "DE",
51
+ "UK": "GB", "GB": "GB",
52
+ "INDIA": "IN", "IN": "IN",
53
+ "BRAZIL": "BR",
54
+ "WORLD": "WLD", "GLOBAL": "WLD",
55
+ "G7": "G7", "G20": "G20",
56
+ }
57
+
58
+
59
+ def _fetch(url: str, timeout: int = 15) -> Optional[List]:
60
+ import json
61
+ try:
62
+ req = urllib.request.Request(
63
+ url + "&format=json&per_page=100",
64
+ headers={"User-Agent": "aria-code/1.0"}
65
+ )
66
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
67
+ data = json.loads(resp.read())
68
+ if isinstance(data, list) and len(data) >= 2:
69
+ return data[1]
70
+ return None
71
+ except Exception as e:
72
+ logger.debug(f"[world_bank] fetch 失败: {e}")
73
+ return None
74
+
75
+
76
+ class WorldBankSource(BaseDataSource):
77
+ """世界银行开放数据 — 宏观经济与发展指标。"""
78
+
79
+ name = "world_bank"
80
+ markets = ["macro", "us", "cn"]
81
+ requires_key = False
82
+
83
+ def is_configured(self) -> bool:
84
+ return True
85
+
86
+ def supports(self, symbol: str) -> bool:
87
+ s = symbol.upper()
88
+ return (
89
+ "." in s and len(s) > 5 or # looks like WB indicator (NY.GDP.*)
90
+ s in INDICATOR_ALIASES
91
+ )
92
+
93
+ def _resolve_indicator(self, indicator: str) -> str:
94
+ return INDICATOR_ALIASES.get(indicator.upper(), indicator)
95
+
96
+ def _resolve_country(self, country: str) -> str:
97
+ return COUNTRY_ALIASES.get(country.upper(), country.upper())
98
+
99
+ def get_indicator(
100
+ self,
101
+ indicator: str,
102
+ country: str = "WLD",
103
+ start_year: int = 2000,
104
+ end_year: int = 2024,
105
+ ) -> Optional[HistoryResult]:
106
+ """
107
+ 获取指定国家/地区的经济指标历史序列。
108
+
109
+ indicator: 世界银行指标代码或别名(GDP/CPI/UNRATE 等)
110
+ country: ISO 2位代码或别名(CN/US/JP/WORLD 等)
111
+ """
112
+ try:
113
+ import pandas as pd
114
+ ind = self._resolve_indicator(indicator)
115
+ cty = self._resolve_country(country)
116
+ url = (f"{_BASE}/country/{cty}/indicator/{ind}"
117
+ f"?date={start_year}:{end_year}")
118
+ rows_raw = _fetch(url)
119
+ if not rows_raw:
120
+ return None
121
+ rows = []
122
+ for r in rows_raw:
123
+ if r.get("value") is not None:
124
+ rows.append({
125
+ "date": f"{r['date']}-12-31",
126
+ "close": float(r["value"]),
127
+ "country": r.get("country", {}).get("value", cty),
128
+ })
129
+ if not rows:
130
+ return None
131
+ df = pd.DataFrame(rows)
132
+ df["date"] = pd.to_datetime(df["date"])
133
+ df = df.set_index("date").sort_index()
134
+ return HistoryResult(symbol=f"{country}:{indicator}", data=df,
135
+ source=self.name, interval="1y")
136
+ except Exception as e:
137
+ logger.debug(f"[world_bank] get_indicator 失败: {e}")
138
+ return None
139
+
140
+ def compare_countries(
141
+ self,
142
+ indicator: str,
143
+ countries: List[str],
144
+ start_year: int = 2010,
145
+ ) -> Optional[Dict[str, Any]]:
146
+ """
147
+ 多国横向对比同一指标。
148
+ 返回: {country: [(year, value), ...], ...}
149
+ """
150
+ ind = self._resolve_indicator(indicator)
151
+ cty_codes = ";".join(self._resolve_country(c) for c in countries)
152
+ url = (f"{_BASE}/country/{cty_codes}/indicator/{ind}"
153
+ f"?date={start_year}:2024")
154
+ rows_raw = _fetch(url)
155
+ if not rows_raw:
156
+ return None
157
+
158
+ result: Dict[str, list] = {}
159
+ for r in rows_raw:
160
+ if r.get("value") is None:
161
+ continue
162
+ cty_name = r.get("country", {}).get("value", "?")
163
+ yr = r.get("date", "")
164
+ val = float(r["value"])
165
+ result.setdefault(cty_name, []).append((yr, val))
166
+
167
+ for k in result:
168
+ result[k].sort(key=lambda x: x[0])
169
+ return result
170
+
171
+ def search_indicators(self, query: str, limit: int = 10) -> List[Dict]:
172
+ """关键词搜索世界银行指标。"""
173
+ import json, urllib.parse
174
+ q = urllib.parse.quote(query)
175
+ url = f"{_BASE}/indicator?format=json&per_page={limit}&mrv=1"
176
+ try:
177
+ req = urllib.request.Request(
178
+ f"{_BASE}/indicator?format=json&per_page={limit}&source=2",
179
+ headers={"User-Agent": "aria-code/1.0"}
180
+ )
181
+ with urllib.request.urlopen(req, timeout=10) as resp:
182
+ data = json.loads(resp.read())
183
+ if isinstance(data, list) and len(data) >= 2:
184
+ return [
185
+ {"id": i["id"], "name": i["name"],
186
+ "source": i.get("source", {}).get("value", "")}
187
+ for i in data[1] if query.lower() in i.get("name", "").lower()
188
+ ][:limit]
189
+ except Exception as e:
190
+ logger.debug(f"[world_bank] search 失败: {e}")
191
+ return []
192
+
193
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
194
+ # World Bank indicators aren't real-time quotes
195
+ return None
196
+
197
+ def history(self, symbol: str, days: int = 365 * 5, interval: str = "1y") -> Optional[HistoryResult]:
198
+ # Parse "CN:GDP" or just "GDP" (default to WLD)
199
+ if ":" in symbol:
200
+ country, indicator = symbol.split(":", 1)
201
+ else:
202
+ country, indicator = "WLD", symbol
203
+ years = max(1, days // 365)
204
+ start = 2024 - years
205
+ return self.get_indicator(indicator, country, start_year=start)
@@ -0,0 +1,152 @@
1
+ """
2
+ datasources/sources/yfinance_source.py — yfinance 美股/港股/加密 数据源
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import Optional
9
+
10
+ from ..base import BaseDataSource, FundamentalsResult, HistoryResult, QuoteResult, _detect_market
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class YFinanceSource(BaseDataSource):
16
+
17
+ name = "yfinance"
18
+ markets = ["us", "hk", "crypto"]
19
+ requires_key = False
20
+
21
+ def _to_yf_symbol(self, symbol: str) -> str:
22
+ s = symbol.upper()
23
+ if "/" in s: # BTC/USDT → BTC-USD
24
+ base, quote = s.split("/", 1)
25
+ return f"{base}-{'USD' if 'USDT' in quote else quote}"
26
+ return s
27
+
28
+ def quote(self, symbol: str) -> Optional[QuoteResult]:
29
+ try:
30
+ import yfinance as yf
31
+ yf_sym = self._to_yf_symbol(symbol)
32
+ ticker = yf.Ticker(yf_sym)
33
+
34
+ price = None
35
+ prev = None
36
+ info = {}
37
+
38
+ # Attempt 1: fast_info (lighter, often succeeds when info is rate-limited)
39
+ try:
40
+ fi = ticker.fast_info
41
+ price = getattr(fi, "last_price", None) or getattr(fi, "previous_close", None)
42
+ prev = getattr(fi, "previous_close", None) or price
43
+ except Exception:
44
+ pass
45
+
46
+ # Attempt 2: full info dict
47
+ if not price:
48
+ try:
49
+ info = ticker.info or {}
50
+ price = (info.get("currentPrice") or info.get("regularMarketPrice")
51
+ or info.get("previousClose"))
52
+ prev = info.get("previousClose") or price
53
+ except Exception:
54
+ pass
55
+
56
+ # Attempt 3: last close from recent history
57
+ if not price:
58
+ try:
59
+ hist = ticker.history(period="5d", interval="1d", auto_adjust=True)
60
+ if hist is not None and not hist.empty:
61
+ close_col = next((c for c in hist.columns if c.lower() == "close"), None)
62
+ if close_col:
63
+ price = float(hist[close_col].iloc[-1])
64
+ prev = float(hist[close_col].iloc[-2]) if len(hist) > 1 else price
65
+ except Exception:
66
+ pass
67
+
68
+ if not price:
69
+ return None
70
+
71
+ # Fetch info lazily if not already fetched
72
+ if not info:
73
+ try:
74
+ info = ticker.info or {}
75
+ except Exception:
76
+ pass
77
+
78
+ chg = price - prev if prev else 0.0
79
+ chg_p = (chg / prev * 100) if prev else 0.0
80
+ market = _detect_market(symbol)
81
+ currency = "HKD" if market == "hk" else "USD"
82
+
83
+ return QuoteResult(
84
+ symbol = symbol,
85
+ name = info.get("shortName") or info.get("longName") or symbol,
86
+ price = float(price),
87
+ change = float(chg),
88
+ change_pct = float(chg_p),
89
+ volume = float(info.get("volume") or info.get("regularMarketVolume") or 0),
90
+ market_cap = float(info.get("marketCap") or 0),
91
+ pe_ttm = float(info.get("trailingPE") or 0),
92
+ pb = float(info.get("priceToBook") or 0),
93
+ high_52w = float(info.get("fiftyTwoWeekHigh") or 0),
94
+ low_52w = float(info.get("fiftyTwoWeekLow") or 0),
95
+ currency = currency,
96
+ market = market,
97
+ source = self.name,
98
+ )
99
+ except Exception as e:
100
+ logger.debug(f"[yfinance] quote {symbol} 失败: {e}")
101
+ return None
102
+
103
+ def history(self, symbol: str, days: int = 90, interval: str = "1d") -> Optional[HistoryResult]:
104
+ try:
105
+ import yfinance as yf
106
+ ticker = yf.Ticker(self._to_yf_symbol(symbol))
107
+ period = f"{days}d" if days <= 730 else "2y"
108
+ df = ticker.history(period=period, interval=interval, auto_adjust=True)
109
+ if df is None or df.empty:
110
+ return None
111
+ df.columns = [c.lower() for c in df.columns]
112
+ return HistoryResult(symbol=symbol, data=df, source=self.name, interval=interval)
113
+ except Exception as e:
114
+ logger.debug(f"[yfinance] history {symbol} 失败: {e}")
115
+ return None
116
+
117
+ def fundamentals(self, symbol: str) -> Optional[FundamentalsResult]:
118
+ try:
119
+ import yfinance as yf
120
+ info = yf.Ticker(self._to_yf_symbol(symbol)).info or {}
121
+ if not info:
122
+ return None
123
+
124
+ def _f(key: str, mult: float = 1.0) -> Optional[float]:
125
+ v = info.get(key)
126
+ if v is None or v != v: # None or NaN
127
+ return None
128
+ fv = float(v) * mult
129
+ return fv if fv != 0.0 else None
130
+
131
+ result = FundamentalsResult(
132
+ symbol = symbol,
133
+ pe_ttm = _f("trailingPE"),
134
+ pb = _f("priceToBook"),
135
+ roe = _f("returnOnEquity", 100),
136
+ revenue_growth = _f("revenueGrowth", 100),
137
+ net_profit_growth = _f("earningsGrowth", 100),
138
+ # trailingAnnualDividendYield is consistently a fraction (e.g. 0.0035 = 0.35%)
139
+ # dividendYield is unreliable (sometimes already pct, sometimes fraction)
140
+ dividend_yield = _f("trailingAnnualDividendYield", 100),
141
+ total_mv = _f("marketCap"),
142
+ source = self.name,
143
+ )
144
+ # Return None only if every field is None (data completely missing)
145
+ has_any = any(
146
+ getattr(result, f) is not None
147
+ for f in ("pe_ttm", "pb", "roe", "revenue_growth", "total_mv")
148
+ )
149
+ return result if has_any else None
150
+ except Exception as e:
151
+ logger.debug(f"[yfinance] fundamentals {symbol} 失败: {e}")
152
+ return None
demo_player.py ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env python3
2
+ """demo_player.py — scripted Aria Code demo for VHS recording.
3
+
4
+ Plays a pre-canned interactive session that looks exactly like the real REPL.
5
+ Run: python3 demo_player.py
6
+ """
7
+ from __future__ import annotations
8
+ import sys, time, os, shutil
9
+
10
+ # ── ANSI helpers ──────────────────────────────────────────────────────────────
11
+ RESET = "\033[0m"
12
+ BOLD = "\033[1m"
13
+ DIM = "\033[2m"
14
+ PURPLE = "\033[38;5;141m"
15
+ CYAN = "\033[38;5;87m"
16
+ GREEN = "\033[38;5;120m"
17
+ YELLOW = "\033[38;5;227m"
18
+ ORANGE = "\033[38;5;215m"
19
+ RED = "\033[38;5;210m"
20
+ GREY = "\033[38;5;244m"
21
+ WHITE = "\033[38;5;255m"
22
+ BG_BAR = "\033[48;5;235m"
23
+
24
+ W = shutil.get_terminal_size((110, 40)).columns
25
+
26
+ def p(*args, end="\n", flush=True, delay=0.0):
27
+ print(*args, end=end, flush=flush)
28
+ if delay:
29
+ time.sleep(delay)
30
+
31
+ def rule(char="─", color=GREY):
32
+ p(f"{color}{char * W}{RESET}")
33
+
34
+ def typewrite(prompt: str, line: str, speed: float = 0.03):
35
+ """Print prompt then type line char by char."""
36
+ p(f"\n{PURPLE}{BOLD}>{RESET} {GREY}{prompt}{RESET}", end="", flush=True)
37
+ time.sleep(0.3)
38
+ # erase prompt hint, type actual input
39
+ p(f"\r{PURPLE}{BOLD}>{RESET} ", end="", flush=True)
40
+ for ch in line:
41
+ p(ch, end="", flush=True)
42
+ time.sleep(speed)
43
+ p() # newline = Enter
44
+ time.sleep(0.6)
45
+
46
+
47
+ # ── Banner ────────────────────────────────────────────────────────────────────
48
+ def show_banner():
49
+ os.system("clear")
50
+ p()
51
+ p(f" {PURPLE}{BOLD}▣ Aria Code{RESET} {GREY}v4.0{RESET} {DIM}本地优先 AI 金融终端{RESET}")
52
+ p(f" {GREY}model {CYAN}qwen2.5-coder:7b{RESET} {GREEN}● local{RESET}")
53
+ p(f" {GREY}status {GREEN}Ollama online{RESET} {GREY}· 3 models ready{RESET}")
54
+ p(f" {GREY}data {YELLOW}Finnhub{RESET} {GREY}·{RESET} {YELLOW}Eastmoney{RESET} {GREY}·{RESET} {YELLOW}akshare{RESET}")
55
+ p()
56
+ p(f" {DIM}try {RESET}{CYAN}quote AAPL 600519{RESET} {GREY}·{RESET} {CYAN}/backtest momentum SPY{RESET} {GREY}·{RESET} {CYAN}/help{RESET}")
57
+ rule()
58
+ time.sleep(1.2)
59
+
60
+
61
+ # ── Scene 1: multi-market quote ───────────────────────────────────────────────
62
+ def scene_quote():
63
+ typewrite("quote AAPL NVDA 600519", "quote AAPL NVDA 600519", speed=0.045)
64
+ time.sleep(0.5)
65
+
66
+ p(f"\n {BOLD}{WHITE}实时行情{RESET} {GREY}Finnhub · Eastmoney · 2026-06-16{RESET}\n")
67
+
68
+ rows = [
69
+ ("AAPL", "Apple Inc", "USD", "297.07", "+2.04%", GREEN, "4.36T", "美股"),
70
+ ("NVDA", "NVIDIA Corp", "USD", "133.38", "+3.21%", GREEN, "3.24T", "美股"),
71
+ ("600519", "贵州茅台", "CNY", "1680.00","+1.83%", GREEN, "2.11T", "A股"),
72
+ ]
73
+
74
+ # header
75
+ p(f" {GREY}{'Symbol':<10}{'Name':<22}{'Price':>12}{'Chg':>9}{'Mkt Cap':>12}{'Market':>8}{RESET}")
76
+ rule("·", GREY)
77
+
78
+ for sym, name, cur, price, chg, chg_col, mcap, mkt in rows:
79
+ p(f" {CYAN}{BOLD}{sym:<10}{RESET}{WHITE}{name:<22}{RESET}"
80
+ f"{BOLD}{cur} {price:>8}{RESET}"
81
+ f" {chg_col}{chg:>7}{RESET}"
82
+ f" {GREY}{mcap:>10} {mkt}{RESET}",
83
+ delay=0.15)
84
+
85
+ rule("·", GREY)
86
+ p(f"\n {GREY}RSI {RESET}{YELLOW}AAPL 40.6 中性{RESET} {GREY}·{RESET} "
87
+ f"{GREEN}NVDA 61.2 偏强{RESET} {GREY}·{RESET} {GREEN}600519 62.3 偏强{RESET}")
88
+ p(f" {GREY}tips {RESET}{DIM}/signal NVDA · /team AAPL · /peer AAPL MSFT GOOGL{RESET}")
89
+ time.sleep(1.5)
90
+
91
+
92
+ # ── Scene 2: AI analysis ──────────────────────────────────────────────────────
93
+ def scene_analyze():
94
+ typewrite("分析 NVDA 动量 — RSI MACD 和投资论点", "分析 NVDA 动量 — RSI MACD 和投资论点", speed=0.04)
95
+ time.sleep(0.8)
96
+
97
+ p(f"\n {BOLD}{WHITE}NVIDIA Corp (NVDA){RESET} {GREY}── 技术快照{RESET}\n")
98
+
99
+ metrics = [
100
+ ("现价", f"{GREEN}{BOLD}USD 133.38{RESET}", f"{GREEN}+3.21% 今日{RESET}"),
101
+ ("RSI(14)", f"{YELLOW}61.2 中性偏强{RESET}", "未进入超买区间"),
102
+ ("MACD", f"{GREEN}+2.87 金叉{RESET}", "3 天前形成,趋势延续"),
103
+ ("布林带", "带宽 0.19", "波动率正常,上轨 $141.2"),
104
+ ("MA20", f"{GREEN}$128.40 价格上方{RESET}", "短期均线多头排列"),
105
+ ]
106
+ for label, val, note in metrics:
107
+ p(f" {GREY}{label:<10}{RESET}{val:<38}{DIM}{note}{RESET}", delay=0.18)
108
+
109
+ p()
110
+ p(f" {BOLD}{GREEN}信号:↑ 看多{RESET} {GREY}(动量完好,关注 RSI 是否突破 70){RESET}")
111
+ p(f" {GREY}支撑:{RESET}$128.4 / $121.6 {GREY}压力:{RESET}$138.0 / $145.5")
112
+ p()
113
+
114
+ # streaming thesis
115
+ p(f" {BOLD}{WHITE}投资论点{RESET}", end="", flush=True)
116
+ thesis = (" AI 基础设施支出周期仍处早期,数据中心 GPU 需求刚性强。"
117
+ "Blackwell 架构供不应求,FY26 营收预期持续上调。"
118
+ "短期技术面动量健康,中期持有逻辑完整。")
119
+ p()
120
+ for ch in thesis:
121
+ sys.stdout.write(ch)
122
+ sys.stdout.flush()
123
+ time.sleep(0.018)
124
+ p(f"\n\n {GREY}2.3s · qwen2.5-coder:7b (local){RESET}")
125
+ time.sleep(1.8)
126
+
127
+
128
+ # ── Scene 3: backtest ─────────────────────────────────────────────────────────
129
+ def scene_backtest():
130
+ typewrite("/backtest momentum NVDA AAPL MSFT 2024-01-01 2025-12-31",
131
+ "/backtest momentum NVDA AAPL MSFT 2024-01-01 2025-12-31", speed=0.04)
132
+ time.sleep(0.6)
133
+
134
+ p(f"\n {BOLD}{WHITE}动量策略回测{RESET} {GREY}2024-01-01 → 2025-12-31{RESET}\n")
135
+
136
+ p(f" {GREY}{'策略':<22}{'总收益':>10}{'夏普比率':>12}{'最大回撤':>12}{'胜率':>10}{RESET}")
137
+ rule("·", GREY)
138
+
139
+ results = [
140
+ ("动量 (Aria)", "+47.3%", "1.82", "-12.4%", "63%", GREEN),
141
+ ("买入持有 SPY", "+26.1%", "1.21", "-19.3%", "—", YELLOW),
142
+ ("买入持有 QQQ", "+31.8%", "1.34", "-17.1%", "—", YELLOW),
143
+ ]
144
+ for name, ret, sharpe, dd, wr, col in results:
145
+ p(f" {col}{BOLD}{name:<22}{RESET}{col}{ret:>10}{RESET}"
146
+ f" {WHITE}{sharpe:>10}{RESET} {RED}{dd:>10}{RESET} {GREY}{wr:>8}{RESET}",
147
+ delay=0.2)
148
+
149
+ rule("·", GREY)
150
+ p(f"\n {GREEN}{BOLD}动量策略跑赢基准 +15.5%{RESET} {GREY}· Kelly 建议仓位 18.4% · 共 248 次交易{RESET}")
151
+ p(f" {DIM}tips /wf NVDA momentum · /kelly NVDA 0.63 2.1 · /corr NVDA AAPL SPY{RESET}")
152
+ time.sleep(1.8)
153
+
154
+
155
+ # ── Scene 4: shell mode + @ autocomplete hint ─────────────────────────────────
156
+ def scene_shell():
157
+ typewrite("! git log --oneline -3", "! git log --oneline -3", speed=0.05)
158
+ time.sleep(0.4)
159
+
160
+ p(f" {GREEN}d4e3ab9{RESET} {WHITE}feat(v4.0): keyboard shortcuts, shell mode, 19+ providers{RESET}")
161
+ p(f" {GREEN}3e80289{RESET} {WHITE}feat(agent): 6项核心思考/输出逻辑改进{RESET}")
162
+ p(f" {GREEN}b854093{RESET} {WHITE}revert(brand): switch back to SVG logo{RESET}")
163
+ p(f" {DIM}↑ shell output injected into AI context{RESET}")
164
+ time.sleep(1.2)
165
+
166
+ typewrite("分析以上 commits 的代码质量趋势", "分析以上 commits 的代码质量趋势", speed=0.04)
167
+ time.sleep(0.5)
168
+
169
+ thesis2 = (" 最近 3 次提交显示:v4.0 功能密度高(+4912/-2245 行),"
170
+ "重构 agent 逻辑合理,logo revert 说明团队注重视觉一致性。"
171
+ "建议下一步补充集成测试覆盖 shell 模式和 @ 文件补全路径。")
172
+ for ch in thesis2:
173
+ sys.stdout.write(ch)
174
+ sys.stdout.flush()
175
+ time.sleep(0.016)
176
+ p(f"\n\n {GREY}1.8s · qwen2.5-coder:7b (local){RESET}")
177
+ time.sleep(2.0)
178
+
179
+
180
+ # ── Bottom toolbar (static, painted last) ─────────────────────────────────────
181
+ def show_toolbar():
182
+ bar = (f"{BG_BAR} {CYAN}qwen2.5-coder:7b{RESET}{BG_BAR}"
183
+ f" {GREY}· ~/aria-code {GREEN}⎇ main{RESET}{BG_BAR}"
184
+ f" {GREY}· {YELLOW}rw{RESET}{BG_BAR}"
185
+ f" {GREY}· local-only{RESET}{BG_BAR}"
186
+ f" {GREY}· /help{RESET}{BG_BAR}"
187
+ f"{' ' * 20}{RESET}")
188
+ p(f"\n{bar}")
189
+
190
+
191
+ # ── Main ──────────────────────────────────────────────────────────────────────
192
+ def main():
193
+ show_banner()
194
+ scene_quote()
195
+ scene_analyze()
196
+ scene_backtest()
197
+ scene_shell()
198
+ show_toolbar()
199
+ p()
200
+ time.sleep(3.0)
201
+
202
+
203
+ if __name__ == "__main__":
204
+ main()