Skip to content

Commit 9601d43

Browse files
committed
fix(binance): 同步服务器时间并在 -1021 时自动重试,稳定签名调用
1 parent 8832557 commit 9601d43

File tree

1 file changed

+174
-68
lines changed

1 file changed

+174
-68
lines changed

trader/binance_futures.go

Lines changed: 174 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package trader
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"log"
78
"strconv"
89
"sync"
910
"time"
1011

12+
"github.com/adshao/go-binance/v2/common"
1113
"github.com/adshao/go-binance/v2/futures"
1214
)
1315

@@ -27,15 +29,74 @@ type FuturesTrader struct {
2729

2830
// 缓存有效期(15秒)
2931
cacheDuration time.Duration
32+
33+
// 服务器时间同步
34+
timeSyncMutex sync.Mutex
35+
lastTimeSync time.Time
36+
timeSyncInterval time.Duration
3037
}
3138

3239
// NewFuturesTrader 创建合约交易器
3340
func NewFuturesTrader(apiKey, secretKey string) *FuturesTrader {
3441
client := futures.NewClient(apiKey, secretKey)
35-
return &FuturesTrader{
36-
client: client,
37-
cacheDuration: 15 * time.Second, // 15秒缓存
42+
trader := &FuturesTrader{
43+
client: client,
44+
cacheDuration: 15 * time.Second, // 15秒缓存
45+
timeSyncInterval: 30 * time.Second,
46+
}
47+
48+
if err := trader.syncServerTime(context.Background(), true); err != nil {
49+
log.Printf("⚠️ 初始化同步币安服务器时间失败: %v", err)
50+
}
51+
52+
return trader
53+
}
54+
55+
// syncServerTime 同步本地与币安服务器的时间偏移
56+
func (t *FuturesTrader) syncServerTime(ctx context.Context, force bool) error {
57+
t.timeSyncMutex.Lock()
58+
defer t.timeSyncMutex.Unlock()
59+
60+
if !force && !t.lastTimeSync.IsZero() && time.Since(t.lastTimeSync) < t.timeSyncInterval {
61+
return nil
62+
}
63+
64+
offset, err := t.client.NewSetServerTimeService().Do(ctx)
65+
if err != nil {
66+
return err
67+
}
68+
69+
t.lastTimeSync = time.Now()
70+
drift := time.Duration(offset) * time.Millisecond
71+
log.Printf("✓ Binance服务器时间同步成功 (offset=%s)", drift)
72+
return nil
73+
}
74+
75+
// callWithTimeSync 在调用需要签名的接口前后处理服务器时间同步,并在时间偏差错误时重试一次
76+
func (t *FuturesTrader) callWithTimeSync(operation string, call func() error) error {
77+
ctx := context.Background()
78+
79+
if err := t.syncServerTime(ctx, false); err != nil {
80+
log.Printf("⚠️ 同步Binance服务器时间失败(%s): %v", operation, err)
3881
}
82+
83+
err := call()
84+
if err == nil {
85+
return nil
86+
}
87+
88+
var apiErr *common.APIError
89+
if errors.As(err, &apiErr) && apiErr.Code == -1021 {
90+
log.Printf("⚠️ Binance返回时间偏差错误(%s),尝试强制同步后重试: %s", operation, apiErr.Message)
91+
if syncErr := t.syncServerTime(ctx, true); syncErr != nil {
92+
log.Printf("❌ Binance服务器时间强制同步失败: %v", syncErr)
93+
return err
94+
}
95+
96+
err = call()
97+
}
98+
99+
return err
39100
}
40101

41102
// GetBalance 获取账户余额(带缓存)
@@ -52,7 +113,13 @@ func (t *FuturesTrader) GetBalance() (map[string]interface{}, error) {
52113

53114
// 缓存过期或不存在,调用API
54115
log.Printf("🔄 缓存过期,正在调用币安API获取账户余额...")
55-
account, err := t.client.NewGetAccountService().Do(context.Background())
116+
117+
var account *futures.Account
118+
err := t.callWithTimeSync("获取账户信息", func() error {
119+
var innerErr error
120+
account, innerErr = t.client.NewGetAccountService().Do(context.Background())
121+
return innerErr
122+
})
56123
if err != nil {
57124
log.Printf("❌ 币安API调用失败: %v", err)
58125
return nil, fmt.Errorf("获取账户信息失败: %w", err)
@@ -91,7 +158,13 @@ func (t *FuturesTrader) GetPositions() ([]map[string]interface{}, error) {
91158

92159
// 缓存过期或不存在,调用API
93160
log.Printf("🔄 缓存过期,正在调用币安API获取持仓信息...")
94-
positions, err := t.client.NewGetPositionRiskService().Do(context.Background())
161+
162+
var positions []*futures.PositionRisk
163+
err := t.callWithTimeSync("获取持仓信息", func() error {
164+
var innerErr error
165+
positions, innerErr = t.client.NewGetPositionRiskService().Do(context.Background())
166+
return innerErr
167+
})
95168
if err != nil {
96169
return nil, fmt.Errorf("获取持仓失败: %w", err)
97170
}
@@ -139,18 +212,20 @@ func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
139212
} else {
140213
marginType = futures.MarginTypeIsolated
141214
}
142-
215+
143216
// 尝试设置仓位模式
144-
err := t.client.NewChangeMarginTypeService().
145-
Symbol(symbol).
146-
MarginType(marginType).
147-
Do(context.Background())
148-
217+
err := t.callWithTimeSync("设置仓位模式", func() error {
218+
return t.client.NewChangeMarginTypeService().
219+
Symbol(symbol).
220+
MarginType(marginType).
221+
Do(context.Background())
222+
})
223+
149224
marginModeStr := "全仓"
150225
if !isCrossMargin {
151226
marginModeStr = "逐仓"
152227
}
153-
228+
154229
if err != nil {
155230
// 如果错误信息包含"No need to change",说明仓位模式已经是目标值
156231
if contains(err.Error(), "No need to change margin type") {
@@ -166,7 +241,7 @@ func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
166241
// 不返回错误,让交易继续
167242
return nil
168243
}
169-
244+
170245
log.Printf(" ✓ %s 仓位模式已设置为 %s", symbol, marginModeStr)
171246
return nil
172247
}
@@ -194,10 +269,13 @@ func (t *FuturesTrader) SetLeverage(symbol string, leverage int) error {
194269
}
195270

196271
// 切换杠杆
197-
_, err = t.client.NewChangeLeverageService().
198-
Symbol(symbol).
199-
Leverage(leverage).
200-
Do(context.Background())
272+
err = t.callWithTimeSync("设置杠杆", func() error {
273+
_, innerErr := t.client.NewChangeLeverageService().
274+
Symbol(symbol).
275+
Leverage(leverage).
276+
Do(context.Background())
277+
return innerErr
278+
})
201279

202280
if err != nil {
203281
// 如果错误信息包含"No need to change",说明杠杆已经是目标值
@@ -238,13 +316,18 @@ func (t *FuturesTrader) OpenLong(symbol string, quantity float64, leverage int)
238316
}
239317

240318
// 创建市价买入订单
241-
order, err := t.client.NewCreateOrderService().
242-
Symbol(symbol).
243-
Side(futures.SideTypeBuy).
244-
PositionSide(futures.PositionSideTypeLong).
245-
Type(futures.OrderTypeMarket).
246-
Quantity(quantityStr).
247-
Do(context.Background())
319+
var order *futures.CreateOrderResponse
320+
err = t.callWithTimeSync("开多仓", func() error {
321+
var innerErr error
322+
order, innerErr = t.client.NewCreateOrderService().
323+
Symbol(symbol).
324+
Side(futures.SideTypeBuy).
325+
PositionSide(futures.PositionSideTypeLong).
326+
Type(futures.OrderTypeMarket).
327+
Quantity(quantityStr).
328+
Do(context.Background())
329+
return innerErr
330+
})
248331

249332
if err != nil {
250333
return nil, fmt.Errorf("开多仓失败: %w", err)
@@ -281,13 +364,18 @@ func (t *FuturesTrader) OpenShort(symbol string, quantity float64, leverage int)
281364
}
282365

283366
// 创建市价卖出订单
284-
order, err := t.client.NewCreateOrderService().
285-
Symbol(symbol).
286-
Side(futures.SideTypeSell).
287-
PositionSide(futures.PositionSideTypeShort).
288-
Type(futures.OrderTypeMarket).
289-
Quantity(quantityStr).
290-
Do(context.Background())
367+
var order *futures.CreateOrderResponse
368+
err = t.callWithTimeSync("开空仓", func() error {
369+
var innerErr error
370+
order, innerErr = t.client.NewCreateOrderService().
371+
Symbol(symbol).
372+
Side(futures.SideTypeSell).
373+
PositionSide(futures.PositionSideTypeShort).
374+
Type(futures.OrderTypeMarket).
375+
Quantity(quantityStr).
376+
Do(context.Background())
377+
return innerErr
378+
})
291379

292380
if err != nil {
293381
return nil, fmt.Errorf("开空仓失败: %w", err)
@@ -331,13 +419,18 @@ func (t *FuturesTrader) CloseLong(symbol string, quantity float64) (map[string]i
331419
}
332420

333421
// 创建市价卖出订单(平多)
334-
order, err := t.client.NewCreateOrderService().
335-
Symbol(symbol).
336-
Side(futures.SideTypeSell).
337-
PositionSide(futures.PositionSideTypeLong).
338-
Type(futures.OrderTypeMarket).
339-
Quantity(quantityStr).
340-
Do(context.Background())
422+
var order *futures.CreateOrderResponse
423+
err = t.callWithTimeSync("平多仓", func() error {
424+
var innerErr error
425+
order, innerErr = t.client.NewCreateOrderService().
426+
Symbol(symbol).
427+
Side(futures.SideTypeSell).
428+
PositionSide(futures.PositionSideTypeLong).
429+
Type(futures.OrderTypeMarket).
430+
Quantity(quantityStr).
431+
Do(context.Background())
432+
return innerErr
433+
})
341434

342435
if err != nil {
343436
return nil, fmt.Errorf("平多仓失败: %w", err)
@@ -385,13 +478,18 @@ func (t *FuturesTrader) CloseShort(symbol string, quantity float64) (map[string]
385478
}
386479

387480
// 创建市价买入订单(平空)
388-
order, err := t.client.NewCreateOrderService().
389-
Symbol(symbol).
390-
Side(futures.SideTypeBuy).
391-
PositionSide(futures.PositionSideTypeShort).
392-
Type(futures.OrderTypeMarket).
393-
Quantity(quantityStr).
394-
Do(context.Background())
481+
var order *futures.CreateOrderResponse
482+
err = t.callWithTimeSync("平空仓", func() error {
483+
var innerErr error
484+
order, innerErr = t.client.NewCreateOrderService().
485+
Symbol(symbol).
486+
Side(futures.SideTypeBuy).
487+
PositionSide(futures.PositionSideTypeShort).
488+
Type(futures.OrderTypeMarket).
489+
Quantity(quantityStr).
490+
Do(context.Background())
491+
return innerErr
492+
})
395493

396494
if err != nil {
397495
return nil, fmt.Errorf("平空仓失败: %w", err)
@@ -413,9 +511,11 @@ func (t *FuturesTrader) CloseShort(symbol string, quantity float64) (map[string]
413511

414512
// CancelAllOrders 取消该币种的所有挂单
415513
func (t *FuturesTrader) CancelAllOrders(symbol string) error {
416-
err := t.client.NewCancelAllOpenOrdersService().
417-
Symbol(symbol).
418-
Do(context.Background())
514+
err := t.callWithTimeSync("取消挂单", func() error {
515+
return t.client.NewCancelAllOpenOrdersService().
516+
Symbol(symbol).
517+
Do(context.Background())
518+
})
419519

420520
if err != nil {
421521
return fmt.Errorf("取消挂单失败: %w", err)
@@ -471,16 +571,19 @@ func (t *FuturesTrader) SetStopLoss(symbol string, positionSide string, quantity
471571
return err
472572
}
473573

474-
_, err = t.client.NewCreateOrderService().
475-
Symbol(symbol).
476-
Side(side).
477-
PositionSide(posSide).
478-
Type(futures.OrderTypeStopMarket).
479-
StopPrice(fmt.Sprintf("%.8f", stopPrice)).
480-
Quantity(quantityStr).
481-
WorkingType(futures.WorkingTypeContractPrice).
482-
ClosePosition(true).
483-
Do(context.Background())
574+
err = t.callWithTimeSync("设置止损", func() error {
575+
_, innerErr := t.client.NewCreateOrderService().
576+
Symbol(symbol).
577+
Side(side).
578+
PositionSide(posSide).
579+
Type(futures.OrderTypeStopMarket).
580+
StopPrice(fmt.Sprintf("%.8f", stopPrice)).
581+
Quantity(quantityStr).
582+
WorkingType(futures.WorkingTypeContractPrice).
583+
ClosePosition(true).
584+
Do(context.Background())
585+
return innerErr
586+
})
484587

485588
if err != nil {
486589
return fmt.Errorf("设置止损失败: %w", err)
@@ -509,16 +612,19 @@ func (t *FuturesTrader) SetTakeProfit(symbol string, positionSide string, quanti
509612
return err
510613
}
511614

512-
_, err = t.client.NewCreateOrderService().
513-
Symbol(symbol).
514-
Side(side).
515-
PositionSide(posSide).
516-
Type(futures.OrderTypeTakeProfitMarket).
517-
StopPrice(fmt.Sprintf("%.8f", takeProfitPrice)).
518-
Quantity(quantityStr).
519-
WorkingType(futures.WorkingTypeContractPrice).
520-
ClosePosition(true).
521-
Do(context.Background())
615+
err = t.callWithTimeSync("设置止盈", func() error {
616+
_, innerErr := t.client.NewCreateOrderService().
617+
Symbol(symbol).
618+
Side(side).
619+
PositionSide(posSide).
620+
Type(futures.OrderTypeTakeProfitMarket).
621+
StopPrice(fmt.Sprintf("%.8f", takeProfitPrice)).
622+
Quantity(quantityStr).
623+
WorkingType(futures.WorkingTypeContractPrice).
624+
ClosePosition(true).
625+
Do(context.Background())
626+
return innerErr
627+
})
522628

523629
if err != nil {
524630
return fmt.Errorf("设置止盈失败: %w", err)

0 commit comments

Comments
 (0)