fix: blacklistCoin 计算退出PnL字段, 避免DB写入零值
blacklistCoin 之前只设 Status=close_failed 就跑了, 没给 ExitDiffPct/ExitNetPnl/ExitLongPnl/ExitTotalFees 等字段赋值。 retryClose 写 TradeRecord 时全为零, 导致DB出现 PnL=0 的假记录。 修复: blacklistCoin 现在接收当前价格, 用 checkExit 相同方式 计算 PnL/价差收敛标签并存入 pos 字段。
This commit is contained in:
@@ -256,7 +256,7 @@ func (t *Trader) Tick(store *PriceStore, notifier *Notifier) {
|
||||
// Blacklist: if position still open after 10 minutes without converging,
|
||||
// the spread is likely stale data. Add coin to blacklist and force close.
|
||||
if pos.Status == "open" && elapsed > 10*time.Minute {
|
||||
t.blacklistCoin(pos, notifier)
|
||||
t.blacklistCoin(pos, bgP, hlP, diffPct, notifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -991,17 +991,52 @@ func (t *Trader) restoreOpenPositions() {
|
||||
}
|
||||
|
||||
// blacklistCoin adds a coin to the blacklist and force-closes its position.
|
||||
func (t *Trader) blacklistCoin(pos *ArbPosition, notifier *Notifier) {
|
||||
// Calculates exit PnL fields so retryClose writes correct data to DB.
|
||||
func (t *Trader) blacklistCoin(pos *ArbPosition, bgP, hlP, diffPct float64, notifier *Notifier) {
|
||||
// Compute exit PnL the same way checkExit does
|
||||
var longCurrent, shortCurrent float64
|
||||
if pos.LongLeg.Exchange == ExBitget {
|
||||
longCurrent, shortCurrent = bgP, hlP
|
||||
} else {
|
||||
longCurrent, shortCurrent = hlP, bgP
|
||||
}
|
||||
longAvg := weightedAvgPrice(pos.LongEntryPrices, t.cfg.TradeAmountUSD)
|
||||
shortAvg := weightedAvgPrice(pos.ShortEntryPrices, t.cfg.TradeAmountUSD)
|
||||
longPnl := (longCurrent - longAvg) / longAvg * 100
|
||||
shortPnl := (shortAvg - shortCurrent) / shortAvg * 100
|
||||
totalFees := 2 * (takerFees[ExBitget] + takerFees[ExHyperLiquid])
|
||||
netPnl := longPnl + shortPnl - totalFees
|
||||
|
||||
pos.ExitDiffPct = diffPct
|
||||
pos.ExitNetPnl = netPnl
|
||||
pos.ExitLongPnl = longPnl
|
||||
pos.ExitShortPnl = shortPnl
|
||||
pos.ExitTotalFees = totalFees
|
||||
pos.LongLeg.ExitPrice = longCurrent
|
||||
pos.ShortLeg.ExitPrice = shortCurrent
|
||||
pos.ExitReasonText = "黑名单强平"
|
||||
|
||||
// Convergence label
|
||||
convergedPct := (pos.EntrySpread - diffPct) / pos.EntrySpread * 100
|
||||
if convergedPct < -10 {
|
||||
pos.ExitConvergence = "价差发散"
|
||||
} else if convergedPct < 10 {
|
||||
pos.ExitConvergence = "价差持平"
|
||||
} else {
|
||||
pos.ExitConvergence = "价差收敛"
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
t.blacklist[pos.Coin] = time.Now()
|
||||
t.mu.Unlock()
|
||||
|
||||
log.Printf("[Trader] ⛔ %s: Blacklisted — position open %.0fm without convergence", pos.Coin, time.Since(pos.StartedAt).Minutes())
|
||||
log.Printf("[Trader] ⛔ %s: Blacklisted — position open %.0fm without convergence | spread=%.4f%% netPnl=%.4f%%", pos.Coin, time.Since(pos.StartedAt).Minutes(), diffPct, netPnl)
|
||||
notifier.Send(fmt.Sprintf(
|
||||
"<b>[黑名单]</b> %s/USDT\n"+
|
||||
" 开仓 %.0f 分钟未收敛\n"+
|
||||
" 价差: %.4f%% 净利: %.4f%%\n"+
|
||||
" 已加入黑名单观察\n",
|
||||
pos.Coin, time.Since(pos.StartedAt).Minutes()))
|
||||
pos.Coin, time.Since(pos.StartedAt).Minutes(), diffPct, netPnl))
|
||||
|
||||
// Force-close the position immediately
|
||||
pos.Status = "close_failed" // triggers retryClose on next tick
|
||||
|
||||
Reference in New Issue
Block a user