一、挂单基础:从订单到成交的完整链路

大家好,我是你们的量化交易工程师朋友。今天咱们来聊聊挂单这件事。

说实话,我刚入行那会儿,觉得挂单不就是点个买入卖出嘛,有啥好学的?结果第一次实盘就被市场狠狠教育了一顿——滑点吃掉了我大半个点的利润。从那以后,我才老老实实把挂单的每个细节都啃了一遍。

嗯,今天这一章,我们就从最基础的东西开始。别小看这些基础,我敢说,很多做了两三年的交易员,对挂单的理解其实都是模糊的。

什么是挂单?

挂单,说白了就是你把交易指令提前挂在交易所的系统里。就像你去菜市场买菜,不是当场跟菜贩子讨价还价,而是写个小纸条:「我要买10斤土豆,3块钱一斤,啥时候有人愿意这个价卖给我,你就自动帮我成交。」

在量化交易里,挂单是我们跟市场对话的基本方式。你的每一个订单,都在向市场传递一个信号:我愿意在这个价格上买(或卖)多少量。

核心要点:挂单的本质是「条件委托」——你设定条件,交易所帮你盯着市场,条件满足时自动撮合。

限价单 vs 市价单:两个极端

这两种订单类型,是每个交易员必须刻在脑子里的东西。我见过太多新手把限价单当市价单用,结果订单挂在那里一整天都没成交,还跑来问我是不是交易所出问题了...

对比维度 限价单 市价单
成交价格 你指定价格,不达到就不成交 按当前最优价成交,价格不确定
成交速度 慢,可能永远不成交 快,几乎立即成交
滑点风险 无滑点(不成交就没滑点) 高,尤其在流动性差时
适用场景 做市、低买高卖、控制成本 抢单、止损、必须成交时

我个人习惯是:做策略交易时,90%的情况用限价单。为什么?因为市价单的滑点太不可控了。你想想看,你明明算好了盈利空间,结果一个市价单进去,滑点直接吃掉一半利润,这谁受得了?

我的经验:如果你必须用市价单,建议先看看订单簿的深度。如果买一卖一之间的价差很大,或者挂单量很少,那就别用市价单了——我曾经在流动性差的币对上吃过这个亏,一个市价单下去,价格直接被打穿了两个档位。

挂单的生命周期

一个订单从出生到消亡,会经历几个阶段。理解这个生命周期,对调试策略、排查问题非常有帮助。

  1. 创建(Created):你提交了订单,交易所收到请求。
  2. 待成交(Pending/Open):订单进入订单簿,等待撮合。这时候你可以撤单。
  3. 部分成交(Partially Filled):你的订单被部分匹配了,剩下的部分还在订单簿上等着。
  4. 完全成交(Filled):订单全部成交,生命周期结束。
  5. 已撤销(Canceled):你主动撤单,或者订单过期了。
  6. 被拒绝(Rejected):交易所认为你的订单有问题(比如余额不足、价格超出限制),直接打回来。

这里有个坑,我曾经踩过:有些交易所的「部分成交」状态不会自动通知你,你得自己轮询。如果你的策略依赖订单状态做决策,一定要处理好这个细节。

注意:不同交易所对订单状态的定义可能略有差异。比如币安把「待成交」叫"NEW",而OKX叫"OPEN"。写代码时别硬编码字符串,最好用枚举或者常量。

订单簿结构解析

订单簿,就是所有挂单的集合。它分为两部分:买单(Bids)和卖单(Asks)。

买单按价格从高到低排列——谁出价高谁排前面。卖单按价格从低到高排列——谁卖得便宜谁排前面。这就是所谓的「价格优先、时间优先」原则。

下面这张图,是我用SVG画的一个简化版订单簿结构,帮你直观理解:

订单簿结构示意图 卖单 (Asks) — 价格从低到高 价格: 100.05 数量: 1,200 累计: 1,200 价格: 100.04 数量: 800 累计: 2,000 价格: 100.03 数量: 500 累计: 2,500 价差 (Spread): 100.03 - 99.98 = 0.05 买单 (Bids) — 价格从高到低 价格: 99.98 数量: 1,000 累计: 1,000 价格: 99.97 数量: 1,500 累计: 2,500 价格: 99.96 数量: 600 累计: 3,100 买一: 99.98 | 卖一: 100.03 | 最新成交价: 100.00

你看,买单和卖单之间有个空隙,这就是「价差」(Spread)。价差越小,说明市场流动性越好。我一般会监控这个指标——如果价差突然变大,说明市场可能出问题了,这时候我会暂停策略。

订单簿里还有一个重要概念叫「深度」。深度就是某个价格附近有多少挂单量。比如上图里,卖一价100.03只有500的量,如果你要卖1000,那你的订单就会吃掉卖一和卖二,成交均价就会变差。

实战要点:做量化交易时,我建议你至少关注订单簿的前5档。很多策略只看买一卖一,结果大单进来直接被干穿好几档,滑点惨不忍睹。

一个简单的挂单代码示例

说了这么多理论,咱们来点实际的。下面是一个用Python写的挂单示例,用的是CCXT库(一个通用的交易所API框架):

import ccxt

# 初始化交易所连接
exchange = ccxt.binance({
    'apiKey': '你的API_KEY',
    'secret': '你的SECRET_KEY',
})

# 限价买单:在100.00价格买入0.1个BTC
order = exchange.create_limit_buy_order(
    symbol='BTC/USDT',
    amount=0.1,
    price=100.00
)

print(f"订单ID: {order['id']}")
print(f"订单状态: {order['status']}")

# 检查订单状态
order_status = exchange.fetch_order(
    id=order['id'],
    symbol='BTC/USDT'
)

if order_status['status'] == 'open':
    print("订单还在挂着,没成交")
elif order_status['status'] == 'closed':
    print(f"成交了!成交价: {order_status['price']}")

这段代码看起来简单,但有几个细节要注意:

  • 价格精度:不同交易对的价格小数位数不一样。BTC/USDT一般是两位小数,但有些山寨币可能是四位甚至更多。我建议你从交易所的markets接口里动态获取精度信息,别写死。
  • 数量精度:同样,数量也有精度限制。比如最小交易量是0.001 BTC,你挂0.0005就会被拒绝。
  • 订单ID:一定要保存好订单ID,后面查状态、撤单都靠它。

避坑指南:我曾经在写策略时,忘记检查订单的filled字段。结果一个订单部分成交了,我还以为它完全没成交,又补了一个单子进去,导致仓位翻倍。嗯,那次亏了不少钱。所以记住:订单状态有「部分成交」这个中间态,千万别忽略。

小结

这一章我们聊了挂单的基础知识。说白了,挂单就是你和市场之间的一个约定——你定好条件,市场来匹配。限价单和市价单各有各的用处,但我的建议是:能限价就限价,除非你真的赶时间。

订单簿是市场的「实时地图」,学会读它,你就能知道资金在往哪个方向流动。下一章我们会深入聊滑点控制,到时候你会更深刻地理解订单簿的重要性。

好了,今天就到这里。有什么问题,欢迎在评论区交流。


专注资料整理