Jenis-Jenis Order dalam Dagangan Algoritma: Dari Limit dengan Pengejaran hingga Order Virtual
Apabila seorang pemula membuka terminal pertukaran, mereka melihat dua butang: "Beli" dan "Jual." Apabila seorang pedagang algo membuka pangkalan kod mereka, mereka melihat dua puluh tujuh jenis order, tiga lapisan abstraksi, dan segunung kes tepi yang membuatkan mereka ingin menutup komputer riba dan pergi menjual timun di pasar tani. Tetapi timun, malangnya, tidak membolehkan anda menjalankan arbitraj kadar pembiayaan pada pukul 3:59 UTC — jadi mari kita teruskan.
Dalam artikel ini, kita akan menelusuri keseluruhan perjalanan dari order pertukaran asas hingga kepada konstruksi virtual sintetik yang hanya wujud dalam sistem anda dan tidak pernah muncul dalam buku order. Jangkakan TypeScript, Python, sedikit kesakitan, dan sedikit pencerahan.
1. Order Pertukaran Standard: Asas yang Tidak Boleh Dilangkau
Pengelasan jenis order standard: dari pasaran hingga iceberg
Sebelum membina sesuatu yang kompleks, kita perlu memastikan kita memahami blok binaan asas dengan betul. Sungguh mengejutkan betapa ramai orang mengelirukan stop-limit dan stop-market, kemudian tertanya-tanya mengapa stop mereka "tidak tercetus" (spoiler: ia memang tercetus, tetapi order limit tidak terisi kerana slippage).
Order pasaran
Jenis yang paling mudah dan serentak paling berbahaya. Anda memberitahu pertukaran: "Beli/jual sekarang, pada harga apa pun yang tersedia." Pertukaran mengambil kecairan dari buku order, bermula pada harga terbaik. Jika volum pada aras terbaik tidak mencukupi — ia tergelincir lebih jauh.
Bila digunakan: keluar dari posisi secara kecemasan, melaksanakan isyarat di mana kelajuan lebih penting daripada harga.
Perangkap: pada pasaran yang nipis, order pasaran untuk 100 BTC boleh menggerakkan harga sebanyak beberapa peratus. Ujian balik yang memodelkan order pasaran tanpa mengambil kira impak adalah khayalan semata-mata.
Order limit
Anda menentukan harga yang tepat. Order masuk ke buku order dan menunggu sehingga seseorang bersetuju dengan harga anda. Jika harga order bid limit melebihi harga pasaran semasa — ia terisi serta-merta (seperti order pasaran, tetapi dengan harga maksimum yang dijamin).
Perkara utama: order limit tidak menjamin pelaksanaan. Harga mungkin mencapai aras anda dan berbalik, meninggalkan anda duduk dalam barisan (lebih lanjut dalam artikel kami tentang kedudukan barisan).
Stop-market dan Stop-limit
Di sinilah kekeliruan bermula. Kedua-dua jenis adalah order "tidur" yang diaktifkan apabila harga pencetus (harga stop) dicapai. Tetapi:
- Stop-market: apabila tercetus, ditukar kepada order pasaran. Menjamin pelaksanaan, tetapi bukan harga.
- Stop-limit: apabila tercetus, ditukar kepada order limit. Menjamin harga (tidak lebih buruk daripada yang ditetapkan), tetapi bukan pelaksanaan.
Pada pasaran kripto yang tidak menentu, stop-limit boleh "terlepas" — harga meledak melepasi stop, order limit ditempatkan, tetapi pasaran sudah melambung jauh. Anda tinggal dengan order limit yang tidak terisi dan kerugian yang semakin besar. Inilah sebabnya stop-market lebih kerap digunakan untuk stop-loss.
Trailing stop
Stop yang "mengikuti" harga pada jarak yang ditetapkan. Harga naik — stop bergerak naik. Harga turun — stop kekal di tempat. Berguna untuk melindungi keuntungan dalam strategi mengikuti trend.
Sokongan pertukaran: tidak semua pertukaran menyokong trailing stop secara natif. Pedagang algo sering melaksanakannya secara programatik — ini memberikan lebih kawalan ke atas parameter (kadar panggil balik, harga pengaktifan, saiz langkah).
Order iceberg
Order di mana hanya sebahagian kecil daripada jumlah volum yang kelihatan dalam buku order. Anda ingin membeli 1,000 BTC, tetapi anda hanya menunjukkan 10 dalam buku. Apabila 10 pertama terisi — 10 seterusnya muncul.
Mengapa: untuk menyembunyikan niat sebenar anda daripada pasaran. Order besar dalam buku memberi isyarat kepada semua orang bahawa "seseorang yang besar ingin membeli/menjual." Sebagai respons, algoritma HFT mula melakukan front-running, dan harga bergerak menjauh dari anda.
Peringatan: pada banyak pertukaran kripto, order iceberg sama ada tidak disokong atau mudah dikesan melalui corak volum yang sama. Algoritma canggih menjana rawak saiz bahagian yang kelihatan.
Parameter masa berlakunya: GTC, GTD, IOC, FOK
Ini bukan jenis order yang berasingan tetapi parameter masa berlaku — berapa lama sesebuah order hidup:
| Parameter | Nama Penuh | Kelakuan |
|---|---|---|
| GTC | Good Till Cancelled | Hidup sehingga dibatalkan. Standard lalai |
| GTD | Good Till Date | Hidup sehingga tarikh/masa yang ditentukan |
| IOC | Immediate or Cancel | Dilaksanakan serta-merta (sepenuhnya atau sebahagian), bakinya dibatalkan |
| FOK | Fill or Kill | Dilaksanakan hanya sepenuhnya dan serta-merta. Jika tidak mungkin — dibatalkan sepenuhnya |
IOC vs FOK: perbezaannya kritikal. IOC boleh terisi sebahagian — anda ingin membeli 100 BTC, membeli 3, bakinya dibatalkan. FOK adalah sama ada 100 atau tiada langsung.
Post-only (Maker-only)
Order yang dijamin masuk ke buku order sebagai maker dan tidak pernah dilaksanakan sebagai taker. Jika pada masa penempatan harga akan menyebabkan pelaksanaan serta-merta — pertukaran menolaknya (atau menyesuaikan harga, bergantung pada pertukaran).
Mengapa: yuran maker biasanya lebih rendah daripada yuran taker (pada Binance — 0.02% berbanding 0.04% untuk peringkat VIP). Bagi pembuat pasaran yang meletakkan ribuan order setiap hari, perbezaan yuran adalah perbezaan antara untung dan rugi.
2. TWAP dan VWAP: Cara Institusi Menyembunyikan Gajah dalam Buku Order
Apabila dana lindung nilai ingin membeli posisi bernilai $50 juta, ia tidak meletakkan satu order pasaran tunggal. Ia menggunakan algoritma pelaksanaan — algoritma yang membahagikan order besar kepada banyak yang lebih kecil dan melaksanakannya dari masa ke semasa, meminimumkan impak pasaran.
TWAP (Time-Weighted Average Price)
Idea ini amat mudah: bahagikan jumlah volum kepada bahagian yang sama dan laksanakan pada selang masa yang sama.
import asyncio
from datetime import datetime, timedelta
class TWAPExecutor:
"""
Pelaksana TWAP: membahagikan order besar kepada bahagian yang sama
dan melaksanakannya pada selang masa yang sama.
"""
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 (Volume-Weighted Average Price)
VWAP lebih pintar: ia mengambil kira profil volum dagangan yang tipikal. Jika 30% daripada volum harian biasanya didagangkan antara 9:00 dan 10:00, VWAP akan melaksanakan 30% daripada order dalam tempoh tersebut. Matlamatnya adalah untuk mendapatkan harga pelaksanaan purata sedekat mungkin dengan VWAP pasaran.
class VWAPExecutor:
"""
Pelaksana VWAP: mengedarkan volum secara berkadar
kepada profil volum sejarah.
"""
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)
Perbezaan TWAP vs VWAP: TWAP lebih mudah dan lebih boleh diramal. VWAP memberikan harga purata yang lebih baik tetapi memerlukan profil volum yang boleh dipercayai. Pada pasaran kripto, di mana volum boleh diperdagangkan secara palsu, profil VWAP perlu dibina dengan teliti.
3. Limit dengan Pengejaran: Apabila Order Anda Tahu Cara Mengejar Harga
Limit pengejaran: order mengejar harga yang bergerak dengan agresiviti yang boleh dikonfigurasi
Kini perkara menjadi benar-benar menarik. Order limit standard adalah entiti pasif: ia duduk dalam buku order dan menunggu. Jika harga bergerak — order kekal tidak terisi. Bagi pedagang algo, ini sering tidak boleh diterima: isyarat masuk telah ternyala, tetapi posisi tidak dibina kerana pasaran bergerak 0.1%.
Order limit pengejaran adalah pembungkus programatik di sekeliling order limit yang:
- Meletakkan order limit pada harga terbaik semasa (atau dengan offset kecil)
- Memantau harga melalui WebSocket
- Jika harga bergerak menjauh dari order — membatalkan dan menggantikannya lebih dekat ke harga semasa
- Mengulangi sehingga order terisi atau melebihi sisihan yang dibenarkan
Parameter Utama
- chase_interval_ms — seberapa kerap untuk memeriksa dan menggantikan order. 100ms — agresif, 1000ms — santai.
- max_chase_distance — sisihan maksimum dari harga awal sebelum order dibatalkan. Perlindungan terhadap mengejar pasaran yang lari.
- aggression_level — seberapa dekat ke harga pasaran untuk meletakkan order limit.
0— pada bid/ask terbaik (pasif),1— merentasi spread (agresif, berkesan sebagai taker). - chase_on_partial — sama ada untuk meneruskan pengejaran jika order terisi sebahagian.
Pelaksanaan TypeScript
interface ChasingOrderParams {
symbol: string;
side: "buy" | "sell";
totalQty: number;
/** 0 = pasif (pada bid/ask terbaik), 1 = merentasi spread */
aggression: number;
/** sisihan harga maksimum dari harga awal */
maxChaseDistance: number;
/** seberapa kerap untuk menilai semula, ms */
chaseIntervalMs: number;
/** berhenti mengejar selepas ini 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) {
// Tamat masa
if (Date.now() - this.startTime > this.params.timeoutMs) {
console.log("[CHASE] timeout reached, cancelling");
await this.cancelCurrent();
break;
}
// Dapatkan buku order semasa
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;
// Kira harga sasaran
let targetPrice: number;
if (this.params.side === "buy") {
targetPrice = bestBid + spread * this.params.aggression;
} else {
targetPrice = bestAsk - spread * this.params.aggression;
}
// Ingat harga awal
if (this.initialPrice === null) {
this.initialPrice = targetPrice;
}
// Semak jarak pengejaran maksimum
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;
}
// Semak order semasa
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;
}
// Kemaskini filledQty untuk isian separa
if (order.filled > 0) {
const newFilled = order.filled - (
fills.reduce((s, f) => s + f.qty, 0) - this.filledQty
);
// Order sedang berjalan — adakah kita perlu menetapkan harga semula?
}
const currentPrice = parseFloat(order.price);
const priceDiff = Math.abs(currentPrice - targetPrice);
const tickSize = spread * 0.1 || 0.01;
if (priceDiff > tickSize) {
// Harga bergerak — tetapkan harga semula
console.log(
`[CHASE] repricing: ${currentPrice} -> ` +
`${targetPrice.toFixed(4)}`
);
await this.cancelCurrent();
} else {
// Order pada harga yang betul — tunggu
await this.sleep(this.params.chaseIntervalMs);
continue;
}
}
// Letakkan order baru
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 { /* order already filled or cancelled */ }
this.currentOrderId = null;
}
}
private sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
Bila Pengejaran Membahayakan
Pengejaran adalah alat yang berkuasa, tetapi mudah dijadikan penjana kerugian:
- Spam batal/ganti. Setiap pembatalan dan penggantian adalah beban pada API. Pertukaran mengehadkan kadar permintaan, dan pengejaran agresif boleh menyebabkan kunci API anda diharamkan.
- Pemilihan buruk. Jika harga lari dari anda — pasaran mungkin mengetahui sesuatu yang anda tidak tahu. Mengejar harga dalam situasi ini bermakna membeli di puncak.
- Peralihan Maker ke Taker. Dengan agresiviti tinggi anda berkesan membayar yuran taker, tetapi dengan kelewatan (batal + order baru). Kadangkala lebih mudah untuk hanya meletakkan order pasaran.
4. Order Berasaskan Masa: Ketepatan Milisaat
Terdapat situasi di mana anda perlu melaksanakan order bukan "pada harga X" tetapi "pada masa T." Kedengaran pelik? Ia sebenarnya adalah seluruh kelas strategi.
Kes penggunaan
Arbitraj kadar pembiayaan. Pada niaga hadapan berterusan, pembiayaan dibayar setiap 8 jam (00:00, 08:00, 16:00 UTC pada Binance). Jika kadar pembiayaan = +0.1%, anda perlu berada dalam posisi pendek pada saat penyelesaian. Strategi: buka posisi pendek beberapa saat sebelum penyelesaian, kumpul pembiayaan, tutup posisi. Masa adalah kritikal — satu saat kelewatan bermakna pembiayaan terlepas.
Pembukaan/penutupan sesi. Pada pasaran tradisional dan beberapa derivatif kripto, terdapat sesi tetap. Lelongan pembukaan (NYSE, CME) adalah saat apabila kecairan berada pada kemuncaknya. Meletakkan order 100ms sebelum lelongan adalah kelebihan.
Pelaksanaan berasaskan berita. Data inflasi dikeluarkan pada masa yang dijadualkan. Algoritma menguraikan nombor dari suapan berita dan meletakkan order dalam masa 50ms. Di sini, pelaksanaan berasaskan masa digabungkan dengan logik dipacu peristiwa.
Pelaksanaan
class TimeBasedOrder {
constructor(
private exchange: any,
private symbol: string,
private side: "buy" | "sell",
private qty: number,
private orderType: "market" | "limit",
private limitPrice?: number
) {}
/**
* Jadualkan pelaksanaan pada masa yang tepat.
* Menggunakan gelung busy-wait untuk ketepatan maksimum.
*/
async executeAt(targetTime: Date): Promise<any> {
const targetMs = targetTime.getTime();
// Fasa 1: tunggu kasar (tidur)
const coarseWait = targetMs - Date.now() - 500; // bangun 500ms lebih awal
if (coarseWait > 0) {
console.log(
`[TIME-ORDER] sleeping for ${(coarseWait / 1000).toFixed(1)}s`
);
await new Promise((r) => setTimeout(r, coarseWait));
}
// Fasa 2: tunggu tepat (busy-wait)
while (Date.now() < targetMs) {
// spin — membakar CPU, tetapi mencapai ketepatan ~1ms
}
// Fasa 3: pelaksanaan
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;
}
}
// Contoh: letakkan order tepat pada 00:00:00 UTC (penyelesaian pembiayaan)
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);
Peringatan penting: ketepatan order berasaskan masa dibatasi bukan oleh kod anda tetapi oleh latensi rangkaian ke pertukaran. Jika ping anda ke API adalah 50ms, walaupun busy-wait yang sempurna akan mempunyai delta 50ms. Untuk HFT yang serius, co-location digunakan — pelayan duduk secara fizikal bersebelahan dengan enjin padanan pertukaran.
5. Order Virtual/Sintetik: Yang Tidak Kelihatan dalam Sistem Anda
Order virtual: order hanya wujud dalam memori bot sehingga pencetus ternyala
Ini mungkin alat yang paling kurang dihargai dalam senjata pedagang algo. Order virtual (juga dikenali sebagai order sintetik) adalah order yang wujud hanya dalam sistem anda. Ia tidak dihantar ke pertukaran sehingga syarat pencetus dipenuhi (biasanya — harga mencapai aras tertentu).
Cara Ia Berfungsi
- Algoritma anda memutuskan: "Saya ingin membeli BTC pada $40,000"
- Daripada menghantar order limit ke pertukaran, ia mencipta order virtual dalam memori
- Melanggan aliran harga WebSocket
- Apabila bid/ask mencapai $40,000 — menghantar order pasaran atau limit sebenar ke pertukaran
Mengapa Order Virtual Penting
Tiada kebocoran maklumat. Order anda tidak kelihatan dalam buku order. Tiada sesiapa — bukan pedagang lain, bukan algoritma HFT, malah bukan pertukaran itu sendiri — mengetahui tentang niat anda sehingga saat pelaksanaan. Ini secara asasnya mengubah keseimbangan kuasa.
Perlindungan front-running. Pada pertukaran kripto, terutama yang kurang telus, terdapat syak wasangka yang munasabah bahawa maklumat tentang order limit besar boleh digunakan untuk front-running (terdapat malah kajian mengenai ini). Order virtual menghapuskan risiko ini.
Bot grid. Bot grid klasik meletakkan grid 50-200 order pada aras harga yang berbeza. Jika anda menghantar semuanya ke pertukaran — itu adalah 200 order dalam buku yang: (a) kelihatan kepada semua orang, (b) menggunakan had order pada pertukaran (biasanya 200-300 order terbuka setiap akaun), (c) jika harga bergerak dengan mendadak, semuanya terisi dan anda berakhir dengan posisi besar. Order virtual menyelesaikan ketiga-tiga masalah.
Menangkap pisau jatuh. Strategi: letakkan order beli virtual pada aras -5%, -10%, -15% di bawah harga semasa. Jika pasaran jatuh — order tercetus secara beransur-ansur. Jika tidak jatuh — anda tidak mengambil risiko apa pun dan tidak menggunakan slot order pertukaran.
Pelaksanaan TypeScript
interface VirtualOrder {
id: string;
symbol: string;
side: "buy" | "sell";
triggerPrice: number;
qty: number;
/** Jenis order yang dihantar ke pertukaran apabila tercetus */
executionType: "market" | "limit";
/** Untuk limit: offset dari harga pencetus */
limitOffset?: number;
status: "pending" | "triggered" | "filled" | "failed";
}
class VirtualOrderManager {
private orders: Map<string, VirtualOrder> = new Map();
private orderCounter = 0;
constructor(private exchange: any) {}
/**
* Cipta order virtual. Tiada apa yang dihantar ke pertukaran.
*/
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;
}
/**
* Dipanggil pada setiap tick harga (dari 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);
}
}
}
/**
* Dapatkan semua order virtual aktif.
*/
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;
}
}
// --- Contoh: Bot grid dengan order virtual ---
async function gridBot(exchange: any) {
const manager = new VirtualOrderManager(exchange);
const currentPrice = 42000;
const gridStep = 200; // langkah grid
const gridLevels = 20; // aras dalam setiap arah
const qtyPerLevel = 0.01; // BTC setiap aras
// Cipta grid virtual
for (let i = 1; i <= gridLevels; i++) {
// Order beli di bawah harga semasa
manager.addOrder({
symbol: "BTC/USDT",
side: "buy",
triggerPrice: currentPrice - gridStep * i,
qty: qtyPerLevel,
executionType: "limit",
limitOffset: 1, // harga limit = pencetus + 1 USDT
});
// Order jual di atas harga semasa
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`
);
// Langganan WebSocket (pseudokod untuk ccxt.pro)
while (true) {
const ticker = await exchange.watchTicker("BTC/USDT");
await manager.onPriceUpdate(
"BTC/USDT", ticker.bid, ticker.ask
);
}
}
Perangkap Order Virtual
-
Jurang latensi. Antara saat anda melihat harga dan saat order sebenar mencapai pertukaran, masa berlalu. Pada pasaran yang tidak menentu, harga boleh melambung dalam 20-100ms tersebut. Penyelesaian: hantar order limit yang sedikit agresif (dengan penampan).
-
Isian terlepas. Jika harga "menembusi" aras anda dalam satu tick (flash crash) dan melantun kembali — anda mungkin tidak sempat bertindak balas. Order limit biasa yang duduk dalam buku akan terisi; yang virtual — tidak.
-
Pengurusan keadaan. Order virtual hidup dalam memori. Jika proses ranap — order hilang. Penyelesaian: storan berterusan (Redis, SQLite, fail) dengan pemulihan semasa dimulakan semula.
6. Order Bersyarat/Pintar: Kombinatorik Order
Apabila satu order tidak mencukupi, pedagang menggabungkannya menjadi konstruksi bersyarat. Sesetengahnya disokong secara natif pada pertukaran, yang lain dilaksanakan secara programatik.
OCO (One Cancels Other)
Dua order dikaitkan: jika satu dilaksanakan — yang lain dibatalkan secara automatik. Contoh klasik: anda dalam posisi panjang dan ingin menetapkan take-profit dan stop-loss sekaligus. Yang mana satu tercetus dahulu — yang lain mesti dibatalkan.
class OCOHandler:
"""
OCO: apabila satu order terisi, yang lain dibatalkan.
"""
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):
"""Menyemak status dan membatalkan order berpasangan."""
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)
Order bracket
Konstruksi tiga komponen: order masuk utama + OCO untuk keluar (take-profit + stop-loss). Pada dasarnya, kitaran hayat dagangan lengkap dalam satu panggilan:
- Masuk: order beli limit
- Take-profit: order jual limit (atas)
- Stop-loss: order jual stop-market (bawah)
Apabila masuk terisi, TP dan SL ditempatkan secara automatik. Apabila mana-mana satu terisi — yang lain dibatalkan.
Logik If-Then
Pilihan paling fleksibel — rantaian order dengan syarat sewenang-wenangnya:
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},
},
]
}
]
Konstruksi sedemikian tidak disokong secara natif oleh mana-mana pertukaran — hanya pelaksanaan programatik. Ini adalah salah satu sebab mengapa sistem algotrading secara tidak dapat dielakkan membina lapisan pengurusan order mereka sendiri.
7. Cara Pembuat Pasaran Menggunakan Jenis Order Khusus
Penbuatan pasaran adalah alam semestanya sendiri, dan alat order sepadan dengannya. Tugas pembuat pasaran adalah untuk terus memetik bid dan ask, memperoleh pendapatan daripada spread, sambil meminimumkan pemilihan buruk (situasi di mana pedagang termaklum berdagang menentang anda).
Post-only sebagai Kemestian
Bagi pembuat pasaran, post-only bukan pilihan — ia adalah keperluan. Jika order anda secara tidak sengaja dilaksanakan sebagai taker — daripada menerima rebat maker, anda membayar yuran taker. Merentas ribuan order setiap hari, itu adalah bencana.
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} # KRITIKAL untuk pembuat pasaran
)
ask = await exchange.create_order(
symbol, "limit", "sell", qty, ask_price,
{"postOnly": True}
)
return bid, ask
Order tersembunyi
Pada sesetengah pertukaran (Kraken, Bitfinex), order tersembunyi tersedia — ia tidak muncul dalam buku order tetapi berada di pertukaran dan mengambil bahagian dalam padanan. Pertukaran: anda membayar yuran taker walaupun sebagai maker, tetapi anda mendapat kerahsiaan.
Bagi pembuat pasaran, ini adalah alat untuk pengurusan inventori: jika posisi besar telah terkumpul, anda boleh meletakkan order tersembunyi untuk melepaskannya tanpa mendedahkan niat anda kepada pasaran.
Order terpegged
Order yang terpegged kepada bid/ask terbaik. Pada Coinbase Advanced Trade, sebagai contoh, anda boleh meletakkan order yang secara automatik menjejaki bid terbaik dan sentiasa duduk di hadapan barisan. Ini adalah order pengejaran natif di peringkat pertukaran — tetapi ia jauh dari tersedia secara universal.
Pengurusan order pukal
Pembuat pasaran profesional menggunakan API kelompok untuk membatalkan dan meletakkan berpuluh-puluh order serentak dalam satu permintaan HTTP. Pada Binance ini adalah batchOrders, pada Bybit — place-batch-order. Ini mengurangkan latensi dan tekanan had kadar.
8. Jadual Perbandingan Jenis Order
| Jenis Order | Jaminan Pelaksanaan | Jaminan Harga | Kelihatan dalam Buku | Natif di Pertukaran | Kerumitan Pelaksanaan |
|---|---|---|---|---|---|
| Pasaran | Ya | Tidak | Tidak (serta-merta) | Ya | Tiada |
| Limit | Tidak | Ya | Ya | Ya | Tiada |
| Stop-market | Ya (selepas pencetus) | Tidak | Tidak | Ya | Tiada |
| Stop-limit | Tidak | Ya | Tidak (sehingga pencetus) | Ya | Tiada |
| Trailing stop | Ya (selepas pencetus) | Tidak | Tidak | Separa | Rendah |
| Iceberg | Tidak | Ya | Separa | Separa | Sederhana |
| Post-only | Tidak | Ya | Ya | Ya | Tiada |
| TWAP | Tidak (bergantung pada hirisan) | Tidak | Separa | Tidak | Sederhana |
| VWAP | Tidak | Tidak | Separa | Tidak | Tinggi |
| Limit pengejaran | Lebih tinggi daripada limit | Separa | Ya (order semasa) | Tidak | Sederhana |
| Berasaskan masa | Bergantung pada jenis | Bergantung pada jenis | Tidak (sehingga masa T) | Tidak | Rendah |
| Virtual/Sintetik | Lebih rendah daripada limit | Bergantung pada jenis | Tidak | Tidak | Sederhana |
| OCO | Ya (salah satu daripada dua) | Separa | Ya (kedua-duanya) | Separa | Sederhana |
| Bracket | Ya | Separa | Ya | Jarang | Tinggi |
| Tersembunyi | Tidak | Ya | Tidak | Jarang | Tiada |
| Terpegged | Tidak | Dinamik | Ya | Sangat jarang | Tinggi (jika programatik) |
Kesimpulan: Order sebagai Blok Binaan Strategi
Jenis order bukan sekadar "butang dalam antara muka." Ia adalah primitif asas dari mana lapisan pelaksanaan mana-mana sistem dagangan dibina. Perbezaan antara "strategi menguntungkan dalam ujian balik" dan "strategi menguntungkan dalam pengeluaran" sering terletak tepat di sini — dalam cara tepat anda menghantar order ke pertukaran.
Beberapa pengajaran praktikal:
- Mulakan dengan order standard, pastikan anda memahami nuansanya (stop-limit vs stop-market, IOC vs FOK). Kebanyakan kesilapan berlaku di sini.
- Order virtual adalah kemestian untuk bot grid. Jika anda meletakkan lebih daripada 50 order — jangan hantar semuanya ke pertukaran.
- Pengejaran diperlukan apabila kadar isian lebih penting daripada harga. Tetapi sentiasa tetapkan max_chase_distance — jika tidak, anda boleh menyimpang sangat jauh.
- Pelaksanaan berasaskan masa adalah niche tetapi berkuasa untuk arb pembiayaan dan strategi dipacu peristiwa.
- Lapisan pengurusan order tersuai adalah tidak dapat dielakkan untuk mana-mana sistem algotrading yang serius. Jenis order natif pertukaran tidak mencukupi.
Jika anda sedang membina sistem dagangan dan ingin mendalami lebih lanjut — lihat artikel kami tentang kedudukan barisan dalam buku order, kaedah WebSocket dalam CCXT, dan arbitraj kadar pembiayaan.
Rujukan dan Sumber
- Perpustakaan CCXT — perpustakaan bersatu untuk bekerja dengan pertukaran kripto, menyokong 100+ pertukaran
- Dokumentasi API Binance — dokumentasi jenis order Binance
- Bybit API v5 — dokumentasi Bybit, termasuk order kelompok
- 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 — bahan mengenai anggaran kedudukan barisan
- Trading Technologies (TT) — platform profesional dengan jenis order lanjutan
Pengarang
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.