Phase 3: Real-time enhancements

P3-1: Scan optimization — only BG↔HL (50+ pair combos → 2)
P3-2: Real-time spread chart — spreadHistory ring buffer +
      /api/spread-history endpoint + Chart.js spread chart
P3-3: Live position PnL — positions SSE now includes
      estimated current profit/loss + current spread
P3-4: Real-time trade events — trader.OnTradeEvent callback
      fires SSE 'trade_open' / 'trade_close' immediately
P3-5: Connection status monitoring — tracks last update time
      per exchange, broadcast via stats.connections + /api/connections

Frontend: spread chart card, PnL column in positions,
          connection status dots in stats bar,
          green/red border flash on trade events
This commit is contained in:
jackyu66git
2026-05-03 18:05:19 +08:00
parent da561325d7
commit beb3611778
7 changed files with 528 additions and 316 deletions
+27
View File
@@ -63,6 +63,8 @@ type Trader struct {
positions map[string]*ArbPosition // coin -> position
lastTradeTime map[string]time.Time
closedTrades []TradeRecord // history of closed trades
OnTradeEvent func(event string, data interface{}) // P3-4: real-time SSE push
}
// TradeRecord stores a finalized trade for stats tracking.
@@ -277,6 +279,17 @@ func (t *Trader) executeEntry(opp *ArbOpportunity, store *PriceStore, notifier *
pos.LongLeg.Exchange, pos.LongLeg.EntryPrice,
pos.ShortLeg.Exchange, pos.ShortLeg.EntryPrice,
diff, t.cfg.TradeAmountUSD))
// P3-4: real-time trade event push
if t.OnTradeEvent != nil {
t.OnTradeEvent("trade_open", map[string]interface{}{
"coin": pos.Coin,
"direction": pos.Direction,
"entry_spread": diff,
"amount_usd": t.cfg.TradeAmountUSD,
"time": time.Now().Format("15:04:05"),
})
}
}
// checkScaleIn adds more position when spread widens further.
@@ -432,6 +445,20 @@ func (t *Trader) checkExit(pos *ArbPosition, bgP, hlP, diffPct float64, notifier
msg += fmt.Sprintf(" 平仓异常: %s\n", closeErr)
}
notifier.Send(msg)
// P3-4: real-time trade event push
if t.OnTradeEvent != nil {
t.OnTradeEvent("trade_close", map[string]interface{}{
"coin": pos.Coin,
"direction": pos.Direction,
"entry_spread": pos.EntrySpread,
"exit_spread": diffPct,
"pnl_pct": netPnl,
"convergence": convergenceLabel,
"duration": elapsed.Round(time.Second).String(),
"time": time.Now().Format("15:04:05"),
})
}
}
func (t *Trader) placeOrder(leg *PositionLeg, side string, store *PriceStore) string {