683 lines
23 KiB
Python
683 lines
23 KiB
Python
"""
|
||
Skill D: PM (基金经理)
|
||
========================
|
||
职能:计算信任指数、风控、生成订单
|
||
|
||
输入:World_Book + Market_Data_JSON
|
||
输出:Trade_Orders (买卖指令列表)
|
||
|
||
设计原则:
|
||
- 核心决策通过量化模型完成(向量点积)
|
||
- 使用 LLM 提供辅助分析和投资建议
|
||
- 支持 LLM 失败时的优雅降级
|
||
"""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||
|
||
from core.world_book import WorldBook
|
||
from core.config import config, llm_call
|
||
from typing import Dict, List, Optional, Tuple
|
||
from datetime import datetime
|
||
import json
|
||
import re
|
||
|
||
|
||
class PortfolioManager:
|
||
"""
|
||
基金经理 - 负责投资决策和订单生成
|
||
|
||
核心职责:
|
||
1. 计算 Trust Index(叙事 × 资金流 交叉验证)
|
||
2. 使用 LLM 提供投资洞察和风险提示
|
||
3. 风险控制(仓位管理、止损)
|
||
4. 生成具体的买卖订单
|
||
"""
|
||
|
||
# LLM 投资分析提示词
|
||
INVESTMENT_ANALYSIS_PROMPT = """你是一位专业的 A 股 ETF 基金经理,擅长根据宏观环境和市场数据做出投资决策。
|
||
|
||
你的核心任务是:
|
||
1. 分析当前宏观环境对各板块的影响
|
||
2. 评估投资机会的风险收益比
|
||
3. 给出具体的投资建议和理由
|
||
4. 提示潜在的风险因素
|
||
|
||
请严格按照 JSON 格式返回分析结果。"""
|
||
|
||
def __init__(
|
||
self,
|
||
world_book: WorldBook,
|
||
total_capital: float = 1000000.0, # 默认100万
|
||
max_position_pct: float = 0.15, # 单个 ETF 最大仓位15%
|
||
min_trust_score: float = 60.0, # 最低信任指数阈值
|
||
use_llm: bool = True
|
||
):
|
||
"""
|
||
Args:
|
||
world_book: WorldBook 实例
|
||
total_capital: 总资金(元)
|
||
max_position_pct: 单个 ETF 最大仓位比例
|
||
min_trust_score: 最低信任指数(低于此值不开仓)
|
||
use_llm: 是否使用 LLM 进行辅助分析
|
||
"""
|
||
self.wb = world_book
|
||
self.total_capital = total_capital
|
||
self.max_position_pct = max_position_pct
|
||
self.min_trust_score = min_trust_score
|
||
self.use_llm = use_llm
|
||
|
||
# 加载资产映射表
|
||
self.asset_map = self._load_asset_map()
|
||
|
||
# 当前持仓(简化版,实际应从数据库读取)
|
||
self.current_positions: Dict[str, Dict] = {}
|
||
|
||
if self.use_llm:
|
||
print("[PM] ✅ LLM 辅助分析启用 - 将提供智能投资洞察")
|
||
else:
|
||
print("[PM] ⚠️ 纯量化模式 - 仅使用数学模型")
|
||
|
||
def _load_asset_map(self) -> Dict:
|
||
"""加载资产映射表"""
|
||
from pathlib import Path
|
||
asset_map_path = Path("core") / "asset_map.json"
|
||
if not asset_map_path.exists():
|
||
print("[PM] 警告: 未找到 asset_map.json")
|
||
return {}
|
||
|
||
with open(asset_map_path, 'r', encoding='utf-8') as f:
|
||
return json.load(f)
|
||
|
||
def calculate_macro_sensitivity_score(self, asset_id: str) -> float:
|
||
"""
|
||
使用向量点积计算资产的宏观敏感度得分
|
||
|
||
这是新的核心计算方法,替代传统的手工评分。
|
||
|
||
公式:
|
||
Score = Σ (macro_factor_value[i] × sensitivity[i])
|
||
|
||
示例:
|
||
- 宏观环境向量:{"interest_rate_down": 1.0, "geopolitics_tension": 0.5, "policy_digital_economy": 1.0}
|
||
- 软件ETF敏感度:{"policy_digital_economy": 1.0, "interest_rate_down": 0.7}
|
||
- 计算:(1.0 × 1.0) + (1.0 × 0.7) + (0.5 × 0) = 1.7分
|
||
|
||
Args:
|
||
asset_id: 资产ID(例如 "tech_software")
|
||
|
||
Returns:
|
||
宏观敏感度得分(可能为负,表示利空)
|
||
"""
|
||
# 获取资产的敏感度矩阵
|
||
asset_data = self.asset_map.get("assets", {}).get(asset_id, {})
|
||
sensitivity_matrix = asset_data.get("sensitivity", {})
|
||
|
||
if not sensitivity_matrix:
|
||
print(f"[PM] 警告: {asset_id} 没有敏感度数据")
|
||
return 0.0
|
||
|
||
# 获取当前宏观因子向量
|
||
macro_vector = self.wb.macro_factor_vector
|
||
|
||
if not macro_vector:
|
||
print("[PM] 警告: 宏观因子向量为空,无法计算")
|
||
return 0.0
|
||
|
||
# 向量点积计算
|
||
score = 0.0
|
||
matched_factors = []
|
||
|
||
for factor_name, sensitivity_value in sensitivity_matrix.items():
|
||
macro_value = macro_vector.get(factor_name, 0.0)
|
||
|
||
if macro_value != 0:
|
||
contribution = macro_value * sensitivity_value
|
||
score += contribution
|
||
matched_factors.append(f"{factor_name}({macro_value}×{sensitivity_value}={contribution:.2f})")
|
||
|
||
print(f"[PM] {asset_id} 宏观得分: {score:.2f} | 因子: {', '.join(matched_factors) if matched_factors else '无匹配'}")
|
||
|
||
return round(score, 2)
|
||
|
||
def calculate_trust_index(
|
||
self,
|
||
etf_code: str,
|
||
asset_id: str,
|
||
narrative_score: float,
|
||
flow_score: float
|
||
) -> Dict:
|
||
"""
|
||
计算 Trust Index(信任指数)- 新版本
|
||
|
||
公式(升级版):
|
||
TrustIndex = (MacroScore × 50 + Narrative × 0.3 + Flow × 0.2) - Penalty
|
||
|
||
其中:
|
||
- MacroScore: 宏观敏感度得分(向量点积)
|
||
- Narrative: 叙事强度 (0-100)
|
||
- Flow: 资金流评分 (0-100)
|
||
|
||
一票否决规则:
|
||
- 如果 MacroScore < -1.0,Penalty = 100(宏观环境严重利空)
|
||
|
||
Args:
|
||
etf_code: ETF 代码
|
||
asset_id: 资产ID
|
||
narrative_score: 叙事评分 (0-100)
|
||
flow_score: 资金流评分 (0-100)
|
||
|
||
Returns:
|
||
Trust Index 结果字典
|
||
"""
|
||
# 1. 计算宏观敏感度得分
|
||
macro_score = self.calculate_macro_sensitivity_score(asset_id)
|
||
|
||
# 2. 归一化到 0-100 范围(假设 macro_score 在 -2 到 +3 之间)
|
||
normalized_macro = max(0, min(100, (macro_score + 2) / 5 * 100))
|
||
|
||
# 3. 加权计算基础得分
|
||
base_score = normalized_macro * 0.5 + narrative_score * 0.3 + flow_score * 0.2
|
||
|
||
# 4. 一票否决:宏观环境严重利空
|
||
penalty = 0
|
||
if macro_score < -1.0:
|
||
penalty = 100
|
||
verdict = "reject"
|
||
else:
|
||
# 正常评判
|
||
trust_index = base_score - penalty
|
||
|
||
if trust_index >= self.min_trust_score:
|
||
verdict = "buy"
|
||
elif trust_index >= 40:
|
||
verdict = "hold"
|
||
else:
|
||
verdict = "sell"
|
||
|
||
final_trust_index = max(0, base_score - penalty)
|
||
|
||
return {
|
||
"code": etf_code,
|
||
"asset_id": asset_id,
|
||
"trust_index": round(final_trust_index, 2),
|
||
"macro_score": macro_score,
|
||
"normalized_macro": round(normalized_macro, 2),
|
||
"narrative_score": round(narrative_score, 2),
|
||
"flow_score": round(flow_score, 2),
|
||
"penalty": penalty,
|
||
"verdict": verdict,
|
||
"timestamp": datetime.now().isoformat()
|
||
}
|
||
|
||
def batch_calculate_trust_index(
|
||
self,
|
||
market_data: Dict[str, Dict]
|
||
) -> List[Dict]:
|
||
"""
|
||
批量计算所有 ETF 的 Trust Index - 新版本
|
||
|
||
逻辑:
|
||
1. 遍历所有资产
|
||
2. 对每个资产下的 ETF 计算宏观敏感度得分
|
||
3. 结合叙事和资金流计算最终 Trust Index
|
||
|
||
Args:
|
||
market_data: Quant.batch_analyze() 的输出
|
||
|
||
Returns:
|
||
Trust Index 列表,按评分排序
|
||
"""
|
||
results = []
|
||
|
||
print(f"[PM] 计算 Trust Index(使用向量点积方法)...")
|
||
|
||
# 遍历所有资产
|
||
for asset_id, asset_data in self.asset_map.get("assets", {}).items():
|
||
etf_list = asset_data.get("etfs", [])
|
||
|
||
for etf_code in etf_list:
|
||
# 跳过没有市场数据的 ETF
|
||
if etf_code not in market_data:
|
||
continue
|
||
|
||
# 获取叙事评分
|
||
narratives = self.wb.get_narratives_by_etf(etf_code)
|
||
|
||
if narratives:
|
||
# 取最高权重的叙事
|
||
narrative_score = max(n.current_weight for n in narratives)
|
||
else:
|
||
narrative_score = 0
|
||
|
||
# 获取资金流评分
|
||
flow_score = market_data[etf_code].get("flow_analysis", {}).get("flow_score", 0)
|
||
|
||
# 计算 Trust Index(使用向量点积)
|
||
trust_result = self.calculate_trust_index(
|
||
etf_code,
|
||
asset_id,
|
||
narrative_score,
|
||
flow_score
|
||
)
|
||
|
||
results.append(trust_result)
|
||
|
||
# 按 Trust Index 排序
|
||
results.sort(key=lambda x: x['trust_index'], reverse=True)
|
||
|
||
print(f"[PM] Trust Index 计算完成,共 {len(results)} 个标的")
|
||
|
||
# 打印 Top 5
|
||
print("\n[PM] Top 5 标的:")
|
||
for i, result in enumerate(results[:5], 1):
|
||
asset_name = self.asset_map.get("assets", {}).get(result['asset_id'], {}).get("name", "未知")
|
||
print(f" {i}. {result['code']} ({asset_name}) - TrustIndex: {result['trust_index']} "
|
||
f"(Macro: {result['macro_score']:.2f}, Narrative: {result['narrative_score']:.1f}, "
|
||
f"Flow: {result['flow_score']:.1f})")
|
||
|
||
# 使用 LLM 提供投资洞察
|
||
if self.use_llm and results:
|
||
self._provide_llm_insights(results[:10])
|
||
|
||
return results
|
||
|
||
def _provide_llm_insights(self, top_results: List[Dict]) -> None:
|
||
"""使用 LLM 提供投资洞察"""
|
||
try:
|
||
# 准备分析数据
|
||
opportunities = []
|
||
for r in top_results:
|
||
asset_name = self.asset_map.get("assets", {}).get(r['asset_id'], {}).get("name", r['asset_id'])
|
||
opportunities.append({
|
||
"code": r['code'],
|
||
"name": asset_name,
|
||
"trust_index": r['trust_index'],
|
||
"macro_score": r['macro_score'],
|
||
"narrative_score": r['narrative_score'],
|
||
"flow_score": r['flow_score'],
|
||
"verdict": r['verdict']
|
||
})
|
||
|
||
# 获取当前宏观状态
|
||
macro_state = self.wb.macro_cycle.to_dict()
|
||
macro_factors = list(self.wb.macro_factor_vector.keys())[:5]
|
||
|
||
prompt = f"""请分析以下 ETF 投资机会并给出简要建议:
|
||
|
||
【当前宏观环境】
|
||
- 市场周期: {macro_state['status']}
|
||
- 流动性: {macro_state['liquidity']}
|
||
- 政策风向: {macro_state['policy_wind']}
|
||
- 活跃因子: {', '.join(macro_factors) if macro_factors else '无'}
|
||
|
||
【Top 投资机会】
|
||
{json.dumps(opportunities, ensure_ascii=False, indent=2)}
|
||
|
||
请以 JSON 格式返回:
|
||
{{
|
||
"market_view": "当前市场整体观点(30字内)",
|
||
"top_pick": "最推荐的标的代码",
|
||
"top_pick_reason": "推荐理由(50字内)",
|
||
"risk_warning": "主要风险提示(30字内)",
|
||
"position_advice": "仓位建议(如:谨慎/标准/积极)"
|
||
}}"""
|
||
|
||
llm_output = llm_call(
|
||
messages=[
|
||
{"role": "system", "content": self.INVESTMENT_ANALYSIS_PROMPT},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=0.3,
|
||
max_tokens=400
|
||
)
|
||
|
||
if llm_output:
|
||
# 清理 JSON
|
||
llm_output = llm_output.strip()
|
||
if llm_output.startswith("```"):
|
||
llm_output = re.sub(r'^```(?:json)?\s*', '', llm_output)
|
||
llm_output = re.sub(r'\s*```$', '', llm_output)
|
||
|
||
insights = json.loads(llm_output)
|
||
|
||
print("\n" + "=" * 50)
|
||
print("🤖 AI 投资洞察:")
|
||
print("=" * 50)
|
||
print(f"📊 市场观点: {insights.get('market_view', '')}")
|
||
print(f"⭐ 最优选择: {insights.get('top_pick', '')} - {insights.get('top_pick_reason', '')}")
|
||
print(f"⚠️ 风险提示: {insights.get('risk_warning', '')}")
|
||
print(f"💰 仓位建议: {insights.get('position_advice', '')}")
|
||
print("=" * 50)
|
||
|
||
except Exception as e:
|
||
print(f"[PM] LLM 洞察生成失败: {e}")
|
||
|
||
def generate_trade_orders(
|
||
self,
|
||
trust_results: List[Dict],
|
||
market_data: Dict[str, Dict]
|
||
) -> List[Dict]:
|
||
"""
|
||
生成交易订单
|
||
|
||
策略:
|
||
1. 买入信号:Trust Index >= 60 且 verdict = "buy"
|
||
2. 卖出信号:verdict = "sell" 或 "reject"
|
||
3. 仓位计算:Trust Index 越高,仓位越大
|
||
|
||
Args:
|
||
trust_results: batch_calculate_trust_index() 的输出
|
||
market_data: 市场数据(用于获取价格)
|
||
|
||
Returns:
|
||
订单列表
|
||
"""
|
||
orders = []
|
||
|
||
print(f"[PM] 生成交易订单...")
|
||
|
||
# 可用资金(假设当前空仓)
|
||
available_capital = self._calculate_available_capital()
|
||
|
||
for trust_result in trust_results:
|
||
etf_code = trust_result['code']
|
||
verdict = trust_result['verdict']
|
||
trust_index = trust_result['trust_index']
|
||
|
||
# 获取当前价格
|
||
realtime_data = market_data.get(etf_code, {}).get("realtime")
|
||
if not realtime_data:
|
||
continue
|
||
|
||
price = realtime_data['price']
|
||
|
||
# 判断操作
|
||
if verdict == "buy" and trust_index >= self.min_trust_score:
|
||
order = self._create_buy_order(
|
||
etf_code,
|
||
trust_index,
|
||
price,
|
||
available_capital
|
||
)
|
||
if order:
|
||
orders.append(order)
|
||
available_capital -= order['amount']
|
||
|
||
elif verdict in ["sell", "reject"] and etf_code in self.current_positions:
|
||
order = self._create_sell_order(etf_code, price)
|
||
if order:
|
||
orders.append(order)
|
||
|
||
print(f"[PM] 生成 {len(orders)} 条订单")
|
||
return orders
|
||
|
||
def _create_buy_order(
|
||
self,
|
||
etf_code: str,
|
||
trust_index: float,
|
||
price: float,
|
||
available_capital: float
|
||
) -> Optional[Dict]:
|
||
"""
|
||
创建买入订单
|
||
|
||
仓位计算:
|
||
position_pct = min(trust_index / 100 * max_position_pct, max_position_pct)
|
||
"""
|
||
# 已持仓则跳过
|
||
if etf_code in self.current_positions:
|
||
return None
|
||
|
||
# 计算目标仓位
|
||
position_pct = min(
|
||
(trust_index / 100) * self.max_position_pct,
|
||
self.max_position_pct
|
||
)
|
||
|
||
target_amount = self.total_capital * position_pct
|
||
|
||
# 检查可用资金
|
||
if target_amount > available_capital:
|
||
target_amount = available_capital * 0.8 # 保留20%缓冲
|
||
|
||
if target_amount < 1000: # 最小交易金额
|
||
return None
|
||
|
||
shares = int(target_amount / price / 100) * 100 # 向下取整到100股
|
||
actual_amount = shares * price
|
||
|
||
return {
|
||
"action": "buy",
|
||
"code": etf_code,
|
||
"price": price,
|
||
"shares": shares,
|
||
"amount": round(actual_amount, 2),
|
||
"position_pct": round(actual_amount / self.total_capital * 100, 2),
|
||
"trust_index": trust_index,
|
||
"reason": f"Trust Index {trust_index:.1f},叙事与资金共振",
|
||
"timestamp": datetime.now().isoformat()
|
||
}
|
||
|
||
def _create_sell_order(
|
||
self,
|
||
etf_code: str,
|
||
price: float
|
||
) -> Optional[Dict]:
|
||
"""创建卖出订单"""
|
||
if etf_code not in self.current_positions:
|
||
return None
|
||
|
||
position = self.current_positions[etf_code]
|
||
shares = position['shares']
|
||
amount = shares * price
|
||
|
||
return {
|
||
"action": "sell",
|
||
"code": etf_code,
|
||
"price": price,
|
||
"shares": shares,
|
||
"amount": round(amount, 2),
|
||
"reason": "Trust Index 下降或一票否决触发",
|
||
"timestamp": datetime.now().isoformat()
|
||
}
|
||
|
||
def _calculate_available_capital(self) -> float:
|
||
"""计算可用资金"""
|
||
# 简化版:假设当前空仓
|
||
# 实际应该是:总资金 - 已用资金
|
||
return self.total_capital
|
||
|
||
def generate_portfolio_report(
|
||
self,
|
||
trust_results: List[Dict],
|
||
orders: List[Dict]
|
||
) -> Dict:
|
||
"""
|
||
生成投资组合报告
|
||
|
||
Args:
|
||
trust_results: Trust Index 结果
|
||
orders: 交易订单
|
||
|
||
Returns:
|
||
报告字典
|
||
"""
|
||
# 统计 verdict 分布
|
||
verdict_counts = {}
|
||
for result in trust_results:
|
||
verdict = result['verdict']
|
||
verdict_counts[verdict] = verdict_counts.get(verdict, 0) + 1
|
||
|
||
# 统计订单金额
|
||
total_buy_amount = sum(
|
||
order['amount'] for order in orders if order['action'] == 'buy'
|
||
)
|
||
total_sell_amount = sum(
|
||
order['amount'] for order in orders if order['action'] == 'sell'
|
||
)
|
||
|
||
# Top 5 高信任度 ETF
|
||
top_5 = trust_results[:5]
|
||
|
||
report = {
|
||
"timestamp": datetime.now().isoformat(),
|
||
"total_capital": self.total_capital,
|
||
"analysis": {
|
||
"total_etfs": len(trust_results),
|
||
"verdict_distribution": verdict_counts,
|
||
"avg_trust_index": round(
|
||
sum(r['trust_index'] for r in trust_results) / len(trust_results), 2
|
||
) if trust_results else 0
|
||
},
|
||
"orders": {
|
||
"total_orders": len(orders),
|
||
"buy_orders": len([o for o in orders if o['action'] == 'buy']),
|
||
"sell_orders": len([o for o in orders if o['action'] == 'sell']),
|
||
"total_buy_amount": round(total_buy_amount, 2),
|
||
"total_sell_amount": round(total_sell_amount, 2)
|
||
},
|
||
"top_opportunities": [
|
||
{
|
||
"code": r['code'],
|
||
"trust_index": r['trust_index'],
|
||
"verdict": r['verdict']
|
||
}
|
||
for r in top_5
|
||
],
|
||
"risk_control": {
|
||
"capital_utilization": round(total_buy_amount / self.total_capital * 100, 2),
|
||
"max_single_position": self.max_position_pct * 100,
|
||
"min_trust_threshold": self.min_trust_score
|
||
}
|
||
}
|
||
|
||
return report
|
||
|
||
def apply_risk_control(
|
||
self,
|
||
orders: List[Dict]
|
||
) -> List[Dict]:
|
||
"""
|
||
风控检查
|
||
|
||
规则:
|
||
1. 单个 ETF 不超过最大仓位
|
||
2. 总仓位不超过90%(保留10%现金)
|
||
3. 同板块 ETF 合计不超过30%
|
||
|
||
Args:
|
||
orders: 原始订单列表
|
||
|
||
Returns:
|
||
风控后的订单列表
|
||
"""
|
||
filtered_orders = []
|
||
total_amount = 0
|
||
|
||
for order in orders:
|
||
if order['action'] == 'buy':
|
||
# 检查单仓位
|
||
if order['position_pct'] > self.max_position_pct * 100:
|
||
print(f"[PM] 风控拒绝: {order['code']} 仓位过大")
|
||
continue
|
||
|
||
# 检查总仓位
|
||
new_total = total_amount + order['amount']
|
||
if new_total / self.total_capital > 0.9:
|
||
print(f"[PM] 风控拒绝: {order['code']} 总仓位将超90%")
|
||
continue
|
||
|
||
total_amount = new_total
|
||
|
||
filtered_orders.append(order)
|
||
|
||
print(f"[PM] 风控完成,通过 {len(filtered_orders)}/{len(orders)} 条订单")
|
||
return filtered_orders
|
||
|
||
|
||
# ==================== 工具函数 ====================
|
||
|
||
def save_orders_to_file(orders: List[Dict], filename: str = "trade_orders.json") -> None:
|
||
"""保存订单到文件"""
|
||
output_path = Path("data") / filename
|
||
|
||
with open(output_path, 'w', encoding='utf-8') as f:
|
||
json.dump({
|
||
"timestamp": datetime.now().isoformat(),
|
||
"orders": orders
|
||
}, f, ensure_ascii=False, indent=2)
|
||
|
||
print(f"[PM] 订单已保存到 {output_path}")
|
||
|
||
|
||
# ==================== 测试代码 ====================
|
||
|
||
if __name__ == "__main__":
|
||
print("=" * 50)
|
||
print("Skill D: PM 基金经理测试")
|
||
print("=" * 50)
|
||
|
||
# 创建 WorldBook 和 PM
|
||
wb = WorldBook(data_dir="data")
|
||
pm = PortfolioManager(wb, total_capital=1000000)
|
||
|
||
# 模拟数据:添加测试叙事
|
||
from core.world_book import Narrative, create_narrative_id
|
||
|
||
narrative1 = Narrative(
|
||
id=create_narrative_id("AI算力"),
|
||
topic="AI算力",
|
||
related_etfs=["515980"],
|
||
lifecycle_stage="realization",
|
||
base_score=88.0,
|
||
current_weight=88.0
|
||
)
|
||
wb.add_narrative(narrative1)
|
||
|
||
# 模拟 Quant 输出的市场数据
|
||
mock_market_data = {
|
||
"515980": {
|
||
"flow_analysis": {"flow_score": 72.5},
|
||
"realtime": {"price": 1.25, "name": "AI算力ETF"}
|
||
},
|
||
"512480": {
|
||
"flow_analysis": {"flow_score": 45.0},
|
||
"realtime": {"price": 2.10, "name": "半导体ETF"}
|
||
}
|
||
}
|
||
|
||
# 1. 批量计算 Trust Index
|
||
print("\n1. 计算 Trust Index:")
|
||
trust_results = pm.batch_calculate_trust_index(mock_market_data)
|
||
|
||
for result in trust_results:
|
||
print(f" {result['code']}: Trust={result['trust_index']}, "
|
||
f"叙事={result['narrative_score']}, 资金={result['flow_score']}, "
|
||
f"判定={result['verdict']}")
|
||
|
||
# 2. 生成交易订单
|
||
print("\n2. 生成交易订单:")
|
||
orders = pm.generate_trade_orders(trust_results, mock_market_data)
|
||
|
||
for order in orders:
|
||
print(f" {order['action'].upper()} {order['code']}: "
|
||
f"{order['shares']}股 @ ¥{order['price']}, "
|
||
f"金额 ¥{order['amount']:,.0f}")
|
||
|
||
# 3. 风控检查
|
||
print("\n3. 风控检查:")
|
||
safe_orders = pm.apply_risk_control(orders)
|
||
|
||
# 4. 生成投资组合报告
|
||
print("\n4. 投资组合报告:")
|
||
report = pm.generate_portfolio_report(trust_results, safe_orders)
|
||
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||
|
||
# 5. 保存订单
|
||
if safe_orders:
|
||
save_orders_to_file(safe_orders)
|
||
|
||
print("\n✅ PM 模块测试完成")
|