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
apps/cli/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# CLI App
|
|
2
|
+
|
|
3
|
+
Target role: terminal UI and command parsing only.
|
|
4
|
+
|
|
5
|
+
The CLI should depend on these services:
|
|
6
|
+
|
|
7
|
+
- `gateway`: session routing and task orchestration.
|
|
8
|
+
- `runtime`: agent loop and tool execution.
|
|
9
|
+
- `ui`: terminal rendering and input.
|
|
10
|
+
- `reports`: local artifact and report output.
|
|
11
|
+
|
|
12
|
+
Migration rule: new slash commands should call package services instead of
|
|
13
|
+
adding more business logic to `aria_cli.py`.
|
apps/cli/__init__.py
ADDED
apps/cli/bootstrap.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""CLI bootstrap utilities shared by legacy and packaged entrypoints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import socket
|
|
7
|
+
import urllib.parse
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from apps.cli.config_paths import resolve_paths
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_aria_env(env_file: Path | None = None) -> None:
|
|
14
|
+
"""Load persisted CLI environment values without overriding the process."""
|
|
15
|
+
target = env_file or (Path.home() / ".aria" / ".env")
|
|
16
|
+
if not target.exists():
|
|
17
|
+
return
|
|
18
|
+
try:
|
|
19
|
+
for line in target.read_text(encoding="utf-8").splitlines():
|
|
20
|
+
line = line.strip()
|
|
21
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
22
|
+
continue
|
|
23
|
+
key, _, value = line.partition("=")
|
|
24
|
+
key = key.strip()
|
|
25
|
+
value = value.strip()
|
|
26
|
+
if key and value:
|
|
27
|
+
os.environ.setdefault(key, value)
|
|
28
|
+
except Exception:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def disable_broken_proxy(timeout: float = 1.5) -> None:
|
|
33
|
+
"""Unset proxy variables when the configured proxy cannot be reached."""
|
|
34
|
+
proxy = (
|
|
35
|
+
os.environ.get("HTTPS_PROXY")
|
|
36
|
+
or os.environ.get("https_proxy")
|
|
37
|
+
or os.environ.get("HTTP_PROXY")
|
|
38
|
+
or os.environ.get("http_proxy")
|
|
39
|
+
)
|
|
40
|
+
if not proxy:
|
|
41
|
+
return
|
|
42
|
+
try:
|
|
43
|
+
parsed = urllib.parse.urlparse(proxy if "://" in proxy else f"http://{proxy}")
|
|
44
|
+
host = parsed.hostname
|
|
45
|
+
port = parsed.port or 80
|
|
46
|
+
if not host:
|
|
47
|
+
return
|
|
48
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
49
|
+
sock.settimeout(timeout)
|
|
50
|
+
alive = sock.connect_ex((host, port)) == 0
|
|
51
|
+
sock.close()
|
|
52
|
+
except Exception:
|
|
53
|
+
alive = False
|
|
54
|
+
if not alive:
|
|
55
|
+
for name in ("HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "ALL_PROXY", "all_proxy"):
|
|
56
|
+
os.environ.pop(name, None)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def initialize_cli_environment() -> None:
|
|
60
|
+
os.environ.setdefault("TQDM_DISABLE", "1")
|
|
61
|
+
load_aria_env()
|
|
62
|
+
disable_broken_proxy()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def default_config() -> dict:
|
|
66
|
+
return {
|
|
67
|
+
"api_url": os.getenv(
|
|
68
|
+
"ARTHERA_API_URL",
|
|
69
|
+
"http://localhost:8000",
|
|
70
|
+
),
|
|
71
|
+
"local_url": "http://localhost:8000",
|
|
72
|
+
"ollama_url": os.getenv("OLLAMA_URL", "http://localhost:11434"),
|
|
73
|
+
"model": "qwen2.5-coder:1.5b",
|
|
74
|
+
"thinking_mode": "auto",
|
|
75
|
+
"watchlist": ["AAPL", "MSFT", "GOOGL", "TSLA", "NVDA"],
|
|
76
|
+
"auth_token": None,
|
|
77
|
+
"user_id": None,
|
|
78
|
+
"last_session_id": None,
|
|
79
|
+
"auto_save_sessions": True,
|
|
80
|
+
"auto_compact_context": True,
|
|
81
|
+
"auto_compact_threshold": 0.78,
|
|
82
|
+
"command_policy": "safe",
|
|
83
|
+
"permission_mode": "workspace-write",
|
|
84
|
+
"network_enabled": True,
|
|
85
|
+
"data_sharing": False,
|
|
86
|
+
"feedback_upload": False,
|
|
87
|
+
"write_policy": "desktop_only",
|
|
88
|
+
"lsp_autocheck": False,
|
|
89
|
+
"input_style": "panel",
|
|
90
|
+
"input_theme": "auto",
|
|
91
|
+
"response_footer": "compact",
|
|
92
|
+
"local_mode": False,
|
|
93
|
+
"conversation_history": [],
|
|
94
|
+
"ui_lang": "",
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def runtime_paths():
|
|
99
|
+
return resolve_paths()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Path helpers for generated code and strategy artifacts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def resolve_user_code_path(
|
|
9
|
+
description: str,
|
|
10
|
+
save_path: str | None = None,
|
|
11
|
+
*,
|
|
12
|
+
user_generated_dir: Path,
|
|
13
|
+
) -> Path:
|
|
14
|
+
"""Resolve a code output path into the user's generated workspace."""
|
|
15
|
+
from artifacts import slugify_topic
|
|
16
|
+
|
|
17
|
+
if save_path:
|
|
18
|
+
p = Path(save_path).expanduser()
|
|
19
|
+
if not p.is_absolute():
|
|
20
|
+
p = user_generated_dir / p
|
|
21
|
+
if p.suffix != ".py":
|
|
22
|
+
p = p.with_suffix(".py")
|
|
23
|
+
return p
|
|
24
|
+
|
|
25
|
+
desc_slug = slugify_topic(description, fallback="generated_code")
|
|
26
|
+
if any(k in description.lower() for k in ("backtest", "strategy", "quant", "portfolio")):
|
|
27
|
+
desc_slug = f"strategy_{desc_slug}"
|
|
28
|
+
return user_generated_dir / f"{desc_slug}.py"
|
|
29
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""CLI command metadata and thin command adapters."""
|
|
2
|
+
from .broker_cmds import BrokerCommandsMixin
|
|
3
|
+
from .backtest_cmds import BacktestCommandsMixin
|
|
4
|
+
from .workspace_cmds import WorkspaceCommandsMixin
|
|
5
|
+
from .model_cmds import ModelCommandsMixin
|
|
6
|
+
from .market_cmds import MarketCommandsMixin
|
|
7
|
+
from .portfolio_cmds import PortfolioCommandsMixin
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"BrokerCommandsMixin",
|
|
11
|
+
"BacktestCommandsMixin",
|
|
12
|
+
"WorkspaceCommandsMixin",
|
|
13
|
+
"ModelCommandsMixin",
|
|
14
|
+
"MarketCommandsMixin",
|
|
15
|
+
"PortfolioCommandsMixin",
|
|
16
|
+
]
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""AnalysisCommandsMixin — market analysis and indicator commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from apps.cli.commands.market import parse_analysis_args
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AnalysisCommandsMixin:
|
|
9
|
+
"""Mixin: market analysis and technical indicator commands."""
|
|
10
|
+
|
|
11
|
+
async def cmd_analyze(self, args: str):
|
|
12
|
+
"""Deep analysis: fetch real quote + TA + fundamentals, then ask LLM."""
|
|
13
|
+
parsed = parse_analysis_args(args)
|
|
14
|
+
symbol = parsed.symbol
|
|
15
|
+
is_cn = _is_ashare_symbol(symbol)
|
|
16
|
+
response_lang = parsed.lang or ("zh" if is_cn else "en")
|
|
17
|
+
|
|
18
|
+
if HAS_RICH:
|
|
19
|
+
with console.status(f"[dim]正在获取 {symbol} 数据...[/dim]", spinner="dots"):
|
|
20
|
+
ctx = await self._build_analyze_context(symbol, is_cn)
|
|
21
|
+
else:
|
|
22
|
+
print(f"Fetching data for {symbol}...")
|
|
23
|
+
ctx = await self._build_analyze_context(symbol, is_cn)
|
|
24
|
+
|
|
25
|
+
if parsed.focus == "volume":
|
|
26
|
+
if response_lang == "en":
|
|
27
|
+
ctx += (
|
|
28
|
+
"\n\n### User Focus\n"
|
|
29
|
+
"- Focus on volume, price-volume confirmation, and volume versus recent average. "
|
|
30
|
+
"If volume is unavailable, state that the source did not return it and do not invent values."
|
|
31
|
+
)
|
|
32
|
+
else:
|
|
33
|
+
ctx += (
|
|
34
|
+
"\n\n### 用户关注\n"
|
|
35
|
+
"- 重点分析成交量、量价关系、成交量相对近期均量的变化;"
|
|
36
|
+
"若成交量数据不可用,请直接说明来源未返回成交量,不要编造数值。"
|
|
37
|
+
)
|
|
38
|
+
elif parsed.focus:
|
|
39
|
+
heading = "User Focus" if response_lang == "en" else "用户关注"
|
|
40
|
+
label = "Focus on" if response_lang == "en" else "重点分析"
|
|
41
|
+
ctx += f"\n\n### {heading}\n- {label}: {parsed.focus}"
|
|
42
|
+
|
|
43
|
+
await self.terminal.send_message(build_analyze_prompt(symbol, ctx, is_cn, response_lang=response_lang))
|
|
44
|
+
try:
|
|
45
|
+
_resp = next((m["content"] for m in reversed(self.terminal.conversation)
|
|
46
|
+
if m.get("role") == "assistant" and m.get("content")), "")
|
|
47
|
+
if _resp:
|
|
48
|
+
self.terminal._record_prediction(symbol, _resp)
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
async def _build_analyze_context(self, symbol: str, is_cn: bool) -> str:
|
|
53
|
+
"""Fetch real market data and return a structured context string for the LLM."""
|
|
54
|
+
return await build_analyze_context(
|
|
55
|
+
symbol,
|
|
56
|
+
is_cn,
|
|
57
|
+
has_mdc=_HAS_MDC,
|
|
58
|
+
get_mdc=_get_mdc if _HAS_MDC else None,
|
|
59
|
+
ashare_name_lookup=_ashare_code_to_name,
|
|
60
|
+
has_brokers=_HAS_BROKERS,
|
|
61
|
+
get_broker_registry=_get_broker_registry if _HAS_BROKERS else None,
|
|
62
|
+
logger=logger,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
async def cmd_macro(self, args: str):
|
|
66
|
+
"""/macro [us|cn|rates|calendar] [indicator] — 宏观经济数据仪表板"""
|
|
67
|
+
import asyncio as _asyncio
|
|
68
|
+
parts = args.strip().lower().split() if args.strip() else []
|
|
69
|
+
region = parts[0] if parts else "all"
|
|
70
|
+
indicator = parts[1] if len(parts) > 1 else "all"
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
from macro_tools import get_us_macro, get_cn_macro, get_central_bank_rates, get_economic_calendar
|
|
74
|
+
except ImportError:
|
|
75
|
+
if HAS_RICH:
|
|
76
|
+
console.print("[red]macro_tools 模块未找到[/red]")
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
loop = _asyncio.get_event_loop()
|
|
80
|
+
|
|
81
|
+
if region in ("us", "all"):
|
|
82
|
+
if HAS_RICH:
|
|
83
|
+
with console.status("[dim]获取美国宏观数据 (FRED)...[/dim]", spinner="dots"):
|
|
84
|
+
r = await loop.run_in_executor(None, lambda: get_us_macro(indicator if region == "us" else "all"))
|
|
85
|
+
else:
|
|
86
|
+
r = get_us_macro(indicator if region == "us" else "all")
|
|
87
|
+
_render_macro_result(r, "🇺🇸 美国宏观")
|
|
88
|
+
|
|
89
|
+
if region in ("cn", "all"):
|
|
90
|
+
if HAS_RICH:
|
|
91
|
+
with console.status("[dim]获取中国宏观数据 (akshare)...[/dim]", spinner="dots"):
|
|
92
|
+
r_cn = await loop.run_in_executor(None, lambda: get_cn_macro(indicator if region == "cn" else "all"))
|
|
93
|
+
else:
|
|
94
|
+
r_cn = get_cn_macro(indicator if region == "cn" else "all")
|
|
95
|
+
_render_macro_result(r_cn, "🇨🇳 中国宏观")
|
|
96
|
+
|
|
97
|
+
if region in ("rates", "all"):
|
|
98
|
+
if HAS_RICH:
|
|
99
|
+
with console.status("[dim]获取央行利率...[/dim]", spinner="dots"):
|
|
100
|
+
r_rates = await loop.run_in_executor(None, get_central_bank_rates)
|
|
101
|
+
else:
|
|
102
|
+
r_rates = get_central_bank_rates()
|
|
103
|
+
_render_cb_rates(r_rates)
|
|
104
|
+
|
|
105
|
+
if region == "calendar":
|
|
106
|
+
if HAS_RICH:
|
|
107
|
+
with console.status("[dim]获取经济日历...[/dim]", spinner="dots"):
|
|
108
|
+
r_cal = await loop.run_in_executor(None, lambda: get_economic_calendar(7))
|
|
109
|
+
else:
|
|
110
|
+
r_cal = get_economic_calendar(7)
|
|
111
|
+
_render_econ_calendar(r_cal)
|
|
112
|
+
|
|
113
|
+
async def cmd_options(self, args: str):
|
|
114
|
+
"""/options <symbol> [calls|puts] [expiry] — 期权链查询"""
|
|
115
|
+
parts = args.strip().split() if args.strip() else []
|
|
116
|
+
symbol = parts[0].upper() if parts else "AAPL"
|
|
117
|
+
opt_type = "both"
|
|
118
|
+
expiry = ""
|
|
119
|
+
for p in parts[1:]:
|
|
120
|
+
if p.lower() in ("calls", "puts"):
|
|
121
|
+
opt_type = p.lower()
|
|
122
|
+
elif "-" in p and len(p) == 10:
|
|
123
|
+
expiry = p
|
|
124
|
+
|
|
125
|
+
if not _HAS_LOCAL_FINANCE:
|
|
126
|
+
if HAS_RICH:
|
|
127
|
+
console.print("[red]local_finance_tools 未加载[/red]")
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
import asyncio as _asyncio
|
|
131
|
+
loop = _asyncio.get_event_loop()
|
|
132
|
+
if HAS_RICH:
|
|
133
|
+
with console.status(f"[dim]获取 {symbol} 期权链...[/dim]", spinner="dots"):
|
|
134
|
+
from local_finance_tools import _get_options_chain
|
|
135
|
+
r = await loop.run_in_executor(None, _get_options_chain,
|
|
136
|
+
{"symbol": symbol, "type": opt_type, "expiry": expiry, "limit": 20})
|
|
137
|
+
else:
|
|
138
|
+
from local_finance_tools import _get_options_chain
|
|
139
|
+
r = _get_options_chain({"symbol": symbol, "type": opt_type, "expiry": expiry, "limit": 20})
|
|
140
|
+
|
|
141
|
+
if not r.get("success"):
|
|
142
|
+
if HAS_RICH:
|
|
143
|
+
console.print(f"[red]{r.get('error')}[/red]")
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
_render_options_chain(r)
|
|
147
|
+
try:
|
|
148
|
+
spot = r.get("current_price") or r.get("spot_price")
|
|
149
|
+
if spot and spot > 0:
|
|
150
|
+
from packages.quant_engine.stochastic.options_pricing import OptionSpec, black_scholes
|
|
151
|
+
T = 30 / 365
|
|
152
|
+
r_f = 0.05
|
|
153
|
+
chain = r.get("calls", []) or r.get("chain", []) or []
|
|
154
|
+
sigma = 0.25
|
|
155
|
+
for row in chain[:5]:
|
|
156
|
+
iv = row.get("impliedVolatility") or row.get("iv")
|
|
157
|
+
if iv and 0.01 < float(iv) < 5.0:
|
|
158
|
+
sigma = float(iv)
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
atm_call = black_scholes(OptionSpec(S=spot, K=round(spot, -1) or spot, T=T, r=r_f, sigma=sigma, option_type="call"))
|
|
162
|
+
atm_put = black_scholes(OptionSpec(S=spot, K=round(spot, -1) or spot, T=T, r=r_f, sigma=sigma, option_type="put"))
|
|
163
|
+
if HAS_RICH:
|
|
164
|
+
from rich.table import Table
|
|
165
|
+
from rich import box as _box
|
|
166
|
+
tbl = Table(title=f"[bold]B-S ATM 理论价格[/bold] σ={sigma:.0%} T=30d r=5%",
|
|
167
|
+
box=_box.SIMPLE, show_header=True, header_style="bold dim")
|
|
168
|
+
tbl.add_column("", style="dim")
|
|
169
|
+
tbl.add_column("理论价", justify="right")
|
|
170
|
+
tbl.add_column("Delta", justify="right")
|
|
171
|
+
tbl.add_column("Gamma", justify="right")
|
|
172
|
+
tbl.add_column("Theta/日", justify="right")
|
|
173
|
+
tbl.add_column("Vega/1%", justify="right")
|
|
174
|
+
tbl.add_column("Vanna", justify="right")
|
|
175
|
+
tbl.add_row("Call", f"{atm_call.price:.2f}", f"{atm_call.delta:+.3f}",
|
|
176
|
+
f"{atm_call.gamma:.4f}", f"{atm_call.theta:+.4f}",
|
|
177
|
+
f"{atm_call.vega:.4f}", f"{atm_call.vanna:.4f}")
|
|
178
|
+
tbl.add_row("Put", f"{atm_put.price:.2f}", f"{atm_put.delta:+.3f}",
|
|
179
|
+
f"{atm_put.gamma:.4f}", f"{atm_put.theta:+.4f}",
|
|
180
|
+
f"{atm_put.vega:.4f}", f"{atm_put.vanna:.4f}")
|
|
181
|
+
console.print(tbl)
|
|
182
|
+
else:
|
|
183
|
+
print(f"B-S ATM call={atm_call.price:.2f} Δ={atm_call.delta:+.3f} put={atm_put.price:.2f} Δ={atm_put.delta:+.3f} σ={sigma:.0%}")
|
|
184
|
+
except Exception:
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
async def cmd_quality(self, args: str):
|
|
188
|
+
"""/quality <symbol> — Piotroski F-Score + Altman Z-Score 双维财务质量评估"""
|
|
189
|
+
symbol = args.strip().upper() if args.strip() else "AAPL"
|
|
190
|
+
if not _HAS_LOCAL_FINANCE:
|
|
191
|
+
if HAS_RICH:
|
|
192
|
+
console.print("[red]local_finance_tools 未加载[/red]")
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
import asyncio as _asyncio
|
|
196
|
+
loop = _asyncio.get_event_loop()
|
|
197
|
+
if HAS_RICH:
|
|
198
|
+
with console.status(f"[dim]计算 {symbol} 财务质量评分...[/dim]", spinner="dots"):
|
|
199
|
+
from local_finance_tools import _piotroski_fscore, _altman_zscore
|
|
200
|
+
f_r = await loop.run_in_executor(None, _piotroski_fscore, {"symbol": symbol})
|
|
201
|
+
z_r = await loop.run_in_executor(None, _altman_zscore, {"symbol": symbol})
|
|
202
|
+
else:
|
|
203
|
+
from local_finance_tools import _piotroski_fscore, _altman_zscore
|
|
204
|
+
f_r = _piotroski_fscore({"symbol": symbol})
|
|
205
|
+
z_r = _altman_zscore({"symbol": symbol})
|
|
206
|
+
|
|
207
|
+
_render_quality_scores(symbol, f_r, z_r)
|
|
208
|
+
|
|
209
|
+
async def cmd_ichimoku(self, args: str):
|
|
210
|
+
"""/ichimoku <symbol> — 一目均衡表分析"""
|
|
211
|
+
symbol = args.strip().upper() if args.strip() else "AAPL"
|
|
212
|
+
if not _HAS_LOCAL_FINANCE:
|
|
213
|
+
if HAS_RICH:
|
|
214
|
+
console.print("[red]local_finance_tools 未加载[/red]")
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
import asyncio as _asyncio
|
|
218
|
+
loop = _asyncio.get_event_loop()
|
|
219
|
+
if HAS_RICH:
|
|
220
|
+
with console.status(f"[dim]计算 {symbol} 一目均衡表...[/dim]", spinner="dots"):
|
|
221
|
+
from local_finance_tools import _calculate_ichimoku
|
|
222
|
+
r = await loop.run_in_executor(None, _calculate_ichimoku, {"symbol": symbol})
|
|
223
|
+
else:
|
|
224
|
+
from local_finance_tools import _calculate_ichimoku
|
|
225
|
+
r = _calculate_ichimoku({"symbol": symbol})
|
|
226
|
+
|
|
227
|
+
_render_ichimoku(r)
|
|
228
|
+
|
|
229
|
+
async def cmd_fear_greed(self, args: str):
|
|
230
|
+
"""/feargreed — 加密货币恐惧贪婪指数"""
|
|
231
|
+
if not _HAS_LOCAL_FINANCE:
|
|
232
|
+
if HAS_RICH:
|
|
233
|
+
console.print("[red]local_finance_tools 未加载[/red]")
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
import asyncio as _asyncio
|
|
237
|
+
loop = _asyncio.get_event_loop()
|
|
238
|
+
if HAS_RICH:
|
|
239
|
+
with console.status("[dim]获取恐惧贪婪指数...[/dim]", spinner="dots"):
|
|
240
|
+
from local_finance_tools import _get_fear_greed_index
|
|
241
|
+
r = await loop.run_in_executor(None, _get_fear_greed_index, {})
|
|
242
|
+
else:
|
|
243
|
+
from local_finance_tools import _get_fear_greed_index
|
|
244
|
+
r = _get_fear_greed_index({})
|
|
245
|
+
|
|
246
|
+
_render_fear_greed(r)
|
|
247
|
+
|
|
248
|
+
async def cmd_funding(self, args: str):
|
|
249
|
+
"""/funding [compare] [BTC ETH SOL] [exchange] — 永续合约资金费率"""
|
|
250
|
+
parts = args.strip().split() if args.strip() else []
|
|
251
|
+
compare_mode = any(p.lower() == "compare" for p in parts)
|
|
252
|
+
parts = [p for p in parts if p.lower() != "compare"]
|
|
253
|
+
|
|
254
|
+
exchange = "binance"
|
|
255
|
+
syms = []
|
|
256
|
+
for p in parts:
|
|
257
|
+
if p.lower() in ("binance", "okx", "bybit", "coinbase"):
|
|
258
|
+
exchange = p.lower()
|
|
259
|
+
else:
|
|
260
|
+
syms.append(p.upper() + "/USDT" if "/" not in p else p.upper())
|
|
261
|
+
if not syms:
|
|
262
|
+
syms = ["BTC/USDT", "ETH/USDT", "SOL/USDT"]
|
|
263
|
+
|
|
264
|
+
if not _HAS_LOCAL_FINANCE:
|
|
265
|
+
if HAS_RICH:
|
|
266
|
+
console.print("[red]local_finance_tools 未加载[/red]")
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
import asyncio as _asyncio
|
|
270
|
+
loop = _asyncio.get_event_loop()
|
|
271
|
+
if compare_mode:
|
|
272
|
+
if HAS_RICH:
|
|
273
|
+
with console.status("[dim]并行查询 binance / okx / bybit...[/dim]", spinner="dots"):
|
|
274
|
+
from local_finance_tools import _get_funding_rates_compare
|
|
275
|
+
r = await loop.run_in_executor(None, _get_funding_rates_compare, {"symbols": syms})
|
|
276
|
+
else:
|
|
277
|
+
from local_finance_tools import _get_funding_rates_compare
|
|
278
|
+
r = _get_funding_rates_compare({"symbols": syms})
|
|
279
|
+
_render_funding_compare(r)
|
|
280
|
+
else:
|
|
281
|
+
if HAS_RICH:
|
|
282
|
+
with console.status(f"[dim]获取 {exchange} 资金费率...[/dim]", spinner="dots"):
|
|
283
|
+
from local_finance_tools import _get_funding_rates
|
|
284
|
+
r = await loop.run_in_executor(None, _get_funding_rates, {"exchange": exchange, "symbols": syms})
|
|
285
|
+
else:
|
|
286
|
+
from local_finance_tools import _get_funding_rates
|
|
287
|
+
r = _get_funding_rates({"exchange": exchange, "symbols": syms})
|
|
288
|
+
_render_funding_rates(r)
|