feat(market): Add WebSocket stream limit protection with auto-downgrade

## Problem
Binance WebSocket has a hard limit of 1024 streams per connection.
Without protection:
- 300 coins × 4 timeframes = 1200 streams → Connection REJECTED
- System fails to start with "全市場掃描" mode

## Solution
Implement intelligent auto-downgrade mechanism with transparent logging.

### Changes to market/monitor.go (+42 lines)

**1. Add Constants**:
```go
const (
    MaxStreamsPerConnection = 1024  // Binance hard limit
    SafeMaxSymbols = 250            // Safe limit (97.7% usage, 2.3% buffer)
)
```

**2. Add Stream Count Check in Initialize()**:
- Calculate total streams: `len(symbols) × len(subKlineTime)`
- If exceeds 250 coins → Auto-downgrade to first 250
- Display detailed adjustment logs

**3. Add Usage Rate Display**:
```
✓ WebSocket 訂閱: X 個幣種 × 4 時間週期 = Y 流 (Z% 用量)
```

**4. Add High Usage Warning (>90%)**:
```
⚠️ 警告: 訂閱流使用率較高 (93.8%),建議減少幣種數量以確保穩定性
```

## Behavior by Scenario

| Coins | Original Streams | Adjusted Streams | Behavior |
|-------|-----------------|------------------|----------|
| 8 | 32 | 32 |  Normal |
| 100 | 400 | 400 |  Normal |
| 150 | 600 | 600 |  Normal |
| 240 | 960 | 960 | ⚠️ Warning |
| 300 | 1200 | **1000** | 🔄 Auto-downgrade |
| 500 | 2000 | **1000** | 🔄 Auto-downgrade |

## Example Logs

### Normal Case (8 coins):
```
找到 8 个交易对
✓ WebSocket 訂閱: 8 個幣種 × 4 時間週期 = 32 流 (3.1% 用量)
```

### Auto-downgrade Case (300 coins):
```
找到 300 个交易对
⚠️  幣種數量過多,自動調整:
   - 原始數量: 300 個幣種 (1200 流)
   - Binance 限制: 1024 流/連接
   - 時間週期: 4 ([3m 15m 1h 4h])
   - 調整後: 250 個幣種 (1000 流)
   - 已過濾: 前 250 個幣種保留,其餘忽略
✓ WebSocket 訂閱: 250 個幣種 × 4 時間週期 = 1000 流 (97.7% 用量)
⚠️  警告: 訂閱流使用率較高 (97.7%),建議減少幣種數量以確保穩定性
```

## Benefits
-  System always starts successfully (no crashes)
-  Transparent logging (users know what happened)
-  Safe buffer (2.3% headroom for reconnections)
-  No breaking changes (8-250 coins unchanged)
-  Covers all realistic use cases (AI500 + OI Top = ~150 coins)

## Why 250, not 256?
- 256 × 4 = 1024 (no buffer, risky during reconnections)
- 250 × 4 = 1000 (24 stream buffer = 2.3% safety margin)
- Future-proof for potential 5th timeframe

Related: 15m/1h timeframe addition, WebSocket architecture merge
This commit is contained in:
ZhouYongyou
2025-11-03 19:27:01 +08:00
parent 221b03b383
commit bb2edfc293

View File

@@ -9,6 +9,14 @@ import (
"time"
)
const (
// MaxStreamsPerConnection Binance WebSocket 單連接最大訂閱流數限制
MaxStreamsPerConnection = 1024
// SafeMaxSymbols 安全的最大幣種數量(留 2.3% 緩衝空間)
// 250 個幣種 × 4 時間週期 = 1000 流 < 1024
SafeMaxSymbols = 250
)
type WSMonitor struct {
wsClient *WSClient
combinedClient *CombinedStreamsClient
@@ -69,6 +77,34 @@ func (m *WSMonitor) Initialize(coins []string) error {
}
log.Printf("找到 %d 个交易对", len(m.symbols))
// WebSocket 訂閱流數檢查與自動調整
totalStreams := len(m.symbols) * len(subKlineTime)
if len(m.symbols) > SafeMaxSymbols {
log.Printf("⚠️ 幣種數量過多,自動調整:")
log.Printf(" - 原始數量: %d 個幣種 (%d 流)", len(m.symbols), totalStreams)
log.Printf(" - Binance 限制: %d 流/連接", MaxStreamsPerConnection)
log.Printf(" - 時間週期: %d (%v)", len(subKlineTime), subKlineTime)
// 調整到安全上限
m.symbols = m.symbols[:SafeMaxSymbols]
totalStreams = len(m.symbols) * len(subKlineTime)
log.Printf(" - 調整後: %d 個幣種 (%d 流)", len(m.symbols), totalStreams)
log.Printf(" - 已過濾: 前 %d 個幣種保留,其餘忽略", SafeMaxSymbols)
}
// 顯示訂閱使用率
usagePercent := float64(totalStreams) / float64(MaxStreamsPerConnection) * 100
log.Printf("✓ WebSocket 訂閱: %d 個幣種 × %d 時間週期 = %d 流 (%.1f%% 用量)",
len(m.symbols), len(subKlineTime), totalStreams, usagePercent)
// 接近上限警告(>90%
if usagePercent > 90 {
log.Printf("⚠️ 警告: 訂閱流使用率較高 (%.1f%%),建議減少幣種數量以確保穩定性", usagePercent)
}
// 初始化历史数据
if err := m.initializeHistoricalData(); err != nil {
log.Printf("初始化历史数据失败: %v", err)