记录一次核对 Hyperliquid Testnet 自动交易亏损原因(2026-07-22)
记录一次核对 Hyperliquid Testnet 自动交易亏损原因(2026-07-22)
执行层修干净了,但亏钱的主因在信号层,那部分从没动过。
这一天起因很简单:有人问 C:\Users\Administrator\Documents\craw_app1_wz 项目第18页 Hyperliquid Testnet 自动交易,开多和开空是不是都会开仓。我顺着这个问题一路读代码,最后发现这个项目的问题不在开仓方向上,而在执行层和信号层,而且执行层的坑一个个都踩得很实。
一、先确认“开多开空都会开仓”
用户问的第18页对应 views/18_hl_testnet.py,核心交易逻辑在 strategy_engine/hl_trader.py。我全项目搜了 Hyperliquid、Testnet、开多、开空,然后读开仓逻辑和信号生成。
证据链逐步收拢:_score_symbol(388–395 行)里 score >= COMBO_THRESHOLD(=1) 产出 BUY(开多),score <= -1 产出 SELL_SHORT(开空)。信号列表 scored 在 984–989 行遍历所有币种,把 SELL_SHORT 也一并收进去,没有过滤。主循环 1067 行 if action not in ("BUY", "SELL_SHORT"): continue,做空和做多并列接受;1073 行 side = "LONG" if action=="BUY" else "SHORT";1109 行对 SELL_SHORT 直接调用 _open_position。
第 393 行那句注释 # ... 先不用 是过期注释。 双向开仓逻辑已经真实生效,不是“先不用”。
二、为什么亏:三个执行层致命伤
接着用户问:为什么亏损?我顺着信号→开仓→平仓整条链路读。
先看一眼信号机制:_score_symbol 对 BTC/ETH/SOL 每 30s 跑一轮 combo 评分——RSI(超卖+3/超买−3)、MACD 金叉死叉(+2.5/−2.5)、EMA9/21 交叉(±1.5)、近 10 tick 动量(±1)。score >= 1 开多,score <= −1 开空。平仓判定 _check_position_close(616–671 行)按 SL 3% / TP 8% / 硬止损 10% / 移动止盈 4% / 信号反转 ≤−5 决定。
逻辑看起来完整,但问题出在执行层——它亏不是因为判断错方向,而是因为亏了之后根本平不掉,止损单形同虚设,还把真实亏损记成保本。
P0-1 平仓单按开仓价挂。 _close_position_hl(496–511 行)把平仓限价挂在 entry ± 0.05%(开仓价),而不是当前市价。LONG 跌到 −3% 触发止损时,市价已经是 entry×0.97,但卖单挂在 entry×1.0005 附近,逆势单永远不成交。
P0-2 PnL 记账保本假象。 resting 单用 limit_px=entry 记 CLOSED + PnL=0——亏着的仓被记成保本离场,账户数据完全失真。
P0-3 孤儿单。 平仓单挂出去不进监控,挂了就没人管,超时不撤、偏离不撤、成交不 finalize。
三个问题叠加起来,就是实仓还在亏、账上已经平掉、挂单永远不会成交的死循环。
三、逐行核对修复结果
用户贴了一份修复总结:P0-1 改成 current_price × (1±offset)、P0-2 resting 单不更新 hl_positions、P0-3 平仓单写入 hl_pending_orders 被 _manage_pending_orders 监控。我不信任粘贴的总结,直接读当前文件核对。
P0-1 确实改对了。 _close_position_hl 挂单价用 current_price(496–519 行),方向也正确:多单止损挂市价下、空单挂市价上。
P0-2 也改对了。 resting 分支(604–609 行)不动 hl_positions,持仓保持 OPEN,等真实成交再算 PnL。
P0-3 框架对了。 _finalize_pending_order 的 close 分支(813–861 行)用 info.user_fills 的 fill_price 结算:重新查 OPEN 持仓、按真实成交价回填 close_price/pnl_usd、回补 hl_account(current_usdc += size_usd + pnl、胜/负计数)、写 hl_trades、commit。_mark_cancelled 把行置 CANCELLED,不会被 PENDING 查询再次 finalize。
但核对中冒出两个新隐患:持仓仍 OPEN 时会每 tick 重复挂平仓单;fill_price=None 时可能被误判成已成交而伪平仓。
四、两个残留隐患补上
用户回来说这两处也修了,我逐行确认。
重复挂平仓单的守卫在 1036–1045 行。 _check_position_close 返回 reason 后,先查 hl_pending_orders WHERE symbol=? AND status='PENDING' AND order_type='close',有就 continue 跳过。
fill_price=None 的短路在 813–820 行。 close 分支里 if fill_price is None: ... return,不标 CLOSED,挂单保持 PENDING,下个 tick _check_position_close 仍会返回 reason 而重挂。
到这一步,执行层基本干净了。trader 已重启,PID 66632。
五、亏损的真正主因在信号层
执行层修好了,亏损不会自动消失。我重读信号评分与风控参数,找到一个真实 bug:
SHORT 移动止盈失效。 _check_position_close(662–667 行)里 highest_price 永远记 max 价,但 SHORT 的盈利峰值在 min 价。对 SHORT 算 peak_pct=(entry-highest)/entry 会趋近于负——peak_pct>=0.03 这个条件永不成立。空单永远拿不到移动止盈,只能吃固定 SL 3% / TP 8% / 硬止损 10% / 信号反转 ≤−5 那几条。
更深一层,评分用的 RSI、MACD、EMA 都是滞后指标,在行情走完才翻多翻空,BUY 往往开在反弹顶、SELL 开在下跌底,紧接着被 3% 止损打掉。这是信号层一直没动的部分。
六、遗留
信号层的优化刚开了头:SHORT 单独 track lowest_price 算移动止盈这件事还没动手,只列在清单里;COMBO_THRESHOLD 那个阈值组合的调参也还没做。下一步是修 SHORT 移动止盈的 bug,再跑一段 testnet 看空单回撤曲线有没有真正改善。