fix(trader): harden API calls with timeouts, strict balance parsing, error context

- binance/bybit/gate: SDK default http.DefaultClient has no timeout; use a
  dedicated 30s-timeout client so a hung connection cannot stall the loop
- bybit: stop mutating http.DefaultClient.Transport, which leaked the
  referer header into every other HTTP request in the process
- add types.ParseFloatField: empty exchange fields stay zero, but malformed
  numeric values now surface as errors instead of silently becoming zero
  balances (applied to GetBalance across 8 exchanges)
- wrap order/market-data errors in auto_trader_orders and okx cancel paths
  with symbol context; log per-order cancel failures in okx CancelAllOrders
This commit is contained in:
tinkle-community
2026-06-11 00:30:34 +08:00
parent 094ab45476
commit 9ea9bd705f
15 changed files with 206 additions and 50 deletions

View File

@@ -55,13 +55,20 @@ func (t *OKXTrader) GetBalance() (map[string]interface{}, error) {
var usdtAvail, usdtUPL float64
for _, detail := range balance.Details {
if detail.Ccy == "USDT" {
usdtAvail, _ = strconv.ParseFloat(detail.AvailBal, 64)
usdtUPL, _ = strconv.ParseFloat(detail.UPL, 64)
if usdtAvail, err = types.ParseFloatField("availBal", detail.AvailBal); err != nil {
return nil, err
}
if usdtUPL, err = types.ParseFloatField("upl", detail.UPL); err != nil {
return nil, err
}
break
}
}
totalEq, _ := strconv.ParseFloat(balance.TotalEq, 64)
totalEq, err := types.ParseFloatField("totalEq", balance.TotalEq)
if err != nil {
return nil, err
}
result := map[string]interface{}{
"totalWalletBalance": totalEq,

View File

@@ -508,7 +508,7 @@ func (t *OKXTrader) cancelAlgoOrders(symbol string, orderType string) error {
path := fmt.Sprintf("%s?instType=SWAP&instId=%s&ordType=conditional", okxAlgoPendingPath, instId)
data, err := t.doRequest("GET", path, nil)
if err != nil {
return err
return fmt.Errorf("failed to get pending algo orders for %s: %w", symbol, err)
}
var orders []struct {
@@ -517,7 +517,7 @@ func (t *OKXTrader) cancelAlgoOrders(symbol string, orderType string) error {
}
if err := json.Unmarshal(data, &orders); err != nil {
return err
return fmt.Errorf("failed to parse pending algo orders for %s: %w", symbol, err)
}
canceledCount := 0
@@ -552,7 +552,7 @@ func (t *OKXTrader) CancelAllOrders(symbol string) error {
path := fmt.Sprintf("%s?instType=SWAP&instId=%s", okxPendingOrdersPath, instId)
data, err := t.doRequest("GET", path, nil)
if err != nil {
return err
return fmt.Errorf("failed to get pending orders for %s: %w", symbol, err)
}
var orders []struct {
@@ -561,7 +561,7 @@ func (t *OKXTrader) CancelAllOrders(symbol string) error {
}
if err := json.Unmarshal(data, &orders); err != nil {
return err
return fmt.Errorf("failed to parse pending orders for %s: %w", symbol, err)
}
// Batch cancel
@@ -570,11 +570,15 @@ func (t *OKXTrader) CancelAllOrders(symbol string) error {
"instId": order.InstId,
"ordId": order.OrdId,
}
t.doRequest("POST", okxCancelOrderPath, body)
if _, err := t.doRequest("POST", okxCancelOrderPath, body); err != nil {
logger.Infof(" ⚠ Failed to cancel order %s for %s: %v", order.OrdId, symbol, err)
}
}
// Also cancel algo orders
t.cancelAlgoOrders(symbol, "")
if err := t.cancelAlgoOrders(symbol, ""); err != nil {
logger.Infof(" ⚠ Failed to cancel algo orders for %s: %v", symbol, err)
}
if len(orders) > 0 {
logger.Infof(" ✓ Canceled all pending orders for %s", symbol)