Files
exchange-monitor-go/types.go
T
jackyu66gitandClaude Opus 4.6 d38782490c feat: 重构为三所价差异动监控系统
删除 HyperLiquid + 全部交易功能,构建自适应 surge 检测器。
- 新增 surge_detector.go: 每币独立滚动窗口基线,检测三所价差异常飙升
- 新增 SpreadCard/SurgeCard 前端组件
- 保留 momentum/trend/cumulative/trend_filter 扫描功能
- 更新文档和配置以反映新系统

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-08 02:01:18 +08:00

110 lines
2.6 KiB
Go

package main
import (
"sync"
"time"
)
// TrackedCoin represents a coin we monitor across exchanges.
type TrackedCoin struct {
Name string // Display name (BTC, ETH, etc.)
BN string // Binance symbol (BTCUSDT)
BG string // Bitget symbol (BTCUSDT)
OK string // OKX symbol (BTC-USDT-SWAP)
}
// PriceTick holds a price update with optional bid/ask.
type PriceTick struct {
Price float64
Bid float64 // 0 if unknown
Ask float64 // 0 if unknown
}
// Spread holds bid/ask data for one exchange+coin.
type Spread struct {
Bid float64
Ask float64
Updated int64 // unix nano
}
// PriceStore holds the latest prices from all exchanges, thread-safe.
type PriceStore struct {
mu sync.RWMutex
prices map[string]map[string]float64 // coin -> exchange -> price
spreads map[string]map[string]*Spread // coin -> exchange -> spread
}
func NewPriceStore() *PriceStore {
return &PriceStore{
prices: make(map[string]map[string]float64),
spreads: make(map[string]map[string]*Spread),
}
}
// Set stores a price update. If bid/ask are non-zero, also stores spread.
func (s *PriceStore) Set(coin, exchange string, price float64) {
s.mu.Lock()
defer s.mu.Unlock()
if s.prices[coin] == nil {
s.prices[coin] = make(map[string]float64)
}
s.prices[coin][exchange] = price
}
// SetWithSpread stores price + bid/ask spread.
func (s *PriceStore) SetWithSpread(coin, exchange string, price, bid, ask float64) {
s.mu.Lock()
defer s.mu.Unlock()
if s.prices[coin] == nil {
s.prices[coin] = make(map[string]float64)
}
s.prices[coin][exchange] = price
if bid > 0 && ask > 0 {
if s.spreads[coin] == nil {
s.spreads[coin] = make(map[string]*Spread)
}
s.spreads[coin][exchange] = &Spread{
Bid: bid, Ask: ask,
Updated: time.Now().UnixNano(),
}
}
}
func (s *PriceStore) Get(coin, exchange string) (float64, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
p, ok := s.prices[coin][exchange]
return p, ok
}
// GetSpread returns the current bid-ask spread (as percentage of mid price).
// Returns 0 if no spread data available.
func (s *PriceStore) GetSpread(coin, exchange string) float64 {
s.mu.RLock()
defer s.mu.RUnlock()
sp, ok := s.spreads[coin][exchange]
if !ok || sp.Bid <= 0 || sp.Ask <= 0 {
return 0
}
mid := (sp.Bid + sp.Ask) / 2
if mid <= 0 {
return 0
}
return (sp.Ask - sp.Bid) / mid * 100
}
// GetAll returns a snapshot of all prices.
func (s *PriceStore) GetAll() map[string]map[string]float64 {
s.mu.RLock()
defer s.mu.RUnlock()
snap := make(map[string]map[string]float64)
for coin, exMap := range s.prices {
snap[coin] = make(map[string]float64)
for ex, p := range exMap {
snap[coin][ex] = p
}
}
return snap
}