mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-07 04:50:57 +08:00
Fixes #818 ## 问题 传递给 AI 决策的持仓盈亏百分比只计算价格变动,未考虑杠杆倍数。 例如:10倍杠杆,价格上涨1%,AI看到的是1%而非实际的10%收益率。 ## 改动 1. 修复 buildTradingContext 中的盈亏百分比计算 - 从基于价格变动改为基于保证金计算 - 收益率 = 未实现盈亏 / 保证金 × 100% 2. 抽取公共函数 calculatePnLPercentage - 消除 buildTradingContext 和 GetPositions 的重复代码 - 确保两处使用相同的计算逻辑 3. 新增单元测试 (trader/auto_trader_test.go) - 9个基础测试用例(正常、边界、异常) - 3个真实场景测试(BTC/ETH/SOL不同杠杆) - 测试覆盖率:100% 4. 更新 .gitignore - 添加 SQLite WAL 相关文件 (config.db-shm, config.db-wal, nofx.db) ## 测试结果 ✅ 所有 12 个单元测试通过 ✅ 代码编译通过 ✅ 与 GetPositions 函数保持一致 ## 影响 - AI 现在能够准确评估持仓真实收益率 - 避免因错误数据导致的过早止盈或延迟止损
119 lines
3.3 KiB
Go
119 lines
3.3 KiB
Go
package trader
|
||
|
||
import (
|
||
"math"
|
||
"testing"
|
||
)
|
||
|
||
func TestCalculatePnLPercentage(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
unrealizedPnl float64
|
||
marginUsed float64
|
||
expected float64
|
||
}{
|
||
{
|
||
name: "正常盈利 - 10倍杠杆",
|
||
unrealizedPnl: 100.0, // 盈利 100 USDT
|
||
marginUsed: 1000.0, // 保证金 1000 USDT
|
||
expected: 10.0, // 10% 收益率
|
||
},
|
||
{
|
||
name: "正常亏损 - 10倍杠杆",
|
||
unrealizedPnl: -50.0, // 亏损 50 USDT
|
||
marginUsed: 1000.0, // 保证金 1000 USDT
|
||
expected: -5.0, // -5% 收益率
|
||
},
|
||
{
|
||
name: "高杠杆盈利 - 价格上涨1%,20倍杠杆",
|
||
unrealizedPnl: 200.0, // 盈利 200 USDT
|
||
marginUsed: 1000.0, // 保证金 1000 USDT
|
||
expected: 20.0, // 20% 收益率
|
||
},
|
||
{
|
||
name: "保证金为0 - 边界情况",
|
||
unrealizedPnl: 100.0,
|
||
marginUsed: 0.0,
|
||
expected: 0.0, // 应该返回 0 而不是除以零错误
|
||
},
|
||
{
|
||
name: "负保证金 - 边界情况",
|
||
unrealizedPnl: 100.0,
|
||
marginUsed: -1000.0,
|
||
expected: 0.0, // 应该返回 0(异常情况)
|
||
},
|
||
{
|
||
name: "盈亏为0",
|
||
unrealizedPnl: 0.0,
|
||
marginUsed: 1000.0,
|
||
expected: 0.0,
|
||
},
|
||
{
|
||
name: "小额交易",
|
||
unrealizedPnl: 0.5,
|
||
marginUsed: 10.0,
|
||
expected: 5.0,
|
||
},
|
||
{
|
||
name: "大额盈利",
|
||
unrealizedPnl: 5000.0,
|
||
marginUsed: 10000.0,
|
||
expected: 50.0,
|
||
},
|
||
{
|
||
name: "极小保证金",
|
||
unrealizedPnl: 1.0,
|
||
marginUsed: 0.01,
|
||
expected: 10000.0, // 100倍收益率
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
result := calculatePnLPercentage(tt.unrealizedPnl, tt.marginUsed)
|
||
|
||
// 使用精度比较,避免浮点数误差
|
||
if math.Abs(result-tt.expected) > 0.0001 {
|
||
t.Errorf("calculatePnLPercentage(%v, %v) = %v, want %v",
|
||
tt.unrealizedPnl, tt.marginUsed, result, tt.expected)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestCalculatePnLPercentage_RealWorldScenarios 真实场景测试
|
||
func TestCalculatePnLPercentage_RealWorldScenarios(t *testing.T) {
|
||
t.Run("BTC 10倍杠杆,价格上涨2%", func(t *testing.T) {
|
||
// 开仓:1000 USDT 保证金,10倍杠杆 = 10000 USDT 仓位
|
||
// 价格上涨 2% = 200 USDT 盈利
|
||
// 收益率 = 200 / 1000 = 20%
|
||
result := calculatePnLPercentage(200.0, 1000.0)
|
||
expected := 20.0
|
||
if math.Abs(result-expected) > 0.0001 {
|
||
t.Errorf("BTC场景: got %v, want %v", result, expected)
|
||
}
|
||
})
|
||
|
||
t.Run("ETH 5倍杠杆,价格下跌3%", func(t *testing.T) {
|
||
// 开仓:2000 USDT 保证金,5倍杠杆 = 10000 USDT 仓位
|
||
// 价格下跌 3% = -300 USDT 亏损
|
||
// 收益率 = -300 / 2000 = -15%
|
||
result := calculatePnLPercentage(-300.0, 2000.0)
|
||
expected := -15.0
|
||
if math.Abs(result-expected) > 0.0001 {
|
||
t.Errorf("ETH场景: got %v, want %v", result, expected)
|
||
}
|
||
})
|
||
|
||
t.Run("SOL 20倍杠杆,价格上涨0.5%", func(t *testing.T) {
|
||
// 开仓:500 USDT 保证金,20倍杠杆 = 10000 USDT 仓位
|
||
// 价格上涨 0.5% = 50 USDT 盈利
|
||
// 收益率 = 50 / 500 = 10%
|
||
result := calculatePnLPercentage(50.0, 500.0)
|
||
expected := 10.0
|
||
if math.Abs(result-expected) > 0.0001 {
|
||
t.Errorf("SOL场景: got %v, want %v", result, expected)
|
||
}
|
||
})
|
||
}
|