fix(binance): 同步服务器时间并在 -1021 时自动重试,稳定签名调用

This commit is contained in:
xiehs211
2025-11-03 17:33:43 +08:00
parent 88325572e6
commit 9601d43c41

View File

@@ -2,12 +2,14 @@ package trader
import (
"context"
"errors"
"fmt"
"log"
"strconv"
"sync"
"time"
"github.com/adshao/go-binance/v2/common"
"github.com/adshao/go-binance/v2/futures"
)
@@ -27,15 +29,74 @@ type FuturesTrader struct {
// 缓存有效期15秒
cacheDuration time.Duration
// 服务器时间同步
timeSyncMutex sync.Mutex
lastTimeSync time.Time
timeSyncInterval time.Duration
}
// NewFuturesTrader 创建合约交易器
func NewFuturesTrader(apiKey, secretKey string) *FuturesTrader {
client := futures.NewClient(apiKey, secretKey)
return &FuturesTrader{
client: client,
cacheDuration: 15 * time.Second, // 15秒缓存
trader := &FuturesTrader{
client: client,
cacheDuration: 15 * time.Second, // 15秒缓存
timeSyncInterval: 30 * time.Second,
}
if err := trader.syncServerTime(context.Background(), true); err != nil {
log.Printf("⚠️ 初始化同步币安服务器时间失败: %v", err)
}
return trader
}
// syncServerTime 同步本地与币安服务器的时间偏移
func (t *FuturesTrader) syncServerTime(ctx context.Context, force bool) error {
t.timeSyncMutex.Lock()
defer t.timeSyncMutex.Unlock()
if !force && !t.lastTimeSync.IsZero() && time.Since(t.lastTimeSync) < t.timeSyncInterval {
return nil
}
offset, err := t.client.NewSetServerTimeService().Do(ctx)
if err != nil {
return err
}
t.lastTimeSync = time.Now()
drift := time.Duration(offset) * time.Millisecond
log.Printf("✓ Binance服务器时间同步成功 (offset=%s)", drift)
return nil
}
// callWithTimeSync 在调用需要签名的接口前后处理服务器时间同步,并在时间偏差错误时重试一次
func (t *FuturesTrader) callWithTimeSync(operation string, call func() error) error {
ctx := context.Background()
if err := t.syncServerTime(ctx, false); err != nil {
log.Printf("⚠️ 同步Binance服务器时间失败%s: %v", operation, err)
}
err := call()
if err == nil {
return nil
}
var apiErr *common.APIError
if errors.As(err, &apiErr) && apiErr.Code == -1021 {
log.Printf("⚠️ Binance返回时间偏差错误%s尝试强制同步后重试: %s", operation, apiErr.Message)
if syncErr := t.syncServerTime(ctx, true); syncErr != nil {
log.Printf("❌ Binance服务器时间强制同步失败: %v", syncErr)
return err
}
err = call()
}
return err
}
// GetBalance 获取账户余额(带缓存)
@@ -52,7 +113,13 @@ func (t *FuturesTrader) GetBalance() (map[string]interface{}, error) {
// 缓存过期或不存在调用API
log.Printf("🔄 缓存过期正在调用币安API获取账户余额...")
account, err := t.client.NewGetAccountService().Do(context.Background())
var account *futures.Account
err := t.callWithTimeSync("获取账户信息", func() error {
var innerErr error
account, innerErr = t.client.NewGetAccountService().Do(context.Background())
return innerErr
})
if err != nil {
log.Printf("❌ 币安API调用失败: %v", err)
return nil, fmt.Errorf("获取账户信息失败: %w", err)
@@ -91,7 +158,13 @@ func (t *FuturesTrader) GetPositions() ([]map[string]interface{}, error) {
// 缓存过期或不存在调用API
log.Printf("🔄 缓存过期正在调用币安API获取持仓信息...")
positions, err := t.client.NewGetPositionRiskService().Do(context.Background())
var positions []*futures.PositionRisk
err := t.callWithTimeSync("获取持仓信息", func() error {
var innerErr error
positions, innerErr = t.client.NewGetPositionRiskService().Do(context.Background())
return innerErr
})
if err != nil {
return nil, fmt.Errorf("获取持仓失败: %w", err)
}
@@ -139,18 +212,20 @@ func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
} else {
marginType = futures.MarginTypeIsolated
}
// 尝试设置仓位模式
err := t.client.NewChangeMarginTypeService().
Symbol(symbol).
MarginType(marginType).
Do(context.Background())
err := t.callWithTimeSync("设置仓位模式", func() error {
return t.client.NewChangeMarginTypeService().
Symbol(symbol).
MarginType(marginType).
Do(context.Background())
})
marginModeStr := "全仓"
if !isCrossMargin {
marginModeStr = "逐仓"
}
if err != nil {
// 如果错误信息包含"No need to change",说明仓位模式已经是目标值
if contains(err.Error(), "No need to change margin type") {
@@ -166,7 +241,7 @@ func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
// 不返回错误,让交易继续
return nil
}
log.Printf(" ✓ %s 仓位模式已设置为 %s", symbol, marginModeStr)
return nil
}
@@ -194,10 +269,13 @@ func (t *FuturesTrader) SetLeverage(symbol string, leverage int) error {
}
// 切换杠杆
_, err = t.client.NewChangeLeverageService().
Symbol(symbol).
Leverage(leverage).
Do(context.Background())
err = t.callWithTimeSync("设置杠杆", func() error {
_, innerErr := t.client.NewChangeLeverageService().
Symbol(symbol).
Leverage(leverage).
Do(context.Background())
return innerErr
})
if err != nil {
// 如果错误信息包含"No need to change",说明杠杆已经是目标值
@@ -238,13 +316,18 @@ func (t *FuturesTrader) OpenLong(symbol string, quantity float64, leverage int)
}
// 创建市价买入订单
order, err := t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeBuy).
PositionSide(futures.PositionSideTypeLong).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
var order *futures.CreateOrderResponse
err = t.callWithTimeSync("开多仓", func() error {
var innerErr error
order, innerErr = t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeBuy).
PositionSide(futures.PositionSideTypeLong).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
return innerErr
})
if err != nil {
return nil, fmt.Errorf("开多仓失败: %w", err)
@@ -281,13 +364,18 @@ func (t *FuturesTrader) OpenShort(symbol string, quantity float64, leverage int)
}
// 创建市价卖出订单
order, err := t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeSell).
PositionSide(futures.PositionSideTypeShort).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
var order *futures.CreateOrderResponse
err = t.callWithTimeSync("开空仓", func() error {
var innerErr error
order, innerErr = t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeSell).
PositionSide(futures.PositionSideTypeShort).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
return innerErr
})
if err != nil {
return nil, fmt.Errorf("开空仓失败: %w", err)
@@ -331,13 +419,18 @@ func (t *FuturesTrader) CloseLong(symbol string, quantity float64) (map[string]i
}
// 创建市价卖出订单(平多)
order, err := t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeSell).
PositionSide(futures.PositionSideTypeLong).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
var order *futures.CreateOrderResponse
err = t.callWithTimeSync("平多仓", func() error {
var innerErr error
order, innerErr = t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeSell).
PositionSide(futures.PositionSideTypeLong).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
return innerErr
})
if err != nil {
return nil, fmt.Errorf("平多仓失败: %w", err)
@@ -385,13 +478,18 @@ func (t *FuturesTrader) CloseShort(symbol string, quantity float64) (map[string]
}
// 创建市价买入订单(平空)
order, err := t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeBuy).
PositionSide(futures.PositionSideTypeShort).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
var order *futures.CreateOrderResponse
err = t.callWithTimeSync("平空仓", func() error {
var innerErr error
order, innerErr = t.client.NewCreateOrderService().
Symbol(symbol).
Side(futures.SideTypeBuy).
PositionSide(futures.PositionSideTypeShort).
Type(futures.OrderTypeMarket).
Quantity(quantityStr).
Do(context.Background())
return innerErr
})
if err != nil {
return nil, fmt.Errorf("平空仓失败: %w", err)
@@ -413,9 +511,11 @@ func (t *FuturesTrader) CloseShort(symbol string, quantity float64) (map[string]
// CancelAllOrders 取消该币种的所有挂单
func (t *FuturesTrader) CancelAllOrders(symbol string) error {
err := t.client.NewCancelAllOpenOrdersService().
Symbol(symbol).
Do(context.Background())
err := t.callWithTimeSync("取消挂单", func() error {
return t.client.NewCancelAllOpenOrdersService().
Symbol(symbol).
Do(context.Background())
})
if err != nil {
return fmt.Errorf("取消挂单失败: %w", err)
@@ -471,16 +571,19 @@ func (t *FuturesTrader) SetStopLoss(symbol string, positionSide string, quantity
return err
}
_, err = t.client.NewCreateOrderService().
Symbol(symbol).
Side(side).
PositionSide(posSide).
Type(futures.OrderTypeStopMarket).
StopPrice(fmt.Sprintf("%.8f", stopPrice)).
Quantity(quantityStr).
WorkingType(futures.WorkingTypeContractPrice).
ClosePosition(true).
Do(context.Background())
err = t.callWithTimeSync("设置止损", func() error {
_, innerErr := t.client.NewCreateOrderService().
Symbol(symbol).
Side(side).
PositionSide(posSide).
Type(futures.OrderTypeStopMarket).
StopPrice(fmt.Sprintf("%.8f", stopPrice)).
Quantity(quantityStr).
WorkingType(futures.WorkingTypeContractPrice).
ClosePosition(true).
Do(context.Background())
return innerErr
})
if err != nil {
return fmt.Errorf("设置止损失败: %w", err)
@@ -509,16 +612,19 @@ func (t *FuturesTrader) SetTakeProfit(symbol string, positionSide string, quanti
return err
}
_, err = t.client.NewCreateOrderService().
Symbol(symbol).
Side(side).
PositionSide(posSide).
Type(futures.OrderTypeTakeProfitMarket).
StopPrice(fmt.Sprintf("%.8f", takeProfitPrice)).
Quantity(quantityStr).
WorkingType(futures.WorkingTypeContractPrice).
ClosePosition(true).
Do(context.Background())
err = t.callWithTimeSync("设置止盈", func() error {
_, innerErr := t.client.NewCreateOrderService().
Symbol(symbol).
Side(side).
PositionSide(posSide).
Type(futures.OrderTypeTakeProfitMarket).
StopPrice(fmt.Sprintf("%.8f", takeProfitPrice)).
Quantity(quantityStr).
WorkingType(futures.WorkingTypeContractPrice).
ClosePosition(true).
Do(context.Background())
return innerErr
})
if err != nil {
return fmt.Errorf("设置止盈失败: %w", err)