Lesson 1.1.4 · 60 min

Order types

The order type decides what is sacrificed: price, execution or control. This lesson codes a small matching engine against NOVA's book, simulates three stops on a gap day and models the life cycle of an order as a state machine.

Fig. — Three sell stops on NOVA from 22 to 25 October 2024, in 15-minute bars: the earnings gap triggers all of them at the open on the 25th
04/05
Module
1.1 · How markets work
Duration
60 min
Updated
Level 1 lessons
  1. 1.1 · How markets work

    1. 1.1.1 Market participants and trading venues
    2. 1.1.2 Sessions, auctions, holidays and calendars
    3. 1.1.3 Order book: bid, ask, spread and depth
    4. 1.1.4 Order types
    5. 1.1.5 Costs, settlement, leverage and margin
  2. 1.2 · Asset classes

    1. 1.2.1 Stocks: splits, dividends and corporate actions
    2. 1.2.2 ETFs: replication, tracking error, leveraged ETFs and UCITS
    3. 1.2.3 Futures: specifications, margin, roll and contango
    4. 1.2.4 Forex: pairs, pips, lots, rollover and CFDs
    5. 1.2.5 Options: calls, puts, intrinsic value and time value
    6. 1.2.6 Crypto: spot, perpetuals, funding and custody
    7. 1.2.7 Fixed income and interest rates; CFDs and leveraged products
  3. 1.3 · Market data sources

    1. 1.3.1 Types of market data: EOD, bars, ticks, order book and reference
    2. 1.3.2 Free market data sources
    3. 1.3.3 Retail and prosumer market data APIs
    4. 1.3.4 Professional market data providers
    5. 1.3.5 Crypto and on-chain data
    6. 1.3.6 Brokers as market data sources
    7. 1.3.7 Market data licensing and terms of use
    8. 1.3.8 How to choose a market data provider
  4. 1.4 · Financial data engineering

    1. 1.4.1 Downloading over REST and websockets: pagination, rate limits and retries
    2. 1.4.2 Data cleaning: missing values, bad ticks, gaps and time zones
    3. 1.4.3 Adjusting for splits and dividends: adjusted and unadjusted prices
    4. 1.4.4 Universes without survivorship bias: delistings, mergers and symbol changes
    5. 1.4.5 Building bars: time, tick, volume, dollar and imbalance
    6. 1.4.6 Continuous futures series: roll rules, difference adjustment and ratio adjustment
    7. 1.4.7 Storage: CSV, Parquet, partitioning, DuckDB, TimescaleDB and ArcticDB
    8. 1.4.8 Reproducible pipeline: download, validation, incremental updates and quality tests
  5. 1.5 · Fundamental and macro data

    1. 1.5.1 Financial statements, ratios, XBRL and SEC EDGAR
    2. 1.5.2 Macro data, economic calendars, estimates and transcripts
  6. 1.6 · Financial exploratory analysis

    1. 1.6.1 Returns, volatility, rolling correlations and drawdown
    2. 1.6.2 Stylized facts: fat tails, clustering, autocorrelation and leverage
    3. 1.6.3 Seasonality: day of the week, month, turn of the month and intraday sessions
  7. 1.7 · Programmed technical analysis

    1. 1.7.1 Technical indicators in Python
    2. 1.7.2 Your own implementation versus TA-Lib: validating results
    3. 1.7.3 Candlestick patterns and support and resistance
    4. 1.7.4 Multi-timeframe analysis without look-ahead

Price or execution: the dilemma of every order

An order tells the broker what to buy or sell, how much and under what conditions. Every variant that exists comes from one basic decision:

  • Market order. Execute now, at whatever price there is. It guarantees execution (if there is a counterparty) and does not guarantee the price.
  • Limit order. Execute only at a price equal to or better than the limit. It guarantees the price and does not guarantee execution.

A limit buy above the best ask is marketable: it is executed immediately, like a market order, but with a cap. A limit buy below the best bid stays in the book, at the end of the queue at its price, and waits (lesson 1.1.3). The other types add to these two a condition (a trigger price, a time, a minimum size) or a relationship with other orders.

Fig. — Where each order type waits relative to the best bid and the best ask (schematic)

The figure places each type relative to the book. Limit orders wait on the “good” side of the price: buys below, sells above. Stops wait on the “bad” side: a sell stop sits below the bid and is triggered if the price falls to it; a buy stop, above the ask. Market orders do not wait: they cross the spread.

A twenty-line matching engine

The best way to understand an order is to execute it. This engine walks the levels of one side of the book in priority order and returns the fills. It uses NOVA's L2 snapshot at 15:00 New York time from lesson 1.1.3, with the best ask at 52.75.

IN [01]python
import pandas as pd

l2 = pd.read_parquet("nova_libro_l2.parquet")
foto = l2[l2["ts"] == "2025-06-02 19:00:00+00:00"]
asks = foto[foto["lado"] == "ask"].sort_values("precio")
bids = foto[foto["lado"] == "bid"].sort_values("precio",
                                                ascending=False)
lado_venta = list(zip(asks["precio"], asks["tamano"]))
lado_compra = list(zip(bids["precio"], bids["tamano"]))
medio = (asks["precio"].iloc[0] + bids["precio"].iloc[0]) / 2
lado_venta[:4]
OUT [01]The sell side of the book at 15:00
[(52.75, 1870), (52.76, 9900), (52.77, 13280), (52.78, 17322)]
IN [02]python
def recorre(niveles, cantidad, limite=None, compra=True):
    """Execute against `niveles` [(price, size)] up to `cantidad`.
    With `limite`, do not go past that price.
    Return (fills, remainder)."""
    ejecuciones, resto = [], cantidad
    for precio, tam in niveles:
        if limite is not None and (precio > limite if compra
                                   else precio < limite):
            break
        q = min(resto, tam)
        ejecuciones.append((precio, q))
        resto -= q
        if resto == 0:
            break
    return ejecuciones, resto

def resumen(ejecuciones, medio):
    q = sum(c for _, c in ejecuciones)
    pm = sum(p * c for p, c in ejecuciones) / q
    return {"acciones": q, "niveles": len(ejecuciones),
            "precio_medio": round(float(pm), 4),
            "sobre_medio_cent": round(float(100 * abs(pm - medio)), 2)}

for n in (2_000, 30_000):
    ejec, resto = recorre(lado_venta, n)
    print(f"market buy of {n}:", resumen(ejec, medio))
OUT [02]Walking the book
market buy of 2000: {'acciones': 2000, 'niveles': 2, 'precio_medio': 52.7507, 'sobre_medio_cent': 0.56}
market buy of 30000: {'acciones': 30000, 'niveles': 4, 'precio_medio': 52.7671, 'sobre_medio_cent': 2.21}

A market buy of 2,000 shares is filled with the whole first level (1,870 shares at 52.75) and 130 from the second, at 52.76: average price 52.7507, 0.56 cents above the mid. One of 30,000 walks four levels and pays on average 52.7671: 2.21 cents over the mid, almost four times more per share. That is the order's impact, the cost that grows with size (lesson 1.1.5 turns it into a curve):

Fig. — Market buy of up to 30,000 shares against the NOVA book at 15:00 New York time: price of the last level reached and average price paid

Time in force: DAY, GTC, IOC and FOK

Besides the price, an order carries a time in force, which says how long it lives:

Time in forceWhat it doesWhen to use it
DAYLives until the end of the session and expiresDefault at almost every broker; daily systems
GTCLives until it is executed or cancelled (brokers usually cap it at a number of days)Stops and targets of multi-day positions
IOCExecutes what it can right away and cancels the restTaking liquidity without leaving a trace in the book
FOKEverything right away or nothingWhen a partial fill is useless (pairs, hedges)
On open (OPG, MOO, LOO)Only takes part in the opening auctionSystems that decide with the previous close
On close (MOC, LOC)Only takes part in the closing auctionRebalancing, matching the closing price

With the previous engine, IOC and FOK take two lines:

IN [03]python
def limitada(niveles, cantidad, limite, validez, compra=True):
    ejec, resto = recorre(niveles, cantidad, limite, compra)
    if validez == "FOK" and resto > 0:
        return [], cantidad, "cancelled: not enough"
    if validez == "IOC":
        estado = "filled" if resto == 0 else f"cancels {resto}"
        return ejec, resto, estado
    return ejec, resto, f"{resto} rest in the book"

for validez in ("IOC", "FOK", "DAY"):
    ejec, resto, estado = limitada(lado_venta, 15_000, 52.76, validez)
    print(validez, "fills", sum(q for _, q in ejec), "·", estado)
OUT [03]IOC and FOK limit orders for 15,000 shares at 52.76
IOC fills 11770 · cancels 3230
FOK fills 0 · cancelled: not enough
DAY fills 11770 · 3230 rest in the book

At 52.76 or better there are only 11,770 shares. The IOC takes them and cancels the 3,230 that are missing; the FOK executes nothing; the DAY executes the same as the IOC and leaves a buy of 3,230 shares at 52.76 in the book, which becomes the new best bid: it has crossed the spread and now defines it.

On-open and on-close orders are matched in the auctions (lesson 1.1.2). On the NYSE there are market variants (MOO, MOC) and limit variants (LOO, LOC); MOC and LOC orders can be sent, modified or cancelled until 15:50, and after that only those that reduce the published imbalance are accepted1Note 1 · NYSE, “NYSE opening and closing auctions” fact sheet (accessed on October 21, 2026)..

Stop, stop-limit and trailing through a gap

A stop is a dormant order with a trigger price. A sell stop at 62.50 does nothing while the price is above it; when a trade prints at 62.50 or lower, it becomes a market order. A stop-limit becomes a limit order: “if it falls to 62.50, sell, but not below 62.30”. A trailing stop moves the trigger behind the price: it stays, for example, 2.5% below the high reached since the entry, and only moves up.

The difference between the three matters in gaps: when the price jumps from one level to another without going through the levels in between, typically from a close to the next open after news. NOVA reported earnings after the close on Thursday, October 24, 2024 and on Friday it opened 5.1% lower:

IN [04]python
diario = pd.read_csv("nova_diario_sin_ajustar.csv",
                     parse_dates=["fecha"])
diario["hueco_pct"] = 100 * (diario["apertura"]
                             / diario["cierre"].shift() - 1)
tramo = diario[diario["fecha"].between("2024-10-21", "2024-10-28")]
tramo.set_index("fecha").round(2)
OUT [04]The October 25, 2024 gap
            apertura  maximo  minimo  cierre   volumen  hueco_pct
fecha
2024-10-21     63.27   64.32   62.90   64.30  13820466       0.02
2024-10-22     64.26   64.47   63.74   64.07   9126443      -0.06
2024-10-23     63.93   64.67   63.76   64.19   8728289      -0.22
2024-10-24     64.11   64.60   63.21   63.40   9874612      -0.12
2024-10-25     60.14   60.87   59.51   59.59  51399629      -5.14
2024-10-28     59.25   59.33   57.11   58.02  18793763      -0.57

A system buys at the close of October 21, at 64.30, and protects the position in three ways: a stop at 62.50, a 62.50/62.30 stop-limit valid for the day and a 2.5% trailing stop. The 5-minute bars let you simulate when each one is triggered. The rule for executing a stop on bars is the honest one: if the bar opens below the stop, the sell goes out at the open; if it opens above and its low touches it, it goes out at the stop (and even that is optimistic: in reality, at the first price traded afterwards).

IN [05]python
barras = pd.read_parquet("nova_barras_5min.parquet")
barras = (barras[barras["ts"] > "2024-10-21 20:00"]
          .reset_index(drop=True))
entrada = 64.30

def stop_venta(barras, stop):
    for b in barras.itertuples():
        if b.minimo <= stop:
            return b.ts, min(b.apertura, stop)
    return None, None

def trailing_venta(barras, entrada, pct):
    maximo = entrada
    for b in barras.itertuples():
        stop = maximo * (1 - pct)
        if b.minimo <= stop:
            return b.ts, min(b.apertura, stop), stop
        maximo = max(maximo, b.maximo)
    return None, None, None

def stop_limit_venta(barras, stop, limite, validez="DAY"):
    disparo = None
    for b in barras.itertuples():
        if disparo is None and b.minimo <= stop:
            disparo = b.ts
            if b.apertura > stop:           # no gap: goes out at the stop
                return disparo, b.ts, stop
        if disparo is not None:
            if validez == "DAY" and b.ts.date() > disparo.date():
                return disparo, None, None  # expired unexecuted
            if b.maximo >= limite:
                return disparo, b.ts, max(limite, b.apertura)
    return disparo, None, None

ts, px = stop_venta(barras, 62.50)
print("stop 62.50:      trigger", ts, "· sell at", px)
ts, px, nivel = trailing_venta(barras, entrada, 0.025)
print(f"trailing 2.5%:   trigger {ts} · level {nivel:.2f}",
      f"· sell at {px}")
d, ts, px = stop_limit_venta(barras, 62.50, 62.30)
print("stop-limit DAY:  trigger", d, "· sell:", px)
OUT [05]Three stops on 5-minute bars
stop 62.50:      trigger 2024-10-25 13:30:00+00:00 · sell at 60.14
trailing 2.5%:   trigger 2024-10-25 13:30:00+00:00 · level 63.05 · sell at 60.14
stop-limit DAY:  trigger 2024-10-25 13:30:00+00:00 · sell: None

All three are triggered in the same bar, the one at the open of October 25 (13:30 UTC, 9:30 New York time):

  • The stop at 62.50 sells at 60.14, the open: $2.36 per share below its price. The stop is not a guaranteed price; it is the condition for a market order to be born.
  • The trailing stop, which had raised its level to 63.05 with the 64.67 high of October 23, sells exactly the same, at 60.14. In a gap, a tighter stop is no help at all.
  • The stop-limit is triggered, but its 62.30 limit is $1.43 above the highest price of the day, 60.87. At 16:00 it expires unexecuted and the position stays open, unprotected.

The figure at the top draws it bar by bar. And what if the stop-limit were GTC?

IN [06]python
disparo, ts, px = stop_limit_venta(barras, 62.50, 62.30, validez="GTC")
print("executed", ts, "at", px)
entre = barras[(barras["ts"] > disparo) & (barras["ts"] < ts)]
print("low while waiting:", entre["minimo"].min(),
      "on", entre.loc[entre["minimo"].idxmin(), "ts"].date())
OUT [06]The stop-limit with GTC time in force
executed 2025-01-16 16:55:00+00:00 at 62.3
low while waiting: 53.51 on 2025-01-03

It is executed almost three months later, on January 16, 2025, when NOVA returns to 62.30. Along the way the stock reaches 53.51 on January 3, almost $9 below the limit. For a portfolio this is the opposite of protection: the stop-limit avoids selling badly in the gap in exchange for not selling at all while the price keeps falling.

The life cycle of an order

An order does not simply go from “sent” to “filled”. The broker and the market send back states, and a real system has to react to each one: a partial fill leaves a position different from the one planned; a rejection (for insufficient funds, a price out of range, a closed market) means there is no position.

NewAcceptedRejectedPartialFilledCancelledExpired
Mermaid source
stateDiagram-v2
  [*] --> New
  New --> Accepted
  New --> Rejected
  Accepted --> Partial
  Accepted --> Filled
  Accepted --> Cancelled
  Accepted --> Expired
  Partial --> Filled
  Partial --> Cancelled
  Partial --> Expired
  Filled --> [*]
  Cancelled --> [*]
  Rejected --> [*]
  Expired --> [*]

Three states are final as far as the order is concerned, though not as far as the position is: an order cancelled or expired after a partial fill has left shares in the account. Modeling it as a state machine avoids the most common mistakes, such as cancelling an order that was already filled or counting a fill twice:

IN [07]python
TRANSICIONES = {
    "nueva": {"aceptada", "rechazada"},
    "aceptada": {"parcial", "ejecutada", "cancelada", "expirada"},
    "parcial": {"parcial", "ejecutada", "cancelada", "expirada"},
}

class Orden:
    def __init__(self, cantidad):
        self.cantidad, self.ejecutada = cantidad, 0
        self.estado, self.historial = "nueva", ["nueva"]

    def pasa(self, estado):
        if estado not in TRANSICIONES.get(self.estado, set()):
            raise ValueError(f"cannot go from {self.estado} to {estado}")
        self.estado = estado
        self.historial.append(estado)

    def ejecuta(self, q):
        if self.ejecutada + q > self.cantidad:
            raise ValueError("fill larger than the order")
        self.ejecutada += q
        lleno = self.ejecutada == self.cantidad
        self.pasa("ejecutada" if lleno else "parcial")

# the IOC for 15,000 shares at 52.76 against the 15:00 book
o = Orden(15_000)
o.pasa("aceptada")
for _, q in recorre(lado_venta, 15_000, limite=52.76)[0]:
    o.ejecuta(q)
o.pasa("cancelada")     # IOC: the rest is cancelled
print(" -> ".join(o.historial), "·", o.ejecutada, "shares")
OUT [07]State machine of an order
nueva -> aceptada -> parcial -> parcial -> cancelada · 11770 shares

The states are code keys, so they print in Spanish: nueva (new), aceptada (accepted), parcial (partial), ejecutada (filled), cancelada (cancelled). A filled order cannot be cancelled. If the system tries (because it received the confirmation late, for example), the state machine stops it instead of letting the error reach the account:

IN [08]python
o2 = Orden(2_000)
o2.pasa("aceptada")
o2.ejecuta(2_000)
o2.pasa("cancelada")
OUT [08]An impossible transition
ValueError: cannot go from ejecutada to cancelada

Linked orders: bracket and OCO

An OCO (one cancels the other) is two linked orders: when one is executed, the system cancels the other. The typical combination is a profit target (limit sell above) and a stop loss (stop sell below). A bracket adds the entry: the OCO pair is only activated when the entry is executed.

filledexecutedexecutedLimit entryActivates OCOLimit targetStop lossCancels the stopCancels target
Mermaid source
flowchart TB
  E[Limit entry] -->|filled| A[Activates OCO]
  A --> TP[Limit target]
  A --> SL[Stop loss]
  TP -->|executed| C1[Cancels the stop]
  SL -->|executed| C2[Cancels target]

Having the broker manage the link has an advantage: if the system goes down, the exit orders remain on the server. The risk lies in partial fills: if the target is half executed, the stop has to be reduced to the remaining quantity, and not every broker does it the same way. A system that sends brackets must read what its broker does and check it in a paper trading account.

Other orders that show up in the data

  • Iceberg. A limit order with a visible part and a hidden reserve that is replenished (lesson 1.1.3).
  • Hidden. A limit order that is not visible at all in the book; it usually loses priority to visible orders at the same price.
  • Pegged. Its price follows the best bid, the best ask or the midpoint; it is what is matched in the dark pool of lesson 1.1.1.
  • Broker algorithms (TWAP, VWAP). They are not market order types, but programs that split a large order into many small ones over the day. They are the source of many of the 100-share trades on the tape.
TypeGuaranteesMain riskTypical use in a system
MarketExecutionPrice, especially with a thin bookExits that cannot wait
LimitPriceNot being executedPatient entries, liquidity provision
StopTriggerGap: execution far from the stopStop loss
Stop-limitPrice after the triggerBeing unable to exitBreakout entries with a cap
TrailingFollows the priceSame as the stopProtecting profits
MOC / LOCClosing priceImbalance; sending before the cutoff timeRebalancing, at-the-close systems
IOC / FOKLeaves no tracePartial or no executionTaking liquidity in a controlled way
Bracket / OCOAutomatic exitBadly linked partialsPositions with a target and a stop

Exercises

  1. Compute the average price and the levels walked by a market sell of 25,000 shares against the 15:00 book and compare it with a buy of the same size. Why do they not cost the same?
Solution
IN [09]python
for lado, niveles, nombre in ((False, lado_compra, "sell"),
                              (True, lado_venta, "buy")):
    ejec, resto = recorre(niveles, 25_000, compra=lado)
    print(nombre, resumen(ejec, medio))
OUT [09]Market sell of 25,000 shares
sell {'acciones': 25000, 'niveles': 3, 'precio_medio': 52.7345, 'sobre_medio_cent': 1.05}
buy {'acciones': 25000, 'niveles': 3, 'precio_medio': 52.7645, 'sobre_medio_cent': 1.95}

Both walk three levels, but the sell pays 1.05 cents per share over the mid and the buy 1.95, almost double. The reason lies in the first level: the sell finds 13,432 shares at the best bid, half a cent from the mid, and the buy only 1,870 at the best ask, so most of the buy is executed at the second and third levels (lesson 1.1.3). If the sell were for 40,000 shares, things would change: beyond the third buy level there is hardly anything. An asymmetric book charges buyers and sellers differently, and the asymmetry changes with size.

  1. On June 2, 2025 a system buys NOVA at the 9:30 open (open of the first bar) with a bracket: target 0.5% above and stop 0.5% below. With the 5-minute bars, which one is executed first? What would you do if the same bar touched both?
Solution
IN [10]python
todas = pd.read_parquet("nova_barras_5min.parquet")
dia = todas[todas["ts"].dt.date == pd.Timestamp("2025-06-02").date()]
ent = dia["apertura"].iloc[0]
tp, sl = round(ent * 1.005, 2), round(ent * 0.995, 2)
print("entry", ent, "· target", tp, "· stop", sl)
for b in dia.itertuples():
    toca_tp, toca_sl = b.maximo >= tp, b.minimo <= sl
    if toca_tp or toca_sl:
        print(b.ts.strftime("%H:%M UTC"), "target" if toca_tp else "",
              "stop" if toca_sl else "")
        break
OUT [10]Bracket on 5-minute bars
entry 53.35 · target 53.62 · stop 53.08
13:55 UTC  stop

The stop is hit in the 13:55 UTC bar (9:55 New York time), 25 minutes after the entry, and the OCO cancels the target. The first bar that touches one of the two levels decides the exit. If the same bar touched both, with bars you cannot know which came first: the prudent rule is to assume the stop (the worst case) or to drop down to higher-resolution data, such as the trades in nova_operaciones.parquet.

  1. A daily system enters at the open with market orders and exits with a 3% GTC stop. Propose a better set of orders and justify each change.
Solution

Entry: an on-open order (OPG or MOO) instead of a market order at 9:30, to be matched in the auction, at a single price and with plenty of liquidity, rather than in the first seconds of continuous trading, when the spread is wider (lesson 1.1.3). If a cap is wanted, an LOO. Exit: the stop can remain a stop (not a stop-limit), because what matters is getting out; but the backtest must simulate it with the open on gaps. If the strategy also has a target, link target and stop in an OCO managed by the broker, so that the exit does not depend on the system being alive. And in every case, handle partial fills and rejections with the state machine.

Footnotes

  1. NYSE, “NYSE opening and closing auctions” fact sheet (accessed on October 21, 2026). ↩

Each answer is folded under its question

Self-assessment

  1. What does a market order guarantee and what does it not?

    • AIt guarantees the price, not the execution
    • BIt guarantees execution if there is a counterparty, not the price
    • CIt guarantees both
    • DIt guarantees neither
    Show answer

    Answer B. It guarantees execution if there is a counterparty, not the price

    A market order is executed against whatever is in the book, level by level, until it is complete. In NOVA's 15:00 book, a market buy of 30,000 shares pays on average 52.7671, almost two cents above the best ask.

  2. An IOC limit buy for 15,000 shares at 52.76 arrives at a book with 1,870 shares at 52.75 and 9,900 at 52.76. What happens?

    • ANothing is executed
    • B15,000 are executed, the last ones at 52.77
    • C11,770 shares are executed and the rest is cancelled
    • D11,770 are executed and the rest stays in the book
    Show answer

    Answer C. 11,770 shares are executed and the rest is cancelled

    IOC (immediate or cancel) executes what it can right away within the limit and cancels the rest. With FOK (fill or kill) nothing would be executed, because there are not 15,000 shares at 52.76 or better.

  3. On October 25, 2024 NOVA opens at 60.14 after closing at 63.40. What does a stop-limit sell with stop 62.50 and limit 62.30, valid for the day, do?

    • AIt is executed at 62.30
    • BIt is executed at 60.14
    • CIt is not triggered
    • DIt is triggered but not executed: the price does not return to 62.30 for the whole session and the order expires
    Show answer

    Answer D. It is triggered but not executed: the price does not return to 62.30 for the whole session and the order expires

    The stop is triggered by the first trade below 62.50, which is the open at 60.14. A limit sell at 62.30 is then born, which is only executed at 62.30 or higher. The day's high is 60.87, so the order expires unexecuted and the position stays open.

  4. What is an OCO order?

    • ATwo linked orders: when one is executed, the other is cancelled
    • BAn order executed at the close
    • CAn order that is only executed in full
    • DA hidden order
    Show answer

    Answer A. Two linked orders: when one is executed, the other is cancelled

    OCO (*one cancels the other*) links two orders, typically a profit target and a stop. A bracket is an entry with an exit OCO that is activated when the entry is executed.

  5. A system decides with the day's close and wants to execute in the NYSE closing auction. Which order, and when?

    • AA market order at 16:00
    • BAn MOC (or LOC) sent before 15:50, with the signal computed from data before that time
    • CA stop at the closing price
    • DA GTC
    Show answer

    Answer B. An MOC (or LOC) sent before 15:50, with the signal computed from data before that time

    NYSE on-close orders are accepted, modified or cancelled until 15:50; after that only those that reduce the published imbalance are allowed. The signal cannot use the closing price, which does not yet exist when the order has to be sent (lesson 1.1.2).

Key points

  • Every order chooses between certainty of execution (market) and certainty of price (limit). The others are combinations of these two with a condition.
  • A market order walks the book: its cost grows with size. For NOVA, 2,000 shares pay 0.56 cents over the mid and 30,000, 2.21.
  • A stop is a dormant market order: in a gap it is executed at the first available price, far from the stop. A stop-limit protects the price, but may not be executed and leave the position open.
  • Time in force matters as much as the type: DAY, GTC, IOC, FOK, on open or on close.
  • Linked orders (bracket, OCO) and the life cycle (partial, cancelled, expired) are what a real system has to manage, and what a backtest usually ignores.

Resources

Data