mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-14 16:26:57 +08:00
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:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user