mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-12 23:36:55 +08:00
Fix: Correct PnL calculation in trade history analysis
Fixed critical issues in historical trade record and performance analysis: 1. PnL Calculation: Changed from percentage-only to actual USDT amount - Now correctly calculates: positionValue × priceChange% × leverage - Previously: 100U@5% and 1000U@5% both showed 5.0 - Now: Properly reflects different position sizes and leverage 2. Position Tracking: Added quantity and leverage to open position records - Store complete trade data for accurate PnL calculation - Previously only stored: side, openPrice, openTime - Now includes: quantity, leverage for proper accounting 3. Position Key: Fixed to distinguish long/short positions - Changed from symbol to symbol_side (e.g., BTCUSDT_long) - Prevents conflicts when holding both long and short positions 4. Sharpe Ratio: Replaced custom Newton's method with math.Sqrt - Simplified standard deviation calculation - More reliable and maintainable Impact: Win rate, profit factor, and Sharpe ratio now based on accurate USDT amounts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@@ -326,7 +327,7 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna
|
|||||||
SymbolStats: make(map[string]*SymbolPerformance),
|
SymbolStats: make(map[string]*SymbolPerformance),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 追踪持仓状态:symbol -> {side, openPrice, openTime}
|
// 追踪持仓状态:symbol_side -> {side, openPrice, openTime, quantity, leverage}
|
||||||
openPositions := make(map[string]map[string]interface{})
|
openPositions := make(map[string]map[string]interface{})
|
||||||
|
|
||||||
// 遍历所有记录
|
// 遍历所有记录
|
||||||
@@ -337,15 +338,23 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna
|
|||||||
}
|
}
|
||||||
|
|
||||||
symbol := action.Symbol
|
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 {
|
switch action.Action {
|
||||||
case "open_long", "open_short":
|
case "open_long", "open_short":
|
||||||
// 记录开仓
|
// 记录开仓(包括数量和杠杆)
|
||||||
openPositions[posKey] = map[string]interface{}{
|
openPositions[posKey] = map[string]interface{}{
|
||||||
"side": action.Action[5:], // "long" or "short"
|
"side": side,
|
||||||
"openPrice": action.Price,
|
"openPrice": action.Price,
|
||||||
"openTime": action.Timestamp,
|
"openTime": action.Timestamp,
|
||||||
|
"quantity": action.Quantity,
|
||||||
|
"leverage": action.Leverage,
|
||||||
}
|
}
|
||||||
|
|
||||||
case "close_long", "close_short":
|
case "close_long", "close_short":
|
||||||
@@ -354,16 +363,21 @@ func (l *DecisionLogger) AnalyzePerformance(lookbackCycles int) (*PerformanceAna
|
|||||||
openPrice := openPos["openPrice"].(float64)
|
openPrice := openPos["openPrice"].(float64)
|
||||||
openTime := openPos["openTime"].(time.Time)
|
openTime := openPos["openTime"].(time.Time)
|
||||||
side := openPos["side"].(string)
|
side := openPos["side"].(string)
|
||||||
|
quantity := openPos["quantity"].(float64)
|
||||||
|
leverage := openPos["leverage"].(int)
|
||||||
|
|
||||||
// 计算盈亏
|
// 计算盈亏百分比
|
||||||
pnl := 0.0
|
|
||||||
pnlPct := 0.0
|
pnlPct := 0.0
|
||||||
if side == "long" {
|
if side == "long" {
|
||||||
pnlPct = ((action.Price - openPrice) / openPrice) * 100
|
pnlPct = ((action.Price - openPrice) / openPrice) * 100
|
||||||
} else {
|
} else {
|
||||||
pnlPct = ((openPrice - action.Price) / openPrice) * 100
|
pnlPct = ((openPrice - action.Price) / openPrice) * 100
|
||||||
}
|
}
|
||||||
pnl = pnlPct // 简化:用百分比代表盈亏
|
|
||||||
|
// 计算实际盈亏(USDT)
|
||||||
|
// PnL = 仓位价值 × 价格变化百分比 × 杠杆倍数
|
||||||
|
positionValue := quantity * openPrice
|
||||||
|
pnl := positionValue * (pnlPct / 100) * float64(leverage)
|
||||||
|
|
||||||
// 记录交易结果
|
// 记录交易结果
|
||||||
outcome := TradeOutcome{
|
outcome := TradeOutcome{
|
||||||
@@ -513,14 +527,7 @@ func (l *DecisionLogger) calculateSharpeRatio(records []*DecisionRecord) float64
|
|||||||
sumSquaredDiff += diff * diff
|
sumSquaredDiff += diff * diff
|
||||||
}
|
}
|
||||||
variance := sumSquaredDiff / float64(len(returns))
|
variance := sumSquaredDiff / float64(len(returns))
|
||||||
stdDev := 0.0
|
stdDev := math.Sqrt(variance)
|
||||||
if variance > 0 {
|
|
||||||
stdDev = 1.0
|
|
||||||
// 简单的平方根计算(牛顿迭代法)
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
stdDev = (stdDev + variance/stdDev) / 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 避免除以零
|
// 避免除以零
|
||||||
if stdDev == 0 {
|
if stdDev == 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user