fix: goroutine leak on shutdown, unsafe type assertions, redundant API calls

- Agent: add stopCh to cleanly stop chat-history-cleanup goroutine on Agent.Stop()
  (previously leaked a goroutine+ticker forever on every restart)
- Agent: add looksLikeStockQuery() guard to avoid hitting Sina search API on every
  single message — only calls external API when text contains stock-related content
  (saves ~200ms latency + API call on crypto-only queries)
- market/historical.go: safe type assertions in kline parsing (was bare .(float64)
  which panics on unexpected API responses), reuse HTTP client for connection pooling
- market/api_client.go: safe comma-ok type assertions for all kline field parsing
  (11 bare assertions → all guarded)
- trader/bybit: fix unsafe type assertion in CloseShort — pos["positionAmt"].(float64)
  could panic if field is nil/wrong type (critical: handles real money)
This commit is contained in:
shinchan-zhai
2026-03-23 19:20:04 +08:00
parent efdf4856c5
commit 1f8493c0cf
4 changed files with 144 additions and 35 deletions

View File

@@ -121,18 +121,40 @@ func parseKline(kr KlineResponse) (Kline, error) {
return kline, fmt.Errorf("invalid kline data")
}
// Parse each field
kline.OpenTime = int64(kr[0].(float64))
kline.Open, _ = strconv.ParseFloat(kr[1].(string), 64)
kline.High, _ = strconv.ParseFloat(kr[2].(string), 64)
kline.Low, _ = strconv.ParseFloat(kr[3].(string), 64)
kline.Close, _ = strconv.ParseFloat(kr[4].(string), 64)
kline.Volume, _ = strconv.ParseFloat(kr[5].(string), 64)
kline.CloseTime = int64(kr[6].(float64))
kline.QuoteVolume, _ = strconv.ParseFloat(kr[7].(string), 64)
kline.Trades = int(kr[8].(float64))
kline.TakerBuyBaseVolume, _ = strconv.ParseFloat(kr[9].(string), 64)
kline.TakerBuyQuoteVolume, _ = strconv.ParseFloat(kr[10].(string), 64)
// Parse each field with safe type assertions to prevent panics on unexpected API responses
if v, ok := kr[0].(float64); ok {
kline.OpenTime = int64(v)
}
if v, ok := kr[1].(string); ok {
kline.Open, _ = strconv.ParseFloat(v, 64)
}
if v, ok := kr[2].(string); ok {
kline.High, _ = strconv.ParseFloat(v, 64)
}
if v, ok := kr[3].(string); ok {
kline.Low, _ = strconv.ParseFloat(v, 64)
}
if v, ok := kr[4].(string); ok {
kline.Close, _ = strconv.ParseFloat(v, 64)
}
if v, ok := kr[5].(string); ok {
kline.Volume, _ = strconv.ParseFloat(v, 64)
}
if v, ok := kr[6].(float64); ok {
kline.CloseTime = int64(v)
}
if v, ok := kr[7].(string); ok {
kline.QuoteVolume, _ = strconv.ParseFloat(v, 64)
}
if v, ok := kr[8].(float64); ok {
kline.Trades = int(v)
}
if v, ok := kr[9].(string); ok {
kline.TakerBuyBaseVolume, _ = strconv.ParseFloat(v, 64)
}
if v, ok := kr[10].(string); ok {
kline.TakerBuyQuoteVolume, _ = strconv.ParseFloat(v, 64)
}
return kline, nil
}