← Мақалаларға оралу
March 23, 2026
5 мин оқу

Алгоритмдік Трейдингтегі Ордер Түрлері: Chasing бар Лимиттен Виртуалды Ордерлерге дейін

Алгоритмдік Трейдингтегі Ордер Түрлері: Chasing бар Лимиттен Виртуалды Ордерлерге дейін
#orders
#algotrading
#limit
#chasing
#virtual-orders
#grid-bot
#market-making
📖
Part 2 of 6 · Collection
Order Book & Market Microstructure

Жаңадан бастаған адам биржа терминалын ашқанда, ол екі батырманы көреді: "Сатып алу" және "Сату". Алго-трейдер өз кодын ашқанда, ол жиырма жеті ордер түрін, үш абстракция деңгейін және ноутбукты жауып, базарда қияр сатқысы келтіретін бір топ шектік жағдайларды көреді. Бірақ, өкінішке орай, қияр сізге 3:59 UTC-де funding rate арбитражын жүргізуге мүмкіндік бермейді — сондықтан тікелей тақырыпқа өтейік.

Бұл мақалада біз негізгі биржа ордерлерінен бастап, тек сіздің жүйеңізде ғана өмір сүретін және ешқашан ордер кітабында пайда болмайтын синтетикалық виртуалды құрылымдарға дейінгі бүкіл жолды өтеміз. TypeScript, Python, аздап ауыртпалық және сәл ағарту күтіп тұр.


1. Стандартты Биржа Ордерлері: Өткізіп Жіберуге Болмайтын Негіз

Стандартты ордерлер Стандартты ордер түрлерінің жіктелуі: маркеттен айсбергке дейін

Күрделі нәрсе құрастырмас бұрын, негізгі құрылыс блоктарын дұрыс түсінгенімізге көз жеткізуіміз керек. Қаншама адам stop-limit пен stop-market-ті шатастырып, кейін неге өз стопы "іске қосылмағанына" таң қалатыны таңғаларлық (спойлер: ол іске қосылған, бірақ лимиттік ордер slippage салдарынан орындалмаған).

Market ордер

Ең қарапайым әрі сонымен бірге ең қауіпті түрі. Сіз биржаға: "қазір, кез келген қолжетімді бағамен сатып ал/сат" дейсіз. Биржа ордер кітабынан ең жақсы бағадан бастап өтімділікті алады. Ең жақсы деңгейдегі көлем жеткіліксіз болса — ол одан әрі жылжиды.

Қашан пайдалану керек: позициядан шұғыл шығу, жылдамдық бағадан маңыздырақ болатын сигналды орындау.

Тұзақтар: сирек нарықта 100 BTC-ге арналған market ордер бағаны бірнеше пайызға жылжыта алады. Ықпалды ескермей market ордерлерді модельдейтін бэктесттер — таза қиял.

Limit ордер

Сіз нақты бағаны көрсетесіз. Ордер ордер кітабына түседі және біреу сіздің бағаңызбен келіскенше күтеді. Егер лимиттік сатып алу ордерінің бағасы ағымдағы нарықтан жоғары болса — ол дереу орындалады (market ордер сияқты, бірақ кепілдендірілген максималды бағамен).

Негізгі сәт: лимиттік ордер орындалуды кепілдендірмейді. Баға сіздің деңгейіңізге жетіп, кері бұрылуы мүмкін, сізді кезекте қалдырып (кезектегі позиция туралы мақаламызда толығырақ).

Stop-market және Stop-limit

Дәл осы жерден шатасу басталады. Екі түрі де іске қосылу бағасына (stop price) жеткенде белсенді болатын "ұйқыдағы" ордерлер. Бірақ:

  • Stop-market: іске қосылғанда market ордерге айналады. Орындалуды кепілдендіреді, бірақ бағаны емес.
  • Stop-limit: іске қосылғанда лимиттік ордерге айналады. Бағаны кепілдендіреді (көрсетілгеннен нашар емес), бірақ орындалуды емес.

Тұрақсыз крипто нарығында stop-limit "өтіп кетуі" мүмкін — баға стопты бұзып өтті, лимиттік ордер қойылды, бірақ нарық әлдеқашан ұшып өтті. Сізде орындалмаған лимиттік ордер мен өсіп келе жатқан шығын қалады. Дәл осы себептен stop-loss үшін stop-market жиірек пайдаланылады.

Trailing stop

Бағаны белгіленген қашықтықта "қуалайтын" стоп. Баға көтеріледі — стоп та жоғары жылжиды. Баға түседі — стоп орнында қалады. Трендке негізделген стратегияларда пайданы қорғау үшін пайдалы.

Биржа қолдауы: барлық биржалар да табиғи trailing stop-ты қолдамайды. Алго-трейдерлер оны жиі бағдарламалық түрде іске асырады — бұл параметрлерге (callback rate, белсендіру бағасы, қадам өлшемі) көбірек бақылау береді.

Iceberg ордер

Тек жалпы көлемнің бір бөлігі ғана ордер кітабында көрінетін ордер. Сіз 1000 BTC сатып алғыңыз келеді, бірақ кітапта тек 10-ын ғана көрсетесіз. Алғашқы 10-ы орындалғанда — келесі 10-ы пайда болады.

Неге: нарықтан шынайы ниеттеріңізді жасыру үшін. Кітаптағы үлкен ордер бәріне "біреу үлкен көлемде сатып алғысы/сатқысы келеді" деген сигнал береді. Жауап ретінде HFT алгоритмдері front-running жасай бастайды, ал баға сізден алшақтайды.

Ескерту: көптеген крипто биржаларда айсберг ордерлер не қолдау таппайды, не бірдей көлемдер үлгісі арқылы оңай анықталады. Озық алгоритмдер көрінетін бөліктің өлшемін кездейсоқтандырады.

Time-in-force параметрлері: GTC, GTD, IOC, FOK

Бұлар бөлек ордер түрлері емес, ал time-in-force параметрлері — ордер қанша уақыт өмір сүреді:

Параметр Толық Атауы Мінез-құлқы
GTC Good Till Cancelled Тоқтатылғанша өмір сүреді. Стандартты default
GTD Good Till Date Көрсетілген күн/уақытқа дейін өмір сүреді
IOC Immediate or Cancel Дереу орындалады (толық немесе ішінара), қалғаны тоқтатылады
FOK Fill or Kill Тек толық және дереу орындалады. Мүмкін болмаса — толығымен тоқтатылады

IOC мен FOK айырмашылығы: айырмашылық маңызды. IOC ішінара орындалуы мүмкін — сіз 100 BTC сатып алғыңыз келді, 3-ін сатып алдыңыз, қалғаны тоқтатылды. FOK не толығымен, не мүлдем жоқ.

Post-only (тек Maker)

Ордер кітабына maker ретінде кіруі кепілдендірілген және ешқашан taker ретінде орындалмайтын ордер. Егер қою кезінде баға дереу орындалуға әкелетін болса — биржа оны қабылдамайды (немесе биржаға байланысты бағаны түзетеді).

Неге: maker комиссиялары әдетте taker комиссияларынан төмен (Binance-де — VIP деңгейлер үшін 0,02% пен 0,04%). Күніне мыңдаған ордер қоятын маркет-мейкер үшін комиссия айырмашылығы — пайда мен шығын арасындағы айырмашылық.


2. TWAP және VWAP: Институттар Ордер Кітабында Пілді Қалай Жасырады

Хедж-қор 50 миллион долларлық позиция сатып алғысы келгенде, ол бір ғана market ордер қоймайды. Ол орындау алгоритмдерін пайдаланады — үлкен ордерді көптеген кішірек бөліктерге бөліп, оларды уақыт бойы орындайтын, нарыққа әсерді азайтатын алгоритмдер.

TWAP (Time-Weighted Average Price)

Идея өте қарапайым: жалпы көлемді тең бөліктерге бөліп, тең уақыт аралықтарында орындау.

import asyncio
from datetime import datetime, timedelta

class TWAPExecutor:
    """
    TWAP executor: splits a large order into equal parts
    and executes them at equal time intervals.
    """
    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 ақылдырақ: ол әдеттегі сауда көлемі профилін ескереді. Егер күндізгі көлемнің 30%-ы әдетте сағат 9:00-10:00 аралығында сатылса, VWAP ордердің 30%-ын дәл сол терезеде орындайды. Мақсат — орташа орындалу бағасын нарықтық VWAP-қа мүмкіндігінше жақындату.

class VWAPExecutor:
    """
    VWAP executor: distributes volume proportionally
    to the historical volume profile.
    """
    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 жақсырақ орташа баға береді, бірақ сенімді көлем профилін талап етеді. Көлемдер wash-trade жасалуы мүмкін крипто нарығында VWAP профилін мұқият құру керек.


3. Chasing бар Limit: Ордеріңіз Бағаны Қуалауды Білгенде

Chasing лимиттік ордерлер Chasing limit: ордер баптауға болатын агрессивтілікпен қозғалатын бағаны қуалайды

Енді нағыз қызық басталады. Стандартты лимиттік ордер — пассивті нысан: ол ордер кітабында отырып күтеді. Баға қозғалса — ордер орындалмай қалады. Алго-трейдер үшін бұл көбіне қолайсыз: кіру сигналы іске қосылды, бірақ нарық 0,1%-ға қозғалғандықтан позиция құрылмады.

Chasing limit order — бұл лимиттік ордердің айналасындағы бағдарламалық орауыш, ол:

  1. Ағымдағы ең жақсы бағада (немесе шағын offset-пен) лимиттік ордер қояды
  2. Бағаны WebSocket арқылы бақылайды
  3. Егер баға ордерден алшақтаса — оны тоқтатып, ағымдағы бағаға жақынырақ қайта қояды
  4. Ордер орындалғанша немесе рұқсат етілген ауытқудан асып кеткенше қайталайды

Негізгі Параметрлер

  • chase_interval_ms — ордерді қаншалықты жиі тексеру және қайта қою. 100мс — агрессивті, 1000мс — босаң.
  • max_chase_distance — ордер тоқтатылғанға дейінгі бастапқы бағадан максималды ауытқу. Қашып бара жатқан нарықты қуалаудан қорғаныс.
  • aggression_level — лимиттік ордерді нарықтық бағаға қаншалықты жақын қою керек. 0 — ең жақсы bid/ask-та (пассивті), 1 — спредті кесіп өту (агрессивті, іс жүзінде taker).
  • 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) {
      // Timeout
      if (Date.now() - this.startTime > this.params.timeoutMs) {
        console.log("[CHASE] timeout reached, cancelling");
        await this.cancelCurrent();
        break;
      }

      // Get current order book
      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;

      // Calculate target price
      let targetPrice: number;
      if (this.params.side === "buy") {
        targetPrice = bestBid + spread * this.params.aggression;
      } else {
        targetPrice = bestAsk - spread * this.params.aggression;
      }

      // Remember the initial price
      if (this.initialPrice === null) {
        this.initialPrice = targetPrice;
      }

      // Check max chase distance
      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;
      }

      // Check current order
      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;
        }

        // Update filledQty for partial fills
        if (order.filled > 0) {
          const newFilled = order.filled - (
            fills.reduce((s, f) => s + f.qty, 0) - this.filledQty
          );
          // Order is in place — do we need to reprice?
        }

        const currentPrice = parseFloat(order.price);
        const priceDiff = Math.abs(currentPrice - targetPrice);
        const tickSize = spread * 0.1 || 0.01;

        if (priceDiff > tickSize) {
          // Price moved — reprice
          console.log(
            `[CHASE] repricing: ${currentPrice} -> ` +
            `${targetPrice.toFixed(4)}`
          );
          await this.cancelCurrent();
        } else {
          // Order is at the right price — wait
          await this.sleep(this.params.chaseIntervalMs);
          continue;
        }
      }

      // Place new order
      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));
  }
}

Chasing Зиян Келтіретін Кездер

Chasing — қуатты құрал, бірақ оны шығын генераторына айналдыру оңай:

  1. Cancel/replace спамы. Әрбір тоқтату мен қайта қою API-ге жүктеме болып табылады. Биржалар сұраныстарды шектейді (rate limit), ал агрессивті chasing API кілтіңіздің бұғатталуына әкелуі мүмкін.
  2. Кері таңдау (adverse selection). Егер баға сізден қашып жатса — нарық сіз білмейтін нәрсені білуі мүмкін. Мұндай жағдайда бағаны қуалау шыңында сатып алуды білдіреді.
  3. Maker-ден Taker-ге ауысу. Жоғары агрессивтілікпен сіз іс жүзінде taker комиссияларын төлейсіз, бірақ кешігумен (тоқтату + жаңа ордер). Кейде жай ғана market ордер қою оңайырақ.

4. Уақытқа Негізделген Ордерлер: Миллисекундтық Дәлдік

Ордерді "X бағада" емес, "T уақытта" орындау қажет жағдайлар бар. Оғаш естіледі ме? Шын мәнінде бұл толық стратегиялар класы.

Пайдалану жағдайлары

Funding rate арбитражы. Мәңгілік фьючерстерде funding әр 8 сағат сайын төленеді (Binance-де 00:00, 08:00, 16:00 UTC). Егер funding ставкасы = +0,1% болса, есеп айырысу сәтінде short позицияда болу керек. Стратегия: есеп айырысудан бірнеше секунд бұрын short ашу, funding жинау, позицияны жабу. Уақыт дәлдігі шешуші — бір секунд кешігу өткізіп алынған funding дегенді білдіреді.

Сессиялардың ашылуы/жабылуы. Дәстүрлі нарықтарда және кейбір крипто туындыларда тұрақты сессиялар бар. Ашылу аукционы (NYSE, CME) — өтімділіктің шыңына жеткен сәт. Аукциондан 100мс бұрын ордер қою — артықшылық.

Жаңалықтарға негізделген орындау. Инфляция деректері жоспарланған уақытта жарияланады. Алгоритм санды жаңалықтар лентасынан талдап, 50мс ішінде ордер қояды. Мұнда уақытқа негізделген орындау оқиғаға негізделген логикамен біріктіріледі.

Іске асыру

class TimeBasedOrder {
  constructor(
    private exchange: any,
    private symbol: string,
    private side: "buy" | "sell",
    private qty: number,
    private orderType: "market" | "limit",
    private limitPrice?: number
  ) {}

  /**
   * Schedule execution at a precise time.
   * Uses a busy-wait loop for maximum precision.
   */
  async executeAt(targetTime: Date): Promise<any> {
    const targetMs = targetTime.getTime();

    // Phase 1: coarse wait (sleep)
    const coarseWait = targetMs - Date.now() - 500; // wake up 500ms early
    if (coarseWait > 0) {
      console.log(
        `[TIME-ORDER] sleeping for ${(coarseWait / 1000).toFixed(1)}s`
      );
      await new Promise((r) => setTimeout(r, coarseWait));
    }

    // Phase 2: precise wait (busy-wait)
    while (Date.now() < targetMs) {
      // spin — burns CPU, but achieves ~1ms precision
    }

    // Phase 3: execution
    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;
  }
}

// Example: place an order exactly at 00:00:00 UTC (funding settlement)
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);

Маңызды ескерту: уақытқа негізделген ордердің дәлдігі сіздің кодыңызбен емес, биржаға дейінгі желі кідірісімен (latency) шектеледі. Егер API-ге пингіңіз 50мс болса, тіпті мінсіз busy-wait та 50мс дельта береді. Байсалды HFT үшін co-location пайдаланылады — сервер физикалық түрде биржаның matching engine-нің жанында орналасады.


5. Виртуалды/Синтетикалық Ордерлер: Жүйеңіздегі Көрінбейтіндер

Grid боттарға арналған виртуалды ордерлер Виртуалды ордерлер: іске қосылғыш іске қосылғанға дейін тек бот жадында өмір сүреді

Бұл, мүмкін, алго-трейдер қаруының ең бағаланбаған құралы. Виртуалды ордер (синтетикалық ордер деп те аталады) — тек сіздің жүйеңізде ғана өмір сүретін ордер. Ол іске қосылу шарты орындалғанға дейін (әдетте — баға белгілі бір деңгейге жеткенге дейін) биржаға жіберілмейді.

Бұл Қалай Жұмыс Істейді

  1. Сіздің алгоритміңіз шешеді: "BTC-ды 40 000 долларға сатып алғым келеді"
  2. Биржаға лимиттік ордер жіберудің орнына, ол жадта виртуалды ордер жасайды
  3. WebSocket баға ағынына жазылады
  4. Bid/ask 40 000 долларға жеткенде — биржаға нақты market немесе лимиттік ордер жіберіледі

Виртуалды Ордерлер Неге Маңызды

Ақпарат ағып кетпейді. Сіздің ордеріңіз ордер кітабында көрінбейді. Ешкім — басқа трейдерлер де, HFT алгоритмдері де, тіпті биржаның өзі де — орындалу сәтіне дейін сіздің ниеттеріңіз туралы білмейді. Бұл күштер тепе-теңдігін түбегейлі өзгертеді.

Front-running-нан қорғаныс. Крипто биржаларда, әсіресе аз ашық болғандарында, үлкен лимиттік ордерлер туралы ақпараттың front-running үшін пайдаланылуы мүмкін деген негізді күдік бар (бұл туралы тіпті зерттеулер де бар). Виртуалды ордерлер бұл тәуекелді жояды.

Grid боттар. Классикалық grid бот әртүрлі баға деңгейлерінде 50-200 ордерден тұратын тор орналастырады. Егер сіз оларды бәрін биржаға жіберсеңіз — бұл кітапта: (а) бәріне көрінетін, (ә) биржадағы ордер лимитін тұтынатын (әдетте аккаунтқа 200-300 ашық ордер), (б) баға күрт қозғалса, олардың бәрі орындалып, сізде орасан позиция қалатын 200 ордер деген сөз. Виртуалды ордерлер осы үш мәселені де шешеді.

Құлап жатқан пышақтарды ұстау. Стратегия: ағымдағы бағадан -5%, -10%, -15% төмен виртуалды сатып алу ордерлерін қою. Нарық құласа — ордерлер бірте-бірте іске қосылады. Құламаса — сіз ештеңеге тәуекел етпейсіз және биржаның ордер слоттарын пайдаланбайсыз.

TypeScript Іске Асыруы

interface VirtualOrder {
  id: string;
  symbol: string;
  side: "buy" | "sell";
  triggerPrice: number;
  qty: number;
  /** Order type sent to the exchange upon triggering */
  executionType: "market" | "limit";
  /** For limit: offset from trigger price */
  limitOffset?: number;
  status: "pending" | "triggered" | "filled" | "failed";
}

class VirtualOrderManager {
  private orders: Map<string, VirtualOrder> = new Map();
  private orderCounter = 0;

  constructor(private exchange: any) {}

  /**
   * Create a virtual order. Nothing is sent to the exchange.
   */
  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;
  }

  /**
   * Called on every price tick (from 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);
      }
    }
  }

  /**
   * Get all active virtual orders.
   */
  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;
  }
}

// --- Example: Grid bot with virtual orders ---

async function gridBot(exchange: any) {
  const manager = new VirtualOrderManager(exchange);
  const currentPrice = 42000;
  const gridStep = 200;      // grid step
  const gridLevels = 20;     // levels in each direction
  const qtyPerLevel = 0.01;  // BTC per level

  // Create virtual grid
  for (let i = 1; i <= gridLevels; i++) {
    // Buy orders below current price
    manager.addOrder({
      symbol: "BTC/USDT",
      side: "buy",
      triggerPrice: currentPrice - gridStep * i,
      qty: qtyPerLevel,
      executionType: "limit",
      limitOffset: 1,  // limit price = trigger + 1 USDT
    });

    // Sell orders above current price
    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 subscription (pseudocode for ccxt.pro)
  while (true) {
    const ticker = await exchange.watchTicker("BTC/USDT");
    await manager.onPriceUpdate(
      "BTC/USDT", ticker.bid, ticker.ask
    );
  }
}

Виртуалды Ордерлердің Осал Тұстары

  1. Кідіріс алшақтығы. Сіз бағаны көрген сәт пен нақты ордер биржаға жеткен сәт арасында уақыт өтеді. Тұрақсыз нарықта баға сол 20-100мс ішінде ұшып кетуі мүмкін. Шешім: сәл агрессивті лимиттік ордер жіберу (буфермен).

  2. Өткізіп алынған орындалулар. Егер баға сіздің деңгейіңізді бір тикте (flash crash) "тесіп өтіп" қайтадан оралса — сіз уақытында әрекет ете алмауыңыз мүмкін. Кітапта отырған қарапайым лимиттік ордер орындалған болар еді; виртуалды ордер — жоқ.

  3. Күй басқару. Виртуалды ордерлер жадта өмір сүреді. Процесс құласа — ордерлер жоғалады. Шешім: қайта іске қосқанда қалпына келтірумен тұрақты сақтау (Redis, SQLite, файл).


6. Шартты/Ақылды Ордерлер: Ордер Комбинаторикасы

Бір ордер жеткіліксіз болғанда, трейдерлер оларды шартты құрылымдарға біріктіреді. Кейбіреулері биржаларда табиғи түрде қолдау табады, басқалары бағдарламалық түрде іске асырылады.

OCO (One Cancels Other)

Екі ордер байланыстырылған: егер біреуі орындалса — екіншісі автоматты түрде тоқтатылады. Классикалық мысал: сіз long позициядасыз және take-profit пен stop-loss екеуін де орнатқыңыз келеді. Қайсысы бірінші іске қосылса — екіншісі тоқтатылуы керек.

class OCOHandler:
    """
    OCO: when one order fills, the other is cancelled.
    """
    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):
        """Checks statuses and cancels the paired order."""
        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 (take-profit + stop-loss). Мәні бойынша, бір ғана шақыруда мәміленің толық өмірлік циклі:

  1. Кіру: сатып алуға лимиттік ордер
  2. Take-profit: сатуға лимиттік ордер (жоғарыда)
  3. Stop-loss: сатуға stop-market ордер (төменде)

Кіру орындалғанда, TP мен SL автоматты түрде қойылады. Олардың бірі орындалғанда — екіншісі тоқтатылады.

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. Маркет-Мейкерлер Мамандандырылған Ордер Түрлерін Қалай Пайдаланады

Market making — өз әлемі бар сала, және ордер құралдары да соған сай. Маркет-мейкердің жұмысы — спредтен пайда таба отырып, bid пен ask-ты үздіксіз белгілеу, сонымен қатар adverse selection-ды (хабардар трейдер сізге қарсы сауда жасайтын жағдайды) азайту.

Post-only — Міндетті Талап

Маркет-мейкер үшін post-only — таңдау емес, талап. Егер сіздің ордеріңіз кездейсоқ taker ретінде орындалса — maker rebate алудың орнына сіз taker комиссиясын төлейсіз. Күніне мыңдаған ордер бойынша бұл апатты.

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}  # CRITICAL for market makers
    )
    ask = await exchange.create_order(
        symbol, "limit", "sell", qty, ask_price,
        {"postOnly": True}
    )
    return bid, ask

Hidden ордерлер

Кейбір биржаларда (Kraken, Bitfinex) жасырын ордерлер қолжетімді — олар ордер кітабында көрінбейді, бірақ биржада бар және matching-ке қатысады. Мәміле: сіз maker ретінде де taker комиссияларын төлейсіз, бірақ анонимдік аласыз.

Маркет-мейкер үшін бұл — тауар қорын (inventory) басқару құралы: егер үлкен позиция жиналып қалса, оны нарыққа ниетіңізді ашпай тарату үшін жасырын ордер қоюға болады.

Pegged ордерлер

Ең жақсы bid/ask-қа байланған ордер. Мысалы, Coinbase Advanced Trade-те сіз ең жақсы bid-ты автоматты түрде қадағалайтын және әрдайым кезектің басында тұратын ордер қоя аласыз. Бұл — биржа деңгейіндегі табиғи chasing ордер, бірақ бұл барлық жерде бірдей қолжетімді емес.

Жаппай ордер басқару

Кәсіби маркет-мейкерлер бір HTTP сұранысында бірнеше ондаған ордерлерді бір мезгілде тоқтату және қою үшін batch API-лерді пайдаланады. Binance-де бұл batchOrders, Bybit-те — place-batch-order. Бұл кідіріс пен rate limit қысымын азайтады.


8. Ордер Түрлерінің Салыстыру Кестесі

Ордер Түрі Орындалу Кепілі Баға Кепілі Кітапта Көрінеді Биржаларда Табиғи Іске Асыру Күрделілігі
Market Иә Жоқ Жоқ (лезде) Иә Жоқ
Limit Жоқ Иә Иә Иә Жоқ
Stop-market Иә (іске қосылғаннан кейін) Жоқ Жоқ Иә Жоқ
Stop-limit Жоқ Иә Жоқ (іске қосылғанша) Иә Жоқ
Trailing stop Иә (іске қосылғаннан кейін) Жоқ Жоқ Ішінара Төмен
Iceberg Жоқ Иә Ішінара Ішінара Орташа
Post-only Жоқ Иә Иә Иә Жоқ
TWAP Жоқ (бөліктерге байланысты) Жоқ Ішінара Жоқ Орташа
VWAP Жоқ Жоқ Ішінара Жоқ Жоғары
Chasing limit Limit-тен жоғары Ішінара Иә (ағымдағы ордер) Жоқ Орташа
Уақытқа негізделген Түріне байланысты Түріне байланысты Жоқ (T уақытқа дейін) Жоқ Төмен
Виртуалды/Синтетикалық Limit-тен төмен Түріне байланысты Жоқ Жоқ Орташа
OCO Иә (екеуінің біреуі) Ішінара Иә (екеуі де) Ішінара Орташа
Bracket Иә Ішінара Иә Сирек Жоғары
Hidden Жоқ Иә Жоқ Сирек Жоқ
Pegged Жоқ Динамикалық Иә Өте сирек Жоғары (бағдарламалық болса)

Қорытынды: Стратегияның Құрылыс Блогі Ретіндегі Ордер

Ордер түрлері жай ғана "интерфейстегі батырмалар" емес. Олар кез келген сауда жүйесінің орындау қабаты құрылатын іргелі примитивтер. "Стратегия бэктестте пайдалы" мен "стратегия өндірісте пайдалы" арасындағы айырмашылық көбіне дәл осында — ордерлерді биржаға дәл қалай жіберетініңізде жатыр.

Бірнеше практикалық қорытынды:

  1. Стандартты ордерлерден бастаңыз, нюанстарды түсінгеніңізге көз жеткізіңіз (stop-limit пен stop-market, IOC пен FOK). Көптеген қателер дәл осында болады.
  2. Виртуалды ордерлер grid боттар үшін міндетті. Егер сіз 50-ден астам ордер қойсаңыз — оларды бәрін биржаға жібермеңіз.
  3. Chasing орындалу жылдамдығы бағадан маңыздырақ болғанда қажет. Бірақ әрдайым max_chase_distance орнатыңыз — әйтпесе өте алысқа жылжып кетуіңіз мүмкін.
  4. Уақытқа негізделген орындау — тар мамандандырылған, бірақ funding арбитражы мен оқиғаға негізделген стратегиялар үшін қуатты.
  5. Жеке ордерлерді басқару қабаты кез келген байсалды алготрейдинг жүйесі үшін сөзсіз қажет. Биржаның табиғи ордер түрлері жеткіліксіз.

Егер сіз сауда жүйесін құрып жатсаңыз және тереңірек барғыңыз келсе — ордер кітабындағы кезектегі позиция, CCXT-тегі WebSocket әдістері және funding rate арбитражы туралы мақалаларымызды қараңыз.


Сілтемелер мен Дереккөздер

  • CCXT Library — крипто биржалармен жұмыс істеуге арналған біріктірілген кітапхана, 100+ биржаны қолдайды
  • Binance API Documentation — Binance ордер түрлерінің құжаттамасы
  • Bybit API v5 — Bybit құжаттамасы, соның ішінде batch ордерлер
  • 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) — озық ордер түрлері бар кәсіби платформа
blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Нарықтан бір қадам алда болыңыз

AI сауда талдаулары, нарық аналитикасы және платформа жаңалықтары үшін біздің ақпараттық бюллетеньге жазылыңыз.

Біз сіздің жекелігіңізді құрметтейміз. Кез келген уақытта жазылымнан шығуға болады.