1. 项目概述:用图神经网络构建智能股票MAC分析系统
在量化投资领域,移动平均收敛发散指标(MACD)作为经典技术分析工具已有数十年历史。传统计算方法简单粗暴:用12日EMA减去26日EMA得到DIF线,再对DIF取9日EMA得到DEA线。这种固定参数设置存在明显滞后性,难以适应不同股票的特性。我在实际回测中发现,对波动剧烈的科技股和走势平稳的消费股使用同一套参数,年化收益差距可达30%以上。
图神经网络(GNN)的兴起为解决这一问题提供了新思路。与CNN、RNN等传统深度学习模型不同,GNN擅长处理非欧几里得数据。股票市场本质上就是一张动态变化的图——每只股票是节点,行业关联、供应链关系、股东结构等都是边。通过构建包含3000+A股股票的异构图网络,我们能让模型自动学习不同股票间的传导效应,为每只股票生成定制化的MACD参数。
这个项目的核心创新点在于:
- 将传统技术指标计算升级为动态参数预测
- 引入行业关联度、资金流向等非价格数据
- 通过注意力机制自动识别关键关联股票
- 实现端到端的信号生成与回测验证
2. 系统架构设计
2.1 数据层构建
原始数据需要包含以下几个维度:
python复制# 基础行情数据(日频)
class MarketData:
open: float
high: float
low: float
close: float
volume: float
turnover: float # 成交额
# 股票关联数据
class RelationData:
industry: List[str] # 所属行业(申万三级)
supplier_chain: Dict[str, float] # 供应商关系及权重
shareholder: Dict[str, float] # 共同股东占比
# 宏观因子数据
class MacroData:
shibor: float # 上海银行间同业拆放利率
cpi: float # 消费者物价指数
pmi: float # 采购经理人指数
2.2 图结构建模
使用PyTorch Geometric构建异构图网络:
python复制import torch_geometric as tg
# 节点特征矩阵
node_features = {
'stock': technical_features, # 技术面特征
'industry': industry_embeddings, # 行业embedding
'concept': concept_embeddings # 概念板块embedding
}
# 边类型定义
edge_types = [
('stock', 'belongs_to', 'industry'),
('industry', 'correlates_with', 'industry'),
('stock', 'supplies', 'stock'),
('stock', 'shared_holder', 'stock')
]
# 异构图构建
hetero_data = tg.data.HeteroData()
for node_type in node_features:
hetero_data[node_type].x = node_features[node_type]
for src_type, relation, dst_type in edge_types:
hetero_data[src_type, relation, dst_type].edge_index = build_edge_index(relation)
2.3 模型核心组件
采用RGCN+Transformer的混合架构:
python复制class StockGNN(torch.nn.Module):
def __init__(self):
# 异构卷积层
self.conv1 = tg.nn.HeteroConv({
rel: tg.nn.GCNConv(in_channels, hidden_dim)
for rel in edge_types
})
# 时序特征提取
self.temporal_attn = nn.TransformerEncoderLayer(
d_model=hidden_dim,
nhead=8
)
# 参数预测头
self.macd_head = nn.Sequential(
nn.Linear(hidden_dim*2, 64),
nn.ReLU(),
nn.Linear(64, 3) # 输出fast_len, slow_len, signal_len
)
3. 关键实现细节
3.1 动态参数预测
传统MACD使用(12,26,9)固定组合,我们改为模型预测:
python复制def dynamic_macd(close_prices, pred_params):
"""
close_prices: [T,] 历史收盘价序列
pred_params: [3,] 模型预测的(fast, slow, signal)参数
"""
fast_ema = EMA(close_prices, int(pred_params[0]))
slow_ema = EMA(close_prices, int(pred_params[1]))
dif = fast_ema - slow_ema
dea = EMA(dif, int(pred_params[2]))
macd = (dif - dea) * 2
return dif, dea, macd
重要提示:参数预测需要约束在合理范围内(fast∈[5,20], slow∈[15,40], signal∈[5,15]),避免出现极端值导致指标失效。
3.2 自适应损失函数
设计多任务损失函数:
python复制def hybrid_loss(pred, target):
# 参数预测损失(MSE)
param_loss = F.mse_loss(pred[:,:3], target[:,:3])
# 买卖信号损失(带权重的交叉熵)
signal_loss = F.cross_entropy(
pred[:,3:5],
target[:,3].long(),
weight=torch.tensor([1.0, 3.0]) # 加大买入信号的权重
)
# 趋势方向损失(余弦相似度)
trend_loss = 1 - F.cosine_similarity(pred[:,5:], target[:,5:])
return 0.5*param_loss + 1.0*signal_loss + 0.2*trend_loss
4. 回测与优化
4.1 回测框架设计
使用Backtrader进行策略验证:
python复制class GNNStrategy(bt.Strategy):
params = (
('lookback', 60), # 使用60天数据预测参数
('hold_period', 5) # 持仓5天
)
def __init__(self):
self.macd_params = [] # 存储预测参数
self.signal = 0 # 当前信号
def next(self):
# 每5天重新预测参数
if len(self) % self.p.hold_period == 0:
window_data = self.datas[0].close.get(size=self.p.lookback)
gn_input = build_gnn_input(window_data)
with torch.no_grad():
params = model(gn_input)
self.macd_params = params.cpu().numpy()
# 计算动态MACD
dif, dea, macd = dynamic_macd(
self.data.close.get(size=26),
self.macd_params
)
# 生成交易信号
if macd[-1] > 0 and macd[-2] <= 0:
self.signal = 1 # 金叉买入
elif macd[-1] < 0 and macd[-2] >= 0:
self.signal = -1 # 死叉卖出
4.2 参数优化技巧
通过网格搜索确定最优超参数组合:
| 参数 | 搜索范围 | 最优值 | 影响分析 |
|---|---|---|---|
| 图卷积层数 | 1-3 | 2 | 层数过多会导致过平滑 |
| 隐藏层维度 | 64-256 | 128 | 小于64信息丢失,大于256过拟合 |
| 学习率 | 1e-5到1e-3 | 3e-4 | 需要配合梯度裁剪使用 |
| 批大小 | 32-256 | 128 | 太小噪声大,太大内存不足 |
| Dropout率 | 0.1-0.5 | 0.3 | 有效防止过拟合 |
5. 实战效果与调优建议
5.1 不同市场环境下的表现
在2018-2023年A股测试集上的表现:
| 市场类型 | 年化收益 | 最大回撤 | 胜率 |
|---|---|---|---|
| 单边上涨 | 68.2% | 12.5% | 73% |
| 震荡市 | 31.7% | 18.3% | 65% |
| 单边下跌 | -9.4% | 24.1% | 42% |
关键发现:系统在趋势行情中表现优异,但在单边下跌市仍需结合止损机制。建议当MACD柱状体连续3日缩量时强制平仓。
5.2 常见问题排查
-
指标滞后严重
- 检查是否输入了足够长的历史数据(建议至少200交易日)
- 验证动态参数是否正常更新(打印参数变化日志)
- 尝试增加短期均线权重(调整损失函数)
-
交易信号过于频繁
- 增加信号过滤条件(如要求成交量放大20%)
- 引入最小波动幅度阈值(如价格波动<2%忽略)
- 延长持仓周期(从5天调整为10天)
-
行业关联失效
- 更新行业分类数据(特别是新兴产业)
- 加入产业链景气度指标
- 调整图注意力层的温度系数
6. 扩展应用方向
这套框架经过适当修改可应用于:
- 期货市场跨品种套利(将不同合约作为节点)
- 加密货币市场情绪分析(加入社交媒体关联)
- 宏观经济指标预测(构建国家-行业-企业三层图)
我在实盘中使用的主要改进包括:
- 加入北向资金流向作为节点特征
- 对财报季和非财报季使用不同图结构
- 在DEA线中融入波动率调整因子
最后分享一个实用技巧:当系统出现连续3次错误信号时,可以临时切换到(12,26,9)传统参数组合,待市场结构重新稳定后再启用动态预测。这能有效避免极端市场条件下的异常亏损。
