fix: 重启后总PnL回到0的问题 + 前端PnL图表公式统一
- trader.go: NewTrader 启动时从DB GetAllClosedTrades() 加载所有已平仓交易到 closedTrades - trader.go: 新增 safeFloat/safeStr 辅助函数处理DB空指针 - db/trade_repo.go: 新增 GetAllClosedTrades() 方法 - frontend: PnL图表标题和曲线改用 2*AmountUSD*NetPnl/100 公式,与后端Stats一致
This commit is contained in:
@@ -183,6 +183,35 @@ func NewTrader(cfg *Config, database *db.DB) *Trader {
|
||||
// Restore open positions from DB on restart
|
||||
if database != nil {
|
||||
t.restoreOpenPositions()
|
||||
|
||||
// Load historical closed trades for PnL stats (so total PnL survives restart)
|
||||
if closed, err := database.GetAllClosedTrades(); err == nil {
|
||||
for i := range closed {
|
||||
dbTr := &closed[i]
|
||||
pnlPct := safeFloat(dbTr.NetPnl)
|
||||
pnlUSD := 2 * dbTr.AmountUSD * pnlPct / 100
|
||||
closedAt := time.Time{}
|
||||
if dbTr.ClosedAt != nil {
|
||||
closedAt = *dbTr.ClosedAt
|
||||
}
|
||||
record := TradeRecord{
|
||||
Coin: dbTr.Coin,
|
||||
Direction: dbTr.Direction,
|
||||
EntrySpread: safeFloat(dbTr.EntrySpread),
|
||||
ExitSpread: safeFloat(dbTr.ExitSpread),
|
||||
PnlPct: pnlPct,
|
||||
PnlUSD: pnlUSD,
|
||||
Convergence: safeStr(dbTr.Convergence),
|
||||
Reason: safeStr(dbTr.ExitReason),
|
||||
Duration: closedAt.Sub(dbTr.OpenedAt).Round(time.Second).String(),
|
||||
OpenedAt: dbTr.OpenedAt,
|
||||
ClosedAt: closedAt,
|
||||
ScaleLevels: dbTr.ScaleCount,
|
||||
AmountUSD: dbTr.AmountUSD,
|
||||
}
|
||||
t.closedTrades = append(t.closedTrades, record)
|
||||
}
|
||||
}
|
||||
// Load historical closed trade stats for convergence display
|
||||
if c, d, f, tot, err := database.GetClosedStats(); err == nil {
|
||||
t.dbConverged, t.dbDiverged, t.dbFlat, t.dbTotal = c, d, f, tot
|
||||
@@ -1282,4 +1311,20 @@ func (t *Trader) RemoveBlacklist(coin string) {
|
||||
defer t.mu.Unlock()
|
||||
delete(t.blacklist, coin)
|
||||
log.Printf("[Trader] ✅ %s: Removed from blacklist", coin)
|
||||
}
|
||||
|
||||
// safeFloat returns 0 for nil float64 pointers (DB nullable fields).
|
||||
func safeFloat(f *float64) float64 {
|
||||
if f == nil {
|
||||
return 0
|
||||
}
|
||||
return *f
|
||||
}
|
||||
|
||||
// safeStr returns empty string for nil string pointers (DB nullable fields).
|
||||
func safeStr(s *string) string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
||||
Reference in New Issue
Block a user