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
macro_tools.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""
|
|
2
|
+
macro_tools.py — 宏观经济数据层
|
|
3
|
+
=================================
|
|
4
|
+
来源:
|
|
5
|
+
- FRED (美联储 St. Louis) — GDP, CPI, 联邦基金利率, 失业率, M2
|
|
6
|
+
- AKShare — 中国 CPI/PPI/PMI/GDP/LPR/社融
|
|
7
|
+
- yfinance — 全球央行政策利率(备用)
|
|
8
|
+
|
|
9
|
+
全部函数返回 {"success": bool, "data": [...], ...} 格式,
|
|
10
|
+
与 local_finance_tools 保持一致。
|
|
11
|
+
|
|
12
|
+
依赖安装(可选):
|
|
13
|
+
pip install requests # FRED REST API
|
|
14
|
+
pip install akshare # 中国数据
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
from datetime import datetime, timedelta
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Dict, List, Optional
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
# ── Optional imports ──────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import requests as _req
|
|
31
|
+
_HAS_REQUESTS = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
_HAS_REQUESTS = False
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
import akshare as ak
|
|
37
|
+
_HAS_AK = True
|
|
38
|
+
except ImportError:
|
|
39
|
+
_HAS_AK = False
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
import pandas as pd
|
|
43
|
+
_HAS_PD = True
|
|
44
|
+
except ImportError:
|
|
45
|
+
_HAS_PD = False
|
|
46
|
+
|
|
47
|
+
# ── FRED API helper ──────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
FRED_BASE = "https://fred.stlouisfed.org/graph/fredgraph.csv"
|
|
50
|
+
FRED_API_BASE = "https://api.stlouisfed.org/fred"
|
|
51
|
+
|
|
52
|
+
def _get_fred_key() -> str:
|
|
53
|
+
"""Read FRED API key from env or ~/.arthera/providers.json."""
|
|
54
|
+
key = os.getenv("FRED_API_KEY", "")
|
|
55
|
+
if not key:
|
|
56
|
+
try:
|
|
57
|
+
import json
|
|
58
|
+
p = Path.home() / ".arthera" / "providers.json"
|
|
59
|
+
if p.exists():
|
|
60
|
+
d = json.loads(p.read_text())
|
|
61
|
+
key = d.get("fred", {}).get("api_key", "") or d.get("fred_api_key", "")
|
|
62
|
+
except Exception:
|
|
63
|
+
pass
|
|
64
|
+
return key
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _fred_series(series_id: str, limit: int = 24, units: str = "") -> List[Dict]:
|
|
68
|
+
"""Fetch a FRED data series. Returns list of {date, value} dicts.
|
|
69
|
+
|
|
70
|
+
``units`` maps to FRED's units transformation, e.g. "pc1" = percent change
|
|
71
|
+
from a year ago (used for YoY rates like CPI inflation, so we don't show
|
|
72
|
+
the raw index level mislabelled as a percentage).
|
|
73
|
+
"""
|
|
74
|
+
if not _HAS_REQUESTS:
|
|
75
|
+
return []
|
|
76
|
+
key = _get_fred_key()
|
|
77
|
+
try:
|
|
78
|
+
params: Dict[str, Any] = {
|
|
79
|
+
"series_id": series_id,
|
|
80
|
+
"limit": limit,
|
|
81
|
+
"sort_order": "desc",
|
|
82
|
+
"file_type": "json",
|
|
83
|
+
}
|
|
84
|
+
if units:
|
|
85
|
+
params["units"] = units
|
|
86
|
+
if key:
|
|
87
|
+
params["api_key"] = key
|
|
88
|
+
url = f"{FRED_API_BASE}/series/observations"
|
|
89
|
+
r = _req.get(url, params=params, timeout=8)
|
|
90
|
+
r.raise_for_status()
|
|
91
|
+
obs = r.json().get("observations", [])
|
|
92
|
+
return [
|
|
93
|
+
{"date": o["date"], "value": float(o["value"]) if o["value"] != "." else None}
|
|
94
|
+
for o in reversed(obs)
|
|
95
|
+
]
|
|
96
|
+
else:
|
|
97
|
+
# Public CSV endpoint (no key needed, slower). fredgraph supports
|
|
98
|
+
# transformation codes via the `transformation` query param.
|
|
99
|
+
obs_url = f"https://fred.stlouisfed.org/graph/fredgraph.csv?id={series_id}"
|
|
100
|
+
if units:
|
|
101
|
+
obs_url += f"&transformation={units}"
|
|
102
|
+
r = _req.get(obs_url, timeout=10)
|
|
103
|
+
r.raise_for_status()
|
|
104
|
+
lines = r.text.strip().split("\n")[1:] # skip header
|
|
105
|
+
result = []
|
|
106
|
+
for line in lines[-limit:]:
|
|
107
|
+
parts = line.split(",")
|
|
108
|
+
if len(parts) == 2:
|
|
109
|
+
try:
|
|
110
|
+
result.append({"date": parts[0], "value": float(parts[1])})
|
|
111
|
+
except ValueError:
|
|
112
|
+
pass
|
|
113
|
+
return result
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.debug("FRED fetch %s failed: %s", series_id, e)
|
|
116
|
+
return []
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ── US Macro ──────────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
_FRED_SERIES_MAP = {
|
|
122
|
+
"gdp": ("GDP", "GDP 实际值 (十亿美元, 季度)", "B"),
|
|
123
|
+
"gdp_growth": ("A191RL1Q225SBEA", "GDP 同比增速 (%)", "%"),
|
|
124
|
+
"cpi": ("CPIAUCSL", "CPI 城市消费者价格指数", "idx"),
|
|
125
|
+
"cpi_yoy": ("CPIAUCSL", "CPI 同比 (%)", "%", "pc1"), # pc1 = YoY %
|
|
126
|
+
"core_cpi_yoy": ("CPILFESL", "核心 CPI 同比 (%)", "%", "pc1"),
|
|
127
|
+
"core_cpi": ("CPILFESL", "核心 CPI (剔除食品能源)", "idx"),
|
|
128
|
+
"pce": ("PCEPI", "PCE 通胀指数", "idx"),
|
|
129
|
+
"fed_rate": ("FEDFUNDS", "联邦基金利率 (%)", "%"),
|
|
130
|
+
"unemployment": ("UNRATE", "美国失业率 (%)", "%"),
|
|
131
|
+
"m2": ("M2SL", "M2 货币供给 (十亿美元)", "B"),
|
|
132
|
+
"10y_yield": ("DGS10", "10年期国债收益率 (%)", "%"),
|
|
133
|
+
"2y_yield": ("DGS2", "2年期国债收益率 (%)", "%"),
|
|
134
|
+
"vix": ("VIXCLS", "VIX 恐慌指数", ""),
|
|
135
|
+
"retail_sales": ("RSAFS", "零售销售额 (百万美元, 季调)", "M"),
|
|
136
|
+
"industrial": ("INDPRO", "工业产出指数", "idx"),
|
|
137
|
+
"housing": ("HOUST", "新屋开工 (千套, 季调年化)", "K"),
|
|
138
|
+
"ppi": ("PPIACO", "PPI 生产者价格指数", "idx"),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_us_macro(indicator: str = "all", periods: int = 12) -> dict:
|
|
143
|
+
"""
|
|
144
|
+
获取美国宏观经济数据。
|
|
145
|
+
|
|
146
|
+
indicator: "gdp" | "cpi" | "fed_rate" | "unemployment" | "m2" |
|
|
147
|
+
"10y_yield" | "retail_sales" | "all"
|
|
148
|
+
periods: 返回最近 N 期数据
|
|
149
|
+
"""
|
|
150
|
+
if indicator == "all":
|
|
151
|
+
keys = ["gdp_growth", "cpi_yoy", "fed_rate", "unemployment", "10y_yield",
|
|
152
|
+
"2y_yield", "m2", "ppi"]
|
|
153
|
+
else:
|
|
154
|
+
keys = [indicator] if indicator in _FRED_SERIES_MAP else []
|
|
155
|
+
if not keys:
|
|
156
|
+
return {"success": False, "error": f"未知指标: {indicator}。可选: {', '.join(_FRED_SERIES_MAP)}"}
|
|
157
|
+
|
|
158
|
+
results = {}
|
|
159
|
+
for key in keys:
|
|
160
|
+
_spec = _FRED_SERIES_MAP[key]
|
|
161
|
+
series_id, label, unit = _spec[0], _spec[1], _spec[2]
|
|
162
|
+
_units = _spec[3] if len(_spec) > 3 else ""
|
|
163
|
+
# pc1 (YoY) needs ~13 months of monthly data to compute the first point
|
|
164
|
+
_lim = max(periods, 14) if _units == "pc1" else periods
|
|
165
|
+
data = _fred_series(series_id, limit=_lim, units=_units)
|
|
166
|
+
if data:
|
|
167
|
+
latest = data[-1]
|
|
168
|
+
prev = data[-2] if len(data) >= 2 else None
|
|
169
|
+
change = None
|
|
170
|
+
if latest["value"] is not None and prev and prev["value"] is not None:
|
|
171
|
+
change = round(latest["value"] - prev["value"], 3)
|
|
172
|
+
results[key] = {
|
|
173
|
+
"label": label,
|
|
174
|
+
"unit": unit,
|
|
175
|
+
"latest": latest,
|
|
176
|
+
"prev": prev,
|
|
177
|
+
"change": change,
|
|
178
|
+
"series": data[-6:], # last 6 periods for sparkline
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if not results:
|
|
182
|
+
return {"success": False, "error": "FRED 数据获取失败(无 API Key 时使用公共 CSV 端点,速度较慢)"}
|
|
183
|
+
|
|
184
|
+
# Yield curve shape
|
|
185
|
+
if "10y_yield" in results and "2y_yield" in results:
|
|
186
|
+
v10 = results["10y_yield"]["latest"]["value"] or 0
|
|
187
|
+
v2 = results["2y_yield"]["latest"]["value"] or 0
|
|
188
|
+
spread = round(v10 - v2, 3)
|
|
189
|
+
results["_yield_curve"] = {
|
|
190
|
+
"spread_10y_2y": spread,
|
|
191
|
+
"shape": "正常" if spread > 0.2 else "倒挂" if spread < 0 else "平坦",
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {"success": True, "country": "US", "indicator": indicator,
|
|
195
|
+
"data": results, "provider": "FRED"}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# ── China Macro ───────────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
def get_cn_macro(indicator: str = "all") -> dict:
|
|
201
|
+
"""
|
|
202
|
+
获取中国宏观经济数据 (via akshare)。
|
|
203
|
+
|
|
204
|
+
indicator: "cpi" | "ppi" | "pmi" | "gdp" | "lpr" | "m2" | "all"
|
|
205
|
+
"""
|
|
206
|
+
if not _HAS_AK:
|
|
207
|
+
return {"success": False, "error": "akshare 未安装,请运行: pip install akshare"}
|
|
208
|
+
|
|
209
|
+
results = {}
|
|
210
|
+
|
|
211
|
+
def _fetch(fn, key, label):
|
|
212
|
+
try:
|
|
213
|
+
df = fn()
|
|
214
|
+
if df is None or (hasattr(df, "empty") and df.empty):
|
|
215
|
+
return
|
|
216
|
+
# Keep last 12 rows
|
|
217
|
+
df = df.tail(12)
|
|
218
|
+
records = df.to_dict("records") if _HAS_PD else []
|
|
219
|
+
latest = records[-1] if records else {}
|
|
220
|
+
results[key] = {"label": label, "latest": latest, "series": records}
|
|
221
|
+
except Exception as e:
|
|
222
|
+
logger.debug("akshare %s failed: %s", key, e)
|
|
223
|
+
|
|
224
|
+
if indicator in ("cpi", "all"):
|
|
225
|
+
_fetch(ak.macro_china_cpi_yearly, "cpi", "中国 CPI 同比 (%)")
|
|
226
|
+
|
|
227
|
+
if indicator in ("ppi", "all"):
|
|
228
|
+
_fetch(ak.macro_china_ppi_yearly, "ppi", "中国 PPI 同比 (%)")
|
|
229
|
+
|
|
230
|
+
if indicator in ("pmi", "all"):
|
|
231
|
+
_fetch(ak.macro_china_pmi_yearly, "pmi_manufacturing",
|
|
232
|
+
"制造业 PMI")
|
|
233
|
+
try:
|
|
234
|
+
df_s = ak.macro_china_non_man_pmi()
|
|
235
|
+
if df_s is not None and not df_s.empty:
|
|
236
|
+
records_s = df_s.tail(12).to_dict("records")
|
|
237
|
+
results["pmi_service"] = {
|
|
238
|
+
"label": "非制造业 PMI",
|
|
239
|
+
"latest": records_s[-1] if records_s else {},
|
|
240
|
+
"series": records_s,
|
|
241
|
+
}
|
|
242
|
+
except Exception:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
if indicator in ("gdp", "all"):
|
|
246
|
+
_fetch(ak.macro_china_gdp_yearly, "gdp", "中国 GDP 同比增速 (%)")
|
|
247
|
+
|
|
248
|
+
if indicator in ("lpr", "all"):
|
|
249
|
+
try:
|
|
250
|
+
df_lpr = ak.macro_china_lpr()
|
|
251
|
+
if df_lpr is not None and not df_lpr.empty:
|
|
252
|
+
records_l = df_lpr.tail(6).to_dict("records")
|
|
253
|
+
results["lpr"] = {"label": "LPR 利率", "series": records_l,
|
|
254
|
+
"latest": records_l[-1] if records_l else {}}
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.debug("LPR fetch failed: %s", e)
|
|
257
|
+
|
|
258
|
+
if indicator in ("m2", "all"):
|
|
259
|
+
_fetch(ak.macro_china_money_supply, "m2", "中国 M2 同比增速 (%)")
|
|
260
|
+
|
|
261
|
+
if not results:
|
|
262
|
+
return {"success": False, "error": "未能获取任何中国宏观数据"}
|
|
263
|
+
|
|
264
|
+
return {"success": True, "country": "CN", "indicator": indicator,
|
|
265
|
+
"data": results, "provider": "akshare"}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# ── Economic Calendar ─────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
def get_economic_calendar(days_ahead: int = 7) -> dict:
|
|
271
|
+
"""
|
|
272
|
+
获取未来 N 天的重大经济事件日历。
|
|
273
|
+
数据来源:投资道/东方财富 (akshare),备用: Finnhub
|
|
274
|
+
"""
|
|
275
|
+
results = []
|
|
276
|
+
|
|
277
|
+
# Try akshare economic calendar (东方财富)
|
|
278
|
+
if _HAS_AK:
|
|
279
|
+
try:
|
|
280
|
+
df = ak.news_economic_baidu(date=datetime.now().strftime("%Y%m%d"))
|
|
281
|
+
if df is not None and not df.empty:
|
|
282
|
+
cols = [c for c in ["time","event","actual","forecast","previous","importance"]
|
|
283
|
+
if c in df.columns]
|
|
284
|
+
results = df[cols].head(30).to_dict("records") if cols else []
|
|
285
|
+
except Exception as e:
|
|
286
|
+
logger.debug("akshare calendar failed: %s", e)
|
|
287
|
+
|
|
288
|
+
# Fallback: Finnhub economic calendar
|
|
289
|
+
if not results and _HAS_REQUESTS:
|
|
290
|
+
try:
|
|
291
|
+
key = _get_finnhub_key()
|
|
292
|
+
if key:
|
|
293
|
+
from_dt = datetime.now().strftime("%Y-%m-%d")
|
|
294
|
+
to_dt = (datetime.now() + timedelta(days=days_ahead)).strftime("%Y-%m-%d")
|
|
295
|
+
r = _req.get(
|
|
296
|
+
"https://finnhub.io/api/v1/calendar/economic",
|
|
297
|
+
params={"from": from_dt, "to": to_dt, "token": key},
|
|
298
|
+
timeout=8,
|
|
299
|
+
)
|
|
300
|
+
r.raise_for_status()
|
|
301
|
+
results = r.json().get("economicCalendar", [])
|
|
302
|
+
except Exception as e:
|
|
303
|
+
logger.debug("Finnhub calendar failed: %s", e)
|
|
304
|
+
|
|
305
|
+
if not results:
|
|
306
|
+
# Static upcoming FOMC/PBOC dates as fallback
|
|
307
|
+
results = [
|
|
308
|
+
{"event": "FOMC Meeting", "importance": "HIGH",
|
|
309
|
+
"note": "具体日期请查阅 federalreserve.gov/monetarypolicy"},
|
|
310
|
+
{"event": "PBOC LPR Announcement", "importance": "HIGH",
|
|
311
|
+
"note": "每月 20 日前后公布"},
|
|
312
|
+
{"event": "US CPI Release", "importance": "HIGH",
|
|
313
|
+
"note": "每月第二周(周二或周三)"},
|
|
314
|
+
{"event": "China PMI Release", "importance": "MEDIUM",
|
|
315
|
+
"note": "每月最后一天(官方)/ 次月第一工作日(财新)"},
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
return {"success": True, "events": results, "days_ahead": days_ahead,
|
|
319
|
+
"provider": "akshare/finnhub"}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _get_finnhub_key() -> str:
|
|
323
|
+
"""Read Finnhub key from env or providers.json."""
|
|
324
|
+
k = os.getenv("FINNHUB_API_KEY", "")
|
|
325
|
+
if not k:
|
|
326
|
+
try:
|
|
327
|
+
import json
|
|
328
|
+
p = Path.home() / ".arthera" / "providers.json"
|
|
329
|
+
if p.exists():
|
|
330
|
+
d = json.loads(p.read_text())
|
|
331
|
+
k = d.get("finnhub", {}).get("api_key", "") or d.get("finnhub_api_key", "")
|
|
332
|
+
except Exception:
|
|
333
|
+
pass
|
|
334
|
+
return k
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
# ── Central Bank Policy Rates ─────────────────────────────────────────────────
|
|
338
|
+
|
|
339
|
+
def get_central_bank_rates() -> dict:
|
|
340
|
+
"""主要央行政策利率快照。"""
|
|
341
|
+
CB_TICKERS = {
|
|
342
|
+
"美联储 (Fed Funds)": "FEDFUNDS",
|
|
343
|
+
"欧央行 (ECB Refi)": "ECBDFR",
|
|
344
|
+
"英央行 (BoE Rate)": "BOERUKM",
|
|
345
|
+
"日央行 (BoJ Rate)": "IRSTCI01JPM156N",
|
|
346
|
+
}
|
|
347
|
+
rates = {}
|
|
348
|
+
for name, series in CB_TICKERS.items():
|
|
349
|
+
data = _fred_series(series, limit=3)
|
|
350
|
+
if data:
|
|
351
|
+
latest = data[-1]
|
|
352
|
+
rates[name] = latest["value"]
|
|
353
|
+
|
|
354
|
+
# PBOC LPR via akshare
|
|
355
|
+
if _HAS_AK:
|
|
356
|
+
try:
|
|
357
|
+
df = ak.macro_china_lpr()
|
|
358
|
+
if df is not None and not df.empty:
|
|
359
|
+
row = df.iloc[-1]
|
|
360
|
+
rates["中国人民银行 LPR 1Y"] = float(row.get("1年期贷款市场报价利率", row.iloc[1]))
|
|
361
|
+
rates["中国人民银行 LPR 5Y"] = float(row.get("5年期贷款市场报价利率", row.iloc[2]))
|
|
362
|
+
except Exception:
|
|
363
|
+
pass
|
|
364
|
+
|
|
365
|
+
if not rates:
|
|
366
|
+
return {"success": False, "error": "无法获取央行利率数据"}
|
|
367
|
+
|
|
368
|
+
return {"success": True, "rates": rates, "provider": "FRED+akshare"}
|