edit | blame | history | raw

GateGridTradeService 代码审计报告

审计日期:2026-08-03
文件:src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
审查范围:全部 2147 行,含交叉验证 GridElement / TraderParam / GateConfig / GateTradeExecutor


一、总体评估

代码结构清晰,多空双方向网格交易的主流程(K线驱动 → 条件单挂单 → 成交回调 → 止损追单 → 级联查表)完整且自洽。发现 4 个 Bug,其中 1 个 Critical 直接影响止损级联计数准确性。


二、BUG 详细分析

Bug #1 🔴 Critical:sameGrid 阻止同网格多个止损单触发

位置

  • handleLongStopLossTriggered — 行 1283 ~ 1287
  • handleShortStopLossTriggered — 行 1494 ~ 1498

问题代码

boolean sameGrid = (gridId == lastLongStopLossGridId);
if (sameGrid) {
    log.info("[Gate] 多仓止损触发 同网格, 忽略");
    return;
}
lastLongStopLossGridId = gridId;
accumulatedLongLossCount++;

根因

extendLongStopLoss / extendShortStopLoss 中当 gridCount == 1 时,所有止损单挂在**同一个 gridId**:

// extendLongStopLoss,行 1759-1761
if (gridCount == 1) {
    orders[0] = totalOrders;   // 多张止损单 → 同一个 gridId
}
// 行 1789:gridId - 2 - 0 = gridId - 2,同一个位置

baseQty=2, qty=2, filledQty=4 为例:
- gridCount = 2/2 = 1totalOrders = 4/2 = 2
- 2 单止损全部挂在 gridId-2
- 第 1 单触发 → lastLongStopLossGridId = gridId,count = 1
- 第 2 单触发 → sameGrid = true被丢弃,count 少 1

为什么可以用 removeXxxStopLossOrderId 替代

handleLongStopLossTriggered 第一步就是 gridElement.removeLongStopLossOrderId(orderId),从列表中删除该 orderId。refreshIndices() 会重建全局索引,确保同一 orderId 不会二次匹配。

重复推送的防线:若 WS 对**同一订单**推送两次,第二次 findByLongStopLossOrderId 找不到(已从索引清除),直接落到后续匹配逻辑,无副作用。

sameGrid 是**按 gridId 去重**,粒度太粗,误杀了**不同 orderId 但同一个 gridId** 的正常多单触发。

修复方案

private void handleLongStopLossTriggered(GridElement gridElement, String orderId) {
    // 用 remove 返回值判断是否重复推送,代替 sameGrid
    if (!gridElement.removeLongStopLossOrderId(orderId)) {
        log.info("[Gate] 多仓止损重复推送 orderId:{}, 忽略", orderId);
        return;
    }
    int gridId = gridElement.getId();
    lastLongStopLossGridId = gridId;
    accumulatedLongLossCount++;
    log.info("[Gate] 多仓止损触发 gridId:{}, 止损次数:{}", gridId, accumulatedLongLossCount);

    int newEntryGridId = gridId + 1;
    // ... 后续追单逻辑不变 ...
}

handleShortStopLossTriggered 同理(gridId - 1)。

影响范围

  • accumulatedLongLossCount / accumulatedShortLossCount 少计数
  • STOP_LOSS_RULES 级联规则表推进不到应有行数
  • 对手止盈/己方止盈缺失,策略漏掉对冲机会
  • 网格数越少 (baseQty/qty 小),问题越严重(所有止损同格)

Bug #2 🟡 Low:三处死代码

方法 行号 功能 调用情况
checkLastTakeProfitAndRestart() 1549 止盈全部清空后检查跨度重启 全项目检索 → 0 处调用
placeOpponentTakeProfit() 2125 旧版对手止盈挂单逻辑 全项目检索 → 0 处调用
cancelAllInitialTakeProfits() 1719 取消初始化阶段止盈单 全项目检索 → 0 处调用

checkLastTakeProfitAndRestart 本身逻辑完整(跨度检查 → 清理条件单 → 平仓 → 重启),可能是某个版本的入口未被接上。

影响:仅代码整洁度,无功能影响。若跨度重启功能需要保留,需在合适回调点接入(如止盈触发后)。


Bug #3 🟡 Low-Medium:整数截断使小 maxPos 下多行规则不可区分

位置:行 1389、1391(checkLongStopShortProfit)及镜像 1445、1447(checkShortStopLongProfit

int fullTpPctNum   = maxPos * (100 - fullTpPct) / 100;   // Java int 除法截断
int thresholdPosNum = maxPos * (100 - thresholdPct) / 100;

maxPos = 4 为例

STOP_LOSS_RULES 行 fullTpPct fullTpPctNum 触发条件 oppPos > N
第 3 行 (times=3) 30 4*70/100 = 2 oppPos ≥ 3
第 6 行 (times=6) 40 4*60/100 = 2 oppPos ≥ 3 ⚠️ 与第3行相同
第 9 行 (times=9) 50 4*50/100 = 2 oppPos ≥ 3 ⚠️ 与第3/6行相同

前三行规则对于对手止盈**完全不可区分**,实际触发门槛都是 maxPos 的 75%。

同样 thresholdPosNum 也有精度损失:

thresholdPct thresholdPosNum (maxPos=4) 实际百分比
40 4*60/100 = 2 excess = selfPos - 2
50 4*50/100 = 2 同上 ⚠️
60 4*40/100 = 1 excess = selfPos - 1

影响

  • maxPos < 10:规则表精度差,多行等价
  • maxPos ≥ 20:每 5% 差 1 张,基本可区分
  • 如需保证小 maxPos 精度,可改用:
    java int fullTpPctNum = (int) Math.ceil(maxPos * (100.0 - fullTpPct) / 100.0);

Bug #4 🟢 Very Minor:perOrderQty 整数截断(已有兜底)

位置:行 1410 ~ 1413(checkLongStopShortProfit)及镜像 1466 ~ 1469

int perOrderQty = maxPos * DEFAULT_STOP_LOSS_PERCENT / 100;   // DEFAULT_STOP_LOSS_PERCENT = 5
if (perOrderQty <= 0) {
    perOrderQty = 1;    // 兜底
}

maxPos = 44 * 5 / 100 = 0 → 兜底为 1。

影响:几乎无。perOrderQty 从设计的 "maxPos 的 5%" 降级为固定 1 张,但兜底保证了逻辑不崩溃。maxPos ≥ 20 后恢复正常。


三、审查确认正确的模块 ✅

以下模块经逐行审查,确认无 Bug:

模块 验证点 结论
checkLongStopShortProfit 对手(空)盈止位 gridId,己方(多)盈止位 0+2,4,6... ✅ 正确
checkShortStopLongProfit 对手(多)盈止位 gridId,己方(空)盈止位 0-2,-4,-6... ✅ 正确
cancelAllXxxTakeProfitsAndStopLosses 先清后建:取消 → extend → checkXxxProfit 重挂 ✅ 无遗漏
onAutoOrder 入口匹配 止损单 → 入口单 的顺序匹配 ✅ 正确
extendStopLossInProgress 防重入 同格多成交 → pendingReExtend → 重挂 ✅ 正确
processShortGrid/processLongGrid 动量方向、队列匹配、下行守卫 ✅ 正确
checkProfitAndReset 估算手续费、轮次控制 ✅ 正确
onKline 单边归零触发 longActive==false 时触发 processShortGrid ✅ 正确

四、优先级总结

优先级 Bug 修复难度 建议
🔴 P0 #1 sameGrid 丢止损 低(删 3 行加 4 行) 立即修复
🟡 P2 #2 死代码 清理或恢复接入
🟡 P3 #3 小 maxPos 精度 中(需改计算逻辑) maxPos ≥ 10 可暂缓
🟢 P4 #4 perOrderQty 截断 已有兜底,不需处理