@@ -2,12 +2,14 @@ package trader
22
33import (
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 创建合约交易器
3340func 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 取消该币种的所有挂单
415513func (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