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,
|
// Blacklist: if position still open after 10 minutes without converging,
|
||||||
// the spread is likely stale data. Add coin to blacklist and force close.
|
// the spread is likely stale data. Add coin to blacklist and force close.
|
||||||
if pos.Status == "open" && elapsed > 10*time.Minute {
|
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.
|
// 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.mu.Lock()
|
||||||
t.blacklist[pos.Coin] = time.Now()
|
t.blacklist[pos.Coin] = time.Now()
|
||||||
t.mu.Unlock()
|
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(
|
notifier.Send(fmt.Sprintf(
|
||||||
"<b>[黑名单]</b> %s/USDT\n"+
|
"<b>[黑名单]</b> %s/USDT\n"+
|
||||||
" 开仓 %.0f 分钟未收敛\n"+
|
" 开仓 %.0f 分钟未收敛\n"+
|
||||||
|
" 价差: %.4f%% 净利: %.4f%%\n"+
|
||||||
" 已加入黑名单观察\n",
|
" 已加入黑名单观察\n",
|
||||||
pos.Coin, time.Since(pos.StartedAt).Minutes()))
|
pos.Coin, time.Since(pos.StartedAt).Minutes(), diffPct, netPnl))
|
||||||
|
|
||||||
// Force-close the position immediately
|
// Force-close the position immediately
|
||||||
pos.Status = "close_failed" // triggers retryClose on next tick
|
pos.Status = "close_failed" // triggers retryClose on next tick
|
||||||
|
|||||||
Reference in New Issue
Block a user