grid-trading-system
ETF网格交易执行系统 - 整合自 grid-trading-monitor (608行),采用达尔文进化方法论。
核心定位
交易执行层:专注单一ETF的网格交易自动化执行,与监控预警层协同工作。
架构设计
六大核心模块
┌─────────────────────────────────────────────────────────┐
│ grid-trading-system │
├─────────────────────────────────────────────────────────┤
│ 1. ConfigManager 配置管理模块 │
│ 2. MarketDataFetcher 行情获取模块 │
│ 3. TradingCalendar 交易日历模块 │
│ 4. TriggerDetector 触发检测模块 │
│ 5. TradeExecutor 交易执行模块 │
│ 6. StateManager 状态管理模块 │
└─────────────────────────────────────────────────────────┘
模块一:ConfigManager(配置管理模块)
核心职责
- 加载网格参数配置(基准价、间距、格数、金额)
- 验证配置完整性(必填字段检查)
- 支持多品种配置管理
- 配置热更新(无需重启)
配置文件位置
~/.hermes/grid-trading/config.json
配置示例
{
"symbol": "513130",
"name": "恒生科技ETF",
"base_price": 0.625,
"grid_spacing": 0.025,
"grid_spacing_pct": 0.04,
"upper_grids": 6,
"lower_grids": 8,
"amount_per_grid": 715,
"initial_position": 3,
"total_capital": 10000,
"last_updated": "2026-05-02T10:00:00Z"
}
✅ 检查点(4个)
| 检查点 |
检查内容 |
通过标准 |
| CP-CONFIG-01 |
配置文件是否存在 |
os.path.exists(config_path) |
| CP-CONFIG-02 |
JSON格式是否合法 |
json.load() 无异常 |
| CP-CONFIG-03 |
必填字段是否完整 |
所有必填字段存在且非空 |
| CP-CONFIG-04 |
数值范围是否合理 |
基准价>0、间距>0、格数>0、金额>0 |
⚠️ 异常处理(4个)
| 异常场景 |
处理策略 |
日志级别 |
| 文件不存在 |
使用默认配置 + 警告 |
WARNING |
| JSON解析失败 |
拒绝运行 + 错误提示 |
ERROR |
| 字段缺失 |
使用默认值 + 警告 |
WARNING |
| 数值异常 |
拒绝运行 + 错误提示 |
ERROR |
🔲 边界条件(6个)
| 边界条件 |
触发场景 |
处理方式 |
| BC-CONFIG-01 |
基准价≤0 |
拒绝加载,记录错误 |
| BC-CONFIG-02 |
间距≤0或>基准价50% |
拒绝加载,记录错误 |
| BC-CONFIG-03 |
格数为0或负数 |
拒绝加载,记录错误 |
| BC-CONFIG-04 |
金额为0或负数 |
拒绝加载,记录错误 |
| BC-CONFIG-05 |
上方格数≠下方格数(不对称网格) |
允许,记录INFO日志 |
| BC-CONFIG-06 |
配置文件编码非UTF-8 |
自动转换或拒绝加载 |
模块二:MarketDataFetcher(行情获取模块)
核心职责
- 四重数据源冗余(新浪→腾讯→东财→AkShare)
- 自动切换失败数据源
- 响应时间监控
- 价格一致性验证
数据源优先级
| 优先级 |
数据源 |
API |
响应时间 |
状态 |
| P0 |
新浪财经 |
http://hq.sinajs.cn/list={symbol} |
~129ms |
✅ 正常 |
| P1 |
腾讯财经 |
http://qt.gtimg.cn/q={symbol} |
~121ms |
✅ 正常 |
| P2 |
东方财富 |
http://push2.eastmoney.com/api/qt/stock/get |
~271ms |
✅ 正常 |
| P3 |
AkShare |
ak.fund_etf_spot_em() |
~17秒 |
✅ 正常 |
✅ 检查点(4个)
| 检查点 |
检查内容 |
通过标准 |
| CP-MARKET-01 |
数据源响应状态 |
HTTP 200 |
| CP-MARKET-02 |
价格数值有效性 |
价格>0 |
| CP-MARKET-03 |
时间戳新鲜度 |
时间戳<5分钟 |
| CP-MARKET-04 |
多源价格差异 |
差异<0.1% |
⚠️ 异常处理(4个)
| 异常场景 |
处理策略 |
日志级别 |
| 数据源超时(>10秒) |
切换下一数据源 |
WARNING |
| 价格解析失败 |
尝试下一数据源 |
WARNING |
| 所有数据源失败 |
返回None + 错误日志 |
ERROR |
| 价格差异>0.1% |
记录警告 + 使用中位数 |
WARNING |
🔲 边界条件(6个)
| 边界条件 |
触发场景 |
处理方式 |
| BC-MARKET-01 |
网络完全断开 |
返回None,记录错误 |
| BC-MARKET-02 |
所有数据源同时失效 |
返回None,推送错误通知 |
| BC-MARKET-03 |
返回价格=0或负数 |
拒绝使用,切换数据源 |
| BC-MARKET-04 |
时间戳未来时间(时钟错误) |
拒绝使用,记录错误 |
| BC-MARKET-05 |
ETF停牌(价格不变) |
检测连续3次价格相同,标记停牌 |
| BC-MARKET-06 |
数据源限流(429错误) |
延迟重试或切换数据源 |
模块三:TradingCalendar(交易日历模块)
核心职责
- 判断是否交易日(排除周末+法定假期)
- 判断是否交易时段(09:30-11:30、13:00-15:00)
- 交易日历缓存(避免重复查询)
交易时段划分
| 北京时间 |
状态 |
监控建议 |
| < 09:25 |
盘前 |
不监控 |
| 09:25-09:30 |
集合竞价 |
可监控 |
| 09:30-11:30 |
早盘 |
监控中 |
| 11:30-13:00 |
午间休市 |
不监控 |
| 13:00-15:00 |
午盘 |
监控中 |
| ≥ 15:00 |
收盘 |
不监控 |
✅ 检查点(3个)
| 检查点 |
检查内容 |
通过标准 |
| CP-TIME-01 |
AkShare交易日历可用性 |
查询成功 |
| CP-TIME-02 |
日期格式正确性 |
YYYY-MM-DD格式 |
| CP-TIME-03 |
交易时段边界判断 |
时段划分正确 |
⚠️ 异常处理(3个)
| 异常场景 |
处理策略 |
日志级别 |
| AkShare查询失败 |
使用简单判断(排除周末) |
WARNING |
| 日期格式错误 |
使用当前日期 |
WARNING |
| 缓存过期 |
重新查询 |
INFO |
🔲 边界条件(6个)
| 边界条件 |
触发场景 |
处理方式 |
| BC-TIME-01 |
五一/国庆/春节长假 |
正确识别为非交易日 |
| BC-TIME-02 |
周末调休工作日(股市不开盘) |
使用交易日历,不依赖星期几 |
| BC-TIME-03 |
11:30-13:00午休时段 |
不触发交易 |
| BC-TIME-04 |
15:00后收盘时段 |
不触发交易 |
| BC-TIME-05 |
09:25集合竞价时段 |
可监控但不交易 |
| BC-TIME-06 |
数据源时区不一致 |
强制转换为北京时间 |
模块四:TriggerDetector(触发检测模块)
核心职责
- 计算网格价位(买入/卖出档位)
- 检测价格穿越网格线
- 避免重复触发(同一档位冷却期)
- 触发信号生成
网格计算公式
# 买入网格(下方)
buy_grid_price = base_price - grid_spacing × grid_level
# 卖出网格(上方)
sell_grid_price = base_price + grid_spacing × grid_level
触发逻辑
# 买入触发:价格跌破网格价
if grid["type"] == "BUY" and current_price <= grid["price"]:
return grid
# 卖出触发:价格突破网格价
if grid["type"] == "SELL" and current_price >= grid["price"]:
return grid
✅ 检查点(5个)
| 检查点 |
检查内容 |
通过标准 |
| CP-TRIGGER-01 |
价格是否在网格范围内 |
min_grid ≤ price ≤ max_grid |
| CP-TRIGGER-02 |
是否穿越网格线 |
价格穿越网格价 |
| CP-TRIGGER-03 |
档位冷却期检查 |
距上次触发>冷却时间 |
| CP-TRIGGER-04 |
持仓上限检查(卖出时) |
持仓>0 |
| CP-TRIGGER-05 |
资金下限检查(买入时) |
可用资金≥单格金额 |
⚠️ 异常处理(4个)
| 异常场景 |
处理策略 |
日志级别 |
| 价格超出网格范围 |
记录日志 + 不触发 |
INFO |
| 冷却期内重复触发 |
忽略 |
DEBUG |
| 持仓不足卖出 |
记录错误 + 不执行 |
ERROR |
| 资金不足买入 |
记录错误 + 不执行 |
ERROR |
🔲 边界条件(6个)
| 边界条件 |
触发场景 |
处理方式 |
| BC-TRIGGER-01 |
价格触及网格边界(最高/最低档) |
触发交易 + 标记边界 |
| BC-TRIGGER-02 |
连续穿越多档(快速涨跌) |
只触发最接近的一档 |
| BC-TRIGGER-03 |
价格刚好等于网格价(边界条件) |
触发交易(允许边界触发) |
| BC-TRIGGER-04 |
持仓=0时触发卖出 |
拒绝执行,记录错误 |
| BC-TRIGGER-05 |
资金=0时触发买入 |
拒绝执行,记录错误 |
| BC-TRIGGER-06 |
网格间距过小(频繁触发) |
警告间距过小,建议调整 |
模块五:TradeExecutor(交易执行模块)
核心职责
- 生成交易指令(模拟交易)
- 更新持仓状态
- 记录交易历史
- 计算盈亏
交易执行流程
触发信号 → 生成交易指令 → 更新持仓 → 记录历史 → 计算盈亏
✅ 检查点(3个)
| 检查点 |
检查内容 |
通过标准 |
| CP-EXEC-01 |
交易指令完整性 |
所有字段存在且合法 |
| CP-EXEC-02 |
状态更新一致性 |
状态文件正确更新 |
| CP-EXEC-03 |
历史记录追加成功 |
历史文件正确追加 |
⚠️ 异常处理(3个)
| 异常场景 |
处理策略 |
日志级别 |
| 状态文件写入失败 |
重试3次 |
ERROR |
| 历史文件损坏 |
备份 + 重建 |
ERROR |
| 计算错误 |
回滚状态 |
ERROR |
🔲 边界条件(5个)
| 边界条件 |
触发场景 |
处理方式 |
| BC-EXEC-01 |
首笔交易(初始建仓) |
正常执行,初始化持仓 |
| BC-EXEC-02 |
最后一档平仓 |
正常执行,标记空仓 |
| BC-EXEC-03 |
满仓状态 |
拒绝买入,允许卖出 |
| BC-EXEC-04 |
空仓状态 |
拒绝卖出,允许买入 |
| BC-EXEC-05 |
单日多次交易 |
正常执行,记录所有交易 |
模块六:StateManager(状态管理模块)
核心职责
- 持久化当前持仓、历史交易
- 状态恢复(重启后继续)
- 状态备份(防止丢失)
状态文件位置
~/.hermes/grid-trading/status.json
状态文件结构
{
"symbol": "513130",
"current_position": 3,
"cash": 7850,
"grid_states": [
{
"level": 1,
"type": "SELL",
"price": 0.65,
"status": "WAITING"
}
],
"trade_history": [
{
"timestamp": "2026-05-02T10:15:00Z",
"type": "BUY",
"level": 2,
"price": 0.575,
"amount": 715
}
],
"last_updated": "2026-05-02T10:15:00Z"
}
✅ 检查点(4个)
| 检查点 |
检查内容 |
通过标准 |
| CP-STATE-01 |
状态文件完整性 |
所有必填字段存在 |
| CP-STATE-02 |
JSON格式正确性 |
json.load() 无异常 |
| CP-STATE-03 |
时间戳更新 |
更新时间戳为当前时间 |
| CP-STATE-04 |
备份文件存在 |
status.json.bak 存在 |
⚠️ 异常处理(3个)
| 异常场景 |
处理策略 |
日志级别 |
| 文件损坏 |
从备份恢复 |
ERROR |
| 备份也损坏 |
从头初始化 |
ERROR |
| 并发写入冲突 |
文件锁 |
WARNING |
🔲 边界条件(4个)
| 边界条件 |
触发场景 |
处理方式 |
| BC-STATE-01 |
首次运行(无状态文件) |
初始化状态文件 |
| BC-STATE-02 |
状态文件过大(>10MB) |
归档历史 + 重建 |
| BC-STATE-03 |
历史记录超长(>1000条) |
归档历史 + 重建 |
| BC-STATE-04 |
异常中断后的状态恢复 |
从备份恢复或重建 |
运行模式
1. 查看当前状态
python ~/.hermes/grid-trading/grid_monitor.py --status
2. 单次检查(Cronjob模式)
python ~/.hermes/grid-trading/grid_monitor.py --check
输出 JSON 格式,便于 Cronjob 解析:
{
"symbol": "513130",
"current_price": 0.612,
"triggered": {
"type": "BUY",
"level": 2,
"price": 0.575
},
"position": 3,
"cash": 7850
}
3. 持续监控(实时盯盘)
python ~/.hermes/grid-trading/grid_monitor.py --monitor --interval 30
Cronjob 集成
配置示例
name: 网格交易监控-恒生科技ETF
schedule: "*/5 9-11,13-15 * * 1-5" # 交易时间每5分钟
prompt: |
执行网格交易检查:
1. 运行 python ~/.hermes/grid-trading/grid_monitor.py --check
2. 如果有 triggered 字段,推送微信提醒
enabled_toolsets: ["terminal", "send_message"]
微信推送格式
📊 恒生科技ETF 网格交易提醒
📉/📈 [买入/卖出] 信号触发
💹 触发价格: X.XXX 元
📊 成交档位: Lv+X
💰 成交金额: 5,000 元
💼 当前持仓: X 格
⏰ 时间: YYYY-MM-DD HH:MM:SS
与 market-intelligence-system 协作
接收数据
| 数据类型 |
来源模块 |
用途 |
| 市场预警信号 |
MarketMonitor |
异常波动时暂停交易 |
| 交易日历更新 |
TimeAnchor |
假期调整 |
| 数据源健康度 |
DataSourceManager |
切换数据源 |
发送数据
| 数据类型 |
目标模块 |
用途 |
| 交易信号 |
BriefGenerator |
简报展示 |
| 持仓状态 |
BriefGenerator |
简报展示 |
| 异常事件 |
PushNotifier |
错误通知 |
风险提示
| 风险类型 |
说明 |
应对策略 |
| 单边上涨 |
踏空(卖飞),错过后续涨幅 |
预留部分底仓 |
| 单边下跌 |
持续补仓,可能满仓套牢 |
设置最大持仓上限 |
| 震荡市 |
最佳场景,持续吃差价 |
正常运行 |
| 仓位控制 |
需预留资金应对下跌 |
设置资金下限 |
完整度统计
| 检查项 |
目标 |
实际 |
达标 |
| 检查点 |
15+ |
23 |
✅ |
| 异常处理 |
10+ |
21 |
✅ |
| 边界条件 |
15+ |
33 |
✅ |
| 完整度 |
100% |
100% |
✅ |
整合成果
- 源 Skill: grid-trading-monitor (608行)
- 整合后: grid-trading-system (本文件)
- 核心改进:
- 模块化架构(6大模块)
- 完整的检查点机制(23个)
- 系统化异常处理(21处)
- 边界条件全覆盖(33项)
- 与监控预警层协同工作
参考
- 原始 Skill:
~/.hermes/skills/finance/grid-trading-monitor/SKILL.md
- 监控脚本:
~/.hermes/grid-trading/grid_monitor.py
- 配置文件:
~/.hermes/grid-trading/config.json
- 状态文件:
~/.hermes/grid-trading/status.json
⚠️ Known Gotchas
交易执行问题
交易失败: 余额不足
# 检查可用资金
available = broker.get_available_cash()
if available < required:
print("资金不足,无法执行交易")
滑点过大: 市价单偏离预期
# 使用限价单
order = broker.place_limit_order(
price=current_price * 0.99, # 1% 滑点容忍
quantity=100
)
网格配置问题
监控问题
推送失败: 通知渠道失效
# 检查企业微信 Webhook
curl -X POST $WECOM_WEBHOOK_URL -d '{"msgtype": "text", "text": {"content": "test"}}'
数据源中断: 行情获取失败
# 多数据源切换
try:
price = get_price_from_sina(code)
except:
price = get_price_from_eastmoney(code)