From 02f6b9fdf49d0427c94ddf56a4819e533587d5df Mon Sep 17 00:00:00 2001 From: Jett Wang Date: Tue, 28 Oct 2025 16:27:34 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=20OCO=20=E5=88=86?= =?UTF-8?q?=E7=BB=84=E5=B8=B8=E9=87=8F=E5=A4=A7=E5=B0=8F=E5=86=99=E4=BB=A5?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=20HyperLiquid=20SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 normalTpSl -> normalTpsl (小写 s) - 修改 positionTpSl -> positionTpsl (小写 s) - 与 HyperLiquid SDK Grouping 类型定义保持一致 - 修复设置止盈止损时的 422 JSON 反序列化错误 - 更新相关测试和验证脚本 - 添加调试脚本用于验证订单格式 Fixes: 设置止盈止损时的 422 错误 --- services/constants.py | 6 ++- services/hyperliquid_services.py | 4 +- test_scripts/debug_tpsl.py | 70 +++++++++++++++++++++++++++++++ test_scripts/test_grouping_fix.py | 24 +++++++++++ test_scripts/verify_completion.py | 7 +++- tests/unit/test_constants.py | 9 ++-- 6 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 test_scripts/debug_tpsl.py create mode 100644 test_scripts/test_grouping_fix.py diff --git a/services/constants.py b/services/constants.py index 71b6e11..37d3e99 100644 --- a/services/constants.py +++ b/services/constants.py @@ -1,8 +1,10 @@ """HyperLiquid MCP 常量定义""" # OCO 订单分组类型 -OCO_GROUP_NEW_POSITION = "normalTpSl" # 新仓位的括号订单 -OCO_GROUP_EXISTING_POSITION = "positionTpSl" # 现有仓位的止盈止损 +# 注意: 必须与 HyperLiquid SDK 中的 Grouping 类型定义一致 +# SDK 定义: Literal["na"], Literal["normalTpsl"], Literal["positionTpsl"] +OCO_GROUP_NEW_POSITION = "normalTpsl" # 新仓位的括号订单 (小写 s) +OCO_GROUP_EXISTING_POSITION = "positionTpsl" # 现有仓位的止盈止损 (小写 s) # 订单类型常量 ORDER_TYPE_LIMIT_GTC = {"limit": {"tif": "Gtc"}} diff --git a/services/hyperliquid_services.py b/services/hyperliquid_services.py index 95ce426..c7772fe 100644 --- a/services/hyperliquid_services.py +++ b/services/hyperliquid_services.py @@ -810,12 +810,12 @@ async def set_position_tpsl( # Add stop loss order if specified if sl_px is not None: # For SL orders, use market order for fast execution - # No limit_px needed when isMarket=True + # When isMarket=True, limit_px should be 0 or the trigger price sl_order = { "coin": coin, "is_buy": not is_long, "sz": float(position_size), - "limit_px": float(sl_px), # Use trigger price as limit_px + "limit_px": float(sl_px), # For market orders, use trigger price "order_type": { "trigger": { "triggerPx": float(sl_px), diff --git a/test_scripts/debug_tpsl.py b/test_scripts/debug_tpsl.py new file mode 100644 index 0000000..5fe0683 --- /dev/null +++ b/test_scripts/debug_tpsl.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +""" +调试止盈止损订单的 JSON 格式 +""" + +import json +import os +import sys + +# Add parent directory to path +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from hyperliquid.utils.signing import order_type_to_wire + +# 模拟一个止盈订单 +tp_order = { + "coin": "BTC", + "is_buy": False, # 平多仓 + "sz": 0.001, + "limit_px": 95000.0, + "order_type": { + "trigger": { + "triggerPx": 95000.0, + "isMarket": False, + "tpsl": "tp", + } + }, + "reduce_only": True, +} + +# 模拟一个止损订单 +sl_order = { + "coin": "BTC", + "is_buy": False, # 平多仓 + "sz": 0.001, + "limit_px": 90000.0, + "order_type": { + "trigger": { + "triggerPx": 90000.0, + "isMarket": True, + "tpsl": "sl", + } + }, + "reduce_only": True, +} + +print("=" * 60) +print("止盈订单 (TP Order):") +print("=" * 60) +print(json.dumps(tp_order, indent=2)) + +print("\n" + "=" * 60) +print("止损订单 (SL Order):") +print("=" * 60) +print(json.dumps(sl_order, indent=2)) + +# 测试 order_type 转换 +print("\n" + "=" * 60) +print("TP Order Type Wire:") +print("=" * 60) +tp_wire_type = order_type_to_wire(tp_order["order_type"]) +print(json.dumps(tp_wire_type, indent=2)) + +print("\n" + "=" * 60) +print("SL Order Type Wire:") +print("=" * 60) +sl_wire_type = order_type_to_wire(sl_order["order_type"]) +print(json.dumps(sl_wire_type, indent=2)) + +print("\n✅ 订单类型格式验证通过!") diff --git a/test_scripts/test_grouping_fix.py b/test_scripts/test_grouping_fix.py new file mode 100644 index 0000000..6cb1f1f --- /dev/null +++ b/test_scripts/test_grouping_fix.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +"""测试 OCO 分组常量修复""" + +from services.constants import OCO_GROUP_EXISTING_POSITION, OCO_GROUP_NEW_POSITION + +print("=" * 60) +print("OCO 分组常量验证") +print("=" * 60) + +print(f"\nOCO_GROUP_NEW_POSITION = {repr(OCO_GROUP_NEW_POSITION)}") +print(f"OCO_GROUP_EXISTING_POSITION = {repr(OCO_GROUP_EXISTING_POSITION)}") + +# 验证与 SDK 定义一致 +assert OCO_GROUP_NEW_POSITION == "normalTpsl", f"错误: {OCO_GROUP_NEW_POSITION}" +assert ( + OCO_GROUP_EXISTING_POSITION == "positionTpsl" +), f"错误: {OCO_GROUP_EXISTING_POSITION}" + +print("\n✅ 所有常量值正确!") +print("✅ 与 HyperLiquid SDK Grouping 类型定义一致 (小写 's')") +print("\n修复说明:") +print("- 之前: normalTpSl, positionTpSl (大写 S - 错误)") +print("- 现在: normalTpsl, positionTpsl (小写 s - 正确)") +print("\n这个修复解决了 '422 Failed to deserialize the JSON body' 错误") diff --git a/test_scripts/verify_completion.py b/test_scripts/verify_completion.py index a906a24..7c28d08 100644 --- a/test_scripts/verify_completion.py +++ b/test_scripts/verify_completion.py @@ -10,8 +10,11 @@ def check_constants(): OCO_GROUP_NEW_POSITION, ) - assert OCO_GROUP_NEW_POSITION == "normalTpSl", "新仓位分组常量错误" - assert OCO_GROUP_EXISTING_POSITION == "positionTpSl", "现有仓位分组常量错误" + # 必须与 HyperLiquid SDK 中的 Grouping 类型定义完全一致(小写 's') + assert OCO_GROUP_NEW_POSITION == "normalTpsl", "新仓位分组常量错误(应为小写s)" + assert ( + OCO_GROUP_EXISTING_POSITION == "positionTpsl" + ), "现有仓位分组常量错误(应为小写s)" assert ADDRESS_PREFIX_LEN == 6, "地址前缀长度常量错误" print("✅ 常量检查通过") diff --git a/tests/unit/test_constants.py b/tests/unit/test_constants.py index 84b2ae6..fdb512b 100644 --- a/tests/unit/test_constants.py +++ b/tests/unit/test_constants.py @@ -12,10 +12,11 @@ ) -def test_oco_group_constants(): - """测试 OCO 分组常量值""" - assert OCO_GROUP_NEW_POSITION == "normalTpSl" - assert OCO_GROUP_EXISTING_POSITION == "positionTpSl" +def test_oco_grouping_constants(): + """测试 OCO 分组常量与 SDK 定义一致""" + # 必须与 HyperLiquid SDK 中的 Grouping 类型定义完全一致 + assert OCO_GROUP_NEW_POSITION == "normalTpsl" # 注意小写 's' + assert OCO_GROUP_EXISTING_POSITION == "positionTpsl" # 注意小写 's' def test_order_type_constants(): From bffc944c650f12090d401d788871c84cd8dcd212 Mon Sep 17 00:00:00 2001 From: Jett Wang Date: Tue, 28 Oct 2025 16:29:32 +0800 Subject: [PATCH 2/4] =?UTF-8?q?chore:=20=E5=8F=91=E5=B8=83=20v0.1.8=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新版本号 0.1.7 -> 0.1.8 - 更新 CHANGELOG 记录修复内容 --- CHANGELOG.md | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aba996..a79cb7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,19 +5,42 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.8] - 2025-10-28 + +### Fixed + +- **关键修复**: 修正 OCO 分组常量大小写,解决止盈止损订单 422 错误 + - 修改 `normalTpSl` -> `normalTpsl` (小写 s) + - 修改 `positionTpSl` -> `positionTpsl` (小写 s) + - 与 HyperLiquid SDK `Grouping` 类型定义保持完全一致 + - 修复设置止盈止损时的 "Failed to deserialize the JSON body" 错误 + +### Added + +- 新增调试脚本 `test_scripts/debug_tpsl.py` 用于验证订单格式 +- 新增验证脚本 `test_scripts/test_grouping_fix.py` 用于测试修复 + +### Changed + +- 更新相关测试和验证脚本以匹配新的常量值 +- 改进订单结构注释说明 + ## [0.1.6] - 2025-10-27 ### Fixed + - 清理错误的 v0.2.0 标签,恢复正确的版本历史 - 维护版本一致性 ## [0.1.5] - 2025-10-27 ### Added + - 自动格式化代码(black 和 isort) - 优化 release.prompt 支持自动版本号递增 ### Changed + - 清理冗余文档和重组文件 ## [0.1.4] - Previous Release diff --git a/pyproject.toml b/pyproject.toml index b510639..e831917 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "hyperliquid-mcp-python" -version = "0.1.7" +version = "0.1.8" description = "HyperLiquid MCP Server with FastMCP - Trading tools and account management" authors = [ {name = "jettwang", email = "jamiesun.net@gmail.com"} From 8792fb621302b1a164a90bde94a988053e007ed2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Oct 2025 08:33:24 +0000 Subject: [PATCH 3/4] style: auto-format code with Ruff [skip ci] --- test_scripts/test_grouping_fix.py | 6 +++--- test_scripts/verify_completion.py | 6 +++--- uv.lock | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test_scripts/test_grouping_fix.py b/test_scripts/test_grouping_fix.py index 6cb1f1f..72f0293 100644 --- a/test_scripts/test_grouping_fix.py +++ b/test_scripts/test_grouping_fix.py @@ -12,9 +12,9 @@ # 验证与 SDK 定义一致 assert OCO_GROUP_NEW_POSITION == "normalTpsl", f"错误: {OCO_GROUP_NEW_POSITION}" -assert ( - OCO_GROUP_EXISTING_POSITION == "positionTpsl" -), f"错误: {OCO_GROUP_EXISTING_POSITION}" +assert OCO_GROUP_EXISTING_POSITION == "positionTpsl", ( + f"错误: {OCO_GROUP_EXISTING_POSITION}" +) print("\n✅ 所有常量值正确!") print("✅ 与 HyperLiquid SDK Grouping 类型定义一致 (小写 's')") diff --git a/test_scripts/verify_completion.py b/test_scripts/verify_completion.py index 7c28d08..08b4ef6 100644 --- a/test_scripts/verify_completion.py +++ b/test_scripts/verify_completion.py @@ -12,9 +12,9 @@ def check_constants(): # 必须与 HyperLiquid SDK 中的 Grouping 类型定义完全一致(小写 's') assert OCO_GROUP_NEW_POSITION == "normalTpsl", "新仓位分组常量错误(应为小写s)" - assert ( - OCO_GROUP_EXISTING_POSITION == "positionTpsl" - ), "现有仓位分组常量错误(应为小写s)" + assert OCO_GROUP_EXISTING_POSITION == "positionTpsl", ( + "现有仓位分组常量错误(应为小写s)" + ) assert ADDRESS_PREFIX_LEN == 6, "地址前缀长度常量错误" print("✅ 常量检查通过") diff --git a/uv.lock b/uv.lock index 309d30f..9827c18 100644 --- a/uv.lock +++ b/uv.lock @@ -849,7 +849,7 @@ wheels = [ [[package]] name = "hyperliquid-mcp-python" -version = "0.1.7" +version = "0.1.8" source = { editable = "." } dependencies = [ { name = "fastmcp" }, From 717109326b0f7a5ff12b9bd906351dd165cc58a1 Mon Sep 17 00:00:00 2001 From: Jett Wang Date: Tue, 28 Oct 2025 16:37:14 +0800 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20=E8=A7=A6=E5=8F=91=20CI=20?= =?UTF-8?q?=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit