Skip to main content

订单、仓位与杠杆:下单流程完整技术文档

版本: v1.0
日期: 2026-04-13
适用范围: ZTDX.io USDT 保证金永续合约


目录

  1. 概述
  2. 核心概念
  3. 下单请求
  4. 杠杆验证
  5. 保证金计算与余额冻结
  6. 撮合引擎处理
  7. 成交后仓位处理
  8. 仓位生命周期
  9. 强平价格计算
  10. 同方向加仓
  11. 反方向对冲与平仓
  12. 完整流程图
  13. 公式汇总
  14. 设计注意事项
  15. API 参考

1. 概述

ZTDX.io 永续合约的下单流程涉及三个核心实体的交互:

实体说明
订单(Order)用户提交的交易指令,携带杠杆参数
仓位(Position)订单撮合成交后产生或更新的持仓记录
杠杆(Leverage)决定保证金需求和仓位规模的倍数因子

核心设计原则:

  • 杠杆跟随订单:每笔订单独立指定杠杆,而非全局设置
  • 最大杠杆按交易对配置:不同交易对有不同的最大杠杆上限
  • 仓位按方向合并:同一用户、同一交易对、同一方向仅维持一个仓位
  • 反方向自动对冲:新订单方向与已有仓位相反时,优先平仓

2. 核心概念

2.1 杠杆的作用

杠杆本质上是一个资金效率倍数器,它影响两个方面:

下单时 — 决定需要冻结多少保证金:

所需保证金 = 名义价值 / 杠杆

持仓时 — 决定仓位规模和强平距离:

仓位规模 = 保证金 × 杠杆
强平距离 ∝ 1 / 杠杆 (杠杆越高,强平价格越近)

2.2 杠杆范围

参数说明
最小杠杆1x所有交易对统一
默认最大杠杆50x未单独配置的交易对
可配置最大杠杆最高 100x按交易对在服务端独立配置

2.3 保证金体系

参数说明
维持保证金率(MMR)0.5%低于此比例触发强制平仓
开仓费率0.1%从保证金中扣除
保证金缓冲0.5%下单时额外冻结,覆盖手续费和滑点
最小保证金$10单笔订单最低保证金
最小仓位规模$100低于此值不创建仓位

3. 下单请求

3.1 请求端点

POST /fapi/v1/order

3.2 请求参数

参数类型必填说明
symbolString交易对,如 BTCUSDT
sideEnumBUYSELL
order_typeEnumLIMITMARKET
priceDecimal限价必填委托价格
amountDecimal下单数量(代币数量)
leverageInteger杠杆倍数(1 ~ max_leverage)
signatureStringJWT 必填EIP-712 签名
timestampIntegerJWT 必填Unix 时间戳(5 分钟有效窗口)

3.3 EIP-712 签名内容

签名覆盖以下字段,确保杠杆参数不可篡改:

CreateOrder(
address wallet,
string symbol,
string side,
string orderType,
uint256 price,
uint256 amount,
uint32 leverage,
uint256 timestamp
)

注意: API Key 认证方式下,signaturetimestamp 为可选参数。


4. 杠杆验证

订单提交后,系统按以下顺序验证杠杆:

4.1 验证流程

请求中的 leverage


① 检查 leverage ≥ 1


② 读取交易对配置,获取该交易对的最大杠杆


③ 检查 leverage ≤ max_leverage


④ 验证通过 → 继续下单流程

4.2 交易对杠杆配置

每个交易对的杠杆上限由服务端配置管理,可通过 Admin API 动态调整,无需重启服务。调整后对新订单立即生效。

4.3 验证失败响应

{
"code": -1128,
"msg": "Invalid leverage. Must be between 1 and 100 for BTCUSDT"
}

5. 保证金计算与余额冻结

杠杆验证通过后,系统计算本次订单所需的保证金并冻结用户余额。

5.1 标准保证金计算

当用户没有反方向仓位时,使用标准公式:

名义价值 = amount × price
基础保证金 = 名义价值 / leverage
缓冲金额 = 基础保证金 × 0.5%
所需保证金 = 基础保证金 + 缓冲金额

计算示例:

参数
交易对BTCUSDT
方向BUY
数量1 BTC
价格$60,000
杠杆10x
名义价值 = 1 × $60,000 = $60,000
基础保证金 = $60,000 / 10 = $6,000
缓冲金额 = $6,000 × 0.5% = $30
所需保证金 = $6,000 + $30 = $6,030

5.2 对冲保证金计算

当用户已有反方向仓位时,保证金需求会降低:

场景 A:订单完全平掉反向仓位

条件: 订单名义价值 ≤ 反向仓位规模
所需保证金 = 订单名义价值 × 0.5% (仅需手续费)

示例: 持有 $50,000 空仓,下买单 $30,000

所需保证金 = $30,000 × 0.5% = $150 (而非 $30,000/leverage)

场景 B:订单平掉反向仓位后还有剩余

条件: 订单名义价值 > 反向仓位规模
净增部分 = 订单名义价值 - 反向仓位规模
净增保证金 = 净增部分 / leverage
缓冲 = 净增保证金 × 0.5%
所需保证金 = 净增保证金 + 缓冲

示例: 持有 $50,000 空仓,下买单 $80,000,杠杆 10x

净增部分 = $80,000 - $50,000 = $30,000
净增保证金 = $30,000 / 10 = $3,000
缓冲 = $3,000 × 0.5% = $15
所需保证金 = $3,000 + $15 = $3,015

场景 C:无反方向仓位

使用 5.1 节的标准公式。

5.3 余额冻结

验证用户可用余额充足后,执行冻结:

  1. 将本次订单所需保证金从「可用余额」划入「冻结余额」
  2. 订单对象记录 frozen_margin(冻结金额),用于订单成交或取消时精确释放
  3. 冻结操作与订单落库在同一事务内完成,保证一致性

6. 撮合引擎处理

6.1 订单提交到撮合引擎

保证金冻结完成后,订单写入数据库(status = pending),随后提交到内存撮合引擎:

matching_engine.submit_order(
order_id,
symbol,
user_address,
side,
order_type,
amount,
price,
leverage ← 杠杆传入撮合引擎
)

6.2 撮合过程

撮合引擎按价格-时间优先算法匹配买卖订单。成交时产生 TradeEvent

TradeEvent {
symbol: "BTCUSDT",
trade_id: UUID,
maker_order_id: UUID, // 挂单方
taker_order_id: UUID, // 吃单方
maker_address: "0x...",
taker_address: "0x...",
side: "buy", // 吃单方方向
price: 60000.0, // 成交价
amount: 1.0, // 成交数量
maker_fee: 12.0, // 0.02% of $60,000
taker_fee: 30.0, // 0.05% of $60,000
maker_leverage: 5, // 挂单方杠杆
taker_leverage: 10, // 吃单方杠杆
timestamp: ...
}

关键点: 成交事件中保留了双方各自的杠杆值(maker_leveragetaker_leverage),因为它们可能不同。

6.3 订单状态更新

订单类型全部成交部分成交未成交
市价单filledpartially_filledcancelled(IOC 特性)
限价单filledpartially_filledopen(挂入订单簿)

7. 成交后仓位处理

成交事件通过 Broadcast Channel 发送到持久化 Worker,Worker 在写入成交记录后更新仓位。

7.1 并发控制

仓位更新在同一用户同一交易对维度上串行执行,防止并发更新冲突:

键 = hash(user + symbol)
try_lock(键)
→ 成功: 继续执行
→ 失败: 等待 500ms 后重试(最多 10 次)
锁随事务结束自动释放

7.2 仓位方向确定

成交方向吃单方仓位方向挂单方仓位方向
BUY多头(Long)空头(Short)
SELL空头(Short)多头(Long)

7.3 仓位处理决策树

对于成交的每一方(maker 和 taker),系统按以下逻辑处理:

成交事件


查询反方向仓位

├─ 存在反向仓位
│ │
│ ├─ 反向仓位 ≥ 成交规模 → 减少反向仓位(部分平仓或全部平仓)
│ │ → 结束,不创建新仓位
│ │
│ └─ 反向仓位 < 成交规模 → 全部平掉反向仓位
│ → 计算剩余规模
│ → 继续创建新仓位(使用剩余规模)

└─ 无反向仓位


查询同方向仓位

├─ 存在同向仓位 → 合并仓位(加仓)

└─ 无同向仓位 → 创建新仓位

8. 仓位生命周期

8.1 仓位创建

当需要创建新仓位时,系统按以下方式计算仓位参数:

输入:
collateral_amount = 成交规模(USD) / leverage
leverage = 订单中的杠杆值
execution_price = 成交价格

计算:
size_in_usd = collateral_amount × leverage // 仓位名义规模
size_in_tokens = size_in_usd / execution_price // 仓位代币数量
position_fee = size_in_usd × 0.1% // 开仓手续费
actual_collateral = collateral_amount - position_fee // 实际保证金(扣除手续费)
liquidation_price = 根据杠杆和方向计算(见第 9 节)

仓位记录包含的字段:

字段说明
size_in_usd仓位名义规模
size_in_tokens仓位代币数量
collateral_amount实际保证金(已扣开仓费)
entry_price成交价 / 加权平均入场价
leverage杠杆倍数
liquidation_price强平价格
unrealized_pnl未实现盈亏(初始为 0)
realized_pnl已实现盈亏(初始为 0)
status仓位状态,初始为 open

8.2 仓位状态

状态说明
open活跃持仓
closed用户主动平仓
liquidated触发强制平仓

8.3 计算示例

场景:用户以 10x 杠杆买入 1 BTC @ $60,000

步骤计算结果
成交规模1 × $60,000$60,000
保证金$60,000 / 10$6,000
开仓费$60,000 × 0.1%$60
实际保证金$6,000 - $60$5,940
仓位规模$60,000$60,000
代币数量$60,000 / $60,0001 BTC
强平价格$60,000 × (1 - 0.005 - 0.1)$53,700

9. 强平价格计算

强平价格由入场价、杠杆和维持保证金率共同决定。

9.1 多头(Long)强平价格

liquidation_price = entry_price × (1 - maintenance_margin_rate - 1/leverage)

直观理解:价格下跌到保证金无法覆盖维持保证金要求时触发强平。

9.2 空头(Short)强平价格

liquidation_price = entry_price × (1 + maintenance_margin_rate + 1/leverage)

直观理解:价格上涨到保证金无法覆盖维持保证金要求时触发强平。

9.3 不同杠杆下的强平距离

以 BTC 入场价 $60,000 为例,维持保证金率 0.5%:

杠杆多头强平价距入场价空头强平价距入场价
2x$29,700-50.5%$90,300+50.5%
5x$47,700-20.5%$72,300+20.5%
10x$53,700-10.5%$66,300+10.5%
20x$56,700-5.5%$63,300+5.5%
50x$58,500-2.5%$61,500+2.5%
100x$59,100-1.5%$60,900+1.5%

结论: 杠杆每翻一倍,强平距离约减半。100x 杠杆下,价格波动 1.5% 即触发强平。


10. 同方向加仓

当用户已有同方向仓位时,新成交会合并到现有仓位中。

10.1 合并逻辑

新总规模(USD) = 原规模 + 新成交规模
新总代币数 = 原代币数 + 新成交代币数
新入场价 = 新总规模(USD) / 新总代币数 ← 加权平均
新保证金 = 原保证金 + 新保证金(扣除开仓费后)
新杠杆 = 新订单的杠杆值 ← 覆盖,非加权平均
新强平价格 = 根据新入场价和新杠杆重新计算

10.2 加仓示例

第一笔:10x 杠杆买入 1 BTC @ $60,000

字段
size_in_usd$60,000
size_in_tokens1.0 BTC
entry_price$60,000
collateral$5,940(扣除 $60 开仓费)
leverage10
liquidation_price$53,700

第二笔:20x 杠杆买入 0.5 BTC @ $62,000

新成交规模 = 0.5 × $62,000 = $31,000

字段计算新值
size_in_usd$60,000 + $31,000$91,000
size_in_tokens1.0 + 0.51.5 BTC
entry_price$91,000 / 1.5$60,666.67
collateral$5,940 + ($1,550 - $31)$7,459
leverage覆盖为新订单值20
liquidation_price根据新入场价和 20x 重算$57,581.67

10.3 重要注意事项

杠杆字段在加仓时会被最新订单的杠杆值覆盖,而非取加权平均。这意味着:

  • 仓位的 leverage 字段反映的是最近一次加仓的杠杆
  • 实际保证金(collateral_amount)是历次加仓的累加值
  • 仓位的有效杠杆size_in_usd / collateral_amount)可能与 leverage 字段不一致
  • 强平价格基于 leverage 字段重新计算,因此加仓可能导致强平价格突变

示例:

初始: 保证金 $5,940, 规模 $60,000, leverage=10, 有效杠杆=10.1x
加仓后: 保证金 $7,459, 规模 $91,000, leverage=20, 有效杠杆=12.2x
↑ 字段值 ↑ 实际值不一致

11. 反方向对冲与平仓

11.1 完全平仓

当新订单方向与现有仓位相反,且订单规模 ≤ 仓位规模时:

场景: 持有 $60,000 多仓,下卖单 $60,000

处理:
1. 计算已实现 PnL = (卖出价 - 入场价) × 代币数量
2. 将 PnL 加入用户余额
3. 释放仓位保证金到可用余额
4. 释放订单冻结保证金(如果有)
5. 仓位状态 → closed

11.2 部分平仓

场景: 持有 $60,000 多仓,下卖单 $30,000

处理:
1. 仓位规模减半: $60,000 → $30,000
2. 代币数量减半
3. 保证金按比例释放
4. 入场价不变
5. 已实现 PnL 入账
6. 仓位状态保持 open

11.3 反转仓位

场景: 持有 $50,000 多仓,下卖单 $80,000

处理:
1. 先全部平掉 $50,000 多仓(计算 PnL,释放保证金)
2. 剩余 $30,000 作为新空仓开仓
3. 新空仓使用订单中的杠杆
4. 新空仓保证金 = $30,000 / leverage - 开仓费

11.4 对冲时的保证金优势

反向下单时保证金需求显著降低:

场景所需保证金说明
无仓位,开 $60,000 多仓 (10x)$6,030标准计算
持有 $60,000 空仓,开 $60,000 多仓$300仅手续费(完全平仓)
持有 $60,000 空仓,开 $80,000 多仓 (10x)$2,010仅净增 $20,000 的保证金

12. 完整流程图

用户提交订单
POST /fapi/v1/order
│ { symbol, side, type, price, amount, leverage, signature, timestamp }


┌──────────────────────────────────────────────────┐
│ ① 请求验证 │
│ │
│ 交易对可交易? ─── 否 → 返回错误 │
│ │ 是 │
│ 时间戳有效?(5分钟窗口)─── 否 → 返回错误 │
│ │ 是 │
│ 1 ≤ leverage ≤ max_leverage? ─── 否 → 返回错误 │
│ │ 是 │
│ amount > 0? ─── 否 → 返回错误 │
│ │ 是 │
│ 限价单有 price? ─── 否 → 返回错误 │
│ │ 是 │
│ EIP-712 签名验证 ─── 失败 → 返回错误 │
│ │ 通过 │
└───────┼──────────────────────────────────────────┘

┌──────────────────────────────────────────────────┐
│ ② 保证金计算 │
│ │
│ 查询反向仓位 ────────────────────┐ │
│ │ │ │
│ ┌────┴────┐ ┌────────────┐ ┌─┴──────────┐ │
│ │ 无反向 │ │ 订单≤反向 │ │ 订单>反向 │ │
│ │ 仓位 │ │ 仓位 │ │ 仓位 │ │
│ ├─────────┤ ├────────────┤ ├────────────┤ │
│ │标准保证金│ │仅手续费 │ │净增部分 │ │
│ │= 名义值 │ │= 名义值 │ │的保证金 │ │
│ │ /杠杆 │ │ × 0.5% │ │=(净额/杠杆) │ │
│ │ +0.5% │ │ │ │ +0.5% │ │
│ └────┬────┘ └─────┬─────┘ └─────┬──────┘ │
│ └──────────────┴──────────────┘ │
│ │ │
└──────────────────────┼───────────────────────────┘

┌──────────────────────────────────────────────────┐
│ ③ 余额检查与冻结 │
│ │
│ 可用余额 ≥ 所需保证金? ─── 否 → 返回余额不足 │
│ │ 是 │
│ available -= 所需保证金 │
│ frozen += 所需保证金 │
│ 订单写入 DB(status = pending, frozen_margin) │
└───────┼──────────────────────────────────────────┘

┌──────────────────────────────────────────────────┐
│ ④ 撮合引擎匹配 │
│ │
│ submit_order(leverage) → orderbook.match_order() │
│ │ │
│ ┌────┴──────────┐ ┌────────────────────────┐ │
│ │ 成交 │ │ 未成交(限价单) │ │
│ │ → TradeEvent │ │ → 挂入订单簿等待 │ │
│ │ maker_lev │ │ status = open │ │
│ │ taker_lev │ │ │ │
│ └────┬──────────┘ └────────────────────────┘ │
└───────┼──────────────────────────────────────────┘

┌──────────────────────────────────────────────────┐
│ ⑤ 成交持久化 │
│ │
│ 记录成交(含双方杠杆) │
│ 累加订单已成交数量 │
│ 记录返佣收益(如适用) │
└───────┼──────────────────────────────────────────┘

┌──────────────────────────────────────────────────┐
│ ⑥ 仓位更新 │
│ (串行互斥保护, 最多重试 3 次) │
│ │
│ 对 maker 和 taker 分别执行: │
│ │
│ 查反向仓位 ─┬─ 有且 ≥ 成交 → 减仓/平仓 │
│ ├─ 有且 < 成交 → 平反向 + 开新仓 │
│ └─ 无 → 查同向仓位 │
│ │ │
│ ┌──────────┴──────────┐ │
│ │ 有同向仓位 │ 无同向仓位 │
│ │ → 合并仓位 │ → 创建新仓位 │
│ │ 加权平均入场价 │ │
│ │ 累加保证金 │ │
│ │ 覆盖杠杆 │ │
│ │ 重算强平价格 │ │
│ └─────────────────────┘ │
│ │
│ 成交标记为仓位同步完成 │
└──────────────────────────────────────────────────┘

13. 公式汇总

13.1 保证金相关

公式说明
required_margin = (amount × price / leverage) × 1.005下单所需保证金(含 0.5% 缓冲)
position_fee = size_in_usd × 0.001开仓手续费(0.1%)
actual_collateral = (size_in_usd / leverage) - position_fee扣费后实际保证金

13.2 仓位相关

公式说明
size_in_usd = collateral × leverage仓位名义规模
size_in_tokens = size_in_usd / execution_price仓位代币数量
entry_price = total_size_usd / total_size_tokens加权平均入场价(加仓时)

13.3 强平价格

方向公式
多头liq_price = entry_price × (1 - MMR - 1/leverage)
空头liq_price = entry_price × (1 + MMR + 1/leverage)

其中 MMR(维持保证金率)= 0.5%

13.4 盈亏计算

方向公式
多头未实现 PnL(mark_price - entry_price) × size_in_tokens
空头未实现 PnL(entry_price - mark_price) × size_in_tokens

13.5 手续费

类型费率计算基础
Maker Fee0.02%成交名义价值
Taker Fee0.05%成交名义价值
Position Fee0.1%仓位名义规模
Liquidation Fee0.5%仓位名义规模

14. 设计注意事项

14.1 杠杆覆盖问题

当前行为: 加仓时仓位的 leverage 字段被新订单的杠杆值直接覆盖。

潜在影响:

初始: 5x 杠杆, $10,000 保证金, $50,000 仓位
加仓: 50x 杠杆, $1,000 保证金, $50,000 仓位

合并后:
保证金 = $11,000(累加)
仓位规模 = $100,000
leverage 字段 = 50(被覆盖)
有效杠杆 = $100,000 / $11,000 ≈ 9.1x
强平价格按 50x 计算 ← 与实际保证金率不匹配

建议: 在评估风险时使用有效杠杆(size_in_usd / collateral_amount)而非 leverage 字段。

14.2 并发安全

  • 仓位更新在同一用户同一交易对上串行执行,保证互斥
  • 竞争到锁失败时会退避重试(最多 10 次,间隔 500ms)
  • 持久化 Worker 限制并发度,避免突发成交冲击数据库

14.3 失败恢复

  • 成交持久化失败的交易进入死信队列,由运维侧统一处理
  • 仓位更新最多重试 3 次(间隔 100ms)
  • 每条成交记录带同步状态标记,用于追踪仓位是否已写入

15. API 参考

15.1 下单

POST /fapi/v1/order

请求体:
{
"symbol": "BTCUSDT",
"side": "BUY",
"order_type": "LIMIT",
"price": "60000",
"amount": "1.0",
"leverage": 10,
"signature": "0x...",
"timestamp": 1712966400
}

响应:
{
"code": 0,
"data": {
"order_id": "550e8400-e29b-41d4-a716-446655440000",
"symbol": "BTCUSDT",
"side": "BUY",
"order_type": "LIMIT",
"price": "60000",
"amount": "1.0",
"filled_amount": "0",
"leverage": 10,
"status": "open",
"frozen_margin": "6030",
"created_at": "2026-04-13T10:00:00Z"
}
}

15.2 调整杠杆

POST /fapi/v1/leverage

请求体:
{
"symbol": "BTCUSDT",
"leverage": 20
}

注意: 调整杠杆仅影响后续新订单,不会修改已有仓位的杠杆和强平价格。

15.3 查询仓位

GET /fapi/v1/positionRisk

响应:
{
"code": 0,
"data": [
{
"symbol": "BTCUSDT",
"side": "LONG",
"size_in_usd": "60000",
"size_in_tokens": "1.0",
"collateral_amount": "5940",
"entry_price": "60000",
"mark_price": "61500",
"leverage": 10,
"liquidation_price": "53700",
"unrealized_pnl": "1500",
"margin_ratio": "0.097"
}
]
}