-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
[P0 嚴重] 修改交易員初始餘額後 P&L 不更新
[P0 Critical] P&L Not Updated After Editing Trader Initial Balance
📋 問題描述 | Bug Description
中文:
修改交易員的初始餘額後,盈虧(P&L)計算仍基於舊餘額,導致統計數據錯誤,無法反映真實收益率。
English:
After editing trader's initial balance, P&L calculation still uses old balance, causing incorrect statistics and failing to reflect true ROI.
嚴重性 | Severity: 🔴 P0 - 數據準確性錯誤,影響用戶決策 | Data accuracy error, affects user decisions
🔄 復現步驟 | Reproduction Steps
-
創建交易員,設定初始餘額為 1000 USDT
- Create trader, set initial balance to 1000 USDT
-
啟動交易員,等待產生交易記錄(至少 1 筆)
- Start trader, wait for trade records (at least 1)
-
停止交易員
- Stop trader
-
編輯交易員,將初始餘額修改為 1 USDT
- Edit trader, change initial balance to 1 USDT
-
保存修改
- Save changes
-
查看交易員卡片的 P&L 數據
- Check trader card P&L data
期望結果 | Expected Result:
- P&L 應重新計算,基準改為 1 USDT
- P&L should recalculate based on 1 USDT
- 收益率應大幅變化(如從 -0.17% 變為 -17%)
- ROI should change significantly (e.g., from -0.17% to -17%)
實際結果 | Actual Result:
- P&L 保持不變,仍基於 1000 USDT 計算
- P&L unchanged, still calculated based on 1000 USDT
- 收益率未更新
- ROI not updated
✅ 期望行為 | Expected Behavior
| 操作 Operation | 初始餘額 Initial | 當前總資產 Current | 期望 P&L Expected | 期望收益率 ROI |
|---|---|---|---|---|
| 創建時填 1000 Create with 1000 |
1000 | 999.83 | -0.17 | -0.17% |
| 修改成 1 Edit to 1 |
1 | 999.83 | +998.83 | +99883% |
核心邏輯 | Core Logic:
P&L = 當前總資產 - 初始餘額
P&L = Current Equity - Initial Balance
收益率 = (P&L / 初始餘額) × 100%
ROI = (P&L / Initial Balance) × 100%
❌ 實際行為 | Actual Behavior
| 操作 Operation | 初始餘額(UI) Initial (UI) |
當前總資產 Current |
實際 P&L Actual |
實際收益率 Actual ROI |
|---|---|---|---|---|
| 創建時填 1000 | 1000 | 999.83 | -0.17 | -0.17% |
| 修改成 1 | 1(已修改) 1 (edited) |
999.83 | -0.17 ❌ | -0.17% ❌ |
P&L 計算仍使用舊的 1000 作為基準,未隨著初始餘額修改而更新。
- P&L still uses old 1000 as baseline, not updated with balance change.
🔬 技術根因 | Root Cause
推測原因 1 | Possible Cause 1: 後端只更新資料庫,未觸發重算 | Backend only updates DB, no recalculation
代碼位置 | Code Location: api/server.go handleUpdateTrader 函數 | function
// ❌ 當前邏輯:只更新資料庫,未重算 P&L
// ❌ Current logic: Only updates DB, no P&L recalculation
func (s *Server) handleUpdateTrader(c *gin.Context) {
var req UpdateTraderRequest
c.BindJSON(&req)
// 更新資料庫 | Update database
err := s.database.UpdateTrader(userID, traderID, req)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
// ❌ 缺少:重新計算 P&L 的邏輯
// ❌ Missing: P&L recalculation logic
// ❌ 缺少:通知運行中交易員更新基準的機制
// ❌ Missing: Mechanism to notify running trader to update baseline
c.JSON(200, gin.H{"message": "更新成功 | Update successful"})
}推測原因 2 | Possible Cause 2: P&L 使用快取的初始餘額 | P&L uses cached initial balance
代碼位置 | Code Location: trader/trader.go 或前端計算邏輯 | or frontend calculation
// ❌ 可能的問題:P&L 計算時使用了創建時的快取值
// ❌ Possible issue: P&L calculation uses cached value from creation
type Trader struct {
initialBalanceCache float64 // ← 創建時設定,從未更新 | Set on creation, never updated
}
func (t *Trader) CalculatePnL() float64 {
return t.currentEquity - t.initialBalanceCache // ← 使用舊值! | Uses old value!
}推測原因 3 | Possible Cause 3: 前端顯示使用靜態值 | Frontend displays static value
// ❌ 前端可能從 API 獲取的是舊的 P&L 值
// ❌ Frontend may be getting old P&L value from API
<div>
Total P&L: {trader.pnl} {/* ← API 返回的舊值 | Old value from API */}
</div>💡 建議修復方案 | Suggested Fix
方案 A(推薦)| Option A (Recommended): 禁止修改已交易過的交易員的初始餘額 | Prevent editing initial balance of traded traders
理由 | Rationale:
- 修改初始餘額會導致歷史數據失真
- Editing initial balance distorts historical data
- P&L 的意義是「相對於起始資金的盈虧」,改變起始資金會讓歷史記錄變得無意義
- P&L means "profit/loss relative to starting capital"; changing starting capital makes history meaningless
實現 | Implementation:
// api/server.go - handleUpdateTrader
// 檢查交易員是否已有交易記錄
// Check if trader has trade history
hasTraded, _ := s.database.HasTradeHistory(traderID)
if hasTraded && req.InitialBalance != nil {
c.JSON(400, gin.H{
"error": "無法修改已交易的交易員的初始餘額 | Cannot edit initial balance of traded trader",
"hint": "初始餘額只能在首次啟動前設定 | Initial balance can only be set before first start",
})
return
}前端配合 | Frontend Support:
// 如果交易員已啟動過,禁用初始餘額輸入框
// If trader has been started, disable initial balance input
<input
type="number"
value={initialBalance}
disabled={trader.hasTraded} // ← 已交易過則禁用 | Disabled if traded
/>
{trader.hasTraded && (
<p className="text-amber-600">
⚠️ 初始餘額無法修改(交易員已啟動過)
⚠️ Initial balance cannot be edited (trader has been started)
</p>
)}方案 B(不推薦)| Option B (Not Recommended): 允許修改,但強制重算並清空歷史 | Allow editing, force recalculation and clear history
實現 | Implementation:
// api/server.go - handleUpdateTrader
if req.InitialBalance != nil && req.InitialBalance != trader.InitialBalance {
// 警告用戶 | Warn user
log.Printf("⚠️ 修改初始餘額:%.2f → %.2f | Editing initial balance: %.2f → %.2f",
trader.InitialBalance, req.InitialBalance, trader.InitialBalance, req.InitialBalance)
// 更新餘額 | Update balance
trader.InitialBalance = req.InitialBalance
// 重算 P&L | Recalculate P&L
trader.RecalculatePnL()
// ❌ 風險:歷史數據失真 | Risk: Historical data distorted
// 選項 1:清空歷史交易記錄(用戶可能不願意)
// Option 1: Clear history (users may not want)
// 選項 2:保留歷史但標記「餘額已修改,P&L 僅供參考」
// Option 2: Keep history but mark "balance edited, P&L for reference only"
}前端確認對話框 | Frontend Confirmation:
const handleUpdateBalance = () => {
if (window.confirm(
'修改初始餘額將導致歷史 P&L 數據失真,確定要修改嗎?\n' +
'建議:創建新交易員代替修改現有交易員。\n\n' +
'Editing initial balance will distort historical P&L data. Continue?\n' +
'Suggestion: Create new trader instead of editing existing one.'
)) {
// 執行修改 | Execute edit
}
}🧪 測試案例 | Test Cases
測試 1 | Test 1: 修改未啟動的交易員 | Edit un-started trader
- 創建交易員,初始餘額 1000 | Create trader with balance 1000
- 不啟動,直接修改餘額為 500 | Don't start, edit to 500
- 啟動交易員 | Start trader
- 期望 | Expected: P&L 基於 500 計算 | P&L calculated based on 500
測試 2 | Test 2: 修改已啟動的交易員(方案 A)| Edit started trader (Option A)
- 創建並啟動交易員,初始餘額 1000 | Create and start, balance 1000
- 產生交易記錄 | Generate trade records
- 嘗試修改餘額為 500 | Try to edit to 500
- 期望 | Expected: 後端返回錯誤「無法修改已交易的交易員的初始餘額」| Backend returns error
測試 3 | Test 3: P&L 實時更新 | P&L real-time update
- 創建交易員,初始餘額 1000 | Create with balance 1000
- 啟動並產生交易(假設當前總資產為 1050)| Start and trade (assume equity 1050)
- 查看 P&L → 應顯示 +50 (+5%) | Check P&L → Should show +50 (+5%)
- 交易繼續,總資產變為 980 | Trading continues, equity becomes 980
- 查看 P&L → 應顯示 -20 (-2%) | Check P&L → Should show -20 (-2%)
🎯 相關改進建議 | Related Improvements
-
明確初始餘額的意義 | Clarify initial balance meaning
<Tooltip content="初始餘額代表交易員首次啟動時的資金基準,用於計算盈虧。一旦啟動,無法修改。 Initial balance represents the capital baseline at first start, used for P&L calculation. Cannot be edited once started."> <InfoIcon /> </Tooltip>
-
添加「重置交易員」功能 | Add "Reset Trader" feature
- 如果用戶確實需要修改餘額,建議「重置」(清空歷史 + 修改餘額)
- If user really needs to edit, suggest "Reset" (clear history + edit balance)
- 或直接創建新交易員
- Or create new trader
- 如果用戶確實需要修改餘額,建議「重置」(清空歷史 + 修改餘額)
-
P&L 計算透明化 | Make P&L calculation transparent
<div className="pnl-breakdown"> <p>當前總資產 | Current Equity: 999.83 USDT</p> <p>初始餘額 | Initial Balance: 1000 USDT</p> <p>盈虧 (P&L): 999.83 - 1000 = <strong>-0.17 USDT</strong></p> <p>收益率 | ROI: -0.17 / 1000 × 100% = <strong>-0.17%</strong></p> </div>
📊 環境資訊 | Environment
- 版本 | Version: 基於 NoFxAiOS/nofx dev 分支 | Based on dev branch
- 測試交易所 | Test Exchange: HYPERLIQUID, Binance
- 資料庫 | Database: SQLite
🔗 相關 Issue | Related Issues
- Bug coin_pool_api_url 这两个api是你自己写的吗 #2: 創建交易員時初始餘額被固定為 1000(可能共享同一根因)
- Initial balance locked at 1000 when creating trader (may share same root cause)
優先級建議 | Priority: 🔴 P0 - 影響數據準確性,建議採用方案 A(禁止修改)快速修復 | Affects data accuracy, suggest Option A (prevent editing) for quick fix