From 1c0618583c1471825bec4edfb0c93e6eee276f2d Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Mon, 4 May 2026 01:55:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B6=E6=95=9B=E9=80=80=E5=87=BA?= =?UTF-8?q?=E6=94=BE=E5=AE=BD=E5=88=B00.02%+=E5=87=80=E5=88=A9=E4=B8=BA?= =?UTF-8?q?=E6=AD=A3,=20=E6=81=A2=E5=A4=8D=E5=88=B00.02%=E5=8D=B3=E9=80=80?= =?UTF-8?q?=E4=B8=8D=E4=BA=8F=E9=92=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++-- test_dydx_ping.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++ trader.go | 11 +++++-- 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 test_dydx_ping.go diff --git a/README.md b/README.md index b58a878..924b48e 100644 --- a/README.md +++ b/README.md @@ -135,8 +135,9 @@ Round trip (2 legs entry + 2 legs exit): configurable, default **0.21%** total f - `entering` map prevents duplicate entries on same coin 3. **Scale-in** adds another leg-worth when spread widens another `scale_step_pct` (default 0.10%) 4. **Exit** conditions (whichever hits first): - - **Net profit ≥ `take_profit_pct`** → **利润止盈** - - **Spread converges to ≤ 0 (prices equal or reversed)** → **价差收敛止盈** + - **Net profit ≥ `take_profit_pct`** → **利润止盈**(大盈利退出) + - **Spread narrowed to ≤ 0.02% + netPnl > 0** → **价差收敛止盈**(小盈利退出) + - **Spread flipped negative** → **价差反转平仓**(紧急止损) - **Position held > `position_timeout_sec`** → **超时平仓** 5. **Direction**: BG → HL (buy BG, sell HL) or HL → BG (buy HL, sell BG) diff --git a/test_dydx_ping.go b/test_dydx_ping.go new file mode 100644 index 0000000..681a3d1 --- /dev/null +++ b/test_dydx_ping.go @@ -0,0 +1,76 @@ +// +build ignore + +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/url" + "time" + + "github.com/gorilla/websocket" +) + +func main() { + u := url.URL{Scheme: "wss", Host: "indexer.dydx.trade", Path: "/v4/ws"} + log.Printf("Connecting to %s", u.String()) + + c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + log.Fatal("dial:", err) + } + defer c.Close() + + done := make(chan struct{}) + + go func() { + defer close(done) + for { + _, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + return + } + log.Printf("recv: %s", string(message)) + } + }() + + // Subscribe + sub := map[string]string{"type": "subscribe", "channel": "v4_markets"} + subData, _ := json.Marshal(sub) + c.WriteMessage(websocket.TextMessage, subData) + log.Printf("sent sub: %s", string(subData)) + + // Try pings + for i := 0; i < 5; i++ { + time.Sleep(10 * time.Second) + + // Try JSON ping + ping := map[string]string{"type": "ping"} + pingData, _ := json.Marshal(ping) + err := c.WriteMessage(websocket.TextMessage, pingData) + if err != nil { + log.Printf("JSON ping error: %v", err) + } else { + log.Printf("sent JSON ping: %s", string(pingData)) + } + + // Try raw WS ping frame + err = c.WriteMessage(websocket.PingMessage, []byte("ping")) + if err != nil { + log.Printf("WS ping error: %v", err) + } else { + log.Printf("sent WS ping frame") + } + } + + time.Sleep(30 * time.Second) + fmt.Println("Done - checking if still connected") + select { + case <-done: + fmt.Println("Connection closed") + default: + fmt.Println("Still connected!") + } +} diff --git a/trader.go b/trader.go index fae0295..930e82e 100644 --- a/trader.go +++ b/trader.go @@ -534,12 +534,19 @@ func (t *Trader) checkExit(pos *ArbPosition, bgP, hlP, diffPct float64, notifier exitReason = "利润止盈" } - // Exit when spread converges to zero or reverses (prices same or flipped) - if diffPct <= 0 { + // Convergence exit: spread narrowed significantly and we're profitable + // Prevents positions from sitting at near-zero spread waiting for timeout + if diffPct <= 0.02 && netPnl > 0 { shouldExit = true exitReason = "价差收敛止盈" } + // Emergency reversal: spread flipped negative — cut losses + if diffPct < 0 { + shouldExit = true + exitReason = "价差反转平仓" + } + // Timeout: configured max hold time if elapsed > t.cfg.PositionTimeout { shouldExit = true