mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-14 00:07:01 +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:
@@ -1553,18 +1553,21 @@ func (at *AutoTrader) checkPositionDrawdown() {
|
|||||||
currentPnLPct = ((entryPrice - markPrice) / entryPrice) * float64(leverage) * 100
|
currentPnLPct = ((entryPrice - markPrice) / entryPrice) * float64(leverage) * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 构造持仓唯一标识(区分多空)
|
||||||
|
posKey := symbol + "_" + side
|
||||||
|
|
||||||
// 获取该持仓的历史最高收益
|
// 获取该持仓的历史最高收益
|
||||||
at.peakPnLCacheMutex.RLock()
|
at.peakPnLCacheMutex.RLock()
|
||||||
peakPnLPct, exists := at.peakPnLCache[symbol]
|
peakPnLPct, exists := at.peakPnLCache[posKey]
|
||||||
at.peakPnLCacheMutex.RUnlock()
|
at.peakPnLCacheMutex.RUnlock()
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
// 如果没有历史最高记录,使用当前盈亏作为初始值
|
// 如果没有历史最高记录,使用当前盈亏作为初始值
|
||||||
peakPnLPct = currentPnLPct
|
peakPnLPct = currentPnLPct
|
||||||
at.UpdatePeakPnL(symbol, currentPnLPct)
|
at.UpdatePeakPnL(symbol, side, currentPnLPct)
|
||||||
} else {
|
} 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)
|
log.Printf("❌ 回撤平仓失败 (%s %s): %v", symbol, side, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("✅ 回撤平仓成功: %s %s", symbol, side)
|
log.Printf("✅ 回撤平仓成功: %s %s", symbol, side)
|
||||||
// 平仓后清理该symbol的缓存
|
// 平仓后清理该持仓的缓存
|
||||||
at.ClearPeakPnLCache(symbol)
|
at.ClearPeakPnLCache(symbol, side)
|
||||||
}
|
}
|
||||||
} else if currentPnLPct > 5.0 {
|
} else if currentPnLPct > 5.0 {
|
||||||
// 记录接近平仓条件的情况(用于调试)
|
// 记录接近平仓条件的情况(用于调试)
|
||||||
@@ -1630,25 +1633,27 @@ func (at *AutoTrader) GetPeakPnLCache() map[string]float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePeakPnL 更新最高收益缓存
|
// UpdatePeakPnL 更新最高收益缓存
|
||||||
func (at *AutoTrader) UpdatePeakPnL(symbol string, currentPnLPct float64) {
|
func (at *AutoTrader) UpdatePeakPnL(symbol, side string, currentPnLPct float64) {
|
||||||
at.peakPnLCacheMutex.Lock()
|
at.peakPnLCacheMutex.Lock()
|
||||||
defer at.peakPnLCacheMutex.Unlock()
|
defer at.peakPnLCacheMutex.Unlock()
|
||||||
|
|
||||||
if peak, exists := at.peakPnLCache[symbol]; exists {
|
posKey := symbol + "_" + side
|
||||||
|
if peak, exists := at.peakPnLCache[posKey]; exists {
|
||||||
// 更新峰值(如果是多头,取较大值;如果是空头,currentPnLPct为负,也要比较)
|
// 更新峰值(如果是多头,取较大值;如果是空头,currentPnLPct为负,也要比较)
|
||||||
if currentPnLPct > peak {
|
if currentPnLPct > peak {
|
||||||
at.peakPnLCache[symbol] = currentPnLPct
|
at.peakPnLCache[posKey] = currentPnLPct
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 首次记录
|
// 首次记录
|
||||||
at.peakPnLCache[symbol] = currentPnLPct
|
at.peakPnLCache[posKey] = currentPnLPct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearPeakPnLCache 清除指定symbol的峰值缓存
|
// ClearPeakPnLCache 清除指定持仓的峰值缓存
|
||||||
func (at *AutoTrader) ClearPeakPnLCache(symbol string) {
|
func (at *AutoTrader) ClearPeakPnLCache(symbol, side string) {
|
||||||
at.peakPnLCacheMutex.Lock()
|
at.peakPnLCacheMutex.Lock()
|
||||||
defer at.peakPnLCacheMutex.Unlock()
|
defer at.peakPnLCacheMutex.Unlock()
|
||||||
|
|
||||||
delete(at.peakPnLCache, symbol)
|
posKey := symbol + "_" + side
|
||||||
|
delete(at.peakPnLCache, posKey)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user