erlangshen 0.1.0

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.
Files changed (93) hide show
  1. package/.claude/agents/equity-agent.md +26 -0
  2. package/.claude/agents/macro-agent.md +25 -0
  3. package/.claude/commands/analyze.md +40 -0
  4. package/.claude/commands/macro.md +29 -0
  5. package/.claude/settings.json +12 -0
  6. package/CODEX_GOAL.md +46 -0
  7. package/README.md +206 -0
  8. package/bin/cli.js +67 -0
  9. package/bin/erlangshen +2 -0
  10. package/bin/xiaoergod +2 -0
  11. package/frontend/index.html +700 -0
  12. package/knowledge/crypto_guide.md +147 -0
  13. package/knowledge/economic_indicators.md +125 -0
  14. package/knowledge/financial_glossary.md +148 -0
  15. package/knowledge/first_principles.md +50 -0
  16. package/knowledge/first_principles_deep.md +115 -0
  17. package/knowledge/global_markets.md +173 -0
  18. package/knowledge/insights.md +141 -0
  19. package/knowledge/market_basics.md +116 -0
  20. package/knowledge/memos/session_20260513_003616.json +6 -0
  21. package/knowledge/memos/session_20260513_003822.json +6 -0
  22. package/knowledge/risk_management.md +151 -0
  23. package/knowledge/team_context.md +42 -0
  24. package/knowledge/trading_strategies.md +114 -0
  25. package/package.json +42 -0
  26. package/requirements.txt +14 -0
  27. package/scripts/postinstall.js +188 -0
  28. package/scripts/preuninstall.js +22 -0
  29. package/src/__init__.py +4 -0
  30. package/src/__pycache__/__init__.cpython-313.pyc +0 -0
  31. package/src/agents/__init__.py +3 -0
  32. package/src/agents/base.py +103 -0
  33. package/src/agents/base_agent.py +86 -0
  34. package/src/agents/equity.py +136 -0
  35. package/src/agents/equity_agent.py +91 -0
  36. package/src/agents/erlang.py +165 -0
  37. package/src/agents/macro.py +137 -0
  38. package/src/agents/macro_agent.py +81 -0
  39. package/src/agents/multi_asset.py +147 -0
  40. package/src/agents/multi_asset_agent.py +87 -0
  41. package/src/api/__init__.py +1 -0
  42. package/src/api/__pycache__/__init__.cpython-313.pyc +0 -0
  43. package/src/api/__pycache__/server.cpython-313.pyc +0 -0
  44. package/src/api/cli.py +435 -0
  45. package/src/api/cli_enhanced.py +537 -0
  46. package/src/api/server.py +266 -0
  47. package/src/brain.py +200 -0
  48. package/src/cli.py +153 -0
  49. package/src/commands/__init__.py +3 -0
  50. package/src/commands/analyze.py +131 -0
  51. package/src/commands/macro.py +100 -0
  52. package/src/commands/memo.py +216 -0
  53. package/src/commands/portfolio.py +154 -0
  54. package/src/commands/report.py +228 -0
  55. package/src/commands/risk.py +183 -0
  56. package/src/commands/search.py +183 -0
  57. package/src/commands/stock.py +124 -0
  58. package/src/config.py +327 -0
  59. package/src/core/__init__.py +1 -0
  60. package/src/core/brain.py +645 -0
  61. package/src/core/cerebellum.py +175 -0
  62. package/src/core/investment_universe.py +423 -0
  63. package/src/core/knowledge.py +207 -0
  64. package/src/core/memory.py +115 -0
  65. package/src/hooks/__init__.py +3 -0
  66. package/src/hooks/session_end.py +57 -0
  67. package/src/hooks/session_start.py +75 -0
  68. package/src/knowledge/__init__.py +1 -0
  69. package/src/mcp/__init__.py +3 -0
  70. package/src/mcp/feishu.py +331 -0
  71. package/src/mcp/fund_tools.py +323 -0
  72. package/src/mcp/macro.py +452 -0
  73. package/src/mcp/market.py +331 -0
  74. package/src/mcp/registry.py +168 -0
  75. package/src/network/__init__.py +15 -0
  76. package/src/network/detector.py +125 -0
  77. package/src/network/proxy.py +199 -0
  78. package/src/network/router.py +103 -0
  79. package/src/prompts/__init__.py +1 -0
  80. package/src/prompts/analysis_framework.md +164 -0
  81. package/src/prompts/persona.md +65 -0
  82. package/src/prompts/report_template.md +144 -0
  83. package/src/skills/__init__.py +3 -0
  84. package/src/skills/framework.py +105 -0
  85. package/src/skills/templates.py +342 -0
  86. package/src/tools/__init__.py +1 -0
  87. package/src/tools/file_tools.py +209 -0
  88. package/src/tools/macro_tools.py +152 -0
  89. package/src/tools/market_tools.py +1172 -0
  90. package/src/tools/registry.py +398 -0
  91. package/src/tools/search_tools.py +777 -0
  92. package/tests/__init__.py +1 -0
  93. package/tests/test_erlangshen.py +140 -0
@@ -0,0 +1,131 @@
1
+ """
2
+ /analyze 命令 - 综合分析
3
+ """
4
+
5
+ from typing import Optional
6
+ from src.agents.macro import MacroAgent
7
+ from src.agents.equity import EquityAgent
8
+ from src.agents.multi_asset import MultiAssetAgent
9
+ from src.brain import Brain
10
+ from src.mcp.registry import MCPRegistry
11
+
12
+
13
+ class AnalyzeCommand:
14
+ """
15
+ 综合分析命令处理器
16
+
17
+ 用法:
18
+ /analyze A股当前走势
19
+ /analyze 茅台投资价值
20
+ /analyze 黄金避险情绪
21
+ """
22
+
23
+ def __init__(self, brain: Brain, mcp: MCPRegistry):
24
+ self.brain = brain
25
+ self.mcp = mcp
26
+ self.macro_agent = MacroAgent(brain, mcp)
27
+ self.equity_agent = EquityAgent(brain, mcp)
28
+ self.multi_asset_agent = MultiAssetAgent(brain, mcp)
29
+
30
+ async def execute(self, args: str) -> str:
31
+ """
32
+ 执行分析
33
+
34
+ Args:
35
+ args: 分析查询
36
+
37
+ Returns:
38
+ 分析结果
39
+ """
40
+ if not args:
41
+ return self._help()
42
+
43
+ # 意图识别
44
+ intent = self._classify_intent(args)
45
+
46
+ if intent == "macro":
47
+ return await self.macro_agent.process(args)
48
+ elif intent == "equity":
49
+ return await self.equity_agent.process(args)
50
+ elif intent == "multi_asset":
51
+ return await self.multi_asset_agent.process(args)
52
+ else:
53
+ # 默认综合分析
54
+ return await self._comprehensive_analysis(args)
55
+
56
+ def _classify_intent(self, query: str) -> str:
57
+ """
58
+ 分类查询意图
59
+
60
+ Returns:
61
+ intent: macro/equity/multi_asset
62
+ """
63
+ query_lower = query.lower()
64
+
65
+ # 宏观关键词
66
+ macro_keywords = ["宏观", "经济", "gdp", "cpi", "ppi", "pmi", "利率", "汇率",
67
+ "货币", "财政", "政策", "出口", "进口", "社融", "m2"]
68
+
69
+ # 股票关键词
70
+ equity_keywords = ["股票", "股价", "茅台", "腾讯", "苹果", "估值", "财报",
71
+ "营收", "利润", "板块", "行业", "涨停", "跌停"]
72
+
73
+ # 多资产关键词
74
+ multi_asset_keywords = ["配置", "组合", "分散", "风险", "收益", "再平衡",
75
+ "仓位", "持仓", "资产配置"]
76
+
77
+ scores = {"macro": 0, "equity": 0, "multi_asset": 0}
78
+
79
+ for kw in macro_keywords:
80
+ if kw in query_lower:
81
+ scores["macro"] += 1
82
+
83
+ for kw in equity_keywords:
84
+ if kw in query_lower:
85
+ scores["equity"] += 1
86
+
87
+ for kw in multi_asset_keywords:
88
+ if kw in query_lower:
89
+ scores["multi_asset"] += 1
90
+
91
+ max_score = max(scores.values())
92
+ if max_score == 0:
93
+ return "macro" # 默认宏观
94
+
95
+ for intent, score in scores.items():
96
+ if score == max_score:
97
+ return intent
98
+
99
+ return "macro"
100
+
101
+ async def _comprehensive_analysis(self, query: str) -> str:
102
+ """综合分析"""
103
+ prompt = f"""请对以下主题进行全面综合分析:
104
+
105
+ 主题:{query}
106
+
107
+ 请从以下维度进行分析:
108
+ 1. 宏观背景
109
+ 2. 市场现状
110
+ 3. 关键因素
111
+ 4. 风险提示
112
+ 5. 投资建议
113
+ """
114
+ return await self.brain.analyze(prompt, framework="综合分析框架")
115
+
116
+ def _help(self) -> str:
117
+ """帮助信息"""
118
+ return """
119
+ /analyze - 综合分析命令
120
+
121
+ 用法:
122
+ /analyze <查询内容>
123
+
124
+ 示例:
125
+ /analyze A股当前走势
126
+ /analyze 茅台投资价值
127
+ /analyze 黄金避险情绪
128
+ /analyze 当前宏观经济形势
129
+
130
+ 系统会自动识别查询意图并调用相应的分析师Agent。
131
+ """
@@ -0,0 +1,100 @@
1
+ """
2
+ /macro 命令 - 宏观分析
3
+ """
4
+
5
+ from typing import Optional
6
+ from src.agents.macro import MacroAgent
7
+ from src.brain import Brain
8
+ from src.mcp.registry import MCPRegistry
9
+
10
+
11
+ class MacroCommand:
12
+ """
13
+ 宏观分析命令处理器
14
+
15
+ 用法:
16
+ /macro CPI走势
17
+ /macro LPR利率
18
+ /macro 美联储政策
19
+ """
20
+
21
+ def __init__(self, brain: Brain, mcp: MCPRegistry):
22
+ self.brain = brain
23
+ self.mcp = mcp
24
+ self.agent = MacroAgent(brain, mcp)
25
+
26
+ async def execute(self, args: str) -> str:
27
+ """
28
+ 执行宏观分析
29
+
30
+ Args:
31
+ args: 分析查询
32
+
33
+ Returns:
34
+ 分析结果
35
+ """
36
+ if not args:
37
+ return self._help()
38
+
39
+ # 解析查询,提取指标
40
+ indicators = self._extract_indicators(args)
41
+
42
+ return await self.agent.process(
43
+ args,
44
+ indicators=indicators if indicators else None
45
+ )
46
+
47
+ def _extract_indicators(self, query: str) -> list:
48
+ """从查询中提取宏观指标"""
49
+ indicator_map = {
50
+ "gdp": "GDP",
51
+ "国内生产总值": "GDP",
52
+ "cpi": "CPI",
53
+ "物价": "CPI",
54
+ "通胀": "CPI",
55
+ "ppi": "PPI",
56
+ "生产物价": "PPI",
57
+ "pmi": "PMI",
58
+ "采购经理": "PMI",
59
+ "社融": "社融",
60
+ "社会融资": "社融",
61
+ "m2": "M2",
62
+ "货币": "M2",
63
+ "lpr": "LPR",
64
+ "利率": "LPR",
65
+ "汇率": "汇率",
66
+ "人民币": "汇率",
67
+ "美元": "汇率",
68
+ "出口": "出口",
69
+ "进口": "进口",
70
+ }
71
+
72
+ query_lower = query.lower()
73
+ found = []
74
+
75
+ for keyword, indicator in indicator_map.items():
76
+ if keyword in query_lower and indicator not in found:
77
+ found.append(indicator)
78
+
79
+ return found
80
+
81
+ def _help(self) -> str:
82
+ """帮助信息"""
83
+ return """
84
+ /macro - 宏观分析命令
85
+
86
+ 用法:
87
+ /macro <查询内容>
88
+
89
+ 示例:
90
+ /macro CPI走势
91
+ /macro PPI同比环比
92
+ /macro 制造业PMI
93
+ /macro LPR利率
94
+ /macro 人民币汇率
95
+ /macro 社融数据
96
+ /macro 美联储加息
97
+
98
+ 支持的指标:
99
+ GDP, CPI, PPI, PMI, 社融, M2, LPR, 汇率, 出口, 进口
100
+ """
@@ -0,0 +1,216 @@
1
+ """
2
+ /memo 命令 - 纪要管理
3
+ """
4
+
5
+ from typing import Optional, List
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+ import json
9
+ from src.brain import Brain
10
+ from src.mcp.registry import MCPRegistry
11
+
12
+
13
+ class MemoCommand:
14
+ """
15
+ 纪要管理命令处理器
16
+
17
+ 用法:
18
+ /memo 记录今天的会议
19
+ /memo 列表
20
+ /memo 查看 20240101
21
+ /memo 搜索 茅台
22
+ """
23
+
24
+ def __init__(self, brain: Brain, mcp: MCPRegistry):
25
+ self.brain = brain
26
+ self.mcp = mcp
27
+ self.memos_dir = Path("~/.openclaw-agent-06/workspace/erlangshen/knowledge/memos").expanduser()
28
+ self.memos_dir.mkdir(parents=True, exist_ok=True)
29
+
30
+ async def execute(self, args: str) -> str:
31
+ """
32
+ 执行纪要操作
33
+
34
+ Args:
35
+ args: 操作类型和内容
36
+
37
+ Returns:
38
+ 操作结果
39
+ """
40
+ if not args:
41
+ return self._help()
42
+
43
+ args = args.strip()
44
+ parts = args.split(maxsplit=1)
45
+ action = parts[0]
46
+ content = parts[1] if len(parts) > 1 else ""
47
+
48
+ if action in ["记", "记录", "add", "create"]:
49
+ return await self._create_memo(content)
50
+ elif action in ["列表", "list"]:
51
+ return self._list_memos()
52
+ elif action in ["查看", "show", "read"]:
53
+ return await self._read_memo(content)
54
+ elif action in ["搜索", "search"]:
55
+ return await self._search_memos(content)
56
+ elif action in ["删除", "delete"]:
57
+ return self._delete_memo(content)
58
+ else:
59
+ # 默认为记录
60
+ return await self._create_memo(args)
61
+
62
+ async def _create_memo(self, content: str) -> str:
63
+ """创建纪要"""
64
+ if not content:
65
+ return "请提供纪要内容"
66
+
67
+ # 生成文件名
68
+ date_str = datetime.now().strftime("%Y%m%d_%H%M%S")
69
+ filename = f"{date_str}.md"
70
+ filepath = self.memos_dir / filename
71
+
72
+ # 构建纪要内容
73
+ memo_content = f"""# 纪要 - {datetime.now().strftime("%Y年%m月%d日 %H:%M")}
74
+
75
+ ## 内容
76
+
77
+ {content}
78
+
79
+ ---
80
+ *由二郎神自动生成*
81
+ """
82
+
83
+ # 保存
84
+ with open(filepath, "w", encoding="utf-8") as f:
85
+ f.write(memo_content)
86
+
87
+ return f"✅ 纪要已保存: {filepath.name}"
88
+
89
+ def _list_memos(self) -> str:
90
+ """列出所有纪要"""
91
+ memos = list(self.memos_dir.glob("*.md"))
92
+
93
+ if not memos:
94
+ return "暂无纪要"
95
+
96
+ memos.sort(key=lambda x: x.stem, reverse=True)
97
+
98
+ output = f"📝 共 {len(memos)} 条纪要:\n\n"
99
+
100
+ for memo in memos[:20]: # 最多显示20条
101
+ try:
102
+ content = memo.read_text(encoding="utf-8")
103
+ # 获取标题
104
+ title_line = content.split("\n")[0].replace("#", "").strip()
105
+ # 获取前50字符
106
+ preview = content[content.find("## 内容"):].replace("## 内容", "").strip()[:50]
107
+
108
+ date_str = memo.stem[:8]
109
+ date_formatted = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:8]}"
110
+
111
+ output += f"- **{date_formatted}** {title_line}\n"
112
+ output += f" {preview}...\n\n"
113
+ except Exception:
114
+ continue
115
+
116
+ if len(memos) > 20:
117
+ output += f"\n... 还有 {len(memos) - 20} 条纪要"
118
+
119
+ return output
120
+
121
+ async def _read_memo(self, identifier: str) -> str:
122
+ """读取指定纪要"""
123
+ if not identifier:
124
+ return "请提供纪要标识"
125
+
126
+ # 尝试按日期或文件名查找
127
+ if identifier.isdigit() and len(identifier) == 8:
128
+ # 按日期查找
129
+ pattern = f"{identifier}*"
130
+ matches = list(self.memos_dir.glob(f"{pattern}.md"))
131
+ else:
132
+ # 按文件名或内容搜索
133
+ matches = list(self.memos_dir.glob("*.md"))
134
+ matches = [m for m in matches if identifier in m.stem or identifier in m.read_text()]
135
+
136
+ if not matches:
137
+ return f"未找到纪要: {identifier}"
138
+
139
+ # 返回最新的
140
+ memo = sorted(matches, key=lambda x: x.stem, reverse=True)[0]
141
+ content = memo.read_text(encoding="utf-8")
142
+
143
+ return f"📝 **{memo.stem}**\n\n{content}"
144
+
145
+ async def _search_memos(self, keyword: str) -> str:
146
+ """搜索纪要"""
147
+ if not keyword:
148
+ return "请提供搜索关键词"
149
+
150
+ matches = []
151
+
152
+ for memo in self.memos_dir.glob("*.md"):
153
+ try:
154
+ content = memo.read_text(encoding="utf-8")
155
+ if keyword.lower() in content.lower():
156
+ matches.append({
157
+ "file": memo,
158
+ "content": content[:200]
159
+ })
160
+ except Exception:
161
+ continue
162
+
163
+ if not matches:
164
+ return f"未找到包含'{keyword}'的纪要"
165
+
166
+ output = f"🔍 找到 {len(matches)} 条相关纪要:\n\n"
167
+
168
+ for match in matches[:10]:
169
+ output += f"- **{match['file'].stem}**\n"
170
+ output += f" {match['content'][:100]}...\n\n"
171
+
172
+ return output
173
+
174
+ def _delete_memo(self, identifier: str) -> str:
175
+ """删除纪要"""
176
+ if not identifier:
177
+ return "请提供要删除的纪要标识"
178
+
179
+ # 查找纪要
180
+ pattern = f"*{identifier}*" if identifier.isdigit() else f"*{identifier}*.md"
181
+ matches = list(self.memos_dir.glob(pattern))
182
+
183
+ if not matches:
184
+ return f"未找到纪要: {identifier}"
185
+
186
+ # 删除第一个匹配
187
+ memo = matches[0]
188
+ memo.unlink()
189
+
190
+ return f"✅ 已删除: {memo.name}"
191
+
192
+ def _help(self) -> str:
193
+ """帮助信息"""
194
+ return """
195
+ /memo - 纪要管理命令
196
+
197
+ 用法:
198
+ /memo <内容> - 快速记录
199
+ /memo 记录 <内容> - 记录新纪要
200
+ /memo 列表 - 查看所有纪要
201
+ /memo 查看 <标识> - 查看指定纪要
202
+ /memo 搜索 <关键词> - 搜索纪要
203
+ /memo 删除 <标识> - 删除纪要
204
+
205
+ 示例:
206
+ /memo 今天讨论了量化策略
207
+ /memo 列表
208
+ /memo 查看 20240101
209
+ /memo 搜索 茅台
210
+ /memo 删除 20240101
211
+
212
+ 说明:
213
+ - 纪要自动保存到本地知识库
214
+ - 支持按日期或关键词搜索
215
+ - 自动生成时间戳
216
+ """
@@ -0,0 +1,154 @@
1
+ """
2
+ /portfolio 命令 - 组合分析
3
+ """
4
+
5
+ from typing import Optional, Dict, List
6
+ from src.agents.multi_asset import MultiAssetAgent
7
+ from src.brain import Brain
8
+ from src.mcp.registry import MCPRegistry
9
+
10
+
11
+ class PortfolioCommand:
12
+ """
13
+ 组合分析命令处理器
14
+
15
+ 用法:
16
+ /portfolio 分析
17
+ /portfolio 再平衡
18
+ /portfolio 风险
19
+ """
20
+
21
+ def __init__(self, brain: Brain, mcp: MCPRegistry):
22
+ self.brain = brain
23
+ self.mcp = mcp
24
+ self.agent = MultiAssetAgent(brain, mcp)
25
+
26
+ async def execute(self, args: str) -> str:
27
+ """
28
+ 执行组合分析
29
+
30
+ Args:
31
+ args: 分析类型
32
+
33
+ Returns:
34
+ 分析结果
35
+ """
36
+ if not args:
37
+ return self._help()
38
+
39
+ args_lower = args.lower()
40
+
41
+ if "分析" in args or "查看" in args:
42
+ return await self._analyze_portfolio()
43
+ elif "再平衡" in args or "rebalance" in args_lower:
44
+ return await self._rebalance_suggestion()
45
+ elif "风险" in args:
46
+ return await self._risk_analysis()
47
+ elif "收益" in args or "业绩" in args:
48
+ return await self._performance_analysis()
49
+ else:
50
+ return await self._general_analysis(args)
51
+
52
+ async def _analyze_portfolio(self) -> str:
53
+ """分析组合"""
54
+ # 默认组合配置
55
+ default_portfolio = {
56
+ "沪深300": 0.30,
57
+ "中证500": 0.15,
58
+ "恒生科技": 0.10,
59
+ "纳斯达克": 0.10,
60
+ "黄金": 0.10,
61
+ "债券": 0.15,
62
+ "现金": 0.10,
63
+ }
64
+
65
+ return await self.agent.analyze_allocation(default_portfolio)
66
+
67
+ async def _rebalance_suggestion(self) -> str:
68
+ """再平衡建议"""
69
+ current = {
70
+ "沪深300": 0.32,
71
+ "中证500": 0.14,
72
+ "恒生科技": 0.12,
73
+ "纳斯达克": 0.08,
74
+ "黄金": 0.08,
75
+ "债券": 0.16,
76
+ "现金": 0.10,
77
+ }
78
+
79
+ target = {
80
+ "沪深300": 0.30,
81
+ "中证500": 0.15,
82
+ "恒生科技": 0.10,
83
+ "纳斯达克": 0.10,
84
+ "黄金": 0.10,
85
+ "债券": 0.15,
86
+ "现金": 0.10,
87
+ }
88
+
89
+ return await self.agent.rebalance_suggestion(current, target)
90
+
91
+ async def _risk_analysis(self) -> str:
92
+ """风险分析"""
93
+ portfolio = {
94
+ "沪深300": 0.30,
95
+ "中证500": 0.15,
96
+ "恒生科技": 0.10,
97
+ "纳斯达克": 0.10,
98
+ "黄金": 0.10,
99
+ "债券": 0.15,
100
+ "现金": 0.10,
101
+ }
102
+
103
+ return await self.agent.risk_analysis(portfolio)
104
+
105
+ async def _performance_analysis(self) -> str:
106
+ """业绩分析"""
107
+ prompt = """请分析以下组合的历史业绩表现:
108
+
109
+ 组合配置:
110
+ - 沪深300: 30%
111
+ - 中证500: 15%
112
+ - 恒生科技: 10%
113
+ - 纳斯达克: 10%
114
+ - 黄金: 10%
115
+ - 债券: 15%
116
+ - 现金: 10%
117
+
118
+ 请从以下维度进行分析:
119
+ 1. 收益表现 (YTD, 1Y, 3Y)
120
+ 2. 风险指标 (波动率, 最大回撤)
121
+ 3. 风险调整收益 (夏普比率, 卡玛比率)
122
+ 4. 业绩归因
123
+ """
124
+
125
+ return await self.brain.analyze(prompt, framework="业绩归因框架")
126
+
127
+ async def _general_analysis(self, args: str) -> str:
128
+ """通用分析"""
129
+ return await self.brain.analyze(
130
+ f"关于组合管理的问题:{args}",
131
+ framework="多资产配置框架"
132
+ )
133
+
134
+ def _help(self) -> str:
135
+ """帮助信息"""
136
+ return """
137
+ /portfolio - 组合分析命令
138
+
139
+ 用法:
140
+ /portfolio 分析 - 分析当前组合配置
141
+ /portfolio 再平衡 - 提供再平衡建议
142
+ /portfolio 风险 - 组合风险分析
143
+ /portfolio 业绩 - 业绩表现分析
144
+
145
+ 示例:
146
+ /portfolio 分析
147
+ /portfolio 再平衡
148
+ /portfolio 风险
149
+ /portfolio 业绩归因
150
+
151
+ 说明:
152
+ 系统使用默认的参考组合配置进行分析。
153
+ 如需分析自定义组合,请提供具体的配置比例。
154
+ """