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,258 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/financial/technical.py — 技术分析 Agent
|
|
3
|
+
===============================================
|
|
4
|
+
分析 K线形态、均线结构、MACD/RSI、布林带、关键价位。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Dict, List
|
|
11
|
+
|
|
12
|
+
from ..base import BaseAgent, AgentResult
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TechnicalAgent(BaseAgent):
|
|
18
|
+
|
|
19
|
+
name = "technical"
|
|
20
|
+
description = "技术分析:图形形态、动量指标、关键价位"
|
|
21
|
+
|
|
22
|
+
_SYSTEM = (
|
|
23
|
+
"You are a quantitative technical analyst. Analyze price action, "
|
|
24
|
+
"chart patterns, momentum indicators (RSI, MACD), moving averages, "
|
|
25
|
+
"and Bollinger Bands. Provide concise, data-driven insights. "
|
|
26
|
+
"Conclude with a clear directional bias: BULLISH / NEUTRAL / BEARISH."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def fetch_data(self, symbol: str) -> Dict[str, Any]:
|
|
30
|
+
data = await super().fetch_data(symbol)
|
|
31
|
+
if self.data:
|
|
32
|
+
try:
|
|
33
|
+
h = self.data.history(symbol, days=120)
|
|
34
|
+
if h and h.data is not None:
|
|
35
|
+
df = h.data
|
|
36
|
+
data["history"] = _compute_indicators(df)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
logger.debug(f"[technical] fetch history {symbol}: {e}")
|
|
39
|
+
return data
|
|
40
|
+
|
|
41
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
42
|
+
quote = data.get("quote", {})
|
|
43
|
+
history = data.get("history", {})
|
|
44
|
+
price = quote.get("price", 0)
|
|
45
|
+
|
|
46
|
+
# 提取指标用于 prompt
|
|
47
|
+
indicators = _format_indicators(history)
|
|
48
|
+
pattern = history.get("pattern", "无特殊形态")
|
|
49
|
+
|
|
50
|
+
prompt = (
|
|
51
|
+
f"Stock: {symbol} Current Price: {price}\n"
|
|
52
|
+
f"Technical Indicators:\n{indicators}\n"
|
|
53
|
+
f"Pattern: {pattern}\n\n"
|
|
54
|
+
"Provide: 1) Trend assessment 2) Key support/resistance levels "
|
|
55
|
+
"3) Signal strength 4) Short-term outlook (3-10 days) "
|
|
56
|
+
"5) Conclusion: BULLISH / NEUTRAL / BEARISH"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
analysis = await self._call_llm(self._SYSTEM, prompt, max_tokens=600, quote=quote)
|
|
60
|
+
if not analysis:
|
|
61
|
+
analysis = _template_analysis(symbol, price, history)
|
|
62
|
+
|
|
63
|
+
signal = _extract_signal(analysis, history)
|
|
64
|
+
confidence = history.get("signal_strength", 0.5)
|
|
65
|
+
key_points = _extract_key_points(history, price)
|
|
66
|
+
|
|
67
|
+
return AgentResult(
|
|
68
|
+
agent=self.name, symbol=symbol,
|
|
69
|
+
analysis=analysis, confidence=confidence,
|
|
70
|
+
signal=signal, key_points=key_points,
|
|
71
|
+
data_used={"price": price, "indicators": history},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ── 技术指标计算 ──────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
def _compute_indicators(df) -> Dict[str, Any]:
|
|
78
|
+
"""从 OHLCV DataFrame 计算常用指标"""
|
|
79
|
+
try:
|
|
80
|
+
import numpy as np
|
|
81
|
+
close = df["close"].values if "close" in df.columns else df.iloc[:, -1].values
|
|
82
|
+
|
|
83
|
+
# MA
|
|
84
|
+
ma5 = float(np.mean(close[-5:])) if len(close) >= 5 else 0
|
|
85
|
+
ma20 = float(np.mean(close[-20:])) if len(close) >= 20 else 0
|
|
86
|
+
ma60 = float(np.mean(close[-60:])) if len(close) >= 60 else 0
|
|
87
|
+
|
|
88
|
+
# RSI(14)
|
|
89
|
+
delta = np.diff(close[-15:])
|
|
90
|
+
gains = np.where(delta > 0, delta, 0)
|
|
91
|
+
losses= np.where(delta < 0, -delta, 0)
|
|
92
|
+
rs = np.mean(gains[-14:]) / (np.mean(losses[-14:]) + 1e-9)
|
|
93
|
+
rsi = round(100 - 100 / (1 + rs), 1)
|
|
94
|
+
|
|
95
|
+
# MACD
|
|
96
|
+
ema12 = _ema(close, 12)
|
|
97
|
+
ema26 = _ema(close, 26)
|
|
98
|
+
macd = ema12 - ema26
|
|
99
|
+
signal= _ema_arr(macd[-50:] if len(macd) >= 50 else macd, 9)
|
|
100
|
+
macd_val = round(float(macd[-1]), 4)
|
|
101
|
+
signal_val= round(float(signal[-1]), 4)
|
|
102
|
+
hist_val = round(macd_val - signal_val, 4)
|
|
103
|
+
|
|
104
|
+
# 布林带
|
|
105
|
+
std20 = float(np.std(close[-20:])) if len(close) >= 20 else 0
|
|
106
|
+
bb_up = round(ma20 + 2 * std20, 2)
|
|
107
|
+
bb_lo = round(ma20 - 2 * std20, 2)
|
|
108
|
+
|
|
109
|
+
# 信号强度
|
|
110
|
+
price = float(close[-1])
|
|
111
|
+
ma_bull = price > ma5 > ma20
|
|
112
|
+
strength = 0.5
|
|
113
|
+
if ma_bull and macd_val > signal_val:
|
|
114
|
+
strength = 0.75
|
|
115
|
+
elif not ma_bull and macd_val < signal_val:
|
|
116
|
+
strength = 0.25
|
|
117
|
+
|
|
118
|
+
# 形态检测(简单)
|
|
119
|
+
pattern = _detect_simple_pattern(close)
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
"price": round(price, 2),
|
|
123
|
+
"ma5": round(ma5, 2),
|
|
124
|
+
"ma20": round(ma20, 2),
|
|
125
|
+
"ma60": round(ma60, 2),
|
|
126
|
+
"rsi": rsi,
|
|
127
|
+
"macd": macd_val,
|
|
128
|
+
"macd_signal": signal_val,
|
|
129
|
+
"macd_hist": hist_val,
|
|
130
|
+
"bb_upper": bb_up,
|
|
131
|
+
"bb_lower": bb_lo,
|
|
132
|
+
"signal_strength": strength,
|
|
133
|
+
"pattern": pattern,
|
|
134
|
+
}
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.debug(f"compute_indicators 失败: {e}")
|
|
137
|
+
return {}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _ema(arr, period):
|
|
141
|
+
import numpy as np
|
|
142
|
+
k = 2 / (period + 1)
|
|
143
|
+
ema = np.zeros(len(arr))
|
|
144
|
+
ema[0] = arr[0]
|
|
145
|
+
for i in range(1, len(arr)):
|
|
146
|
+
ema[i] = arr[i] * k + ema[i-1] * (1 - k)
|
|
147
|
+
return ema
|
|
148
|
+
|
|
149
|
+
def _ema_arr(arr, period):
|
|
150
|
+
return _ema(arr, period)
|
|
151
|
+
|
|
152
|
+
def _detect_simple_pattern(close) -> str:
|
|
153
|
+
if len(close) < 3:
|
|
154
|
+
return "数据不足"
|
|
155
|
+
o, h, c = close[-3], close[-2], close[-1]
|
|
156
|
+
if c > o and c > h and (c - h) > abs(c - o) * 2:
|
|
157
|
+
return "锤子线"
|
|
158
|
+
if c > o and o < close[-4] and c > close[-4]:
|
|
159
|
+
return "阳线吞噬"
|
|
160
|
+
if abs(c - o) / (max(close[-3:]) - min(close[-3:]) + 1e-9) < 0.1:
|
|
161
|
+
return "十字星"
|
|
162
|
+
return "无特殊形态"
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _format_indicators(history: Dict) -> str:
|
|
166
|
+
if not history:
|
|
167
|
+
return " (无指标数据)"
|
|
168
|
+
return (
|
|
169
|
+
f" MA5={history.get('ma5',0):.2f} MA20={history.get('ma20',0):.2f} "
|
|
170
|
+
f"MA60={history.get('ma60',0):.2f}\n"
|
|
171
|
+
f" RSI={history.get('rsi',50):.1f} "
|
|
172
|
+
f"MACD={history.get('macd',0):.4f} Signal={history.get('macd_signal',0):.4f}\n"
|
|
173
|
+
f" BB Upper={history.get('bb_upper',0):.2f} BB Lower={history.get('bb_lower',0):.2f}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _extract_signal(analysis: str, history: Dict) -> str:
|
|
178
|
+
import re as _re
|
|
179
|
+
text = analysis.upper()
|
|
180
|
+
|
|
181
|
+
# Unambiguous strong signals always win
|
|
182
|
+
if "STRONG_BUY" in text or "强烈买入" in text:
|
|
183
|
+
return "STRONG_BUY"
|
|
184
|
+
if "STRONG_SELL" in text or "强烈卖出" in text:
|
|
185
|
+
return "STRONG_SELL"
|
|
186
|
+
|
|
187
|
+
# The LLM prompt asks the model to conclude with BULLISH/NEUTRAL/BEARISH.
|
|
188
|
+
# Use the LAST occurrence — it's the conclusion, not mid-text context like
|
|
189
|
+
# "a potential BULLISH reversal" appearing before "currently BEARISH".
|
|
190
|
+
matches = _re.findall(r'\b(BULLISH|BEARISH|NEUTRAL)\b', text)
|
|
191
|
+
if matches:
|
|
192
|
+
last = matches[-1]
|
|
193
|
+
if last == "BULLISH":
|
|
194
|
+
return "BUY"
|
|
195
|
+
if last == "BEARISH":
|
|
196
|
+
return "SELL"
|
|
197
|
+
# NEUTRAL → fall through to quantitative check below
|
|
198
|
+
|
|
199
|
+
# Explicit Chinese directional signals
|
|
200
|
+
if "看多" in analysis:
|
|
201
|
+
return "BUY"
|
|
202
|
+
if "看空" in analysis:
|
|
203
|
+
return "SELL"
|
|
204
|
+
|
|
205
|
+
# Quantitative fallback: score RSI + MACD crossover
|
|
206
|
+
rsi = history.get("rsi", 50)
|
|
207
|
+
macd = history.get("macd", 0)
|
|
208
|
+
msig = history.get("macd_signal", 0)
|
|
209
|
+
score = 0
|
|
210
|
+
if rsi < 30: score += 2 # oversold — strong mean-reversion signal
|
|
211
|
+
elif rsi < 45: score -= 1
|
|
212
|
+
elif rsi > 70: score -= 2 # overbought
|
|
213
|
+
elif rsi > 55: score += 1
|
|
214
|
+
if macd > msig: score += 1
|
|
215
|
+
elif macd < msig: score -= 1
|
|
216
|
+
if score >= 2: return "BUY"
|
|
217
|
+
if score <= -2: return "SELL"
|
|
218
|
+
return "HOLD"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _extract_key_points(history: Dict, price: float) -> List[str]:
|
|
222
|
+
points = []
|
|
223
|
+
rsi = history.get("rsi", 50)
|
|
224
|
+
ma20 = history.get("ma20", 0)
|
|
225
|
+
ma60 = history.get("ma60", 0)
|
|
226
|
+
if rsi < 35:
|
|
227
|
+
points.append(f"RSI超卖({rsi:.0f}),有反弹机会")
|
|
228
|
+
elif rsi > 70:
|
|
229
|
+
points.append(f"RSI超买({rsi:.0f}),注意回调风险")
|
|
230
|
+
if ma20 > 0:
|
|
231
|
+
diff_pct = (price - ma20) / ma20 * 100
|
|
232
|
+
points.append(f"距MA20 {diff_pct:+.1f}%")
|
|
233
|
+
macd = history.get("macd", 0)
|
|
234
|
+
sig = history.get("macd_signal", 0)
|
|
235
|
+
if macd > sig and history.get("macd_hist", 0) > 0:
|
|
236
|
+
points.append("MACD金叉,多头动能")
|
|
237
|
+
elif macd < sig:
|
|
238
|
+
points.append("MACD死叉,空头压力")
|
|
239
|
+
pattern = history.get("pattern", "")
|
|
240
|
+
if pattern and pattern != "无特殊形态":
|
|
241
|
+
points.append(f"K线形态: {pattern}")
|
|
242
|
+
return points
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _template_analysis(symbol: str, price: float, history: Dict) -> str:
|
|
246
|
+
rsi = history.get("rsi", 50)
|
|
247
|
+
ma20 = history.get("ma20", 0)
|
|
248
|
+
macd = history.get("macd", 0)
|
|
249
|
+
sig = history.get("macd_signal", 0)
|
|
250
|
+
trend = "上升趋势" if price > ma20 else "下降趋势"
|
|
251
|
+
momentum = "偏多" if macd > sig else "偏空"
|
|
252
|
+
return (
|
|
253
|
+
f"{symbol} 技术面分析(模板):\n"
|
|
254
|
+
f"• 当前价格 {price},处于 {trend}(MA20={ma20:.2f})\n"
|
|
255
|
+
f"• RSI={rsi:.0f},{'超卖' if rsi<35 else '超买' if rsi>70 else '正常区间'}\n"
|
|
256
|
+
f"• MACD 动能{momentum}\n"
|
|
257
|
+
f"• 整体技术面{'BULLISH' if momentum=='偏多' and trend=='上升趋势' else 'NEUTRAL'}"
|
|
258
|
+
)
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/portfolio_agent.py — 组合级分析 Agent
|
|
3
|
+
=============================================
|
|
4
|
+
跨标的分析:相关性矩阵、波动率、集中度风险、再平衡建议。
|
|
5
|
+
数据源:yfinance 1年日线(免费,无需 key)
|
|
6
|
+
触发:/portfolio analyze [symbols…]
|
|
7
|
+
/portfolio rebalance [symbols…]
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import Any, Dict, List, Optional
|
|
15
|
+
|
|
16
|
+
from .base import BaseAgent, AgentResult
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PortfolioAgent(BaseAgent):
|
|
22
|
+
|
|
23
|
+
name = "portfolio"
|
|
24
|
+
description = "组合级分析 — 相关性、分散度、整体风险、再平衡建议"
|
|
25
|
+
|
|
26
|
+
_SYSTEM = (
|
|
27
|
+
"You are a portfolio risk analyst. Given price history and statistics for "
|
|
28
|
+
"a portfolio of stocks, your job is to:\n"
|
|
29
|
+
"1. Evaluate overall portfolio risk (Low / Medium / High)\n"
|
|
30
|
+
"2. Identify diversification gaps — highly correlated pairs, sector crowding\n"
|
|
31
|
+
"3. Flag concentration risk if any single position dominates\n"
|
|
32
|
+
"4. Give 2-3 actionable rebalancing suggestions (be specific: which stocks to "
|
|
33
|
+
"trim / add / replace and why)\n"
|
|
34
|
+
"5. End with a one-line verdict: HEALTHY / NEEDS_ATTENTION / HIGH_RISK\n"
|
|
35
|
+
"Be concrete. Avoid boilerplate."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# ── BaseAgent compatibility ───────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
async def fetch_data(self, symbol: str) -> Dict[str, Any]:
|
|
41
|
+
symbols = [s.strip().upper() for s in symbol.split(",") if s.strip()]
|
|
42
|
+
return await self.fetch_portfolio_data(symbols)
|
|
43
|
+
|
|
44
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
45
|
+
symbols = data.get("symbols") or [s.strip().upper() for s in symbol.split(",") if s.strip()]
|
|
46
|
+
return await self.analyze_portfolio(symbols, data)
|
|
47
|
+
|
|
48
|
+
# ── Multi-symbol interface ────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
async def run_portfolio(self, symbols: List[str]) -> AgentResult:
|
|
51
|
+
"""Primary entry point for multi-symbol analysis."""
|
|
52
|
+
data = await self.fetch_portfolio_data(symbols)
|
|
53
|
+
return await self.analyze_portfolio(symbols, data)
|
|
54
|
+
|
|
55
|
+
async def fetch_portfolio_data(self, symbols: List[str]) -> Dict[str, Any]:
|
|
56
|
+
data: Dict[str, Any] = {"symbols": symbols}
|
|
57
|
+
if len(symbols) < 2:
|
|
58
|
+
return data
|
|
59
|
+
|
|
60
|
+
price_series: Dict[str, Any] = {}
|
|
61
|
+
latest_prices: Dict[str, float] = {}
|
|
62
|
+
sector_map: Dict[str, str] = {}
|
|
63
|
+
|
|
64
|
+
for sym in symbols:
|
|
65
|
+
try:
|
|
66
|
+
import yfinance as yf
|
|
67
|
+
ticker = yf.Ticker(sym)
|
|
68
|
+
hist = ticker.history(period="1y")
|
|
69
|
+
if not hist.empty and len(hist) > 20:
|
|
70
|
+
price_series[sym] = hist["Close"]
|
|
71
|
+
latest_prices[sym] = float(hist["Close"].iloc[-1])
|
|
72
|
+
# Try to get sector info
|
|
73
|
+
try:
|
|
74
|
+
info = ticker.info or {}
|
|
75
|
+
sector = info.get("sector") or info.get("industry", "")
|
|
76
|
+
if sector:
|
|
77
|
+
sector_map[sym] = sector
|
|
78
|
+
except Exception:
|
|
79
|
+
pass
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.debug("[portfolio] yfinance %s: %s", sym, e)
|
|
82
|
+
|
|
83
|
+
if len(price_series) < 2:
|
|
84
|
+
data["error"] = "insufficient_data"
|
|
85
|
+
return data
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
import pandas as pd
|
|
89
|
+
import numpy as np
|
|
90
|
+
|
|
91
|
+
df = pd.DataFrame(price_series).dropna()
|
|
92
|
+
returns = df.pct_change().dropna()
|
|
93
|
+
|
|
94
|
+
corr = returns.corr()
|
|
95
|
+
ann_returns = (returns.mean() * 252).round(4)
|
|
96
|
+
ann_vols = (returns.std() * np.sqrt(252)).round(4)
|
|
97
|
+
cov_matrix = returns.cov() * 252
|
|
98
|
+
valid_syms = list(df.columns)
|
|
99
|
+
n = len(valid_syms)
|
|
100
|
+
weights = np.ones(n) / n
|
|
101
|
+
port_vol = float(np.sqrt(weights @ cov_matrix.values @ weights))
|
|
102
|
+
|
|
103
|
+
# High correlation pairs (|r| > 0.70)
|
|
104
|
+
high_corr: List[Dict] = []
|
|
105
|
+
for i in range(len(valid_syms)):
|
|
106
|
+
for j in range(i + 1, len(valid_syms)):
|
|
107
|
+
c = float(corr.iloc[i, j])
|
|
108
|
+
if abs(c) > 0.70:
|
|
109
|
+
high_corr.append({
|
|
110
|
+
"sym1": valid_syms[i],
|
|
111
|
+
"sym2": valid_syms[j],
|
|
112
|
+
"corr": round(c, 3),
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
# Diversification ratio: weighted avg individual vol / portfolio vol
|
|
116
|
+
avg_vol = float(np.mean([float(ann_vols.get(s, 0)) for s in valid_syms]))
|
|
117
|
+
div_ratio = round(avg_vol / port_vol, 2) if port_vol > 0 else 1.0
|
|
118
|
+
|
|
119
|
+
# 52-week return per symbol
|
|
120
|
+
returns_1y: Dict[str, float] = {}
|
|
121
|
+
for sym in valid_syms:
|
|
122
|
+
first = float(df[sym].iloc[0])
|
|
123
|
+
last = float(df[sym].iloc[-1])
|
|
124
|
+
if first > 0:
|
|
125
|
+
returns_1y[sym] = round((last - first) / first, 4)
|
|
126
|
+
|
|
127
|
+
# Best/worst performer
|
|
128
|
+
sorted_ret = sorted(returns_1y.items(), key=lambda x: x[1], reverse=True)
|
|
129
|
+
|
|
130
|
+
data.update({
|
|
131
|
+
"valid_symbols": valid_syms,
|
|
132
|
+
"latest_prices": latest_prices,
|
|
133
|
+
"ann_returns": {s: float(v) for s, v in ann_returns.items()},
|
|
134
|
+
"ann_vols": {s: float(v) for s, v in ann_vols.items()},
|
|
135
|
+
"port_vol_ann": round(port_vol, 4),
|
|
136
|
+
"div_ratio": div_ratio,
|
|
137
|
+
"corr_matrix": corr.round(3).to_dict(),
|
|
138
|
+
"high_corr": high_corr,
|
|
139
|
+
"returns_1y": returns_1y,
|
|
140
|
+
"best_performer": sorted_ret[0] if sorted_ret else None,
|
|
141
|
+
"worst_performer": sorted_ret[-1] if sorted_ret else None,
|
|
142
|
+
"sector_map": sector_map,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
except ImportError:
|
|
146
|
+
data["error"] = "pandas/numpy not available"
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.warning("[portfolio] stats calculation: %s", e)
|
|
149
|
+
data["error"] = str(e)
|
|
150
|
+
|
|
151
|
+
return data
|
|
152
|
+
|
|
153
|
+
async def analyze_portfolio(
|
|
154
|
+
self, symbols: List[str], data: Dict[str, Any]
|
|
155
|
+
) -> AgentResult:
|
|
156
|
+
if len(symbols) < 2:
|
|
157
|
+
return AgentResult(
|
|
158
|
+
agent=self.name, symbol=",".join(symbols),
|
|
159
|
+
analysis="组合分析需要至少 2 个标的。",
|
|
160
|
+
confidence=0.0, signal="HOLD",
|
|
161
|
+
key_points=["标的数量不足"],
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if data.get("error") == "insufficient_data":
|
|
165
|
+
return AgentResult(
|
|
166
|
+
agent=self.name, symbol=",".join(symbols),
|
|
167
|
+
analysis="无法获取足够的历史价格数据进行组合分析。",
|
|
168
|
+
confidence=0.3, signal="HOLD",
|
|
169
|
+
key_points=["历史数据不足(需要 >20 个交易日)"],
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
valid_syms = data.get("valid_symbols", symbols)
|
|
173
|
+
port_block = _format_portfolio_stats(data)
|
|
174
|
+
|
|
175
|
+
prompt = (
|
|
176
|
+
f"Portfolio: {', '.join(valid_syms)} ({len(valid_syms)} positions, equal weight)\n\n"
|
|
177
|
+
f"{port_block}\n\n"
|
|
178
|
+
"Analyze this portfolio:\n"
|
|
179
|
+
"1. Overall risk level (Low / Medium / High)\n"
|
|
180
|
+
"2. Diversification quality — any dangerous correlations or sector crowding?\n"
|
|
181
|
+
"3. Top 2-3 concerns (be specific about which symbols)\n"
|
|
182
|
+
"4. Concrete rebalancing suggestions\n"
|
|
183
|
+
"5. End with: HEALTHY / NEEDS_ATTENTION / HIGH_RISK"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
analysis = await self._call_llm(self._SYSTEM, prompt, max_tokens=650)
|
|
187
|
+
if not analysis:
|
|
188
|
+
analysis = _template_analysis(valid_syms, data)
|
|
189
|
+
|
|
190
|
+
verdict = _extract_verdict(analysis)
|
|
191
|
+
signal = _verdict_to_signal(verdict)
|
|
192
|
+
confidence = _estimate_confidence(data)
|
|
193
|
+
key_points = _build_key_points(data, verdict)
|
|
194
|
+
|
|
195
|
+
return AgentResult(
|
|
196
|
+
agent=self.name,
|
|
197
|
+
symbol=",".join(valid_syms),
|
|
198
|
+
analysis=analysis,
|
|
199
|
+
confidence=confidence,
|
|
200
|
+
signal=signal,
|
|
201
|
+
key_points=key_points,
|
|
202
|
+
data_used={
|
|
203
|
+
"n": len(valid_syms),
|
|
204
|
+
"port_vol_ann": data.get("port_vol_ann"),
|
|
205
|
+
"div_ratio": data.get("div_ratio"),
|
|
206
|
+
"high_corr_count": len(data.get("high_corr", [])),
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
def _format_portfolio_stats(d: Dict) -> str:
|
|
214
|
+
lines = []
|
|
215
|
+
|
|
216
|
+
# Returns & volatility table
|
|
217
|
+
vols = d.get("ann_vols", {})
|
|
218
|
+
rets = d.get("ann_returns", {})
|
|
219
|
+
rets_1y = d.get("returns_1y", {})
|
|
220
|
+
if vols:
|
|
221
|
+
lines.append("Symbol │ Ann.Vol │ Ann.Ret │ 1Y Return")
|
|
222
|
+
lines.append("─" * 42)
|
|
223
|
+
for sym in d.get("valid_symbols", list(vols)):
|
|
224
|
+
v = vols.get(sym, 0)
|
|
225
|
+
r = rets.get(sym, 0)
|
|
226
|
+
y = rets_1y.get(sym, 0)
|
|
227
|
+
lines.append(
|
|
228
|
+
f"{sym:<6} │ {v*100:>6.1f}% │ {r*100:>6.1f}% │ {y*100:>+7.1f}%"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
port_vol = d.get("port_vol_ann", 0)
|
|
232
|
+
div_r = d.get("div_ratio", 1)
|
|
233
|
+
if port_vol:
|
|
234
|
+
lines.append(f"\nPortfolio Ann.Vol: {port_vol*100:.1f}%")
|
|
235
|
+
lines.append(f"Diversification Ratio: {div_r:.2f}x "
|
|
236
|
+
f"({'good' if div_r >= 1.3 else 'poor'})")
|
|
237
|
+
|
|
238
|
+
high_corr = d.get("high_corr", [])
|
|
239
|
+
if high_corr:
|
|
240
|
+
lines.append(f"\nHigh-Correlation Pairs (|r|>0.70):")
|
|
241
|
+
for p in high_corr[:5]:
|
|
242
|
+
lines.append(f" {p['sym1']} ↔ {p['sym2']}: {p['corr']:+.2f}")
|
|
243
|
+
|
|
244
|
+
sectors = d.get("sector_map", {})
|
|
245
|
+
if sectors:
|
|
246
|
+
from collections import Counter
|
|
247
|
+
sector_counts = Counter(sectors.values())
|
|
248
|
+
dominant = [(s, c) for s, c in sector_counts.items() if c >= 2]
|
|
249
|
+
if dominant:
|
|
250
|
+
lines.append("\nSector Concentration:")
|
|
251
|
+
for sec, cnt in dominant:
|
|
252
|
+
lines.append(f" {sec}: {cnt} positions")
|
|
253
|
+
|
|
254
|
+
best = d.get("best_performer")
|
|
255
|
+
worst = d.get("worst_performer")
|
|
256
|
+
if best and worst:
|
|
257
|
+
lines.append(f"\nBest 1Y: {best[0]} ({best[1]*100:+.1f}%)")
|
|
258
|
+
lines.append(f"Worst 1Y: {worst[0]} ({worst[1]*100:+.1f}%)")
|
|
259
|
+
|
|
260
|
+
return "\n".join(lines)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _extract_verdict(analysis: str) -> str:
|
|
264
|
+
text = analysis.upper()
|
|
265
|
+
if "HIGH_RISK" in text or "HIGH RISK" in text:
|
|
266
|
+
return "HIGH_RISK"
|
|
267
|
+
if "NEEDS_ATTENTION" in text or "NEEDS ATTENTION" in text:
|
|
268
|
+
return "NEEDS_ATTENTION"
|
|
269
|
+
if "HEALTHY" in text:
|
|
270
|
+
return "HEALTHY"
|
|
271
|
+
return "NEEDS_ATTENTION"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _verdict_to_signal(verdict: str) -> str:
|
|
275
|
+
return {"HEALTHY": "HOLD", "NEEDS_ATTENTION": "SELL", "HIGH_RISK": "SELL"}.get(verdict, "HOLD")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _estimate_confidence(d: Dict) -> float:
|
|
279
|
+
base = 0.60
|
|
280
|
+
n = len(d.get("valid_symbols", []))
|
|
281
|
+
if n >= 5:
|
|
282
|
+
base += 0.05
|
|
283
|
+
if d.get("div_ratio", 1) < 1.1:
|
|
284
|
+
base += 0.05
|
|
285
|
+
return min(round(base, 2), 0.75)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _build_key_points(d: Dict, verdict: str) -> List[str]:
|
|
289
|
+
pts = []
|
|
290
|
+
n = len(d.get("valid_symbols", []))
|
|
291
|
+
pts.append(f"{n} 个标的,等权配置")
|
|
292
|
+
|
|
293
|
+
port_vol = d.get("port_vol_ann", 0)
|
|
294
|
+
if port_vol:
|
|
295
|
+
risk_lv = "高风险" if port_vol > 0.30 else ("中等" if port_vol > 0.18 else "低风险")
|
|
296
|
+
pts.append(f"组合年化波动 {port_vol*100:.1f}%({risk_lv})")
|
|
297
|
+
|
|
298
|
+
div_r = d.get("div_ratio", 1)
|
|
299
|
+
if div_r < 1.1:
|
|
300
|
+
pts.append(f"分散度不足(ratio {div_r:.2f}x,标的相关性高)")
|
|
301
|
+
else:
|
|
302
|
+
pts.append(f"分散度合理(ratio {div_r:.2f}x)")
|
|
303
|
+
|
|
304
|
+
high_corr = d.get("high_corr", [])
|
|
305
|
+
if high_corr:
|
|
306
|
+
top = high_corr[0]
|
|
307
|
+
pts.append(f"最高相关对: {top['sym1']}↔{top['sym2']} ({top['corr']:+.2f})")
|
|
308
|
+
|
|
309
|
+
pts.append(f"整体评级: {verdict}")
|
|
310
|
+
return pts[:6]
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _template_analysis(symbols: List[str], d: Dict) -> str:
|
|
314
|
+
high_corr = d.get("high_corr", [])
|
|
315
|
+
port_vol = d.get("port_vol_ann", 0)
|
|
316
|
+
div_r = d.get("div_ratio", 1)
|
|
317
|
+
|
|
318
|
+
verdict = "NEEDS_ATTENTION"
|
|
319
|
+
if not high_corr and port_vol < 0.20 and div_r >= 1.2:
|
|
320
|
+
verdict = "HEALTHY"
|
|
321
|
+
elif len(high_corr) >= 3 or port_vol > 0.30:
|
|
322
|
+
verdict = "HIGH_RISK"
|
|
323
|
+
|
|
324
|
+
lines = [f"{','.join(symbols)} 组合分析报告(模板)"]
|
|
325
|
+
lines.append(f"年化波动率: {port_vol*100:.1f}%")
|
|
326
|
+
lines.append(f"分散度系数: {div_r:.2f}x")
|
|
327
|
+
if high_corr:
|
|
328
|
+
lines.append(f"高相关对 {len(high_corr)} 组,最高: "
|
|
329
|
+
f"{high_corr[0]['sym1']}↔{high_corr[0]['sym2']} {high_corr[0]['corr']:+.2f}")
|
|
330
|
+
else:
|
|
331
|
+
lines.append("无显著高相关对,分散良好。")
|
|
332
|
+
lines.append(f"\n{verdict}")
|
|
333
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/realty — 经营权共创平台 AI Agent 模块
|
|
3
|
+
=============================================
|
|
4
|
+
9 个领域专属 Agent,继承 BaseAgent,用于:
|
|
5
|
+
- 资产诊断与处置建议
|
|
6
|
+
- 业态匹配与经营方推荐
|
|
7
|
+
- 合同条款结构化生成
|
|
8
|
+
- 分账规则配置建议
|
|
9
|
+
- 流水核验与异常检测
|
|
10
|
+
- 能耗异常分析
|
|
11
|
+
- 合同履约风控
|
|
12
|
+
- 运营优化建议
|
|
13
|
+
- 退出清算方案生成
|
|
14
|
+
|
|
15
|
+
用法:
|
|
16
|
+
from agents.realty import AssetDiagnosisAgent, RevenueShareAgent
|
|
17
|
+
from agents.realty import REALTY_TEAM # 默认 team 名称列表
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from .asset_diagnosis import AssetDiagnosisAgent
|
|
21
|
+
from .business_match import BusinessMatchAgent
|
|
22
|
+
from .contract_rules import ContractRulesAgent
|
|
23
|
+
from .revenue_share import RevenueShareAgent
|
|
24
|
+
from .cashflow_verify import CashFlowVerifyAgent
|
|
25
|
+
from .energy_anomaly import EnergyAnomalyAgent
|
|
26
|
+
from .fulfillment_risk import FulfillmentRiskAgent
|
|
27
|
+
from .ops_optimize import OpsOptimizeAgent
|
|
28
|
+
from .exit_settlement import ExitSettlementAgent
|
|
29
|
+
|
|
30
|
+
# 默认完整 team
|
|
31
|
+
REALTY_TEAM = [
|
|
32
|
+
"asset_diagnosis",
|
|
33
|
+
"business_match",
|
|
34
|
+
"contract_rules",
|
|
35
|
+
"revenue_share",
|
|
36
|
+
"cashflow_verify",
|
|
37
|
+
"energy_anomaly",
|
|
38
|
+
"fulfillment_risk",
|
|
39
|
+
"ops_optimize",
|
|
40
|
+
"exit_settlement",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
# 风控专项 team(快速预警)
|
|
44
|
+
RISK_TEAM = [
|
|
45
|
+
"cashflow_verify",
|
|
46
|
+
"energy_anomaly",
|
|
47
|
+
"fulfillment_risk",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"AssetDiagnosisAgent",
|
|
52
|
+
"BusinessMatchAgent",
|
|
53
|
+
"ContractRulesAgent",
|
|
54
|
+
"RevenueShareAgent",
|
|
55
|
+
"CashFlowVerifyAgent",
|
|
56
|
+
"EnergyAnomalyAgent",
|
|
57
|
+
"FulfillmentRiskAgent",
|
|
58
|
+
"OpsOptimizeAgent",
|
|
59
|
+
"ExitSettlementAgent",
|
|
60
|
+
"REALTY_TEAM",
|
|
61
|
+
"RISK_TEAM",
|
|
62
|
+
]
|