mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-07 11:17:56 +08:00
fix: use symbol_side as peakPnLCache key to support dual-side positions (#657)
Fixes #652 Previously, peakPnLCache used only 'symbol' as the key, causing LONG and SHORT positions of the same symbol to share the same peak P&L value. This led to incorrect drawdown calculations and emergency close triggers. Changes: - checkPositionDrawdown: use posKey (symbol_side) for cache access - UpdatePeakPnL: add side parameter and use posKey internally - ClearPeakPnLCache: add side parameter and use posKey internally Example fix: - Before: peakPnLCache["BTCUSDT"] shared by both LONG and SHORT - After: peakPnLCache["BTCUSDT_long"] and peakPnLCache["BTCUSDT_short"] Impact: - Fixes incorrect drawdown monitoring for dual positions - Prevents false emergency close triggers on profitable positions
This commit is contained in:
+17
-12
@@ -1553,18 +1553,21 @@ func (at *AutoTrader) checkPositionDrawdown() {
|
||||
currentPnLPct = ((entryPrice - markPrice) / entryPrice) * float64(leverage) * 100
|
||||
}
|
||||
|
||||
// 构造持仓唯一标识(区分多空)
|
||||
posKey := symbol + "_" + side
|
||||
|
||||
// 获取该持仓的历史最高收益
|
||||
at.peakPnLCacheMutex.RLock()
|
||||
peakPnLPct, exists := at.peakPnLCache[symbol]
|
||||
peakPnLPct, exists := at.peakPnLCache[posKey]
|
||||
at.peakPnLCacheMutex.RUnlock()
|
||||
|
||||
if !exists {
|
||||
// 如果没有历史最高记录,使用当前盈亏作为初始值
|
||||
peakPnLPct = currentPnLPct
|
||||
at.UpdatePeakPnL(symbol, currentPnLPct)
|
||||
at.UpdatePeakPnL(symbol, side, currentPnLPct)
|
||||
} else {
|
||||
// 更新峰值缓存
|
||||
at.UpdatePeakPnL(symbol, currentPnLPct)
|
||||
at.UpdatePeakPnL(symbol, side, currentPnLPct)
|
||||
}
|
||||
|
||||
// 计算回撤(从最高点下跌的幅度)
|
||||
@@ -1583,8 +1586,8 @@ func (at *AutoTrader) checkPositionDrawdown() {
|
||||
log.Printf("❌ 回撤平仓失败 (%s %s): %v", symbol, side, err)
|
||||
} else {
|
||||
log.Printf("✅ 回撤平仓成功: %s %s", symbol, side)
|
||||
// 平仓后清理该symbol的缓存
|
||||
at.ClearPeakPnLCache(symbol)
|
||||
// 平仓后清理该持仓的缓存
|
||||
at.ClearPeakPnLCache(symbol, side)
|
||||
}
|
||||
} else if currentPnLPct > 5.0 {
|
||||
// 记录接近平仓条件的情况(用于调试)
|
||||
@@ -1630,25 +1633,27 @@ func (at *AutoTrader) GetPeakPnLCache() map[string]float64 {
|
||||
}
|
||||
|
||||
// UpdatePeakPnL 更新最高收益缓存
|
||||
func (at *AutoTrader) UpdatePeakPnL(symbol string, currentPnLPct float64) {
|
||||
func (at *AutoTrader) UpdatePeakPnL(symbol, side string, currentPnLPct float64) {
|
||||
at.peakPnLCacheMutex.Lock()
|
||||
defer at.peakPnLCacheMutex.Unlock()
|
||||
|
||||
if peak, exists := at.peakPnLCache[symbol]; exists {
|
||||
posKey := symbol + "_" + side
|
||||
if peak, exists := at.peakPnLCache[posKey]; exists {
|
||||
// 更新峰值(如果是多头,取较大值;如果是空头,currentPnLPct为负,也要比较)
|
||||
if currentPnLPct > peak {
|
||||
at.peakPnLCache[symbol] = currentPnLPct
|
||||
at.peakPnLCache[posKey] = currentPnLPct
|
||||
}
|
||||
} else {
|
||||
// 首次记录
|
||||
at.peakPnLCache[symbol] = currentPnLPct
|
||||
at.peakPnLCache[posKey] = currentPnLPct
|
||||
}
|
||||
}
|
||||
|
||||
// ClearPeakPnLCache 清除指定symbol的峰值缓存
|
||||
func (at *AutoTrader) ClearPeakPnLCache(symbol string) {
|
||||
// ClearPeakPnLCache 清除指定持仓的峰值缓存
|
||||
func (at *AutoTrader) ClearPeakPnLCache(symbol, side string) {
|
||||
at.peakPnLCacheMutex.Lock()
|
||||
defer at.peakPnLCacheMutex.Unlock()
|
||||
|
||||
delete(at.peakPnLCache, symbol)
|
||||
posKey := symbol + "_" + side
|
||||
delete(at.peakPnLCache, posKey)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user