← Torna agli articoli
March 23, 2026
5 min di lettura

Tipi di ordine nel trading algoritmico: dal limite con inseguimento agli ordini virtuali

Tipi di ordine nel trading algoritmico: dal limite con inseguimento agli ordini virtuali
#ordini
#algotrading
#limite
#inseguimento
#ordini-virtuali
#grid-bot
#market-making

Quando un principiante apre il terminale di borsa, vede due pulsanti: "Compra" e "Vendi." Quando un trader algoritmico apre il suo codebase, vede ventisette tipi di ordine, tre livelli di astrazione e un mucchio di edge case che fanno venire voglia di chiudere il laptop e andare a vendere cetrioli al mercato contadino. Ma i cetrioli, purtroppo, non ti permettono di fare arbitraggio sul funding rate alle 3:59 UTC — quindi andiamo a fondo.

In questo articolo percorreremo l'intero viaggio dagli ordini di borsa di base alle costruzioni virtuali sintetiche che esistono solo all'interno del tuo sistema e non compaiono mai nel book degli ordini. Aspettatevi TypeScript, Python, un po' di dolore e un pizzico di illuminazione.


1. Ordini standard di borsa: le basi che non puoi saltare

Ordini standard Classificazione dei tipi di ordine standard: dal market all'iceberg

Prima di costruire qualcosa di complesso, dobbiamo assicurarci di capire correttamente i mattoni di base. È sorprendente quante persone confondano stop-limit e stop-market, e poi si chiedano perché il loro stop "non ha scattato" (spoiler: ha scattato, ma l'ordine limite non è stato eseguito a causa dello slippage).

Ordine market

Il tipo più semplice e allo stesso tempo il più pericoloso. Dici alla borsa: "Compra/vendi adesso, a qualsiasi prezzo disponibile." La borsa preleva liquidità dal book degli ordini, partendo dal prezzo migliore. Se il volume al livello migliore non è sufficiente — scivola oltre.

Quando usarlo: uscita di emergenza da una posizione, esecuzione di un segnale dove la velocità conta più del prezzo.

Insidie: su un mercato sottile, un ordine market per 100 BTC può spostare il prezzo di diversi punti percentuali. I backtest che modellano gli ordini market senza tener conto dell'impatto sono pura fantasia.

Ordine limite

Specifichi un prezzo esatto. L'ordine entra nel book degli ordini e aspetta che qualcuno accetti il tuo prezzo. Se il prezzo di un ordine limite bid è superiore al mercato corrente — si esegue immediatamente (come un ordine market, ma con un prezzo massimo garantito).

Punto chiave: un ordine limite non garantisce l'esecuzione. Il prezzo può raggiungere il tuo livello e invertire, lasciandoti in coda (ne parleremo nel nostro articolo sulla posizione in coda).

Stop-market e Stop-limit

È qui che inizia la confusione. Entrambi i tipi sono ordini "dormienti" che si attivano quando viene raggiunto il prezzo di innesco (stop price). Ma:

  • Stop-market: all'attivazione, si converte in un ordine market. Garantisce l'esecuzione, ma non il prezzo.
  • Stop-limit: all'attivazione, si converte in un ordine limite. Garantisce il prezzo (non peggiore del specificato), ma non l'esecuzione.

Sul volatile mercato crypto, uno stop-limit può "mancarlo" — il prezzo ha attraversato lo stop, l'ordine limite è stato piazzato, ma il mercato era già volato via. Ti ritrovi con un ordine limite non eseguito e una perdita crescente. È esattamente per questo che lo stop-market è più comunemente usato per gli stop-loss.

Trailing stop

Uno stop che "segue" il prezzo a una distanza stabilita. Il prezzo sale — lo stop sale. Il prezzo scende — lo stop rimane fermo. Utile per proteggere i profitti nelle strategie trend-following.

Supporto degli exchange: non tutti gli exchange supportano i trailing stop nativi. I trader algoritmici spesso li implementano programmaticamente — questo dà più controllo sui parametri (callback rate, prezzo di attivazione, dimensione del passo).

Ordine iceberg

Un ordine in cui solo una frazione del volume totale è visibile nel book degli ordini. Vuoi comprare 1.000 BTC, ma mostri solo 10 nel book. Quando i primi 10 si eseguono — compaiono i successivi 10.

Perché: per nascondere le tue vere intenzioni al mercato. Un ordine grande nel book segnala a tutti che "qualcuno di importante vuole comprare/vendere." In risposta, gli algoritmi HFT iniziano il front-running e il prezzo si allontana da te.

Avvertenza: su molti exchange crypto, gli ordini iceberg non sono supportati o sono facilmente rilevabili dal pattern di volumi identici. Gli algoritmi avanzati randomizzano la dimensione della porzione visibile.

Parametri time-in-force: GTC, GTD, IOC, FOK

Questi non sono tipi di ordine separati ma parametri time-in-force — quanto a lungo vive un ordine:

Parametro Nome completo Comportamento
GTC Good Till Cancelled Vive fino alla cancellazione. Lo standard predefinito
GTD Good Till Date Vive fino a una data/ora specificata
IOC Immediate or Cancel Si esegue immediatamente (totalmente o parzialmente), il resto viene cancellato
FOK Fill or Kill Si esegue solo integralmente e immediatamente. Se impossibile — cancellato del tutto

IOC vs FOK: la differenza è critica. IOC può eseguirsi parzialmente — volevi comprare 100 BTC, ne hai comprati 3, il resto è stato cancellato. FOK è o 100 o niente.

Post-only (Maker-only)

Un ordine che è garantito di entrare nel book degli ordini come maker e non viene mai eseguito come taker. Se al momento del piazzamento il prezzo causerebbe un'esecuzione immediata — l'exchange lo rifiuta (o aggiusta il prezzo, a seconda dell'exchange).

Perché: le commissioni maker sono di solito inferiori alle commissioni taker (su Binance — 0,02% vs 0,04% per i livelli VIP). Per un market maker che piazza migliaia di ordini al giorno, la differenza di commissione è la differenza tra profitto e perdita.


2. TWAP e VWAP: come le istituzioni nascondono un elefante nel book degli ordini

Quando un hedge fund vuole comprare una posizione da 50 milioni di dollari, non piazza un singolo ordine market. Utilizza algoritmi di esecuzione — algoritmi che suddividono un ordine grande in molti più piccoli e li eseguono nel tempo, minimizzando l'impatto di mercato.

TWAP (Time-Weighted Average Price)

L'idea è semplicissima: suddividi il volume totale in parti uguali e le esegui a intervalli di tempo uguali.

import asyncio
from datetime import datetime, timedelta

class TWAPExecutor:
    """
    Executor TWAP: suddivide un ordine grande in parti uguali
    e le esegue a intervalli di tempo uguali.
    """
    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 è più intelligente: tiene conto del profilo tipico dei volumi di trading. Se il 30% del volume giornaliero viene tipicamente scambiato tra le 9:00 e le 10:00, VWAP eseguirà il 30% dell'ordine in quella finestra. L'obiettivo è avvicinare il più possibile il prezzo medio di esecuzione al VWAP di mercato.

class VWAPExecutor:
    """
    Executor VWAP: distribuisce il volume proporzionalmente
    al profilo storico dei volumi.
    """
    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)

Differenza TWAP vs VWAP: TWAP è più semplice e prevedibile. VWAP offre un prezzo medio migliore ma richiede un profilo di volume affidabile. Sul mercato crypto, dove i volumi possono essere wash-traded, il profilo VWAP deve essere costruito con attenzione.


3. Limite con inseguimento: quando il tuo ordine sa come inseguire il prezzo

Ordini limite con inseguimento Chasing limit: l'ordine insegue un prezzo in movimento con aggressività configurabile

Ora le cose si fanno davvero interessanti. Un ordine limite standard è un'entità passiva: sta nel book degli ordini e aspetta. Se il prezzo si è spostato — l'ordine rimane non eseguito. Per un trader algoritmico, questo è spesso inaccettabile: il segnale di entrata è scattato, ma la posizione non è stata costruita perché il mercato si è spostato dello 0,1%.

Un ordine limite con inseguimento (chasing limit) è un wrapper programmatico attorno a un ordine limite che:

  1. Piazza un ordine limite al prezzo migliore corrente (o con un piccolo offset)
  2. Monitora il prezzo via WebSocket
  3. Se il prezzo si allontana dall'ordine — lo cancella e lo rimpiazza più vicino al prezzo corrente
  4. Ripete finché l'ordine non viene eseguito o supera la deviazione consentita

Parametri chiave

  • chase_interval_ms — ogni quanto controllare e sostituire l'ordine. 100ms — aggressivo, 1000ms — rilassato.
  • max_chase_distance — deviazione massima dal prezzo iniziale prima che l'ordine venga cancellato. Protezione contro l'inseguimento di un mercato in fuga.
  • aggression_level — quanto vicino al prezzo di mercato piazzare l'ordine limite. 0 — al miglior bid/ask (passivo), 1 — attraversando lo spread (aggressivo, effettivamente un taker).
  • chase_on_partial — se continuare l'inseguimento se l'ordine è parzialmente eseguito.

Implementazione 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 l'inseguimento è dannoso

L'inseguimento è uno strumento potente, ma è facile trasformarlo in un generatore di perdite:

  1. Spam di cancel/replace. Ogni cancel e replace è un carico sull'API. Gli exchange limitano le richieste, e un inseguimento aggressivo può far bannare la tua chiave API.
  2. Selezione avversa. Se il prezzo ti sta scappando — il mercato potrebbe sapere qualcosa che tu non sai. Inseguire il prezzo in questa situazione significa comprare ai massimi.
  3. Transizione da Maker a Taker. Con alta aggressività stai effettivamente pagando commissioni taker, ma con un ritardo (cancel + nuovo ordine). A volte è più semplice piazzare direttamente un ordine market.

4. Ordini temporizzati: precisione al millisecondo

Ci sono situazioni in cui hai bisogno di eseguire un ordine non "al prezzo X" ma "al tempo T." Suona strano? In realtà è un'intera classe di strategie.

Casi d'uso

Arbitraggio del funding rate. Sui futures perpetui, il funding viene pagato ogni 8 ore (00:00, 08:00, 16:00 UTC su Binance). Se il funding rate = +0,1%, devi essere short al momento del settlement. Strategia: apri uno short pochi secondi prima del settlement, incassi il funding, chiudi la posizione. Il timing è critico — un secondo di ritardo significa funding perso.

Aperture/chiusure di sessione. Sui mercati tradizionali e su alcuni derivati crypto, esistono sessioni fisse. L'asta di apertura (NYSE, CME) è il momento in cui la liquidità è al suo picco. Piazzare un ordine 100ms prima dell'asta è un vantaggio.

Esecuzione basata sulle notizie. I dati sull'inflazione vengono rilasciati a un'ora prestabilita. L'algoritmo analizza il numero da un feed di notizie e piazza un ordine entro 50ms. Qui l'esecuzione temporizzata si combina con la logica event-driven.

Implementazione

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

// Esempio: piazza un ordine esattamente alle 00:00:00 UTC (settlement funding)
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);

Avvertenza importante: la precisione di un ordine temporizzato è limitata non dal tuo codice ma dalla latenza di rete verso l'exchange. Se il tuo ping all'API è di 50ms, anche un busy-wait perfetto avrà un delta di 50ms. Per HFT serio si usa la co-location — il server si trova fisicamente vicino al matching engine dell'exchange.


5. Ordini virtuali/sintetici: gli invisibili nel tuo sistema

Ordini virtuali per grid bot Ordini virtuali: gli ordini esistono solo nella memoria del bot fino a quando non scatta il trigger

Questo è forse lo strumento più sottovalutato nell'arsenale del trader algoritmico. Un ordine virtuale (detto anche ordine sintetico) è un ordine che esiste solo nel tuo sistema. Non viene inviato all'exchange fino a quando non si verifica una condizione trigger (tipicamente — il prezzo che raggiunge un certo livello).

Come funziona

  1. Il tuo algoritmo decide: "Voglio comprare BTC a $40.000"
  2. Invece di inviare un ordine limite all'exchange, crea un ordine virtuale in memoria
  3. Si iscrive allo stream di prezzi WebSocket
  4. Quando bid/ask raggiunge $40.000 — invia un ordine market o limite reale all'exchange

Perché gli ordini virtuali sono importanti

Nessuna fuga di informazioni. Il tuo ordine è invisibile nel book degli ordini. Nessuno — né altri trader, né algoritmi HFT, né l'exchange stesso — conosce le tue intenzioni fino al momento dell'esecuzione. Questo sposta fondamentalmente il bilanciamento del potere.

Protezione dal front-running. Sugli exchange crypto, specialmente quelli meno trasparenti, c'è il ragionevole sospetto che le informazioni sugli ordini limite grandi possano essere usate per il front-running (esistono persino studi al riguardo). Gli ordini virtuali eliminano questo rischio.

Grid bot. Un classico grid bot piazza una griglia di 50-200 ordini a diversi livelli di prezzo. Se li invii tutti all'exchange — sono 200 ordini nel book che: (a) sono visibili a tutti, (b) usano il limite di ordini sull'exchange (tipicamente 200-300 ordini aperti per account), (c) se il prezzo si muove bruscamente, si eseguono tutti e ti ritrovi con una posizione massiccia. Gli ordini virtuali risolvono tutti e tre i problemi.

Catchare i coltelli in caduta. Strategia: piazza ordini di acquisto virtuali a livelli -5%, -10%, -15% sotto il prezzo corrente. Se il mercato scende — gli ordini si attivano gradualmente. Se non scende — non rischi nulla e non usi slot di ordini sull'exchange.

Implementazione 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;
  }
}

// --- Esempio: Grid bot con ordini virtuali ---

async function gridBot(exchange: any) {
  const manager = new VirtualOrderManager(exchange);
  const currentPrice = 42000;
  const gridStep = 200;      // passo della griglia
  const gridLevels = 20;     // livelli in ogni direzione
  const qtyPerLevel = 0.01;  // BTC per livello

  // Crea griglia virtuale
  for (let i = 1; i <= gridLevels; i++) {
    // Ordini di acquisto sotto il prezzo corrente
    manager.addOrder({
      symbol: "BTC/USDT",
      side: "buy",
      triggerPrice: currentPrice - gridStep * i,
      qty: qtyPerLevel,
      executionType: "limit",
      limitOffset: 1,  // limit price = trigger + 1 USDT
    });

    // Ordini di vendita sopra il prezzo corrente
    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`
  );

  // Iscrizione WebSocket (pseudocodice per ccxt.pro)
  while (true) {
    const ticker = await exchange.watchTicker("BTC/USDT");
    await manager.onPriceUpdate(
      "BTC/USDT", ticker.bid, ticker.ask
    );
  }
}

Insidie degli ordini virtuali

  1. Latency gap. Tra il momento in cui vedi il prezzo e il momento in cui l'ordine reale raggiunge l'exchange, passa del tempo. Su un mercato volatile, il prezzo può volare via in quei 20-100ms. Soluzione: invia un ordine limite leggermente aggressivo (con un buffer).

  2. Esecuzioni mancate. Se il prezzo ha "attraversato" il tuo livello in un singolo tick (flash crash) e è rimbalzato — potresti non reagire in tempo. Un normale ordine limite seduto nel book avrebbe eseguito; uno virtuale — no.

  3. Gestione dello stato. Gli ordini virtuali vivono in memoria. Se il processo va in crash — gli ordini vengono persi. Soluzione: storage persistente (Redis, SQLite, file) con recovery al riavvio.


6. Ordini condizionali/smart: combinatoria degli ordini

Quando un singolo ordine non è sufficiente, i trader li combinano in costruzioni condizionali. Alcune sono supportate nativamente dagli exchange, altre vengono implementate programmaticamente.

OCO (One Cancels Other)

Due ordini sono collegati: se uno si esegue — l'altro viene automaticamente cancellato. Esempio classico: sei in una posizione long e vuoi impostare sia un take-profit che uno stop-loss. Qualunque dei due scatta per primo — l'altro deve essere cancellato.

class OCOHandler:
    """
    OCO: quando un ordine si esegue, l'altro viene cancellato.
    """
    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):
        """Controlla gli stati e cancella l'ordine accoppiato."""
        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)

Ordine bracket

Una costruzione a tre componenti: un ordine di entrata primario + OCO per l'uscita (take-profit + stop-loss). Essenzialmente, un intero ciclo di vita del trade in una singola chiamata:

  1. Entrata: ordine limite di acquisto
  2. Take-profit: ordine limite di vendita (sopra)
  3. Stop-loss: ordine stop-market di vendita (sotto)

Quando l'entrata si esegue, TP e SL vengono piazzati automaticamente. Quando uno dei due si esegue — l'altro viene cancellato.

Logica If-Then

L'opzione più flessibile — catene di ordini con condizioni arbitrarie:


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

Tali costruzioni non sono supportate nativamente da nessun exchange — solo implementazione programmatica. Questo è uno dei motivi per cui i sistemi di algotrading inevitabilmente crescono il proprio layer di gestione degli ordini.


7. Come i market maker usano tipi di ordine specializzati

Il market making è il proprio universo, e il toolkit degli ordini è all'altezza. Il lavoro di un market maker è quotare continuamente bid e ask, guadagnando sullo spread, mentre minimizza la selezione avversa (la situazione in cui un trader informato opera contro di te).

Post-only come Must-Have

Per un market maker, il post-only non è un'opzione — è un requisito. Se il tuo ordine si esegue accidentalmente come taker — invece di ricevere un rebate maker, paghi una commissione taker. Su migliaia di ordini al giorno, è catastrofico.

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

Ordini nascosti

Su alcuni exchange (Kraken, Bitfinex), sono disponibili ordini nascosti — non appaiono nel book degli ordini ma sono sull'exchange e partecipano al matching. Il compromesso: paghi commissioni taker anche come maker, ma guadagni anonimato.

Per un market maker, questo è uno strumento per la gestione dell'inventario: se si è accumulata una posizione grande, puoi piazzare un ordine nascosto per smontarla senza rivelare le tue intenzioni al mercato.

Ordini pegged

Un ordine ancorato al miglior bid/ask. Su Coinbase Advanced Trade, ad esempio, puoi piazzare un ordine che traccia automaticamente il miglior bid e si trova sempre in testa alla coda. Questo è un ordine di inseguimento nativo a livello di exchange — ma è tutt'altro che universalmente disponibile.

Gestione degli ordini in bulk

I market maker professionisti usano le API batch per cancellare e piazzare contemporaneamente decine di ordini in una singola richiesta HTTP. Su Binance è batchOrders, su Bybitplace-batch-order. Questo riduce la latenza e la pressione sui rate limit.


8. Tabella di confronto dei tipi di ordine

Tipo di ordine Garanzia di esecuzione Garanzia di prezzo Visibile nel book Nativo sugli exchange Complessità di implementazione
Market No No (istantaneo) Nessuna
Limite No Nessuna
Stop-market Sì (dopo il trigger) No No Nessuna
Stop-limit No No (fino al trigger) Nessuna
Trailing stop Sì (dopo il trigger) No No Parziale Bassa
Iceberg No Parziale Parziale Media
Post-only No Nessuna
TWAP No (dipende dalle slice) No Parziale No Media
VWAP No No Parziale No Alta
Chasing limit Più alta del limite Parziale Sì (ordine corrente) No Media
Temporizzato Dipende dal tipo Dipende dal tipo No (fino al tempo T) No Bassa
Virtuale/Sintetico Più bassa del limite Dipende dal tipo No No Media
OCO Sì (uno dei due) Parziale Sì (entrambi) Parziale Media
Bracket Parziale Raro Alta
Nascosto No No Raro Nessuna
Pegged No Dinamico Molto raro Alta (se programmatico)

Conclusione: l'ordine come mattone della strategia

I tipi di ordine non sono solo "pulsanti in un'interfaccia." Sono primitivi fondamentali da cui è costruito il layer di esecuzione di qualsiasi sistema di trading. La differenza tra "la strategia è redditizia nel backtesting" e "la strategia è redditizia in produzione" spesso risiede proprio qui — in esattamente come invii gli ordini all'exchange.

Alcune conclusioni pratiche:

  1. Inizia con gli ordini standard, assicurati di capire le sfumature (stop-limit vs stop-market, IOC vs FOK). La maggior parte degli errori avviene qui.
  2. Gli ordini virtuali sono indispensabili per i grid bot. Se stai piazzando più di 50 ordini — non inviarli tutti all'exchange.
  3. L'inseguimento è necessario quando il tasso di esecuzione conta più del prezzo. Ma imposta sempre max_chase_distance — altrimenti puoi andare molto lontano.
  4. L'esecuzione temporizzata è di nicchia ma potente per l'arb del funding e le strategie event-driven.
  5. Un layer personalizzato di gestione degli ordini è inevitabile per qualsiasi sistema di algotrading serio. I tipi di ordine nativi degli exchange non sono sufficienti.

Se stai costruendo un sistema di trading e vuoi approfondire — leggi i nostri articoli sulla posizione in coda nel book degli ordini, metodi WebSocket in CCXT, e arbitraggio del funding rate.


Riferimenti e fonti

Disclaimer: le informazioni fornite in questo articolo hanno solo scopo didattico e informativo e non costituiscono consulenza finanziaria, di investimento o di trading. Il trading di criptovalute comporta un rischio significativo di perdita.

Autori

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

Resta un Passo Avanti al Mercato

Iscriviti alla nostra newsletter per approfondimenti esclusivi sul trading con IA, analisi di mercato e aggiornamenti sulla piattaforma.

Rispettiamo la tua privacy. Annulla l'iscrizione in qualsiasi momento.