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.
- agents/__init__.py +32 -0
- agents/base.py +190 -0
- agents/deep/__init__.py +37 -0
- agents/deep/calibration_loop.py +144 -0
- agents/deep/critic.py +125 -0
- agents/deep/deepen.py +193 -0
- agents/deep/models.py +149 -0
- agents/deep/pipeline.py +164 -0
- agents/deep/quant_fusion.py +192 -0
- agents/deep/themes.py +95 -0
- agents/deep/tiers.py +106 -0
- agents/financial/__init__.py +10 -0
- agents/financial/catalyst.py +279 -0
- agents/financial/debate.py +145 -0
- agents/financial/earnings.py +303 -0
- agents/financial/fundamental.py +159 -0
- agents/financial/macro.py +99 -0
- agents/financial/news.py +207 -0
- agents/financial/risk.py +132 -0
- agents/financial/sector.py +279 -0
- agents/financial/synthesis.py +274 -0
- agents/financial/technical.py +258 -0
- agents/portfolio_agent.py +333 -0
- agents/realty/__init__.py +62 -0
- agents/realty/asset_diagnosis.py +150 -0
- agents/realty/business_match.py +165 -0
- agents/realty/cashflow_verify.py +208 -0
- agents/realty/contract_rules.py +209 -0
- agents/realty/energy_anomaly.py +188 -0
- agents/realty/exit_settlement.py +207 -0
- agents/realty/fulfillment_risk.py +205 -0
- agents/realty/ops_optimize.py +159 -0
- agents/realty/revenue_share.py +214 -0
- agents/registry.py +144 -0
- agents/sports/__init__.py +0 -0
- agents/sports/football_agent.py +169 -0
- agents/team.py +289 -0
- aliyun_data_client.py +660 -0
- apps/README.md +12 -0
- apps/__init__.py +2 -0
- apps/channels/README.md +15 -0
- apps/cli/README.md +13 -0
- apps/cli/__init__.py +2 -0
- apps/cli/bootstrap.py +99 -0
- apps/cli/codegen_paths.py +29 -0
- apps/cli/commands/__init__.py +16 -0
- apps/cli/commands/analysis_cmds.py +288 -0
- apps/cli/commands/backtest_cmds.py +1887 -0
- apps/cli/commands/broker_cmds.py +1154 -0
- apps/cli/commands/business_workflow_cmds.py +289 -0
- apps/cli/commands/catalog.py +84 -0
- apps/cli/commands/data_cmds.py +405 -0
- apps/cli/commands/diagnostic_cmds.py +179 -0
- apps/cli/commands/diagnostic_ops_cmds.py +696 -0
- apps/cli/commands/finance_render.py +12 -0
- apps/cli/commands/market.py +399 -0
- apps/cli/commands/market_cmds.py +1276 -0
- apps/cli/commands/market_context.py +425 -0
- apps/cli/commands/market_render.py +7 -0
- apps/cli/commands/model_cmds.py +1579 -0
- apps/cli/commands/ops_cmds.py +668 -0
- apps/cli/commands/portfolio_cmds.py +962 -0
- apps/cli/commands/report.py +377 -0
- apps/cli/commands/scaffold_templates.py +617 -0
- apps/cli/commands/session_cmds.py +179 -0
- apps/cli/commands/session_ux_cmds.py +280 -0
- apps/cli/commands/team.py +588 -0
- apps/cli/commands/team_render.py +8 -0
- apps/cli/commands/ui_cmds.py +358 -0
- apps/cli/commands/workflow_cmds.py +279 -0
- apps/cli/commands/workspace_cmds.py +1414 -0
- apps/cli/config_paths.py +70 -0
- apps/cli/config_store.py +61 -0
- apps/cli/deterministic.py +122 -0
- apps/cli/direct.py +48 -0
- apps/cli/github_app_auth.py +135 -0
- apps/cli/handlers/__init__.py +11 -0
- apps/cli/handlers/broker_handlers.py +122 -0
- apps/cli/handlers/chart_handlers.py +1309 -0
- apps/cli/handlers/market_handlers.py +2509 -0
- apps/cli/handlers/realty_handlers.py +114 -0
- apps/cli/handlers/strategy_advice.py +82 -0
- apps/cli/hooks.py +180 -0
- apps/cli/i18n.py +284 -0
- apps/cli/intent.py +136 -0
- apps/cli/intent_router.py +217 -0
- apps/cli/lifecycle_hooks.py +48 -0
- apps/cli/main.py +29 -0
- apps/cli/market_metadata.py +135 -0
- apps/cli/market_universe.py +265 -0
- apps/cli/message_processing.py +257 -0
- apps/cli/plan_mode.py +139 -0
- apps/cli/plotly_html.py +15 -0
- apps/cli/prediction_feedback.py +202 -0
- apps/cli/preflight.py +497 -0
- apps/cli/project_aria.py +60 -0
- apps/cli/prompts/__init__.py +0 -0
- apps/cli/prompts/coding.py +658 -0
- apps/cli/prompts/system_prompts.py +531 -0
- apps/cli/prompts/ui.py +434 -0
- apps/cli/providers/__init__.py +1 -0
- apps/cli/providers/base.py +271 -0
- apps/cli/providers/chat_routing.py +80 -0
- apps/cli/providers/llm/__init__.py +1 -0
- apps/cli/providers/llm/ollama_stream.py +1170 -0
- apps/cli/providers/llm/sse_stream.py +216 -0
- apps/cli/providers/runtime_bridge.py +185 -0
- apps/cli/runtime_consumer.py +489 -0
- apps/cli/session_export.py +87 -0
- apps/cli/session_jsonl.py +207 -0
- apps/cli/session_store.py +112 -0
- apps/cli/todo_tracker.py +190 -0
- apps/cli/tools/__init__.py +40 -0
- apps/cli/tools/context.py +46 -0
- apps/cli/tools/file_tools.py +112 -0
- apps/cli/tools/market_tools.py +549 -0
- apps/cli/tools/notebook_tools.py +111 -0
- apps/cli/tools/system_tools.py +669 -0
- apps/cli/tools/write_tools.py +715 -0
- apps/cli/tradingview_bridge.py +434 -0
- apps/cli/update_check.py +152 -0
- apps/cli/utils/__init__.py +0 -0
- apps/cli/utils/market_detect.py +1578 -0
- apps/daemon/README.md +14 -0
- apps/vscode/README.md +115 -0
- apps/vscode/package.json +70 -0
- aria_cli.py +11636 -0
- aria_code-4.1.3.dist-info/METADATA +952 -0
- aria_code-4.1.3.dist-info/RECORD +284 -0
- aria_code-4.1.3.dist-info/WHEEL +5 -0
- aria_code-4.1.3.dist-info/entry_points.txt +2 -0
- aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
- aria_code-4.1.3.dist-info/top_level.txt +50 -0
- aria_daemon.py +1295 -0
- aria_feishu_bot.py +1359 -0
- aria_relay_client.py +182 -0
- aria_relay_server.py +405 -0
- aria_telegram_bot.py +202 -0
- ariarc.py +328 -0
- artifacts.py +491 -0
- backtest_report.py +472 -0
- brokers/__init__.py +72 -0
- brokers/base.py +207 -0
- brokers/capabilities.py +264 -0
- brokers/cn/__init__.py +10 -0
- brokers/cn/easytrader_broker.py +193 -0
- brokers/cn/futu_broker.py +194 -0
- brokers/cn/longbridge_broker.py +190 -0
- brokers/cn/tiger_broker.py +196 -0
- brokers/cn/xtquant_broker.py +175 -0
- brokers/config.py +364 -0
- brokers/intl/__init__.py +5 -0
- brokers/intl/alpaca_broker.py +183 -0
- brokers/intl/ibkr_broker.py +215 -0
- brokers/intl/webull_broker.py +156 -0
- brokers/paper_broker.py +259 -0
- brokers/planning.py +296 -0
- brokers/registry.py +181 -0
- brokers/trading.py +237 -0
- change_store.py +127 -0
- command_safety.py +19 -0
- computer_use_tools.py +504 -0
- dashboard_generator.py +578 -0
- data_analysis_tools.py +808 -0
- data_cleaner.py +483 -0
- data_service.py +481 -0
- datasources/__init__.py +23 -0
- datasources/base.py +166 -0
- datasources/router.py +221 -0
- datasources/sources/__init__.py +15 -0
- datasources/sources/akshare_source.py +269 -0
- datasources/sources/alpha_vantage_source.py +202 -0
- datasources/sources/edgar_source.py +218 -0
- datasources/sources/finnhub_source.py +197 -0
- datasources/sources/fred_source.py +219 -0
- datasources/sources/tushare_source.py +141 -0
- datasources/sources/web_scraper_source.py +278 -0
- datasources/sources/world_bank_source.py +205 -0
- datasources/sources/yfinance_source.py +152 -0
- demo_player.py +204 -0
- doctor.py +508 -0
- file_analysis_tools.py +734 -0
- finance_formulas.py +389 -0
- football_data_client.py +1670 -0
- intent_classifier.py +358 -0
- local_finance_tools.py +3221 -0
- local_llm_provider.py +552 -0
- macro_tools.py +368 -0
- market_data_client.py +1899 -0
- mcp_client.py +506 -0
- memory_manager.py +245 -0
- model_capability.py +416 -0
- notification_tools.py +248 -0
- packages/__init__.py +23 -0
- packages/aria_agents/__init__.py +5 -0
- packages/aria_agents/manifest.py +69 -0
- packages/aria_core/__init__.py +34 -0
- packages/aria_core/architecture.py +192 -0
- packages/aria_core/export.py +124 -0
- packages/aria_core/manifest.py +65 -0
- packages/aria_infra/__init__.py +15 -0
- packages/aria_infra/arthera.py +52 -0
- packages/aria_infra/doctor.py +246 -0
- packages/aria_infra/product.py +37 -0
- packages/aria_mcp/__init__.py +25 -0
- packages/aria_mcp/bridge.py +38 -0
- packages/aria_mcp/config.py +97 -0
- packages/aria_mcp/tools.py +61 -0
- packages/aria_sdk/__init__.py +19 -0
- packages/aria_sdk/client.py +396 -0
- packages/aria_sdk/providers.py +70 -0
- packages/aria_sdk/streaming.py +73 -0
- packages/aria_sdk/types.py +86 -0
- packages/aria_services/__init__.py +55 -0
- packages/aria_services/context.py +258 -0
- packages/aria_services/data.py +11 -0
- packages/aria_services/provider_health.py +189 -0
- packages/aria_services/registry.py +213 -0
- packages/aria_services/usage.py +138 -0
- packages/aria_skills/__init__.py +5 -0
- packages/aria_skills/registry.py +59 -0
- packages/aria_tools/__init__.py +5 -0
- packages/aria_tools/registry.py +128 -0
- packages/quant_engine/__init__.py +6 -0
- packages/quant_engine/sports/__init__.py +72 -0
- packages/quant_engine/sports/calibrator.py +353 -0
- packages/quant_engine/sports/dixon_coles.py +234 -0
- packages/quant_engine/sports/elo.py +299 -0
- packages/quant_engine/sports/form.py +188 -0
- packages/quant_engine/sports/h2h.py +195 -0
- packages/quant_engine/sports/ml_model.py +354 -0
- packages/quant_engine/sports/predictor.py +311 -0
- packages/quant_engine/sports/tracker.py +664 -0
- packages/quant_engine/stochastic/__init__.py +27 -0
- packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- packages/quant_engine/stochastic/ito_calculus.py +477 -0
- packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- packages/quant_engine/stochastic/options_pricing.py +573 -0
- packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- plan_utils.py +194 -0
- plugin_loader.py +328 -0
- portfolio_ledger.py +262 -0
- privacy/__init__.py +5 -0
- privacy/feedback.py +123 -0
- project_tools.py +525 -0
- providers/__init__.py +30 -0
- providers/llm/__init__.py +19 -0
- providers/llm/anthropic.py +184 -0
- providers/llm/base.py +139 -0
- providers/llm/ollama.py +128 -0
- providers/llm/openai_compat.py +282 -0
- providers/llm/registry.py +358 -0
- realty_data_tools.py +659 -0
- report_generator.py +1314 -0
- runtime/__init__.py +103 -0
- runtime/agent_loop.py +1183 -0
- runtime/approval.py +51 -0
- runtime/events.py +102 -0
- runtime/gateway.py +128 -0
- runtime/lsp.py +346 -0
- runtime/subagent.py +258 -0
- runtime/tool_executor.py +104 -0
- runtime/tool_policy.py +106 -0
- safety/__init__.py +21 -0
- safety/permissions.py +275 -0
- setup_wizard.py +653 -0
- strategy_vault.py +420 -0
- ui/__init__.py +100 -0
- ui/banner.py +310 -0
- ui/completer.py +391 -0
- ui/console.py +271 -0
- ui/image_render.py +243 -0
- ui/input_box.py +376 -0
- ui/picker.py +195 -0
- ui/render/__init__.py +11 -0
- ui/render/finance.py +1480 -0
- ui/render/market.py +225 -0
- ui/render/output.py +681 -0
- ui/render/team.py +346 -0
- ui/robot.py +235 -0
- workspace/__init__.py +6 -0
- workspace/files.py +170 -0
- 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()
|