当新手打开交易所终端时,他看到两个按钮:"买入"和"卖出"。当算法交易者打开自己的代码库时,他看到二十七种订单类型、三层抽象和一堆让人想合上笔记本去菜市场卖黄瓜的边界情况。但遗憾的是,卖黄瓜无法在UTC时间3:59做资金费率套利——所以让我们深入研究吧。
在这篇文章中,我们将从基础的交易所订单一路走到只存在于你系统中、永远不会出现在订单簿中的合成虚拟构造。期待TypeScript、Python、一些痛苦和一点顿悟。
1. 标准交易所订单:不可或缺的基础
标准订单类型分类:从市价单到冰山订单
在构建复杂的东西之前,我们需要确保正确理解基本构建模块。令人惊讶的是,有多少人混淆了止损限价单和止损市价单,然后奇怪为什么他们的止损"没有触发"(剧透:确实触发了,但限价单因滑点而未能成交)。
市价单(Market order)
最简单同时也是最危险的类型。你告诉交易所:"现在买/卖,以任何可用价格。"交易所从订单簿中获取流动性,从最优价格开始。如果最优价位的数量不够——就会继续滑向更深的价位。
何时使用: 紧急平仓、执行速度重于价格的信号。
陷阱: 在流动性稀薄的市场上,100 BTC的市价单可能将价格推动几个百分点。不考虑市场冲击来模拟市价单的回测纯属幻想。
限价单(Limit order)
你指定一个具体的价格。订单进入订单簿等待,直到有人接受你的价格。如果限价买入单的价格高于当前市场价——它会立即成交(类似市价单,但有最高价格保证)。
关键点: 限价单不保证成交。价格可能触及你的水平后反转,而你仍然排在队列中(详见我们关于队列位置的文章)。
止损市价单和止损限价单
这里是混淆开始的地方。两种类型都是"休眠"订单,在达到触发价格(止损价格)时激活。但是:
- 止损市价单(Stop-market):触发后转为市价单。保证成交,但不保证价格。
- 止损限价单(Stop-limit):触发后转为限价单。保证价格(不差于指定价),但不保证成交。
在波动剧烈的加密市场上,止损限价单可能会"失效"——价格突破了止损位,限价单已挂出,但市场已经飞走了。你留下了一个未成交的限价单和不断增长的亏损。这正是为什么止损通常使用止损市价单的原因。
追踪止损(Trailing stop)
一种以设定距离"跟随"价格的止损。价格上涨——止损上移。价格下跌——止损保持不动。适用于趋势策略中保护利润。
交易所支持: 并非所有交易所都支持原生追踪止损。算法交易者通常以编程方式实现——这样可以更好地控制参数(回调率、激活价格、步长)。
冰山订单(Iceberg order)
订单簿中只显示总量的一部分。你想买1000 BTC,但只在簿上显示10个。当前10个成交后——下一个10个出现。
为什么: 为了不向市场暴露你的真实意图。订单簿中的大单是向所有人发出的信号,表明"有大户想买/卖"。高频交易算法会开始抢先交易,价格就会偏离你的方向。
注意: 在许多加密交易所,冰山订单要么不支持,要么很容易通过相同数量的模式被检测到。高级算法会随机化可见部分的大小。
有效时间参数:GTC、GTD、IOC、FOK
这些不是独立的订单类型,而是有效时间参数——订单存活多长时间:
| 参数 | 全称 | 行为 |
|---|---|---|
| GTC | Good Till Cancelled | 直到取消。默认标准 |
| GTD | Good Till Date | 直到指定的日期/时间 |
| IOC | Immediate or Cancel | 立即执行(全部或部分),剩余取消 |
| FOK | Fill or Kill | 仅全部立即执行。如不可能——完全取消 |
IOC与FOK: 区别至关重要。IOC可以部分成交——你想买100 BTC,买了3个,其余被取消。FOK要么100个,要么全不成交。
仅挂单(Post-only / Maker-only)
保证以挂单方进入订单簿、永远不以吃单方成交的订单。如果下单时的价格会导致立即成交——交易所会拒绝它(或调整价格,取决于交易所)。
为什么: 挂单手续费通常低于吃单手续费(Binance上VIP级别为0.02% vs 0.04%)。对于每天下数千单的做市商来说,手续费差异就是盈利和亏损之间的差异。
2. TWAP和VWAP:机构如何在订单簿中隐藏大象
当对冲基金想建立5000万美元的仓位时,它不会下一个市价单。它使用执行算法——将大订单拆分成许多小订单,并在一段时间内执行,以最小化市场冲击。
TWAP(时间加权平均价格)
思路非常简单:将总量分成等份,在等间隔的时间段内执行。
import asyncio
from datetime import datetime, timedelta
class TWAPExecutor:
"""
TWAP执行器:将大订单分成等份,
并在等间隔的时间段内执行。
"""
def __init__(self, exchange, symbol: str, side: str,
total_qty: float, duration_minutes: int, num_slices: int):
self.exchange = exchange
self.symbol = symbol
self.side = side
self.total_qty = total_qty
self.slice_qty = total_qty / num_slices
self.interval = (duration_minutes * 60) / num_slices
self.num_slices = num_slices
self.executed_qty = 0.0
self.fills: list[dict] = []
async def execute(self):
for i in range(self.num_slices):
remaining = self.total_qty - self.executed_qty
qty = min(self.slice_qty, remaining)
if qty <= 0:
break
try:
order = await self.exchange.create_order(
symbol=self.symbol,
type="market",
side=self.side,
amount=qty,
)
self.executed_qty += float(order["filled"])
self.fills.append(order)
print(f"[TWAP] slice {i+1}/{self.num_slices}: "
f"filled {order['filled']} @ {order['average']}")
except Exception as e:
print(f"[TWAP] slice {i+1} failed: {e}")
if i < self.num_slices - 1:
await asyncio.sleep(self.interval)
avg_price = (
sum(f["cost"] for f in self.fills) /
sum(f["filled"] for f in self.fills)
) if self.fills else 0
print(f"[TWAP] done: {self.executed_qty}/{self.total_qty} "
f"avg price: {avg_price:.2f}")
VWAP(成交量加权平均价格)
VWAP更智能:它考虑了典型的成交量分布。如果9:00到10:00通常交易了日成交量的30%,那么VWAP会在这个时段执行30%的订单。目标是让平均成交价尽可能接近市场VWAP。
class VWAPExecutor:
"""
VWAP执行器:按照历史成交量分布
按比例分配订单量。
"""
def __init__(self, exchange, symbol: str, side: str,
total_qty: float, volume_profile: list[float]):
self.exchange = exchange
self.symbol = symbol
self.side = side
self.total_qty = total_qty
total_weight = sum(volume_profile)
self.weights = [w / total_weight for w in volume_profile]
async def execute(self, interval_seconds: float = 60.0):
executed = 0.0
for i, weight in enumerate(self.weights):
qty = self.total_qty * weight
remaining = self.total_qty - executed
qty = min(qty, remaining)
if qty <= 0:
break
order = await self.exchange.create_order(
symbol=self.symbol,
type="market",
side=self.side,
amount=qty,
)
executed += float(order["filled"])
print(f"[VWAP] period {i+1}: weight={weight:.2%}, "
f"filled={order['filled']} @ {order['average']}")
await asyncio.sleep(interval_seconds)
TWAP与VWAP的区别: TWAP更简单、更可预测。VWAP能获得更好的平均价格,但需要可靠的成交量分布。在加密市场中,由于成交量可能存在刷量行为,VWAP分布需要谨慎构建。
3. 追价限价单:当你的订单会追着价格跑
追价限价单:订单以可配置的激进程度追逐移动的价格
现在进入最有趣的部分。标准限价单是一个被动实体:它挂在订单簿中等待。如果价格移走了——订单保持未成交。对于算法交易者来说,这通常是不可接受的:入场信号已发出,但因为市场移动了0.1%而未能建仓。
追价限价单(Chasing limit order) 是对限价单的程序化封装,它:
- 以当前最优价格(或略有偏移)挂出限价单
- 通过WebSocket监控价格
- 如果价格偏离订单——取消并以更接近当前价格重新挂单
- 重复直到订单成交或超过允许的偏差
关键参数
- chase_interval_ms — 检查和重新挂单的频率。100ms——激进,1000ms——温和。
- max_chase_distance — 距初始价格的最大偏差,超过后取消订单。防止追逐失控的市场。
- aggression_level — 限价单距市场价多近。
0——在最优买/卖价(被动),1——穿越价差(激进,实际上是吃单)。 - chase_on_partial — 订单部分成交后是否继续追价。
TypeScript实现
interface ChasingOrderParams {
symbol: string;
side: "buy" | "sell";
totalQty: number;
/** 0 = passive (at best bid/ask), 1 = cross spread */
aggression: number;
/** max price deviation from initial price */
maxChaseDistance: number;
/** how often to re-evaluate, ms */
chaseIntervalMs: number;
/** stop chasing after this many ms */
timeoutMs: number;
}
class ChasingLimitOrder {
private currentOrderId: string | null = null;
private filledQty = 0;
private initialPrice: number | null = null;
private startTime = Date.now();
constructor(
private exchange: any, // ccxt exchange instance
private params: ChasingOrderParams
) {}
async execute(): Promise<{ filledQty: number; avgPrice: number }> {
const fills: Array<{ qty: number; price: number }> = [];
while (this.filledQty < this.params.totalQty) {
// 超时检查
if (Date.now() - this.startTime > this.params.timeoutMs) {
console.log("[CHASE] timeout reached, cancelling");
await this.cancelCurrent();
break;
}
// 获取当前订单簿
const book = await this.exchange.fetchOrderBook(
this.params.symbol, 5
);
const bestBid = book.bids[0][0];
const bestAsk = book.asks[0][0];
const spread = bestAsk - bestBid;
// 计算目标价格
let targetPrice: number;
if (this.params.side === "buy") {
targetPrice = bestBid + spread * this.params.aggression;
} else {
targetPrice = bestAsk - spread * this.params.aggression;
}
// 记录初始价格
if (this.initialPrice === null) {
this.initialPrice = targetPrice;
}
// 检查最大追价距离
const deviation = Math.abs(targetPrice - this.initialPrice);
if (deviation > this.params.maxChaseDistance) {
console.log(
`[CHASE] max deviation exceeded: ${deviation.toFixed(4)} > ` +
`${this.params.maxChaseDistance}`
);
await this.cancelCurrent();
break;
}
// 检查当前订单
if (this.currentOrderId) {
const order = await this.exchange.fetchOrder(
this.currentOrderId, this.params.symbol
);
if (order.status === "closed") {
fills.push({ qty: order.filled, price: order.average });
this.filledQty += order.filled;
this.currentOrderId = null;
continue;
}
// 更新部分成交的filledQty
if (order.filled > 0) {
const newFilled = order.filled - (
fills.reduce((s, f) => s + f.qty, 0) - this.filledQty
);
// 订单还在——需要重新挂单吗?
}
const currentPrice = parseFloat(order.price);
const priceDiff = Math.abs(currentPrice - targetPrice);
const tickSize = spread * 0.1 || 0.01;
if (priceDiff > tickSize) {
// 价格移动了——重新挂单
console.log(
`[CHASE] repricing: ${currentPrice} -> ` +
`${targetPrice.toFixed(4)}`
);
await this.cancelCurrent();
} else {
// 订单在正确的价格上——等待
await this.sleep(this.params.chaseIntervalMs);
continue;
}
}
// 挂出新订单
const remainingQty = this.params.totalQty - this.filledQty;
const order = await this.exchange.createLimitOrder(
this.params.symbol,
this.params.side,
remainingQty,
targetPrice
);
this.currentOrderId = order.id;
console.log(
`[CHASE] placed ${this.params.side} ${remainingQty} ` +
`@ ${targetPrice.toFixed(4)}`
);
await this.sleep(this.params.chaseIntervalMs);
}
const totalCost = fills.reduce((s, f) => s + f.qty * f.price, 0);
const avgPrice = this.filledQty > 0 ? totalCost / this.filledQty : 0;
return { filledQty: this.filledQty, avgPrice };
}
private async cancelCurrent(): Promise<void> {
if (this.currentOrderId) {
try {
await this.exchange.cancelOrder(
this.currentOrderId, this.params.symbol
);
} catch { /* 订单已成交或已取消 */ }
this.currentOrderId = null;
}
}
private sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
追价的危害
追价是一个强大的工具,但很容易变成亏损制造机:
- 取消/重挂洪水。 每次取消和重新挂单都会给API带来负载。交易所限制请求频率,激进的追价可能导致API密钥被封禁。
- 逆向选择。 如果价格一直在远离你——市场可能知道一些你不知道的事情。在这种情况下追价意味着在顶部买入。
- 从挂单方变为吃单方。 高激进度下你实际上在支付吃单手续费,只是有延迟(取消+新订单)。有时直接下市价单更简单。
4. 基于时间的订单:毫秒级精度
有些情况下,你需要执行的不是"以价格X"的订单,而是"在时间T"的订单。听起来奇怪?这实际上是一整类策略。
使用场景
资金费率套利。 在永续合约上,资金费率每8小时支付一次(Binance上为UTC时间00:00、08:00、16:00)。如果资金费率=+0.1%,你需要在结算时刻持有空头。策略:在结算前几秒开空,收取资金费率,然后平仓。时间把控至关重要——延迟一秒就意味着错过资金费率。
交易时段开盘/收盘。 在传统市场和一些加密衍生品上,有固定的交易时段。开盘竞价(NYSE、CME)是流动性最大的时刻。在竞价前100ms下单是一种优势。
基于新闻的执行。 通胀数据在预定时间发布。算法从新闻源解析数字,在50ms内下单。这里基于时间的执行与事件驱动逻辑相结合。
实现
class TimeBasedOrder {
constructor(
private exchange: any,
private symbol: string,
private side: "buy" | "sell",
private qty: number,
private orderType: "market" | "limit",
private limitPrice?: number
) {}
/**
* 安排在精确时间执行。
* 使用忙等待循环以获得最高精度。
*/
async executeAt(targetTime: Date): Promise<any> {
const targetMs = targetTime.getTime();
// 阶段1:粗略等待(sleep)
const coarseWait = targetMs - Date.now() - 500; // 提前500ms唤醒
if (coarseWait > 0) {
console.log(
`[TIME-ORDER] sleeping for ${(coarseWait / 1000).toFixed(1)}s`
);
await new Promise((r) => setTimeout(r, coarseWait));
}
// 阶段2:精确等待(忙等待)
while (Date.now() < targetMs) {
// 自旋——消耗CPU,但获得约1ms的精度
}
// 阶段3:执行
const sendTime = Date.now();
const order = await this.exchange.createOrder(
this.symbol,
this.orderType,
this.side,
this.qty,
this.limitPrice
);
console.log(
`[TIME-ORDER] executed at ${new Date(sendTime).toISOString()}, ` +
`target was ${targetTime.toISOString()}, ` +
`delta: ${sendTime - targetMs}ms`
);
return order;
}
}
// 示例:在UTC时间00:00:00精确下单(资金费率结算)
const executor = new TimeBasedOrder(exchange, "BTC/USDT", "sell", 0.1, "market");
const target = new Date("2026-03-24T00:00:00.000Z");
await executor.executeAt(target);
重要提示: 基于时间订单的精度不受你代码的限制,而是受到交易所网络延迟的限制。如果到API的ping是50ms,即使完美的忙等待也会有50ms的偏差。对于严肃的高频交易,会使用托管服务——服务器物理上就在交易所撮合引擎旁边。
5. 虚拟/合成订单:系统中的隐形人
虚拟订单:订单仅存在于机器人内存中,直到触发条件满足
这可能是算法交易者武器库中最被低估的工具。虚拟订单(也称为合成订单)是一种仅存在于你系统中的订单。在触发条件满足之前(通常是价格达到某个水平),它不会被发送到交易所。
工作原理
- 你的算法决定:"我想以40,000美元买入BTC"
- 它不是向交易所发送限价单,而是在内存中创建虚拟订单
- 订阅WebSocket价格流
- 当bid/ask达到40,000美元时——向交易所发送真实的市价单或限价单
为什么需要虚拟订单
无信息泄露。 你的订单在订单簿中不可见。没有人——其他交易者、高频交易算法、甚至交易所本身——在执行时刻之前不知道你的意图。这从根本上改变了力量平衡。
防止抢先交易。 在加密交易所,特别是透明度较低的交易所,有合理的怀疑认为大型限价单的信息可能被用于抢先交易(甚至有相关研究)。虚拟订单消除了这个风险。
网格机器人。 经典的网格机器人在不同价格水平挂出50-200个订单。如果全部发送到交易所——那就是订单簿中200个订单,它们:(a) 对所有人可见,(b) 占用交易所的订单限额(通常每个账户200-300个未完成订单),(c) 如果价格剧烈波动,全部成交,你会得到一个巨大的仓位。虚拟订单解决了这三个问题。
接飞刀。 策略:在当前价格下方-5%、-10%、-15%的水平设置虚拟买入订单。如果市场下跌——订单逐步触发。如果没有下跌——你不承担任何风险,也不占用交易所的订单限额。
TypeScript实现
interface VirtualOrder {
id: string;
symbol: string;
side: "buy" | "sell";
triggerPrice: number;
qty: number;
/** 触发时发送到交易所的订单类型 */
executionType: "market" | "limit";
/** 对于限价单:距触发价格的偏移量 */
limitOffset?: number;
status: "pending" | "triggered" | "filled" | "failed";
}
class VirtualOrderManager {
private orders: Map<string, VirtualOrder> = new Map();
private orderCounter = 0;
constructor(private exchange: any) {}
/**
* 创建虚拟订单。不会向交易所发送任何内容。
*/
addOrder(params: Omit<VirtualOrder, "id" | "status">): string {
const id = `virt_${++this.orderCounter}`;
this.orders.set(id, { ...params, id, status: "pending" });
console.log(
`[VIRTUAL] created ${params.side} ${params.qty} ` +
`${params.symbol} @ trigger ${params.triggerPrice}`
);
return id;
}
/**
* 在每个价格tick时调用(来自WebSocket)。
*/
async onPriceUpdate(
symbol: string, bestBid: number, bestAsk: number
): Promise<void> {
for (const [id, order] of this.orders) {
if (order.symbol !== symbol || order.status !== "pending") continue;
const triggered =
(order.side === "buy" && bestAsk <= order.triggerPrice) ||
(order.side === "sell" && bestBid >= order.triggerPrice);
if (!triggered) continue;
order.status = "triggered";
console.log(
`[VIRTUAL] ${id} triggered! bid=${bestBid} ask=${bestAsk}`
);
try {
let realOrder: any;
if (order.executionType === "market") {
realOrder = await this.exchange.createMarketOrder(
order.symbol, order.side, order.qty
);
} else {
const limitPrice = order.side === "buy"
? order.triggerPrice + (order.limitOffset ?? 0)
: order.triggerPrice - (order.limitOffset ?? 0);
realOrder = await this.exchange.createLimitOrder(
order.symbol, order.side, order.qty, limitPrice
);
}
order.status = "filled";
console.log(
`[VIRTUAL] ${id} filled: ${realOrder.filled} ` +
`@ ${realOrder.average ?? realOrder.price}`
);
} catch (err) {
order.status = "failed";
console.error(`[VIRTUAL] ${id} execution failed:`, err);
}
}
}
/**
* 获取所有活跃的虚拟订单。
*/
getPendingOrders(): VirtualOrder[] {
return [...this.orders.values()].filter(
(o) => o.status === "pending"
);
}
cancelOrder(id: string): boolean {
const order = this.orders.get(id);
if (order && order.status === "pending") {
this.orders.delete(id);
return true;
}
return false;
}
}
// --- 示例:使用虚拟订单的网格机器人 ---
async function gridBot(exchange: any) {
const manager = new VirtualOrderManager(exchange);
const currentPrice = 42000;
const gridStep = 200; // 网格步长
const gridLevels = 20; // 每个方向的级数
const qtyPerLevel = 0.01; // 每级BTC数量
// 创建虚拟网格
for (let i = 1; i <= gridLevels; i++) {
// 当前价格下方的买入订单
manager.addOrder({
symbol: "BTC/USDT",
side: "buy",
triggerPrice: currentPrice - gridStep * i,
qty: qtyPerLevel,
executionType: "limit",
limitOffset: 1, // limit price = trigger + 1 USDT
});
// 当前价格上方的卖出订单
manager.addOrder({
symbol: "BTC/USDT",
side: "sell",
triggerPrice: currentPrice + gridStep * i,
qty: qtyPerLevel,
executionType: "limit",
limitOffset: 1,
});
}
console.log(
`[GRID] created ${gridLevels * 2} virtual orders, ` +
`0 on exchange`
);
// WebSocket订阅(ccxt.pro伪代码)
while (true) {
const ticker = await exchange.watchTicker("BTC/USDT");
await manager.onPriceUpdate(
"BTC/USDT", ticker.bid, ticker.ask
);
}
}
虚拟订单的陷阱
-
延迟间隙。 从你看到价格到真实订单到达交易所之间存在时间差。在波动剧烈的市场上,价格可能在这20-100ms内飞走。解决方案:发送略微激进的限价单(留有余量)。
-
错过成交。 如果价格在一个tick内"穿过"了你的水平(闪崩)并反弹——你可能来不及反应。挂在簿上的普通限价单会成交,虚拟订单则不会。
-
状态管理。 虚拟订单存在于内存中。如果进程崩溃——订单丢失。解决方案:持久化存储(Redis、SQLite、文件),重启时恢复。
6. 条件/智能订单:订单组合学
当单个订单不够时,交易者将它们组合成条件结构。有些在交易所原生支持,有些则通过编程实现。
OCO(二择一订单)
两个订单相互关联:如果一个成交——另一个自动取消。经典示例:你持有多头仓位,想同时设置止盈和止损。无论哪个先触发——另一个必须被取消。
class OCOHandler:
"""
OCO:一个订单成交时,另一个被取消。
"""
def __init__(self, exchange, symbol: str):
self.exchange = exchange
self.symbol = symbol
self.order_a_id: str | None = None
self.order_b_id: str | None = None
async def place(
self,
take_profit_price: float,
stop_loss_price: float,
qty: float,
):
tp = await self.exchange.create_limit_sell_order(
self.symbol, qty, take_profit_price
)
self.order_a_id = tp["id"]
sl = await self.exchange.create_order(
self.symbol, "stop", "sell", qty,
None, {"stopPrice": stop_loss_price}
)
self.order_b_id = sl["id"]
print(f"[OCO] TP @ {take_profit_price}, SL @ {stop_loss_price}")
async def monitor(self):
"""检查状态并取消配对订单。"""
while True:
if self.order_a_id:
a = await self.exchange.fetch_order(
self.order_a_id, self.symbol
)
if a["status"] == "closed":
print("[OCO] take-profit filled, cancelling stop-loss")
await self.exchange.cancel_order(
self.order_b_id, self.symbol
)
break
if self.order_b_id:
b = await self.exchange.fetch_order(
self.order_b_id, self.symbol
)
if b["status"] == "closed":
print("[OCO] stop-loss filled, cancelling take-profit")
await self.exchange.cancel_order(
self.order_a_id, self.symbol
)
break
await asyncio.sleep(0.5)
括号订单(Bracket order)
三部分结构:主入场订单 + OCO出场(止盈+止损)。本质上是在一次调用中完成完整的交易周期:
- 入场: 限价买入订单
- 止盈: 限价卖出订单(更高价位)
- 止损: 止损市价卖出订单(更低价位)
当入场订单成交时,自动挂出止盈和止损。当其中任何一个成交时——另一个被取消。
If-Then逻辑
最灵活的方案——具有任意条件的订单链:
rules = [
{
"condition": {"symbol": "BTC/USDT", "price_above": 50000},
"action": {"type": "market_buy", "symbol": "ETH/USDT", "qty": 10},
"then": [
{
"condition": {"symbol": "ETH/USDT", "price_above": 4000},
"action": {"type": "market_sell", "symbol": "ETH/USDT", "qty": 10},
},
{
"condition": {"symbol": "ETH/USDT", "price_below": 3500},
"action": {"type": "market_sell", "symbol": "ETH/USDT", "qty": 10},
},
]
}
]
这种构造没有任何交易所原生支持——只能通过编程实现。这也是为什么算法交易系统不可避免地会发展出自己的订单管理层的原因之一。
7. 做市商如何使用特殊订单类型
做市是一个独立的世界,其订单工具箱也相应匹配。做市商的任务是持续报价买卖价差,通过价差盈利,同时最小化逆向选择(知情交易者对你进行反向交易的情况)。
Post-only是必需品
对于做市商来说,post-only不是可选项——而是必要条件。如果你的订单意外地以吃单方成交——你不是获得挂单方返佣,而是支付吃单手续费。在每天数千个订单的规模下,这是灾难性的。
async def quote(exchange, symbol, mid_price, half_spread, qty):
bid_price = mid_price - half_spread
ask_price = mid_price + half_spread
bid = await exchange.create_order(
symbol, "limit", "buy", qty, bid_price,
{"postOnly": True} # 对做市商至关重要
)
ask = await exchange.create_order(
symbol, "limit", "sell", qty, ask_price,
{"postOnly": True}
)
return bid, ask
隐藏订单
在一些交易所(Kraken、Bitfinex),可以使用隐藏订单——它们不显示在订单簿中,但在交易所上参与撮合。权衡:即使作为挂单方你也要支付吃单手续费,但你获得了匿名性。
对于做市商来说,这是库存管理的工具:如果积累了大量仓位,可以挂一个隐藏订单来减仓,而不向市场暴露你的意图。
挂钩订单(Pegged orders)
与最优买/卖价挂钩的订单。在Coinbase Advanced Trade上,例如,你可以挂一个自动跟踪最优买价、始终排在队列最前面的订单。这是交易所层面的原生追价订单——但并非随处可用。
批量订单管理
专业做市商使用批量API在单个HTTP请求中同时取消和挂出数十个订单。在Binance上这是batchOrders,在Bybit上是place-batch-order。这降低了延迟和频率限制压力。
8. 订单类型对比表
| 订单类型 | 成交保证 | 价格保证 | 在簿中可见 | 交易所原生支持 | 实现复杂度 |
|---|---|---|---|---|---|
| 市价单 | 是 | 否 | 否(即时) | 是 | 无 |
| 限价单 | 否 | 是 | 是 | 是 | 无 |
| 止损市价单 | 是(触发后) | 否 | 否 | 是 | 无 |
| 止损限价单 | 否 | 是 | 否(触发前) | 是 | 无 |
| 追踪止损 | 是(触发后) | 否 | 否 | 部分 | 低 |
| 冰山订单 | 否 | 是 | 部分 | 部分 | 中 |
| 仅挂单 | 否 | 是 | 是 | 是 | 无 |
| TWAP | 否(取决于分片) | 否 | 部分 | 否 | 中 |
| VWAP | 否 | 否 | 部分 | 否 | 高 |
| 追价限价单 | 高于限价单 | 部分 | 是(当前订单) | 否 | 中 |
| 基于时间 | 取决于类型 | 取决于类型 | 否(直到时间T) | 否 | 低 |
| 虚拟/合成 | 低于限价单 | 取决于类型 | 否 | 否 | 中 |
| OCO | 是(二选一) | 部分 | 是(两个) | 部分 | 中 |
| 括号订单 | 是 | 部分 | 是 | 少见 | 高 |
| 隐藏订单 | 否 | 是 | 否 | 少见 | 无 |
| 挂钩订单 | 否 | 动态 | 是 | 极少 | 高(如编程实现) |
结论:订单作为策略的构建模块
订单类型不只是"界面上的按钮"。它们是构建任何交易系统执行层的基本原语。"策略在回测中盈利"和"策略在生产中盈利"之间的差异往往就在这里——在你如何向交易所发送订单。
几条实践建议:
- 从标准订单开始,确保你理解细微差别(止损限价单 vs 止损市价单,IOC vs FOK)。大多数错误都出在这里。
- 虚拟订单是网格机器人的必需品。 如果你要挂超过50个订单——不要全部发送到交易所。
- 当成交率比价格更重要时需要追价。 但一定要设置max_chase_distance——否则可能会偏离很远。
- 基于时间的执行是小众但强大的工具,适用于资金费率套利和事件驱动策略。
- 自定义订单管理层对于任何严肃的算法交易系统都是不可避免的。交易所原生订单类型是不够的。
如果你正在构建交易系统并想深入了解——请查看我们关于订单簿中的队列位置、CCXT WebSocket方法和资金费率套利的文章。
参考文献和来源
- CCXT Library — 统一的加密交易所对接库,支持100+交易所
- Binance API Documentation — Binance订单类型文档
- Bybit API v5 — Bybit文档,包括批量订单
- Moallemi, C. & Yuan, K. (2017). The Value of Queue Position in a Limit Order Book. Columbia Business School Research Paper
- Cartea, A., Jaimungal, S., & Penalva, J. (2015). Algorithmic and High-Frequency Trading. Cambridge University Press
- Avellaneda, M. & Stoikov, S. (2008) — High-frequency trading in a limit order book. Quantitative Finance
- Erik Rigtorp — Order Queue Position Estimation — 队列位置估算相关资料
- Trading Technologies (TT) — 具有高级订单类型的专业交易平台
MarketMaker.cc Team
量化研究与策略