From 22f7e8b11d984dc73ba63f8c029d3324e4f9d7a9 Mon Sep 17 00:00:00 2001 From: ZhouYongyou <128128010+zhouyongyou@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:07:58 +0800 Subject: [PATCH] fix(binance): initialize dual-side position mode to prevent code=-4061 errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem When opening positions with explicit `PositionSide` parameter (LONG/SHORT), Binance API returned **code=-4061** error: ``` "No need to change position side." "code":-4061 ``` **Root cause:** - Binance accounts default to **single-side position mode** ("One-Way Mode") - In this mode, `PositionSide` parameter is **not allowed** - Code使用了 `PositionSide` 參數 (LONG/SHORT),但帳戶未啟用雙向持倉模式 **Position Mode Comparison:** | Mode | PositionSide Required | Can Hold Long+Short Simultaneously | |------|----------------------|------------------------------------| | One-Way (default) | ❌ No | ❌ No | | Hedge Mode | ✅ **Required** | ✅ Yes | ## Solution ### 1. Added setDualSidePosition() function Automatically enables Hedge Mode during trader initialization: ```go func (t *FuturesTrader) setDualSidePosition() error { err := t.client.NewChangePositionModeService(). DualSide(true). // Enable Hedge Mode Do(context.Background()) if err != nil { // Ignore "No need to change" error (already in Hedge Mode) if strings.Contains(err.Error(), "No need to change position side") { log.Printf("✓ Account already in Hedge Mode") return nil } return err } log.Printf("✓ Switched to Hedge Mode") return nil } ``` ### 2. Called in NewFuturesTrader() Runs automatically when creating trader instance: ```go func NewFuturesTrader(apiKey, secretKey string) *FuturesTrader { trader := &FuturesTrader{...} // Initialize Hedge Mode if err := trader.setDualSidePosition(); err != nil { log.Printf("⚠️ Failed to set Hedge Mode: %v", err) } return trader } ``` ## Impact - ✅ Prevents code=-4061 errors when opening positions - ✅ Enables simultaneous long+short positions (if needed) - ✅ Fails gracefully if account already in Hedge Mode - ⚠️ **One-time change**: Once enabled, cannot revert to One-Way Mode with open positions ## Testing - ✅ Compiles successfully - ⚠️ Requires Binance testnet/mainnet validation: - [ ] First initialization → switches to Hedge Mode - [ ] Subsequent initializations → ignores "No need to change" error - [ ] Open long position with PositionSide=LONG → succeeds - [ ] Open short position with PositionSide=SHORT → succeeds ## Code Changes ``` trader/binance_futures.go: - Line 3-12: Added strings import - Line 33-47: Modified NewFuturesTrader() to call setDualSidePosition() - Line 49-69: New function setDualSidePosition() Total: +25 lines ``` ## References - Binance Futures API: https://binance-docs.github.io/apidocs/futures/en/#change-position-mode-trade - Error code=-4061: "No need to change position side." - PositionSide ENUM: BOTH (One-Way) | LONG | SHORT (Hedge Mode) --- trader/binance_futures.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/trader/binance_futures.go b/trader/binance_futures.go index 354415a0..5cffd96c 100644 --- a/trader/binance_futures.go +++ b/trader/binance_futures.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "strings" "sync" "time" @@ -32,10 +33,40 @@ type FuturesTrader struct { // NewFuturesTrader 创建合约交易器 func NewFuturesTrader(apiKey, secretKey string) *FuturesTrader { client := futures.NewClient(apiKey, secretKey) - return &FuturesTrader{ + trader := &FuturesTrader{ client: client, cacheDuration: 15 * time.Second, // 15秒缓存 } + + // 设置双向持仓模式(Hedge Mode) + // 这是必需的,因为代码中使用了 PositionSide (LONG/SHORT) + if err := trader.setDualSidePosition(); err != nil { + log.Printf("⚠️ 设置双向持仓模式失败: %v (如果已是双向模式则忽略此警告)", err) + } + + return trader +} + +// setDualSidePosition 设置双向持仓模式(初始化时调用) +func (t *FuturesTrader) setDualSidePosition() error { + // 尝试设置双向持仓模式 + err := t.client.NewChangePositionModeService(). + DualSide(true). // true = 双向持仓(Hedge Mode) + Do(context.Background()) + + if err != nil { + // 如果错误信息包含"No need to change",说明已经是双向持仓模式 + if strings.Contains(err.Error(), "No need to change position side") { + log.Printf(" ✓ 账户已是双向持仓模式(Hedge Mode)") + return nil + } + // 其他错误则返回(但在调用方不会中断初始化) + return err + } + + log.Printf(" ✓ 账户已切换为双向持仓模式(Hedge Mode)") + log.Printf(" ℹ️ 双向持仓模式允许同时持有多单和空单") + return nil } // GetBalance 获取账户余额(带缓存)