Algoritmik Ticarette Emir Türleri: Takipli Limit Emirden Sanal Emirlere
Yeni başlayan biri borsa terminalini açtığında iki düğme görür: "Al" ve "Sat." Bir algo trader kendi kod tabanını açtığında yirmi yedi emir türü, üç soyutlama katmanı ve dizüstü bilgisayarı kapatıp pazarda salatalık satmak isteğini doğuran bir yığın uç durum görür. Ama salatalık satmak, ne yazık ki, 3:59 UTC'de fonlama oranı arbitrajı yapmanıza izin vermiyor — o zaman hadi dalalım.
Bu makalede temel borsa emirlerinden başlayarak yalnızca sisteminiz içinde var olan ve emir defterinde hiç görünmeyen sentetik sanal yapılara kadar tüm yolculuğu ele alacağız. TypeScript, Python, biraz acı ve biraz aydınlanma bekleniyor.
1. Standart Borsa Emirleri: Atlanamayacak Temel
Standart emir türlerinin sınıflandırması: piyasa emrinden buzdağı emrine
Karmaşık bir şey inşa etmeden önce temel yapı taşlarını gerçekten anladığımızdan emin olmamız gerekiyor. Pek çok kişinin stop-limit ile stop-market'ı karıştırıp ardından stop'larının "neden tetiklenmediğini" merak etmesi şaşırtıcı (ipucu: tetiklendi, ancak limit emir kayma nedeniyle dolmadı).
Piyasa emri
En basit ve aynı zamanda en tehlikeli tür. Borsaya şunu söylüyorsunuz: "Şu anda, mevcut herhangi bir fiyattan al/sat." Borsa, en iyi fiyattan başlayarak emir defterinden likidite çekiyor. En iyi seviyedeki hacim yetmezse — daha ileriye kayıyor.
Ne zaman kullanılır: acil pozisyon çıkışı, hızın fiyattan daha önemli olduğu sinyallerin yürütülmesi.
Tuzaklar: ince bir piyasada 100 BTC'lik piyasa emri fiyatı birkaç yüzde hareket ettirebilir. Piyasa emirlerini etki hesaba katmadan modelleyen geriye dönük testler salt hayaldir.
Limit emri
Tam bir fiyat belirtiyorsunuz. Emir, emir defterine giriyor ve biri sizin fiyatınızı kabul edene kadar bekliyor. Limit alış emrinin fiyatı mevcut piyasanın üzerindeyse — hemen doluyor (piyasa emri gibi, ancak garantili bir maksimum fiyatla).
Önemli nokta: limit emri yürütmeyi garanti etmez. Fiyat seviyenize ulaşıp geri dönebilir ve sizi kuyrukta bırakabilir (kuyruk pozisyonu hakkındaki makalemizde daha fazla bilgi bulabilirsiniz).
Stop-market ve Stop-limit
Karışıklık burada başlıyor. Her iki tür de tetikleme fiyatına (stop fiyatı) ulaşıldığında etkinleşen "uyku" emirlerdir. Ancak:
- Stop-market: tetiklendiğinde piyasa emrine dönüşür. Yürütmeyi garanti eder, ancak fiyatı değil.
- Stop-limit: tetiklendiğinde limit emire dönüşür. Fiyatı garanti eder (belirtilenden daha kötü olmaz), ancak yürütmeyi değil.
Değişken kripto piyasasında stop-limit "ıskalanabilir" — fiyat stop'u geçti, limit emri yerleştirildi, ancak piyasa zaten uçup gitti. Geriye doldurulmamış bir limit emri ve büyüyen bir kayıpla kaldınız. Stop-limit yerine stop-market'ın stop-loss için daha yaygın kullanılmasının tam nedeni budur.
İzleyen stop
Belirlenen bir mesafede fiyatı "takip eden" bir stop. Fiyat yükseliyor — stop yukarı hareket ediyor. Fiyat düşüyor — stop yerinde kalıyor. Trend takip stratejilerinde kârları korumak için yararlıdır.
Borsa desteği: tüm borsalar yerel izleyen stopları desteklemiyor. Algo traderlar bunları sıklıkla programatik olarak uygular — bu, parametreler üzerinde daha fazla kontrol sağlar (geri çekilme oranı, aktivasyon fiyatı, adım boyutu).
Buzdağı emri
Toplam hacmin yalnızca bir kısmının emir defterinde göründüğü bir emir. 1.000 BTC satın almak istiyorsunuz, ancak defterde yalnızca 10 gösteriyorsunuz. İlk 10 dolduğunda — sonraki 10 görünüyor.
Neden: gerçek niyetlerinizi piyasadan gizlemek için. Defterdeki büyük bir emir herkese "biri büyük miktarda almak/satmak istiyor" sinyalini verir. Buna yanıt olarak HFT algoritmaları ön-çalıştırmaya başlar ve fiyat sizden uzaklaşır.
Uyarı: pek çok kripto borsasında buzdağı emirleri ya desteklenmiyor ya da özdeş hacim örüntüleriyle kolayca tespit ediliyor. Gelişmiş algoritmalar görünür kısım boyutunu rastgele seçiyor.
Zaman-geçerlilik parametreleri: GTC, GTD, IOC, FOK
Bunlar ayrı emir türleri değil, zaman-geçerlilik parametreleridir — bir emrin ne kadar süre yaşadığını belirtir:
| Parametre | Tam Adı | Davranış |
|---|---|---|
| GTC | Good Till Cancelled (İptal Edilene Kadar) | İptal edilene kadar yaşar. Varsayılan standart |
| GTD | Good Till Date (Tarihe Kadar) | Belirtilen tarih/saate kadar yaşar |
| IOC | Immediate or Cancel (Hemen veya İptal) | Hemen yürütür (tamamen veya kısmen), kalan iptal edilir |
| FOK | Fill or Kill (Doldur veya Öldür) | Yalnızca tam ve anında yürütür. Mümkün değilse — tamamen iptal edilir |
IOC ile FOK farkı: kritiktir. IOC kısmen dolabilir — 100 BTC almak istediniz, 3 aldınız, geri kalan iptal edildi. FOK ya 100'ün tamamı ya da hiçbir şey.
Post-only (Yalnızca Yapıcı)
Emir defterine yapıcı olarak girmeyi kesinlikle garanti eden ve asla alıcı olarak yürütülmeyen bir emir. Yerleştirme anında fiyat anında yürütmeye neden olacaksa — borsa onu reddeder (veya fiyatı ayarlar, borsaya bağlı olarak).
Neden: yapıcı ücretleri genellikle alıcı ücretlerinden düşüktür (Binance'da VIP kademeleri için %0,02'ye karşı %0,04). Günde binlerce emir veren bir piyasa yapıcı için ücret farkı kâr ile zarar arasındaki fark anlamına gelir.
2. TWAP ve VWAP: Kurumlar Emir Defterinde Fili Nasıl Saklar
Bir hedge fon 50 milyon dolarlık pozisyon almak istediğinde tek bir piyasa emri vermez. Büyük bir emri pek çok küçük emre bölen ve piyasa etkisini minimize ederek bunları zaman içinde yürüten yürütme algoritmalarını kullanır.
TWAP (Zamana Göre Ağırlıklı Ortalama Fiyat)
Fikir son derece basit: toplam hacmi eşit parçalara bölün ve eşit zaman aralıklarında yürütün.
import asyncio
from datetime import datetime, timedelta
class TWAPExecutor:
"""
TWAP yürütücü: büyük bir emri eşit parçalara böler
ve eşit zaman aralıklarında yürütür.
"""
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] dilim {i+1}/{self.num_slices}: "
f"dolduruldu {order['filled']} @ {order['average']}")
except Exception as e:
print(f"[TWAP] dilim {i+1} başarısız: {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] tamamlandı: {self.executed_qty}/{self.total_qty} "
f"ortalama fiyat: {avg_price:.2f}")
VWAP (Hacme Göre Ağırlıklı Ortalama Fiyat)
VWAP daha akıllıdır: tipik işlem hacmi profilini dikkate alır. Günlük hacmin %30'u genellikle 9:00 ile 10:00 arasında işlem görüyorsa, VWAP o zaman diliminde emrin %30'unu yürütür. Amaç, ortalama yürütme fiyatını piyasa VWAP'ına olabildiğince yakın tutmaktır.
class VWAPExecutor:
"""
VWAP yürütücü: hacmi tarihsel hacim profiline
orantılı olarak dağıtır.
"""
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] dönem {i+1}: ağırlık={weight:.2%}, "
f"dolduruldu={order['filled']} @ {order['average']}")
await asyncio.sleep(interval_seconds)
TWAP ile VWAP farkı: TWAP daha basit ve öngörülebilirdir. VWAP daha iyi bir ortalama fiyat sunar, ancak güvenilir bir hacim profili gerektirir. Hacimlerin yapay şişirilebileceği kripto piyasasında VWAP profili dikkatli oluşturulmalıdır.
3. Takipli Limit: Emiriniz Fiyatı Kovalamayı Bildiğinde
Takipli limit: emir, yapılandırılabilir agresiflikle hareket eden fiyatı kovalıyor
Şimdi işler gerçekten ilginçleşiyor. Standart bir limit emri pasif bir varlıktır: emir defterinde oturur ve bekler. Fiyat hareket ettiyse — emir doldurulmamış kalır. Algo trader için bu genellikle kabul edilemez: giriş sinyali ateşlendi, ancak piyasa %0,1 hareket ettiği için pozisyon oluşturulmadı.
Takipli limit emri, bir limit etrafında programatik bir sarmalayıcıdır:
- Mevcut en iyi fiyata (veya küçük bir ofsetle) limit emri yerleştirir
- WebSocket aracılığıyla fiyatı izler
- Fiyat emirden uzaklaşırsa — iptal edip mevcut fiyata daha yakın yeniden yerleştirir
- Emir dolana veya izin verilen sapma aşılana kadar tekrar eder
Temel Parametreler
- chase_interval_ms — emri ne sıklıkla kontrol edip değiştireceğiniz. 100ms — agresif, 1000ms — gevşek.
- max_chase_distance — emrin iptal edilmesinden önceki başlangıç fiyatından maksimum sapma. Kaçan piyasayı kovalamaya karşı koruma.
- aggression_level — limit emrini piyasa fiyatına ne kadar yakın yerleştireceğiniz.
0— en iyi alış/satışta (pasif),1— spreadi geçiyor (agresif, fiilen alıcı). - chase_on_partial — emir kısmen dolduysa kovalamaya devam edilip edilmeyeceği.
TypeScript Uygulaması
interface ChasingOrderParams {
symbol: string;
side: "buy" | "sell";
totalQty: number;
/** 0 = pasif (en iyi alış/satışta), 1 = spreadi geç */
aggression: number;
/** başlangıç fiyatından maksimum fiyat sapması */
maxChaseDistance: number;
/** yeniden değerlendirme sıklığı, ms */
chaseIntervalMs: number;
/** bu ms sonra kovalamayı durdur */
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 örneği
private params: ChasingOrderParams
) {}
async execute(): Promise<{ filledQty: number; avgPrice: number }> {
const fills: Array<{ qty: number; price: number }> = [];
while (this.filledQty < this.params.totalQty) {
// Zaman aşımı
if (Date.now() - this.startTime > this.params.timeoutMs) {
console.log("[CHASE] zaman aşımına ulaşıldı, iptal ediliyor");
await this.cancelCurrent();
break;
}
// Mevcut emir defterini al
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;
// Hedef fiyatı hesapla
let targetPrice: number;
if (this.params.side === "buy") {
targetPrice = bestBid + spread * this.params.aggression;
} else {
targetPrice = bestAsk - spread * this.params.aggression;
}
// Başlangıç fiyatını hatırla
if (this.initialPrice === null) {
this.initialPrice = targetPrice;
}
// Maksimum kovalam mesafesini kontrol et
const deviation = Math.abs(targetPrice - this.initialPrice);
if (deviation > this.params.maxChaseDistance) {
console.log(
`[CHASE] maksimum sapma aşıldı: ${deviation.toFixed(4)} > ` +
`${this.params.maxChaseDistance}`
);
await this.cancelCurrent();
break;
}
// Mevcut emri kontrol et
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;
}
// Kısmi dolgular için filledQty güncelle
if (order.filled > 0) {
const newFilled = order.filled - (
fills.reduce((s, f) => s + f.qty, 0) - this.filledQty
);
// Emir yerinde — yeniden fiyatlandırmaya gerek var mı?
}
const currentPrice = parseFloat(order.price);
const priceDiff = Math.abs(currentPrice - targetPrice);
const tickSize = spread * 0.1 || 0.01;
if (priceDiff > tickSize) {
// Fiyat hareket etti — yeniden fiyatlandır
console.log(
`[CHASE] yeniden fiyatlandırılıyor: ${currentPrice} -> ` +
`${targetPrice.toFixed(4)}`
);
await this.cancelCurrent();
} else {
// Emir doğru fiyatta — bekle
await this.sleep(this.params.chaseIntervalMs);
continue;
}
}
// Yeni emir ver
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] ${this.params.side} ${remainingQty} verildi ` +
`@ ${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 { /* emir zaten doldu veya iptal edildi */ }
this.currentOrderId = null;
}
}
private sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
Takibin Zararlı Olduğu Durumlar
Takip güçlü bir araçtır, ancak kolayca kayıp üreticisine dönüşebilir:
- İptal/değiştirme spam'i. Her iptal ve değiştirme API üzerinde bir yüktür. Borsalar istekleri hız sınırlar ve agresif takip API anahtarınızın yasaklanmasına yol açabilir.
- Ters seçim. Fiyat sizden kaçıyorsa — piyasa sizin bilmediğiniz bir şey biliyor olabilir. Bu durumda fiyatı kovalamak tepede almak demektir.
- Yapıcıdan Alıcıya geçiş. Yüksek agresiflikle fiilen alıcı ücretleri ödüyorsunuz, ancak gecikmeyle (iptal + yeni emir). Bazen sadece piyasa emri vermek daha basittir.
4. Zamana Dayalı Emirler: Milisaniye Hassasiyeti
Bir emri "X fiyatında" değil "T zamanında" yürütmeniz gereken durumlar vardır. Garip mi geliyor? Bu aslında başlı başına bir strateji sınıfıdır.
Kullanım senaryoları
Fonlama oranı arbitrajı. Sürekli vadeli işlemlerde fonlama her 8 saatte bir ödenir (Binance'da 00:00, 08:00, 16:00 UTC). Fonlama oranı = +%0,1 ise uzlaşma anında kısa pozisyonda olmanız gerekir. Strateji: uzlaşmadan birkaç saniye önce kısa aç, fondlamayı topla, pozisyonu kapat. Zamanlama kritiktir — bir saniyelik gecikme kaçırılmış fonlama demektir.
Seans açılış/kapanışları. Geleneksel piyasalarda ve bazı kripto türevlerinde sabit seanslar vardır. Açılış müzayedesi (NYSE, CME) likiditanin zirveye ulaştığı andır. Müzayededen 100ms önce emir vermek bir avantajdır.
Habere dayalı yürütme. Enflasyon verileri planlanmış bir zamanda açıklanıyor. Algoritma sayıyı haber akışından ayrıştırıyor ve 50ms içinde emir veriyor. Burada zamana dayalı yürütme olaya dayalı mantıkla birleşiyor.
Uygulama
class TimeBasedOrder {
constructor(
private exchange: any,
private symbol: string,
private side: "buy" | "sell",
private qty: number,
private orderType: "market" | "limit",
private limitPrice?: number
) {}
/**
* Tam bir zamanda yürütmeyi planla.
* Maksimum hassasiyet için meşgul-bekleme döngüsü kullanır.
*/
async executeAt(targetTime: Date): Promise<any> {
const targetMs = targetTime.getTime();
// Aşama 1: kaba bekleme (uyku)
const coarseWait = targetMs - Date.now() - 500; // 500ms önce uyan
if (coarseWait > 0) {
console.log(
`[TIME-ORDER] ${(coarseWait / 1000).toFixed(1)}s uyuyor`
);
await new Promise((r) => setTimeout(r, coarseWait));
}
// Aşama 2: hassas bekleme (meşgul-bekleme)
while (Date.now() < targetMs) {
// döngü — CPU yakar, ancak ~1ms hassasiyet sağlar
}
// Aşama 3: yürütme
const sendTime = Date.now();
const order = await this.exchange.createOrder(
this.symbol,
this.orderType,
this.side,
this.qty,
this.limitPrice
);
console.log(
`[TIME-ORDER] ${new Date(sendTime).toISOString()} tarihinde yürütüldü, ` +
`hedef ${targetTime.toISOString()} idi, ` +
`delta: ${sendTime - targetMs}ms`
);
return order;
}
}
// Örnek: tam 00:00:00 UTC'de emir ver (fonlama uzlaşması)
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);
Önemli uyarı: zamana dayalı emrin hassasiyeti kodunuzla değil, borsaya olan ağ gecikmesiyle sınırlıdır. API'ye ping'iniz 50ms ise mükemmel bir meşgul-bekleme bile 50ms delta üretecektir. Ciddi HFT için ortak yerleşim kullanılır — sunucu, borsanın eşleştirme motorunun fiziksel olarak yanında durur.
5. Sanal/Sentetik Emirler: Sisteminizdeki Görünmezler
Sanal emirler: emirler tetikleyici ateşlenene kadar yalnızca botun belleğinde yaşar
Bu, algo trader'ın cephaneliğindeki en az takdir edilen araç olabilir. Sanal emir (sentetik emir olarak da bilinir), yalnızca sisteminizde var olan bir emirdir. Bir tetikleme koşulu karşılanana kadar borsaya gönderilmez (genellikle — fiyat belirli bir seviyeye ulaşır).
Nasıl Çalışır
- Algoritmanız karar verir: "40.000 dolara BTC almak istiyorum"
- Borsaya limit emri göndermek yerine bellekte sanal bir emir oluşturur
- WebSocket fiyat akışına abone olur
- Alış/satış 40.000 dolara ulaştığında — borsaya gerçek bir piyasa veya limit emri gönderir
Sanal Emirler Neden Önemli
Bilgi sızıntısı yok. Emiriniz emir defterinde görünmez. Hiç kimse — diğer traderlar, HFT algoritmaları, hatta borsanın kendisi bile — yürütme anına kadar niyetlerinizden haberdar değil. Bu, güç dengesini temelden değiştiriyor.
Ön-çalıştırmaya karşı koruma. Kripto borsalarında, özellikle daha az şeffaf olanlarda, büyük limit emirler hakkındaki bilgilerin ön-çalıştırma için kullanılabileceğine dair makul şüpheler var (bu konuda araştırmalar bile var). Sanal emirler bu riski ortadan kaldırıyor.
Grid botlar. Klasik bir grid bot, farklı fiyat seviyelerine 50-200 emir verir. Hepsini borsaya gönderirseniz — defterde 200 emir olur: (a) herkese görünür, (b) borsadaki emir limitini kullanır (genellikle hesap başına 200-300 açık emir), (c) fiyat ani hareket ederse hepsi dolar ve büyük bir pozisyonla kalırsınız. Sanal emirler bu üç sorunu da çözüyor.
Düşen bıçakları yakalamak. Strateji: mevcut fiyatın -%5, -%10, -%15 altındaki seviyelere sanal alış emirleri verin. Piyasa düşerse — emirler kademeli olarak tetiklenir. Düşmezse — hiçbir risk almaz ve borsa emir yuvalarını kullanmazsınız.
TypeScript Uygulaması
interface VirtualOrder {
id: string;
symbol: string;
side: "buy" | "sell";
triggerPrice: number;
qty: number;
/** Tetiklendiğinde borsaya gönderilen emir türü */
executionType: "market" | "limit";
/** Limit için: tetikleme fiyatından ofset */
limitOffset?: number;
status: "pending" | "triggered" | "filled" | "failed";
}
class VirtualOrderManager {
private orders: Map<string, VirtualOrder> = new Map();
private orderCounter = 0;
constructor(private exchange: any) {}
/**
* Sanal emir oluştur. Borsaya hiçbir şey gönderilmez.
*/
addOrder(params: Omit<VirtualOrder, "id" | "status">): string {
const id = `virt_${++this.orderCounter}`;
this.orders.set(id, { ...params, id, status: "pending" });
console.log(
`[VIRTUAL] ${params.side} ${params.qty} oluşturuldu ` +
`${params.symbol} @ tetikleyici ${params.triggerPrice}`
);
return id;
}
/**
* Her fiyat tickinde çağrılır (WebSocket'ten).
*/
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} tetiklendi! 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} dolduruldu: ${realOrder.filled} ` +
`@ ${realOrder.average ?? realOrder.price}`
);
} catch (err) {
order.status = "failed";
console.error(`[VIRTUAL] ${id} yürütme başarısız:`, err);
}
}
}
/**
* Tüm aktif sanal emirleri al.
*/
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;
}
}
// --- Örnek: Sanal emirlerle Grid bot ---
async function gridBot(exchange: any) {
const manager = new VirtualOrderManager(exchange);
const currentPrice = 42000;
const gridStep = 200; // grid adımı
const gridLevels = 20; // her yönde seviye sayısı
const qtyPerLevel = 0.01; // seviye başına BTC
// Sanal grid oluştur
for (let i = 1; i <= gridLevels; i++) {
// Mevcut fiyatın altında alış emirleri
manager.addOrder({
symbol: "BTC/USDT",
side: "buy",
triggerPrice: currentPrice - gridStep * i,
qty: qtyPerLevel,
executionType: "limit",
limitOffset: 1, // limit fiyat = tetikleyici + 1 USDT
});
// Mevcut fiyatın üzerinde satış emirleri
manager.addOrder({
symbol: "BTC/USDT",
side: "sell",
triggerPrice: currentPrice + gridStep * i,
qty: qtyPerLevel,
executionType: "limit",
limitOffset: 1,
});
}
console.log(
`[GRID] ${gridLevels * 2} sanal emir oluşturuldu, ` +
`borsada 0`
);
// WebSocket aboneliği (ccxt.pro için sözde kod)
while (true) {
const ticker = await exchange.watchTicker("BTC/USDT");
await manager.onPriceUpdate(
"BTC/USDT", ticker.bid, ticker.ask
);
}
}
Sanal Emirlerin Tuzakları
-
Gecikme boşluğu. Fiyatı gördüğünüz an ile gerçek emrin borsaya ulaştığı an arasında zaman geçer. Değişken bir piyasada fiyat o 20-100ms'de kaçıp gidebilir. Çözüm: biraz agresif bir limit emri gönderin (tampon ile).
-
Kaçırılan dolgular. Fiyat seviyenizi tek bir tickte "deldiyse" (flash crash) ve geri döndüyse — zamanında tepki veremeyebilirsiniz. Defterde bekleyen normal bir limit emri dolardı; sanal bir emir — dolmayacak.
-
Durum yönetimi. Sanal emirler bellekte yaşar. Süreç çökerse — emirler kaybolur. Çözüm: yeniden başlatmada kurtarma ile kalıcı depolama (Redis, SQLite, dosya).
6. Koşullu/Akıllı Emirler: Emir Kombinatorikleri
Tek bir emir yetmediğinde, traderlar bunları koşullu yapılara birleştirir. Bazıları borsalarda yerel olarak desteklenir, diğerleri programatik olarak uygulanır.
OCO (Biri İptal Eder Diğerini)
İki emir birbirine bağlıdır: biri yürütülürse — diğeri otomatik olarak iptal edilir. Klasik örnek: uzun pozisyondasınız ve hem kâr al hem de zarar durdur ayarlamak istiyorsunuz. Hangisi önce tetiklenirse — diğeri iptal edilmelidir.
class OCOHandler:
"""
OCO: bir emir dolduğunda diğeri iptal edilir.
"""
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):
"""Durumları kontrol eder ve eşleşen emri iptal eder."""
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] kâr al doldu, zarar durdur iptal ediliyor")
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] zarar durdur doldu, kâr al iptal ediliyor")
await self.exchange.cancel_order(
self.order_a_id, self.symbol
)
break
await asyncio.sleep(0.5)
Bracket emri
Üç bileşenli yapı: birincil giriş emri + çıkış için OCO (kâr al + zarar durdur). Özünde, tek bir çağrıda tam bir işlem yaşam döngüsü:
- Giriş: limit alış emri
- Kâr al: limit satış emri (üstte)
- Zarar durdur: stop-market satış emri (altta)
Giriş dolduğunda, TP ve SL otomatik olarak yerleştirilir. Herhangi biri dolduğunda — diğeri iptal edilir.
If-Then Mantığı
En esnek seçenek — keyfi koşullarla emir zincirleri:
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},
},
]
}
]
Bu tür yapılar hiçbir borsada yerel olarak desteklenmiyor — yalnızca programatik uygulama. Bu, algotrading sistemlerinin kaçınılmaz olarak kendi emir yönetim katmanlarını büyütmesinin nedenlerinden biri.
7. Piyasa Yapıcılar Özel Emir Türlerini Nasıl Kullanır
Piyasa yapıcılık kendi evrenini oluşturur ve emir araç seti de buna uygun. Piyasa yapıcının işi, sürekli olarak alış ve satış teklifleri sunmak, spreadden kazanırken ters seçimi (bilgili bir trader'ın size karşı işlem yapması durumu) minimize etmektir.
Post-only Zorunluluk Olarak
Piyasa yapıcı için post-only bir seçenek değil — bir zorunluluktur. Emiriniz kazara alıcı olarak yürütülürse — yapıcı geri ödemesi almak yerine alıcı ücreti ödersiniz. Günde binlerce emir boyunca bu feci sonuçlar doğurur.
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} # Piyasa yapıcılar için KRİTİK
)
ask = await exchange.create_order(
symbol, "limit", "sell", qty, ask_price,
{"postOnly": True}
)
return bid, ask
Gizli emirler
Bazı borsalarda (Kraken, Bitfinex) gizli emirler mevcuttur — emir defterinde görünmezler, ancak borsadadırlar ve eşleştirmeye katılırlar. Ödün: yapıcı olsanız bile alıcı ücreti ödersiniz, ancak anonimlik kazanırsınız.
Piyasa yapıcı için bu bir envanter yönetim aracıdır: büyük bir pozisyon birikirse, piyasaya niyetinizi açıklamadan onu boşaltmak için gizli bir emir verebilirsiniz.
Sabitlenmiş emirler
En iyi alış/satışa sabitlenmiş bir emir. Örneğin Coinbase Advanced Trade'de en iyi alışı otomatik takip eden ve her zaman kuyruğun önünde duran bir emir verebilirsiniz. Bu, borsa düzeyinde yerel bir takip emridir — ancak evrensel olarak mevcut değil.
Toplu emir yönetimi
Profesyonel piyasa yapıcılar, tek bir HTTP isteğinde onlarca emri aynı anda iptal edip vermek için toplu API'leri kullanır. Binance'da bu batchOrders, Bybit'te — place-batch-order. Bu, gecikmeyi ve hız sınırı baskısını azaltır.
8. Emir Türü Karşılaştırma Tablosu
| Emir Türü | Yürütme Garantisi | Fiyat Garantisi | Defterde Görünür | Borsalarda Yerel | Uygulama Karmaşıklığı |
|---|---|---|---|---|---|
| Piyasa | Evet | Hayır | Hayır (anında) | Evet | Yok |
| Limit | Hayır | Evet | Evet | Evet | Yok |
| Stop-market | Evet (tetiklemeden sonra) | Hayır | Hayır | Evet | Yok |
| Stop-limit | Hayır | Evet | Hayır (tetikleyene kadar) | Evet | Yok |
| İzleyen stop | Evet (tetiklemeden sonra) | Hayır | Hayır | Kısmi | Düşük |
| Buzdağı | Hayır | Evet | Kısmi | Kısmi | Orta |
| Post-only | Hayır | Evet | Evet | Evet | Yok |
| TWAP | Hayır (dilimlere bağlı) | Hayır | Kısmi | Hayır | Orta |
| VWAP | Hayır | Hayır | Kısmi | Hayır | Yüksek |
| Takipli limit | Limitten yüksek | Kısmi | Evet (mevcut emir) | Hayır | Orta |
| Zamana dayalı | Türe bağlı | Türe bağlı | Hayır (T zamanına kadar) | Hayır | Düşük |
| Sanal/Sentetik | Limitten düşük | Türe bağlı | Hayır | Hayır | Orta |
| OCO | Evet (ikiden biri) | Kısmi | Evet (ikisi de) | Kısmi | Orta |
| Bracket | Evet | Kısmi | Evet | Nadir | Yüksek |
| Gizli | Hayır | Evet | Hayır | Nadir | Yok |
| Sabitlenmiş | Hayır | Dinamik | Evet | Çok nadir | Yüksek (programatikse) |
Sonuç: Emir, Strateji Yapı Taşı Olarak
Emir türleri yalnızca "bir arayüzdeki düğmeler" değil. Bunlar, herhangi bir ticaret sisteminin yürütme katmanının inşa edildiği temel ilkellerdir. "Strateji geriye dönük testte kârlı" ile "strateji üretimde kârlı" arasındaki fark çoğunlukla tam burada yatar — emirleri borsaya tam olarak nasıl gönderdiğinizde.
Birkaç pratik çıkarım:
- Standart emirlerle başlayın, nüansları anladığınızdan emin olun (stop-limit ile stop-market, IOC ile FOK). Çoğu hata burada oluyor.
- Sanal emirler grid botlar için zorunluluktur. 50'den fazla emir veriyorsanız — hepsini borsaya göndermeyin.
- Takip, doldurma oranı fiyattan daha önemli olduğunda gereklidir. Ancak her zaman max_chase_distance belirleyin — aksi takdirde çok uzaklara sürüklenebilirsiniz.
- Zamana dayalı yürütme, fonlama arbitrajı ve olaya dayalı stratejiler için niş ama güçlüdür.
- Özel emir yönetim katmanı, herhangi bir ciddi algotrading sistemi için kaçınılmazdır. Borsada yerel emir türleri yetmez.
Bir ticaret sistemi kuruyorsanız ve daha derine inmek istiyorsanız — emir defterinde kuyruk pozisyonu, CCXT'de WebSocket yöntemleri ve fonlama oranı arbitrajı hakkındaki makalelerimize göz atın.
Kaynaklar ve Referanslar
- CCXT Kütüphanesi — 100+ borsayı destekleyen kripto borsalarıyla çalışmak için birleşik kütüphane
- Binance API Belgeleri — Binance emir türü belgeleri
- Bybit API v5 — Toplu emirler dahil Bybit belgeleri
- Moallemi, C. & Yuan, K. (2017). The Value of Queue Position in a Limit Order Book. Columbia Business School Araştırma Makalesi
- Cartea, A., Jaimungal, S., & Penalva, J. (2015). Algorithmic and High-Frequency Trading. Cambridge University Press
- Avellaneda, M. & Stoikov, S. (2008) — Limit emir defterinde yüksek frekanslı ticaret. Quantitative Finance
- Erik Rigtorp — Order Queue Position Estimation — Kuyruk pozisyonu tahminine ilişkin materyaller
- Trading Technologies (TT) — Gelişmiş emir türleriyle profesyonel platform
Yazarlar
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.