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
finance_formulas.py
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"""
|
|
2
|
+
finance_formulas.py — Canonical financial formula reference for Aria.
|
|
3
|
+
|
|
4
|
+
Two representations per formula:
|
|
5
|
+
latex : Standard LaTeX (rendered by KaTeX in web terminal)
|
|
6
|
+
plain : Clean Unicode plain-text (used in CLI terminal)
|
|
7
|
+
note : One-line explanation injected into system prompts
|
|
8
|
+
|
|
9
|
+
Usage in system prompts:
|
|
10
|
+
from finance_formulas import FORMULA_PROMPT_BLOCK
|
|
11
|
+
system_prompt += FORMULA_PROMPT_BLOCK
|
|
12
|
+
|
|
13
|
+
Usage for CLI rendering:
|
|
14
|
+
from finance_formulas import formula_to_plaintext
|
|
15
|
+
clean = formula_to_plaintext(raw_latex_string)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
from typing import NamedTuple
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Formula(NamedTuple):
|
|
23
|
+
name: str
|
|
24
|
+
latex: str # $...$ or $$...$$
|
|
25
|
+
plain: str # clean Unicode, no backslashes
|
|
26
|
+
note: str # one-line description
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ── Canonical formula registry ────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
FORMULAS: dict[str, Formula] = {
|
|
32
|
+
|
|
33
|
+
# ── Valuation ─────────────────────────────────────────────────────────────
|
|
34
|
+
"pe": Formula(
|
|
35
|
+
name="P/E Ratio (市盈率)",
|
|
36
|
+
latex=r"$$P/E = \frac{\text{Stock Price}}{\text{EPS}}$$",
|
|
37
|
+
plain="P/E = 股价 ÷ 每股收益(EPS)",
|
|
38
|
+
note="市场愿意为每1元盈利支付的价格;越高代表市场期望越高",
|
|
39
|
+
),
|
|
40
|
+
"pb": Formula(
|
|
41
|
+
name="P/B Ratio (市净率)",
|
|
42
|
+
latex=r"$$P/B = \frac{\text{Stock Price}}{\text{Book Value per Share}}$$",
|
|
43
|
+
plain="P/B = 股价 ÷ 每股净资产",
|
|
44
|
+
note="股价相对账面价值的溢价;<1 可能被低估",
|
|
45
|
+
),
|
|
46
|
+
"ps": Formula(
|
|
47
|
+
name="P/S Ratio (市销率)",
|
|
48
|
+
latex=r"$$P/S = \frac{\text{Market Cap}}{\text{Annual Revenue}}$$",
|
|
49
|
+
plain="P/S = 市值 ÷ 年营收",
|
|
50
|
+
note="适合暂无盈利的成长型公司估值",
|
|
51
|
+
),
|
|
52
|
+
"ev_ebitda": Formula(
|
|
53
|
+
name="EV/EBITDA",
|
|
54
|
+
latex=r"$$EV/EBITDA = \frac{\text{Enterprise Value}}{\text{EBITDA}}$$",
|
|
55
|
+
plain="EV/EBITDA = 企业价值 ÷ 息税折旧摊销前利润",
|
|
56
|
+
note="排除资本结构差异的估值指标;<10 通常被视为合理",
|
|
57
|
+
),
|
|
58
|
+
"peg": Formula(
|
|
59
|
+
name="PEG Ratio",
|
|
60
|
+
latex=r"$$PEG = \frac{P/E}{\text{EPS Growth Rate (\%)}}$$",
|
|
61
|
+
plain="PEG = 市盈率 ÷ 盈利增长率(%)",
|
|
62
|
+
note="<1 通常被视为被低估;综合估值与成长性",
|
|
63
|
+
),
|
|
64
|
+
"dcf": Formula(
|
|
65
|
+
name="DCF 内在价值",
|
|
66
|
+
latex=r"$$V = \sum_{t=1}^{n} \frac{FCF_t}{(1+r)^t} + \frac{TV}{(1+r)^n}$$",
|
|
67
|
+
plain="DCF = Σ [自由现金流t ÷ (1+折现率)^t] + 终值 ÷ (1+折现率)^n",
|
|
68
|
+
note="自由现金流折现;r 为 WACC,TV 为终值",
|
|
69
|
+
),
|
|
70
|
+
"wacc": Formula(
|
|
71
|
+
name="WACC (加权平均资本成本)",
|
|
72
|
+
latex=r"$$WACC = \frac{E}{V} \cdot R_e + \frac{D}{V} \cdot R_d \cdot (1-T)$$",
|
|
73
|
+
plain="WACC = 权益比 × 权益成本 + 债务比 × 债务成本 × (1-税率)",
|
|
74
|
+
note="折现率的基础;E=权益,D=债务,V=E+D,T=税率",
|
|
75
|
+
),
|
|
76
|
+
|
|
77
|
+
# ── Profitability ─────────────────────────────────────────────────────────
|
|
78
|
+
"roe": Formula(
|
|
79
|
+
name="ROE (净资产收益率)",
|
|
80
|
+
latex=r"$$ROE = \frac{\text{Net Income}}{\text{Shareholders' Equity}} \times 100\%$$",
|
|
81
|
+
plain="ROE = 净利润 ÷ 股东权益 × 100%",
|
|
82
|
+
note="巴菲特首选指标;>15% 为优秀",
|
|
83
|
+
),
|
|
84
|
+
"roa": Formula(
|
|
85
|
+
name="ROA (总资产收益率)",
|
|
86
|
+
latex=r"$$ROA = \frac{\text{Net Income}}{\text{Total Assets}} \times 100\%$$",
|
|
87
|
+
plain="ROA = 净利润 ÷ 总资产 × 100%",
|
|
88
|
+
note="衡量资产使用效率;>5% 较好",
|
|
89
|
+
),
|
|
90
|
+
"gross_margin": Formula(
|
|
91
|
+
name="毛利率",
|
|
92
|
+
latex=r"$$\text{Gross Margin} = \frac{\text{Revenue} - \text{COGS}}{\text{Revenue}} \times 100\%$$",
|
|
93
|
+
plain="毛利率 = (营收 - 营业成本) ÷ 营收 × 100%",
|
|
94
|
+
note="产品定价能力的直接体现",
|
|
95
|
+
),
|
|
96
|
+
"net_margin": Formula(
|
|
97
|
+
name="净利率",
|
|
98
|
+
latex=r"$$\text{Net Margin} = \frac{\text{Net Income}}{\text{Revenue}} \times 100\%$$",
|
|
99
|
+
plain="净利率 = 净利润 ÷ 营收 × 100%",
|
|
100
|
+
note="最终盈利能力;科技公司优秀水平 >20%",
|
|
101
|
+
),
|
|
102
|
+
|
|
103
|
+
# ── Risk / Return ─────────────────────────────────────────────────────────
|
|
104
|
+
"sharpe": Formula(
|
|
105
|
+
name="Sharpe Ratio (夏普比率)",
|
|
106
|
+
latex=r"$$Sharpe = \frac{R_p - R_f}{\sigma_p}$$",
|
|
107
|
+
plain="Sharpe = (组合年化收益 - 无风险利率) ÷ 年化波动率",
|
|
108
|
+
note="单位风险的超额收益;>1 良好,>2 优秀",
|
|
109
|
+
),
|
|
110
|
+
"sortino": Formula(
|
|
111
|
+
name="Sortino Ratio",
|
|
112
|
+
latex=r"$$Sortino = \frac{R_p - R_f}{\sigma_d}$$",
|
|
113
|
+
plain="Sortino = (组合年化收益 - 无风险利率) ÷ 下行波动率",
|
|
114
|
+
note="只惩罚下行风险;比 Sharpe 更适合非对称收益",
|
|
115
|
+
),
|
|
116
|
+
"max_drawdown": Formula(
|
|
117
|
+
name="最大回撤",
|
|
118
|
+
latex=r"$$MDD = \frac{\text{Trough} - \text{Peak}}{\text{Peak}} \times 100\%$$",
|
|
119
|
+
plain="最大回撤 = (波谷 - 波峰) ÷ 波峰 × 100%",
|
|
120
|
+
note="负值;衡量最坏情景下的损失幅度",
|
|
121
|
+
),
|
|
122
|
+
"var": Formula(
|
|
123
|
+
name="VaR (Value at Risk)",
|
|
124
|
+
latex=r"$$VaR_{95\%} = \mu - 1.645\sigma$$",
|
|
125
|
+
plain="VaR(95%) = 均值收益 - 1.645 × 波动率",
|
|
126
|
+
note="95%置信度下单日最大可能亏损",
|
|
127
|
+
),
|
|
128
|
+
"beta": Formula(
|
|
129
|
+
name="Beta (贝塔系数)",
|
|
130
|
+
latex=r"$$\beta = \frac{Cov(R_i, R_m)}{Var(R_m)}$$",
|
|
131
|
+
plain="β = 个股与市场收益的协方差 ÷ 市场收益方差",
|
|
132
|
+
note=">1 比市场波动大;<1 防御性;=1 跟随市场",
|
|
133
|
+
),
|
|
134
|
+
"capm": Formula(
|
|
135
|
+
name="CAPM (资本资产定价模型)",
|
|
136
|
+
latex=r"$$E(R_i) = R_f + \beta_i \cdot (E(R_m) - R_f)$$",
|
|
137
|
+
plain="期望收益 = 无风险利率 + β × 市场风险溢价",
|
|
138
|
+
note="理论上的公允收益率;高于此值则α为正",
|
|
139
|
+
),
|
|
140
|
+
|
|
141
|
+
# ── Technical indicators ──────────────────────────────────────────────────
|
|
142
|
+
"rsi": Formula(
|
|
143
|
+
name="RSI (相对强弱指数)",
|
|
144
|
+
latex=r"$$RSI = 100 - \frac{100}{1 + \frac{\bar{U}}{\bar{D}}}$$",
|
|
145
|
+
plain="RSI = 100 - 100 ÷ (1 + 平均涨幅 ÷ 平均跌幅)",
|
|
146
|
+
note=">70 超买,<30 超卖;14日为标准周期",
|
|
147
|
+
),
|
|
148
|
+
"macd_line": Formula(
|
|
149
|
+
name="MACD 线",
|
|
150
|
+
latex=r"$$MACD = EMA_{12} - EMA_{26}$$",
|
|
151
|
+
plain="MACD = 12日EMA - 26日EMA",
|
|
152
|
+
note="快慢线差值;正值为多头区间",
|
|
153
|
+
),
|
|
154
|
+
"bollinger_upper": Formula(
|
|
155
|
+
name="布林带上轨",
|
|
156
|
+
latex=r"$$Upper = MA_{20} + 2\sigma_{20}$$",
|
|
157
|
+
plain="上轨 = 20日均线 + 2 × 20日标准差",
|
|
158
|
+
note="价格突破上轨为超买信号",
|
|
159
|
+
),
|
|
160
|
+
"eps": Formula(
|
|
161
|
+
name="EPS (每股收益)",
|
|
162
|
+
latex=r"$$EPS = \frac{\text{Net Income} - \text{Preferred Dividends}}{\text{Weighted Average Shares}}$$",
|
|
163
|
+
plain="EPS = (净利润 - 优先股股息) ÷ 加权平均流通股数",
|
|
164
|
+
note="P/E 的分母;反映每股创造的盈利",
|
|
165
|
+
),
|
|
166
|
+
"cagr": Formula(
|
|
167
|
+
name="CAGR (复合年均增长率)",
|
|
168
|
+
latex=r"$$CAGR = \left(\frac{V_f}{V_i}\right)^{\frac{1}{n}} - 1$$",
|
|
169
|
+
plain="CAGR = (终值 ÷ 初值)^(1/年数) - 1",
|
|
170
|
+
note="衡量长期增长的标准方式",
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# ── Prompt injection block ────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
def build_formula_prompt_block(keys: list[str] | None = None) -> str:
|
|
178
|
+
"""
|
|
179
|
+
Build a compact formula reference block for injection into system prompts.
|
|
180
|
+
|
|
181
|
+
keys: optional list of formula keys to include (default: all).
|
|
182
|
+
Returns a string ready to append to a system prompt.
|
|
183
|
+
"""
|
|
184
|
+
items = [FORMULAS[k] for k in (keys or FORMULAS.keys()) if k in FORMULAS]
|
|
185
|
+
if not items:
|
|
186
|
+
return ""
|
|
187
|
+
|
|
188
|
+
lines = [
|
|
189
|
+
"\n## 标准金融公式(必须使用以下格式,禁止自创公式或缩写)\n",
|
|
190
|
+
"在 LaTeX 公式中使用 $$ ... $$ 语法(双美元符),终端自动转换为可读格式。\n",
|
|
191
|
+
]
|
|
192
|
+
for f in items:
|
|
193
|
+
lines.append(f"- **{f.name}**: `{f.plain}`")
|
|
194
|
+
lines.append(f" → LaTeX: `{f.latex}`")
|
|
195
|
+
lines.append(f" → {f.note}")
|
|
196
|
+
lines.append("\n⚠️ 禁止使用不存在的术语(如 DSRR、DRRR)。如不确定,用中文描述代替。\n")
|
|
197
|
+
return "\n".join(lines)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# Compact block for finance chat (most common formulas only)
|
|
201
|
+
FORMULA_PROMPT_BLOCK_CORE = build_formula_prompt_block([
|
|
202
|
+
"pe", "pb", "roe", "sharpe", "max_drawdown", "eps", "cagr",
|
|
203
|
+
])
|
|
204
|
+
|
|
205
|
+
# Full block for analysis / research prompts
|
|
206
|
+
FORMULA_PROMPT_BLOCK_FULL = build_formula_prompt_block()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# ── CLI plain-text renderer ───────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
import re as _re
|
|
212
|
+
|
|
213
|
+
_PLAIN_LOOKUP: dict[str, str] = {f.latex: f.plain for f in FORMULAS.values()}
|
|
214
|
+
|
|
215
|
+
# Symbol map: LaTeX → Unicode
|
|
216
|
+
_LATEX_SYMBOLS = {
|
|
217
|
+
r'\times': '×', r'\div': '÷', r'\pm': '±', r'\approx': '≈',
|
|
218
|
+
r'\leq': '≤', r'\geq': '≥', r'\neq': '≠', r'\to': '→',
|
|
219
|
+
r'\alpha': 'α', r'\beta': 'β', r'\gamma': 'γ', r'\sigma': 'σ',
|
|
220
|
+
r'\mu': 'μ', r'\pi': 'π', r'\lambda': 'λ', r'\theta': 'θ',
|
|
221
|
+
r'\Sigma': 'Σ', r'\sum': 'Σ', r'\infty': '∞', r'\cdot': '·',
|
|
222
|
+
r'\sqrt': '√', r'\partial': '∂',
|
|
223
|
+
# LaTeX spacing commands (these are NOT caught by \\[A-Za-z]+ since ; is not a letter)
|
|
224
|
+
r'\;': ' ', r'\,': '', r'\:': ' ', r'\!': '',
|
|
225
|
+
r'\quad': ' ', r'\qquad': ' ',
|
|
226
|
+
r'\medspace': ' ', r'\thickspace': ' ', r'\thinspace': '',
|
|
227
|
+
r'\text{Net Income}': '净利润',
|
|
228
|
+
r'\text{Net Profit}': '净利润',
|
|
229
|
+
r"\text{Shareholders' Equity}": '股东权益',
|
|
230
|
+
r'\text{Shareholders Equity}': '股东权益',
|
|
231
|
+
r'\text{Stock Price}': '股价',
|
|
232
|
+
r'\text{EPS}': 'EPS',
|
|
233
|
+
r'\text{Revenue}': '营收',
|
|
234
|
+
r'\text{COGS}': '营业成本',
|
|
235
|
+
r'\text{Peak}': '波峰',
|
|
236
|
+
r'\text{Trough}': '波谷',
|
|
237
|
+
r'\text{Book Value per Share}': '每股净资产',
|
|
238
|
+
r'\text{Market Cap}': '市值',
|
|
239
|
+
r'\text{Annual Revenue}': '年营收',
|
|
240
|
+
r'\text{Total Assets}': '总资产',
|
|
241
|
+
r'\text{Enterprise Value}': '企业价值',
|
|
242
|
+
r'\text{Preferred Dividends}': '优先股股息',
|
|
243
|
+
r'\text{Weighted Average Shares}': '加权平均流通股数',
|
|
244
|
+
r'\bar': '',
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _replace_frac_once(text: str) -> str:
|
|
249
|
+
"""Replace the first simple or nested LaTeX \frac block with readable text."""
|
|
250
|
+
start = text.find(r'\frac')
|
|
251
|
+
if start < 0:
|
|
252
|
+
return text
|
|
253
|
+
|
|
254
|
+
def _read_group(pos: int) -> tuple[str, int] | None:
|
|
255
|
+
if pos >= len(text) or text[pos] != "{":
|
|
256
|
+
return None
|
|
257
|
+
depth = 0
|
|
258
|
+
chars: list[str] = []
|
|
259
|
+
for idx in range(pos, len(text)):
|
|
260
|
+
ch = text[idx]
|
|
261
|
+
if ch == "{":
|
|
262
|
+
if depth > 0:
|
|
263
|
+
chars.append(ch)
|
|
264
|
+
depth += 1
|
|
265
|
+
elif ch == "}":
|
|
266
|
+
depth -= 1
|
|
267
|
+
if depth == 0:
|
|
268
|
+
return "".join(chars), idx + 1
|
|
269
|
+
chars.append(ch)
|
|
270
|
+
else:
|
|
271
|
+
chars.append(ch)
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
num = _read_group(start + len(r'\frac'))
|
|
275
|
+
if not num:
|
|
276
|
+
return text
|
|
277
|
+
den = _read_group(num[1])
|
|
278
|
+
if not den:
|
|
279
|
+
return text
|
|
280
|
+
|
|
281
|
+
numerator = formula_to_plaintext(num[0])
|
|
282
|
+
denominator = formula_to_plaintext(den[0])
|
|
283
|
+
replacement = f"({numerator}) ÷ ({denominator})"
|
|
284
|
+
return text[:start] + replacement + text[den[1]:]
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def formula_to_plaintext(latex: str) -> str:
|
|
288
|
+
"""
|
|
289
|
+
Convert a LaTeX formula string to clean Unicode plain-text for CLI display.
|
|
290
|
+
|
|
291
|
+
Handles $...$ inline and $$...$$ display math.
|
|
292
|
+
Produces readable output like "P/E = 股价 ÷ EPS" instead of garbled backslashes.
|
|
293
|
+
"""
|
|
294
|
+
# Check canonical lookup first
|
|
295
|
+
stripped = latex.strip().replace("\\\\", "\\")
|
|
296
|
+
if stripped in _PLAIN_LOOKUP:
|
|
297
|
+
return _PLAIN_LOOKUP[stripped]
|
|
298
|
+
|
|
299
|
+
# Remove $$ and $ delimiters
|
|
300
|
+
text = _re.sub(r'\$\$|\$', '', stripped)
|
|
301
|
+
text = _re.sub(r'\\\[|\\\]', '', text)
|
|
302
|
+
text = _re.sub(r'\\\(|\\\)', '', text)
|
|
303
|
+
|
|
304
|
+
# Apply symbol substitutions (longest first to avoid partial matches)
|
|
305
|
+
for cmd, sym in sorted(_LATEX_SYMBOLS.items(), key=lambda x: -len(x[0])):
|
|
306
|
+
text = text.replace(cmd, sym)
|
|
307
|
+
|
|
308
|
+
# \frac{a}{b} → (a) ÷ (b), including common nested fractions.
|
|
309
|
+
for _ in range(12):
|
|
310
|
+
updated = _replace_frac_once(text)
|
|
311
|
+
if updated == text:
|
|
312
|
+
break
|
|
313
|
+
text = updated
|
|
314
|
+
|
|
315
|
+
# \text{...} and common styling commands → content
|
|
316
|
+
text = _re.sub(
|
|
317
|
+
r'\\(?:text|mathrm|mathbf|mathit|mathcal|operatorname|overline|underline|bar|hat|tilde|vec)\{([^{}]*)\}',
|
|
318
|
+
r'\1', text
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# \left, \right → nothing
|
|
322
|
+
text = _re.sub(r'\\(?:left|right)[(\[|)\].]?', '', text)
|
|
323
|
+
|
|
324
|
+
# \sqrt{x} → √(x)
|
|
325
|
+
text = _re.sub(r'\\sqrt\{([^{}]*)\}', r'√(\1)', text)
|
|
326
|
+
|
|
327
|
+
# ^{n} → ^n, _{n} → _n
|
|
328
|
+
text = _re.sub(r'\^\{([^{}]{1,12})\}', r'^\1', text)
|
|
329
|
+
text = _re.sub(r'_\{([^{}]{1,12})\}', r'_\1', text)
|
|
330
|
+
text = _re.sub(r'\^([A-Za-z0-9])', r'^\1', text)
|
|
331
|
+
text = _re.sub(r'_([A-Za-z0-9])', r'_\1', text)
|
|
332
|
+
|
|
333
|
+
# Non-alpha LaTeX spacing commands (\; \, \: \! are NOT caught by \\[A-Za-z]+)
|
|
334
|
+
text = _re.sub(r'\\[;,!:]', ' ', text)
|
|
335
|
+
# Clean remaining backslash commands
|
|
336
|
+
text = _re.sub(r'\\([A-Za-z]+)', r'\1', text)
|
|
337
|
+
text = text.replace(r'\%', '%')
|
|
338
|
+
text = _re.sub(r'\{([^{}]{1,24})\}', r'\1', text)
|
|
339
|
+
|
|
340
|
+
# Clean up whitespace
|
|
341
|
+
text = _re.sub(r'\s{2,}', ' ', text)
|
|
342
|
+
text = _re.sub(r'\s*([=+\-×÷·])\s*', r' \1 ', text)
|
|
343
|
+
text = _re.sub(r'\s{2,}', ' ', text).strip()
|
|
344
|
+
return text
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def strip_latex_for_cli(text: str) -> str:
|
|
348
|
+
"""
|
|
349
|
+
Full-text LaTeX stripping for CLI output.
|
|
350
|
+
Converts $$...$$ and $...$ blocks to clean plaintext.
|
|
351
|
+
Replaces display math with a prefixed formula line.
|
|
352
|
+
"""
|
|
353
|
+
if '\\' not in text and '$' not in text:
|
|
354
|
+
return text
|
|
355
|
+
|
|
356
|
+
# Display math $$...$$ → " ▶ <plain formula>"
|
|
357
|
+
def _render_display(m: _re.Match) -> str:
|
|
358
|
+
inner = m.group(1).strip()
|
|
359
|
+
plain = formula_to_plaintext(f"$${inner}$$")
|
|
360
|
+
return f"\n ▶ {plain}\n"
|
|
361
|
+
|
|
362
|
+
text = _re.sub(r'\$\$(.+?)\$\$', _render_display, text, flags=_re.DOTALL)
|
|
363
|
+
|
|
364
|
+
# Inline math $...$ → plain
|
|
365
|
+
def _render_inline(m: _re.Match) -> str:
|
|
366
|
+
inner = m.group(1).strip()
|
|
367
|
+
return formula_to_plaintext(f"${inner}$")
|
|
368
|
+
|
|
369
|
+
text = _re.sub(r'\$([^$\n]{1,120})\$', _render_inline, text)
|
|
370
|
+
|
|
371
|
+
# Gracefully handle a single dangling "$" before a formula-like expression.
|
|
372
|
+
text = _re.sub(
|
|
373
|
+
r'\$([A-Za-zΑ-ωβμσΣ][^$\n]{1,120})',
|
|
374
|
+
lambda m: formula_to_plaintext(m.group(1)),
|
|
375
|
+
text,
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Display blocks \[...\] → " ▶ <plain formula>"
|
|
379
|
+
def _render_block(m: _re.Match) -> str:
|
|
380
|
+
inner = m.group(1).strip()
|
|
381
|
+
plain = formula_to_plaintext(inner)
|
|
382
|
+
return f"\n ▶ {plain}\n"
|
|
383
|
+
|
|
384
|
+
text = _re.sub(r'\\\[(.+?)\\\]', _render_block, text, flags=_re.DOTALL)
|
|
385
|
+
|
|
386
|
+
# Inline \(...\)
|
|
387
|
+
text = _re.sub(r'\\\((.+?)\\\)', lambda m: formula_to_plaintext(m.group(1)), text)
|
|
388
|
+
|
|
389
|
+
return text
|