From 938926254f0d7ebf7fe3bab4a869787174983118 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Wed, 29 Oct 2025 15:30:32 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20Correct=20PnL=20calculation=20in=20trade?= =?UTF-8?q?=20history=20analysis=20Fixed=20critical=20issues=20in=20histor?= =?UTF-8?q?ical=20trade=20record=20and=20performance=20analysis:=201.=20Pn?= =?UTF-8?q?L=20Calculation:=20Changed=20from=20percentage-only=20to=20actu?= =?UTF-8?q?al=20USDT=20amount=20=20=20=20-=20Now=20correctly=20calculates:?= =?UTF-8?q?=20positionValue=20=C3=97=20priceChange%=20=C3=97=20leverage=20?= =?UTF-8?q?=20=20=20-=20Previously:=20100U@5%=20and=201000U@5%=20both=20sh?= =?UTF-8?q?owed=205.0=20=20=20=20-=20Now:=20Properly=20reflects=20differen?= =?UTF-8?q?t=20position=20sizes=20and=20leverage=202.=20Position=20Trackin?= =?UTF-8?q?g:=20Added=20quantity=20and=20leverage=20to=20open=20position?= =?UTF-8?q?=20records=20=20=20=20-=20Store=20complete=20trade=20data=20for?= =?UTF-8?q?=20accurate=20PnL=20calculation=20=20=20=20-=20Previously=20onl?= =?UTF-8?q?y=20stored:=20side,=20openPrice,=20openTime=20=20=20=20-=20Now?= =?UTF-8?q?=20includes:=20quantity,=20leverage=20for=20proper=20accounting?= =?UTF-8?q?=203.=20Position=20Key:=20Fixed=20to=20distinguish=20long/short?= =?UTF-8?q?=20positions=20=20=20=20-=20Changed=20from=20symbol=20to=20symb?= =?UTF-8?q?ol=5Fside=20(e.g.,=20BTCUSDT=5Flong)=20=20=20=20-=20Prevents=20?= =?UTF-8?q?conflicts=20when=20holding=20both=20long=20and=20short=20positi?= =?UTF-8?q?ons=204.=20Sharpe=20Ratio:=20Replaced=20custom=20Newton's=20met?= =?UTF-8?q?hod=20with=20math.Sqrt=20=20=20=20-=20Simplified=20standard=20d?= =?UTF-8?q?eviation=20calculation=20=20=20=20-=20More=20reliable=20and=20m?= =?UTF-8?q?aintainable=20Impact:=20Win=20rate,=20profit=20factor,=20and=20?= =?UTF-8?q?Sharpe=20ratio=20now=20based=20on=20accurate=20USDT=20amounts?= =?UTF-8?q?=20Co-Authored-By:=20tinkle-community=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logger/decision_logger.go | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/logger/decision_logger.go b/logger/decision_logger.go index 7ac07c85..ee5bb2af 100644 --- a/logger/decision_logger.go +++ b/logger/decision_logger.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math" "os" "path/filepath" "time" @@ -326,7 +327,7 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna SymbolStats: make(map[string]*SymbolPerformance), } - // 追踪持仓状态:symbol -> {side, openPrice, openTime} + // 追踪持仓状态:symbol_side -> {side, openPrice, openTime, quantity, leverage} openPositions := make(map[string]map[string]interface{}) // 遍历所有记录 @@ -337,15 +338,23 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna } symbol := action.Symbol - posKey := symbol // 使用symbol作为key(假设同一时间一个币种只有一个方向的仓位) + side := "" + if action.Action == "open_long" || action.Action == "close_long" { + side = "long" + } else if action.Action == "open_short" || action.Action == "close_short" { + side = "short" + } + posKey := symbol + "_" + side // 使用symbol_side作为key,区分多空持仓 switch action.Action { case "open_long", "open_short": - // 记录开仓 + // 记录开仓(包括数量和杠杆) openPositions[posKey] = map[string]interface{}{ - "side": action.Action[5:], // "long" or "short" + "side": side, "openPrice": action.Price, "openTime": action.Timestamp, + "quantity": action.Quantity, + "leverage": action.Leverage, } case "close_long", "close_short": @@ -354,16 +363,21 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna openPrice := openPos["openPrice"].(float64) openTime := openPos["openTime"].(time.Time) side := openPos["side"].(string) + quantity := openPos["quantity"].(float64) + leverage := openPos["leverage"].(int) - // 计算盈亏 - pnl := 0.0 + // 计算盈亏百分比 pnlPct := 0.0 if side == "long" { pnlPct = ((action.Price - openPrice) / openPrice) * 100 } else { pnlPct = ((openPrice - action.Price) / openPrice) * 100 } - pnl = pnlPct // 简化:用百分比代表盈亏 + + // 计算实际盈亏(USDT) + // PnL = 仓位价值 × 价格变化百分比 × 杠杆倍数 + positionValue := quantity * openPrice + pnl := positionValue * (pnlPct / 100) * float64(leverage) // 记录交易结果 outcome := TradeOutcome{ @@ -513,14 +527,7 @@ func (l *DecisionLogger) calculateSharpeRatio(records []*DecisionRecord) float64 sumSquaredDiff += diff * diff } variance := sumSquaredDiff / float64(len(returns)) - stdDev := 0.0 - if variance > 0 { - stdDev = 1.0 - // 简单的平方根计算(牛顿迭代法) - for i := 0; i < 10; i++ { - stdDev = (stdDev + variance/stdDev) / 2 - } - } + stdDev := math.Sqrt(variance) // 避免除以零 if stdDev == 0 {