Files
nofx/trader/helpers.go
shinchan-zhai acc52f2cf7 fix: prevent panics from unsafe type assertions in trading code + add request body limit
Security & Reliability:
- Add requestBodyLimitMiddleware (1MB) to prevent OOM from oversized API payloads
- Fix defer resp.Body.Close() inside loop in getPublicIPFromAPI (connection leak)
- Add posFloat64/posString safe helpers for position map access

Panic Prevention (critical for trading):
- Convert 30+ unsafe type assertions (pos["key"].(type)) to safe comma-ok
  pattern across all exchange traders: OKX, Hyperliquid, Aster, Bybit,
  KuCoin, Gate, Bitget, Binance
- Fix auto_trader_risk.go: drawdown monitor could panic and silently stop
  monitoring, leaving positions unprotected
- Fix auto_trader_decision.go & auto_trader_loop.go: core trading loop
  position parsing now crash-proof
- All trader/ code now has zero unsafe type assertions

Frontend:
- Fix config.ts: rejected promise cached forever on network error (never retries)
2026-03-23 12:01:44 +08:00

90 lines
2.1 KiB
Go

package trader
import (
"fmt"
"strconv"
)
// posFloat64 extracts a float64 from a position map, returning 0 on failure.
// Use in loops where a malformed position should be skipped, not crash.
func posFloat64(pos map[string]interface{}, key string) float64 {
v, _ := SafeFloat64(pos, key)
return v
}
// posString extracts a string from a position map, returning "" on failure.
func posString(pos map[string]interface{}, key string) string {
v, _ := SafeString(pos, key)
return v
}
// SafeFloat64 Safely extract float64 value from map
func SafeFloat64(data map[string]interface{}, key string) (float64, error) {
value, ok := data[key]
if !ok {
return 0, fmt.Errorf("key '%s' not found", key)
}
switch v := value.(type) {
case float64:
return v, nil
case float32:
return float64(v), nil
case int:
return float64(v), nil
case int64:
return float64(v), nil
case string:
// Try to parse string as float64
parsed, err := strconv.ParseFloat(v, 64)
if err != nil {
return 0, fmt.Errorf("cannot parse string '%s' as float64: %w", v, err)
}
return parsed, nil
default:
return 0, fmt.Errorf("value for key '%s' is not a number (type: %T)", key, v)
}
}
// SafeString Safely extract string value from map
func SafeString(data map[string]interface{}, key string) (string, error) {
value, ok := data[key]
if !ok {
return "", fmt.Errorf("key '%s' not found", key)
}
switch v := value.(type) {
case string:
return v, nil
case fmt.Stringer:
return v.String(), nil
default:
return fmt.Sprintf("%v", v), nil
}
}
// SafeInt Safely extract int value from map
func SafeInt(data map[string]interface{}, key string) (int, error) {
value, ok := data[key]
if !ok {
return 0, fmt.Errorf("key '%s' not found", key)
}
switch v := value.(type) {
case int:
return v, nil
case int64:
return int(v), nil
case float64:
return int(v), nil
case string:
parsed, err := strconv.Atoi(v)
if err != nil {
return 0, fmt.Errorf("cannot parse string '%s' as int: %w", v, err)
}
return parsed, nil
default:
return 0, fmt.Errorf("value for key '%s' is not an integer (type: %T)", key, v)
}
}