← Voltar aos artigos
March 23, 2026
5 min read

Tipos de Ordens em Trading Algorítmico: do Limit com Chasing às Ordens Virtuais

Tipos de Ordens em Trading Algorítmico: do Limit com Chasing às Ordens Virtuais
#orders
#algotrading
#limit
#chasing
#virtual-orders
#grid-bot
#market-making
📖
Part 2 of 6 · Collection
Order Book & Market Microstructure

Quando um iniciante abre o terminal de uma exchange, vê dois botões: "Comprar" e "Vender". Quando um algo trader abre seu código, vê vinte e sete tipos de ordem, três níveis de abstração e uma pilha de casos extremos que dão vontade de fechar o notebook e ir vender pepinos na feira. Mas os pepinos, infelizmente, não permitem rodar uma arbitragem de funding rate às 3:59 UTC — então vamos direto ao assunto.

Neste artigo, percorreremos todo o caminho desde as ordens básicas de exchange até construções sintéticas virtuais que existem apenas dentro do seu sistema e nunca aparecem no order book. Espere TypeScript, Python, alguma dor e um pouco de esclarecimento.


1. Ordens Padrão de Exchange: a Base que Você Não Pode Pular

Ordens padrão Classificação dos tipos de ordem padrão: de market a iceberg

Antes de construir algo complexo, precisamos garantir que entendemos corretamente os blocos básicos. É surpreendente quantas pessoas confundem stop-limit com stop-market, e depois se perguntam por que seu stop "não disparou" (spoiler: ele disparou, mas a ordem limit não foi executada devido ao slippage).

Ordem market

O tipo mais simples e, ao mesmo tempo, o mais perigoso. Você diz à exchange: "compre/venda agora, a qualquer preço disponível." A exchange retira liquidez do order book, começando pelo melhor preço. Se o volume no melhor nível não for suficiente — ela desliza para níveis piores.

Quando usar: saída emergencial de posição, execução de um sinal em que velocidade importa mais que preço.

Armadilhas: em um mercado com pouca liquidez, uma ordem market de 100 BTC pode mover o preço vários pontos percentuais. Backtests que modelam ordens market sem considerar o impacto são pura fantasia.

Ordem limit

Você especifica um preço exato. A ordem entra no order book e espera até que alguém concorde com seu preço. Se o preço de uma ordem limit de compra estiver acima do mercado atual — ela é executada imediatamente (como uma ordem market, mas com preço máximo garantido).

Ponto-chave: uma ordem limit não garante execução. O preço pode alcançar seu nível e reverter, deixando você na fila (mais sobre isso em nosso artigo sobre posição na fila).

Stop-market e Stop-limit

É aqui que começa a confusão. Ambos os tipos são ordens "adormecidas" que se ativam quando o preço de disparo (stop price) é atingido. Mas:

  • Stop-market: ao disparar, converte-se em ordem market. Garante execução, mas não o preço.
  • Stop-limit: ao disparar, converte-se em ordem limit. Garante o preço (não pior que o especificado), mas não a execução.

No volátil mercado cripto, um stop-limit pode "falhar" — o preço rompeu o stop, a ordem limit foi colocada, mas o mercado já passou voando. Você fica com uma ordem limit não executada e um prejuízo crescente. É exatamente por isso que o stop-market é mais usado para stop-loss.

Trailing stop

Um stop que "segue" o preço a uma distância definida. O preço sobe — o stop sobe junto. O preço cai — o stop permanece no lugar. Útil para proteger lucros em estratégias de tendência.

Suporte das exchanges: nem todas as exchanges oferecem trailing stop nativo. Algo traders frequentemente o implementam programaticamente — isso dá mais controle sobre os parâmetros (callback rate, preço de ativação, tamanho do passo).

Ordem iceberg

Uma ordem em que apenas uma fração do volume total é visível no order book. Você quer comprar 1.000 BTC, mas mostra apenas 10 no book. Quando os primeiros 10 são executados — os próximos 10 aparecem.

Por quê: para esconder suas verdadeiras intenções do mercado. Uma ordem grande no book sinaliza a todos que "alguém grande quer comprar/vender." Em resposta, algoritmos de HFT começam a fazer front-running, e o preço se afasta de você.

Ressalva: em muitas exchanges cripto, ordens iceberg não são suportadas ou são facilmente detectadas pelo padrão de volumes idênticos. Algoritmos avançados randomizam o tamanho da porção visível.

Parâmetros de time-in-force: GTC, GTD, IOC, FOK

Não são tipos de ordem separados, mas parâmetros de tempo de vida — quanto tempo uma ordem permanece ativa:

Parâmetro Nome Completo Comportamento
GTC Good Till Cancelled Permanece ativa até ser cancelada. O padrão default
GTD Good Till Date Permanece ativa até uma data/hora especificada
IOC Immediate or Cancel Executa imediatamente (total ou parcialmente), o restante é cancelado
FOK Fill or Kill Executa apenas de forma total e imediata. Se impossível — é cancelada por completo

IOC vs FOK: a diferença é crítica. IOC pode ser executada parcialmente — você queria comprar 100 BTC, comprou 3, o resto foi cancelado. FOK é tudo ou nada.

Post-only (somente Maker)

Uma ordem que garante entrar no order book como maker e nunca é executada como taker. Se, no momento em que é colocada, o preço causaria execução imediata — a exchange a rejeita (ou ajusta o preço, dependendo da exchange).

Por quê: as taxas de maker geralmente são menores que as de taker (na Binance — 0,02% vs 0,04% para os níveis VIP). Para um market maker que coloca milhares de ordens por dia, a diferença de taxa é a diferença entre lucro e prejuízo.


2. TWAP e VWAP: Como as Instituições Escondem um Elefante no Order Book

Quando um hedge fund quer comprar uma posição de US$ 50 milhões, ele não coloca uma única ordem market. Ele usa algoritmos de execução — algoritmos que dividem uma ordem grande em muitas menores e as executam ao longo do tempo, minimizando o impacto no mercado.

TWAP (Time-Weighted Average Price)

A ideia é simples: dividir o volume total em partes iguais e executar em intervalos de tempo iguais.

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)

O VWAP é mais inteligente: leva em conta o perfil típico de volume de negociação. Se 30% do volume diário costuma ser negociado entre 9:00 e 10:00, o VWAP executará 30% da ordem nessa janela. O objetivo é obter o preço médio de execução o mais próximo possível do VWAP de mercado.

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)

Diferença TWAP vs VWAP: o TWAP é mais simples e previsível. O VWAP entrega um preço médio melhor, mas exige um perfil de volume confiável. No mercado cripto, onde volumes podem ser wash-traded, o perfil de VWAP precisa ser construído com cuidado.


3. Limit com Chasing: Quando Sua Ordem Sabe Perseguir o Preço

Ordens limit com chasing Chasing limit: a ordem persegue um preço em movimento com agressividade configurável

Agora as coisas ficam realmente interessantes. Uma ordem limit padrão é uma entidade passiva: fica no order book e espera. Se o preço se mover — a ordem permanece sem execução. Para um algo trader, isso costuma ser inaceitável: o sinal de entrada disparou, mas a posição não foi construída porque o mercado se moveu 0,1%.

Chasing limit order é um wrapper programático em torno de uma ordem limit que:

  1. Coloca uma ordem limit no melhor preço atual (ou com um pequeno offset)
  2. Monitora o preço via WebSocket
  3. Se o preço se afastar da ordem — cancela e recoloca mais perto do preço atual
  4. Repete até que a ordem seja executada ou exceda o desvio permitido

Parâmetros-chave

  • chase_interval_ms — com que frequência verificar e recolocar a ordem. 100ms — agressivo, 1000ms — relaxado.
  • max_chase_distance — desvio máximo em relação ao preço inicial antes de a ordem ser cancelada. Proteção contra perseguir um mercado descontrolado.
  • aggression_level — quão perto do preço de mercado colocar a ordem limit. 0 — no melhor bid/ask (passivo), 1 — cruzando o spread (agressivo, efetivamente um taker).
  • chase_on_partial — se deve continuar perseguindo caso a ordem seja parcialmente executada.

Implementação em 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));
  }
}

Quando o Chasing É Prejudicial

O chasing é uma ferramenta poderosa, mas é fácil transformá-lo em um gerador de perdas:

  1. Spam de cancel/replace. Cada cancelamento e recolocação é uma carga na API. As exchanges limitam requisições (rate limit), e um chasing agressivo pode fazer sua chave de API ser banida.
  2. Seleção adversa. Se o preço está fugindo de você — o mercado pode saber algo que você não sabe. Perseguir o preço nessa situação significa comprar no topo.
  3. Transição de Maker para Taker. Com alta agressividade, você efetivamente paga taxas de taker, mas com atraso (cancel + nova ordem). Às vezes é mais simples apenas colocar uma ordem market.

4. Ordens Baseadas em Tempo: Precisão de Milissegundos

Há situações em que você precisa executar uma ordem não "no preço X", mas "no momento T". Parece estranho? Na verdade é uma classe inteira de estratégias.

Casos de uso

Arbitragem de funding rate. Em futuros perpétuos, o funding é pago a cada 8 horas (00:00, 08:00, 16:00 UTC na Binance). Se a taxa de funding = +0,1%, você precisa estar vendido no momento do acerto. Estratégia: abrir um short poucos segundos antes do acerto, coletar o funding, fechar a posição. O timing é crítico — um segundo de atraso significa funding perdido.

Aberturas/fechamentos de sessão. Em mercados tradicionais e alguns derivativos cripto, existem sessões fixas. O leilão de abertura (NYSE, CME) é o momento em que a liquidez está no auge. Colocar uma ordem 100ms antes do leilão é uma vantagem.

Execução baseada em notícias. Dados de inflação são divulgados em horário programado. O algoritmo faz parsing do número em um feed de notícias e coloca uma ordem em 50ms. Aqui, a execução baseada em tempo é combinada com lógica orientada a eventos.

Implementação

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);

Ressalva importante: a precisão de uma ordem baseada em tempo é limitada não pelo seu código, mas pela latência de rede até a exchange. Se o seu ping para a API é de 50ms, mesmo um busy-wait perfeito terá um delta de 50ms. Para HFT sério, usa-se co-location — o servidor fica fisicamente ao lado do matching engine da exchange.


5. Ordens Virtuais/Sintéticas: as Invisíveis do Seu Sistema

Ordens virtuais para grid bots Ordens virtuais: existem apenas na memória do bot até o gatilho disparar

Esta talvez seja a ferramenta mais subestimada no arsenal do algo trader. Uma ordem virtual (também conhecida como ordem sintética) é uma ordem que existe apenas no seu sistema. Ela não é enviada à exchange até que uma condição de gatilho seja atendida (tipicamente — o preço atingir um determinado nível).

Como Funciona

  1. Seu algoritmo decide: "quero comprar BTC a US$ 40.000"
  2. Em vez de enviar uma ordem limit à exchange, ele cria uma ordem virtual na memória
  3. Assina o stream de preço via WebSocket
  4. Quando o bid/ask atinge US$ 40.000 — envia uma ordem market ou limit real à exchange

Por Que Ordens Virtuais Importam

Nenhum vazamento de informação. Sua ordem é invisível no order book. Ninguém — nem outros traders, nem algoritmos de HFT, nem mesmo a própria exchange — sabe de suas intenções até o momento da execução. Isso muda fundamentalmente o equilíbrio de poder.

Proteção contra front-running. Em exchanges cripto, especialmente as menos transparentes, há suspeita razoável de que informações sobre grandes ordens limit possam ser usadas para front-running (há até estudos sobre isso). Ordens virtuais eliminam esse risco.

Grid bots. Um grid bot clássico coloca uma grade de 50 a 200 ordens em diferentes níveis de preço. Se você enviar todas para a exchange — são 200 ordens no book que: (a) são visíveis para todos, (b) consomem o limite de ordens na exchange (tipicamente 200-300 ordens abertas por conta), (c) se o preço se mover bruscamente, todas são executadas e você acaba com uma posição enorme. Ordens virtuais resolvem os três problemas.

Pegando facas caindo. Estratégia: colocar ordens virtuais de compra nos níveis -5%, -10%, -15% abaixo do preço atual. Se o mercado cair — as ordens disparam gradualmente. Se não cair — você não arrisca nada e não usa nenhum slot de ordem na exchange.

Implementação em 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
    );
  }
}

Armadilhas das Ordens Virtuais

  1. Gap de latência. Entre o momento em que você vê o preço e o momento em que a ordem real chega à exchange, o tempo passa. Em um mercado volátil, o preço pode fugir nesses 20-100ms. Solução: enviar uma ordem limit ligeiramente agressiva (com uma margem).

  2. Execuções perdidas. Se o preço "perfurou" seu nível em um único tick (flash crash) e voltou — você pode não reagir a tempo. Uma ordem limit comum, esperando no book, teria sido executada; uma virtual — não.

  3. Gestão de estado. Ordens virtuais vivem em memória. Se o processo travar — as ordens são perdidas. Solução: armazenamento persistente (Redis, SQLite, arquivo) com recuperação ao reiniciar.


6. Ordens Condicionais/Inteligentes: Combinatória de Ordens

Quando uma única ordem não é suficiente, os traders as combinam em construções condicionais. Algumas são suportadas nativamente pelas exchanges, outras são implementadas programaticamente.

OCO (One Cancels Other)

Duas ordens são vinculadas: se uma é executada — a outra é automaticamente cancelada. Exemplo clássico: você está em uma posição long e quer definir tanto um take-profit quanto um stop-loss. Qual disparar primeiro — a outra deve ser cancelada.

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

Uma construção de três componentes: uma ordem de entrada primária + OCO para saída (take-profit + stop-loss). Essencialmente, um ciclo de vida completo de trade em uma única chamada:

  1. Entrada: ordem limit de compra
  2. Take-profit: ordem limit de venda (acima)
  3. Stop-loss: ordem stop-market de venda (abaixo)

Quando a entrada é executada, TP e SL são colocados automaticamente. Quando um dos dois é executado — o outro é cancelado.

Lógica If-Then

A opção mais flexível — cadeias de ordens com condições arbitrárias:


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},
            },
        ]
    }
]

Tais construções não são suportadas nativamente por nenhuma exchange — apenas por implementação programática. Essa é uma das razões pelas quais os sistemas de algotrading inevitavelmente desenvolvem sua própria camada de gestão de ordens.


7. Como Market Makers Usam Tipos de Ordem Especializados

Market making é um universo à parte, e o conjunto de ferramentas de ordens acompanha isso. O trabalho de um market maker é cotar continuamente bid e ask, ganhando com o spread, ao mesmo tempo minimizando a seleção adversa (a situação em que um trader informado negocia contra você).

Post-only como Obrigatório

Para um market maker, post-only não é uma opção — é um requisito. Se sua ordem acidentalmente for executada como taker — em vez de receber um rebate de maker, você paga uma taxa de taker. Em milhares de ordens por dia, isso é catastrófico.

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

Ordens ocultas

Em algumas exchanges (Kraken, Bitfinex), há ordens ocultas disponíveis — elas não aparecem no order book, mas estão na exchange e participam do matching. A contrapartida: você paga taxas de taker mesmo como maker, mas ganha anonimato.

Para um market maker, essa é uma ferramenta de gestão de inventário: se uma posição grande se acumulou, você pode colocar uma ordem oculta para desfazê-la sem revelar sua intenção ao mercado.

Ordens pegged

Uma ordem ancorada (pegged) ao melhor bid/ask. Na Coinbase Advanced Trade, por exemplo, você pode colocar uma ordem que acompanha automaticamente o melhor bid e sempre fica na frente da fila. Isso é uma ordem de chasing nativa em nível de exchange — mas está longe de ser universalmente disponível.

Gestão de ordens em massa

Market makers profissionais usam APIs em lote (batch) para cancelar e colocar simultaneamente dezenas de ordens em uma única requisição HTTP. Na Binance isso é batchOrders, na Bybitplace-batch-order. Isso reduz a latência e a pressão do rate limit.


8. Tabela Comparativa de Tipos de Ordem

Tipo de Ordem Garantia de Execução Garantia de Preço Visível no Book Nativo nas Exchanges Complexidade de Implementação
Market Sim Não Não (instantâneo) Sim Nenhuma
Limit Não Sim Sim Sim Nenhuma
Stop-market Sim (após disparo) Não Não Sim Nenhuma
Stop-limit Não Sim Não (até disparo) Sim Nenhuma
Trailing stop Sim (após disparo) Não Não Parcial Baixa
Iceberg Não Sim Parcial Parcial Média
Post-only Não Sim Sim Sim Nenhuma
TWAP Não (depende das fatias) Não Parcial Não Média
VWAP Não Não Parcial Não Alta
Chasing limit Maior que limit Parcial Sim (ordem atual) Não Média
Baseada em tempo Depende do tipo Depende do tipo Não (até o momento T) Não Baixa
Virtual/Sintética Menor que limit Depende do tipo Não Não Média
OCO Sim (uma das duas) Parcial Sim (ambas) Parcial Média
Bracket Sim Parcial Sim Raro Alta
Oculta Não Sim Não Raro Nenhuma
Pegged Não Dinâmica Sim Muito raro Alta (se programática)

Conclusão: A Ordem como Bloco de Construção de Estratégia

Tipos de ordem não são apenas "botões em uma interface." São primitivas fundamentais a partir das quais é construída a camada de execução de qualquer sistema de trading. A diferença entre "a estratégia é lucrativa no backtest" e "a estratégia é lucrativa em produção" muitas vezes está exatamente aqui — em como exatamente você envia ordens à exchange.

Algumas conclusões práticas:

  1. Comece com ordens padrão, garanta que entende as nuances (stop-limit vs stop-market, IOC vs FOK). A maioria dos erros acontece aqui.
  2. Ordens virtuais são obrigatórias para grid bots. Se você está colocando mais de 50 ordens — não as envie todas para a exchange.
  3. Chasing é necessário quando a taxa de execução importa mais que o preço. Mas sempre defina max_chase_distance — caso contrário você pode se desviar muito.
  4. Execução baseada em tempo é de nicho, mas poderosa para arbitragem de funding e estratégias orientadas a eventos.
  5. Uma camada customizada de gestão de ordens é inevitável para qualquer sistema de algotrading sério. Os tipos de ordem nativos das exchanges não são suficientes.

Se você está construindo um sistema de trading e quer se aprofundar — confira nossos artigos sobre posição na fila do order book, métodos WebSocket em CCXT, e arbitragem de funding rate.


Referências e Fontes

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

Fique à frente do mercado

Assine nossa newsletter para insights exclusivos sobre trading com IA, análises de mercado e atualizações da plataforma.

Respeitamos sua privacidade. Cancele a inscrição a qualquer momento.