feat: 添加OKX行情接入+趋势检测+累积变动系统+界面重构

- 新增OKX WebSocket行情连接器,扩展4交易所价格监控
- 新增z-score趋势检测引擎(TrendDetector),识别价格异动/趋势启动
- 新增累积变动跟踪(CumulativeTracker),基于1min/5min多交易所共识
- 趋势事件和累积变动事件持久化到SQLite
- 新增Binance/OKX动量检测字段,扩展前端动量卡片至15列
- 迁移至macOS(darwin-arm64),更新前端依赖
- Dashboard网格重构:非交易卡片置顶,交易卡片置底
- TrackedCoin添加OK字段,添加ExBinance/ExOKX常量
- 前端新增趋势检测卡片、趋势历史卡片、累积变动卡片

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jackyu66git
2026-05-06 13:26:05 +08:00
co-authored by Claude Opus 4.6
parent 047571921e
commit b7767c95ae
24 changed files with 2705 additions and 273 deletions
+58 -11
View File
@@ -60,6 +60,21 @@ func main() {
store := NewPriceStore()
notifier := NewNotifier(cfg.TelegramBotToken, cfg.TelegramChatID)
// Initialize momentum tracker (for momentum scanning mode)
momentumTracker := NewMomentumTracker()
// Initialize trend detector (for price anomaly / trend detection)
trendDetector := NewTrendDetector(momentumTracker)
if cfg.TrendEnabled {
trendDetector.Configure(cfg.TrendBaselineWindow, cfg.TrendAnomalyMul, cfg.TrendConfirmTicks, cfg.TrendAlertCooldown)
log.Printf("[Trend] Z-score detection enabled (z-score >= %.1fσ, window=%d ticks, confirm=%d ticks)",
cfg.TrendAnomalyMul, cfg.TrendBaselineWindow, cfg.TrendConfirmTicks)
}
// Initialize cumulative tracker (1min/5min multi-exchange consensus change)
cumulativeTracker := NewCumulativeTracker()
log.Printf("[CM] Cumulative change tracking enabled (1m >= %.1f%%, 3+ exchanges)", cumulativeTracker.surgePct1m)
// Initialize SQLite database
database, err := db.Open("")
if err != nil {
@@ -75,7 +90,7 @@ func main() {
trader.startIPCServer()
// Initialize dashboard (web server + SSE)
dashboard := NewDashboard(store, trader, database, ":8888")
dashboard := NewDashboard(store, trader, database, ":8888", cfg, momentumTracker, trendDetector, cumulativeTracker)
go dashboard.Run()
// Spread window tracker — measures how long spreads stay above threshold
@@ -83,7 +98,9 @@ func main() {
// P3-4: wire real-time trade event broadcast
trader.OnTradeEvent = dashboard.BroadcastEvent
if trader.IsConfigured() {
if cfg.MomentumEnabled {
log.Printf("[Trader] MOMENTUM SCAN mode: arbitrage trading disabled, momentum detection active (threshold >= %.2f%%)", cfg.MomentumThresholdPct)
} else if trader.IsConfigured() {
log.Printf("[Trader] %s mode: automated trading ENABLED (threshold >= %.2f%%, $%.0f/leg, max %d positions, $%.0f capital)",
trader.ModeLabel(), cfg.TradeThreshold, cfg.TradeAmountUSD, cfg.MaxPositions, cfg.InitialCapital)
if cfg.TestMode {
@@ -101,8 +118,8 @@ func main() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGUSR1)
// Collect symbols — only BG and HL for now (BN, dYdX disabled)
var bgSymbols, hlSymbols []string
// Collect symbols for all exchanges
var bgSymbols, hlSymbols, bnSymbols, okxSymbols []string
for _, c := range TrackedCoins {
if c.BG != "" {
bgSymbols = append(bgSymbols, c.BG)
@@ -110,9 +127,15 @@ func main() {
if c.HL != "" {
hlSymbols = append(hlSymbols, c.HL)
}
if c.BN != "" {
bnSymbols = append(bnSymbols, c.BN)
}
if c.OK != "" {
okxSymbols = append(okxSymbols, c.OK)
}
}
// Start exchange WS connections (BG + HL only)
// Start exchange WS connections
startExchange := func(name string, runner func(func(string, float64, float64, float64)) error) {
go func() {
for {
@@ -133,6 +156,8 @@ func main() {
startExchange("HyperLiquid", exchange.NewHyperLiquidWS(hlSymbols).Run)
startExchange("Bitget", exchange.NewBitgetWS(bgSymbols).Run)
startExchange("Binance", exchange.NewBinanceWS(bnSymbols).Run)
startExchange("OKX", exchange.NewOKXWS(okxSymbols).Run)
log.Println("[Monitor] Waiting for initial data...")
time.Sleep(10 * time.Second)
@@ -206,6 +231,25 @@ func main() {
// Scan for arbitrage entries using maker fees (limit orders)
snap := store.GetAll()
// Feed prices to momentum tracker (for momentum scanning or trend detection)
if cfg.MomentumEnabled || cfg.TrendEnabled {
for coin, exMap := range snap {
for ex, price := range exMap {
momentumTracker.Record(coin, ex, price)
}
}
}
// Feed snapshots to cumulative tracker (always on)
for _, tc := range TrackedCoins {
exMap := snap[tc.Name]
if exMap == nil || len(exMap) < 3 {
continue
}
cumulativeTracker.Record(tc.Name, exMap)
}
makerOpps := ScanBGHL(snap)
dashboard.UpdateScan(makerOpps)
t2 := time.Now()
@@ -213,12 +257,15 @@ func main() {
// Track spread window durations (how long each opportunity stays alive)
spreadTracker.Tick(snap, cfg.TradeThreshold)
for _, opp := range makerOpps {
if opp.NetProfit < cfg.ArbThreshold {
continue
}
if trader.TryEntry(opp, store, notifier) {
log.Printf("[Trader] %s: entry initiated for %.4f%%", opp.Coin, opp.NetProfit)
// In momentum mode, arbitrage trading is disabled
if !cfg.MomentumEnabled {
for _, opp := range makerOpps {
if opp.NetProfit < cfg.ArbThreshold {
continue
}
if trader.TryEntry(opp, store, notifier) {
log.Printf("[Trader] %s: entry initiated for %.4f%%", opp.Coin, opp.NetProfit)
}
}
}
t3 := time.Now()