fix: PnL计算改为美元归一化,加仓不再虚增手续费 (calcArbPnL)
This commit is contained in:
@@ -520,8 +520,7 @@ func (t *Trader) checkExit(pos *ArbPosition, bgP, hlP, diffPct float64, notifier
|
||||
|
||||
longPnl := (longCurrent - longAvg) / longAvg * 100
|
||||
shortPnl := (shortAvg - shortCurrent) / shortAvg * 100
|
||||
totalFees := float64(2+pos.ScaleLevels) * (takerFees[ExBitget] + takerFees[ExHyperLiquid]) // 开仓(含加仓) + 平仓手续费
|
||||
netPnl := longPnl + shortPnl - totalFees
|
||||
netPnl, totalFees := calcArbPnL(longPnl, shortPnl, pos.ScaleLevels, t.cfg.TradeAmountUSD) // 净利为总资本的百分比
|
||||
|
||||
elapsed := time.Since(pos.StartedAt)
|
||||
|
||||
@@ -602,7 +601,7 @@ func (t *Trader) checkExit(pos *ArbPosition, bgP, hlP, diffPct float64, notifier
|
||||
EntrySpread: pos.EntrySpread,
|
||||
ExitSpread: diffPct,
|
||||
PnlPct: netPnl,
|
||||
PnlUSD: pos.AmountUSD * netPnl / 100,
|
||||
PnlUSD: 2 * pos.AmountUSD * netPnl / 100,
|
||||
Convergence: convergenceLabel,
|
||||
Reason: exitReason,
|
||||
Duration: elapsed.Round(time.Second).String(),
|
||||
@@ -854,6 +853,27 @@ func (t *Trader) GetOpenPositions() []*ArbPosition {
|
||||
return r
|
||||
}
|
||||
|
||||
// calcArbPnL computes net PnL and total fees in USD, then normalizes to % of total deployed capital.
|
||||
// This correctly handles scale-ins where the old formula (longPnl+shortPnl - (2+N)*0.105)
|
||||
// double-counted fees because it didn't divide by (1+N) batches.
|
||||
func calcArbPnL(longPnl, shortPnl float64, scaleLevels int, tradeAmountUSD float64) (netPnlPct, feePct float64) {
|
||||
numBatches := 1 + scaleLevels
|
||||
legCapital := tradeAmountUSD
|
||||
totalCapital := float64(numBatches) * 2 * legCapital
|
||||
|
||||
// Gross PnL in USD
|
||||
longPnlUSD := longPnl / 100 * float64(numBatches) * legCapital
|
||||
shortPnlUSD := shortPnl / 100 * float64(numBatches) * legCapital
|
||||
grossPnLUSD := longPnlUSD + shortPnlUSD
|
||||
|
||||
// Fee in USD (entry+exit per order-pair)
|
||||
feeUSD := float64(2+scaleLevels) * legCapital * (takerFees[ExBitget] + takerFees[ExHyperLiquid]) / 100
|
||||
|
||||
netPnlPct = (grossPnLUSD - feeUSD) / totalCapital * 100
|
||||
feePct = feeUSD / totalCapital * 100
|
||||
return
|
||||
}
|
||||
|
||||
// weightedAvgPrice computes the weighted average entry price across multiple scale levels.
|
||||
// Each level trades the same USD amount, so the result is the harmonic mean of prices.
|
||||
func weightedAvgPrice(prices []float64, amountPerTrade float64) float64 {
|
||||
@@ -1161,8 +1181,7 @@ func (t *Trader) blacklistCoin(pos *ArbPosition, bgP, hlP, diffPct float64, noti
|
||||
shortAvg := weightedAvgPrice(pos.ShortEntryPrices, t.cfg.TradeAmountUSD)
|
||||
longPnl := (longCurrent - longAvg) / longAvg * 100
|
||||
shortPnl := (shortAvg - shortCurrent) / shortAvg * 100
|
||||
totalFees := float64(2+pos.ScaleLevels) * (takerFees[ExBitget] + takerFees[ExHyperLiquid])
|
||||
netPnl := longPnl + shortPnl - totalFees
|
||||
netPnl, totalFees := calcArbPnL(longPnl, shortPnl, pos.ScaleLevels, t.cfg.TradeAmountUSD)
|
||||
|
||||
pos.ExitDiffPct = diffPct
|
||||
pos.ExitNetPnl = netPnl
|
||||
|
||||
Reference in New Issue
Block a user