Lesson 5 · 20 min

Order types & execution

Market, limit, and stop orders — and how the PortfolioEngine simulates fills.

OrdersSlippageRebalanceTurnover

An order is an instruction to your broker. Beginners drown in jargon — market, limit, stop, MOC — but in systematic trading you usually express *intent* through `set_target_weights` and let the infrastructure translate it into orders.

Definition: Execute now at the best available price.

Pros

Certainty of fill (if liquidity exists).

Cons

Price uncertainty — you pay the spread + slippage.

Quant Buffet: PortfolioEngine rebalances at the daily close with slippage applied — similar to a market-on-close intent.

Daily bar (lab assumption)Close price → engine fill + slippage

The four orders that matter

OrderGuaranteesGives upWhen quants use it
MarketExecutionPriceSmall orders in liquid ETFs
LimitPriceExecutionAnything thin, or large relative to volume
Stop (market)Exit is attemptedPrice entirelyRarely — see the flash-crash lesson
Market-on-closeThe official closing priceIntraday choiceDaily strategies whose signal *is* the close

Market-on-close deserves special attention because it matches what the lab simulates. The closing auction is typically the single most liquid moment of the US trading day — often around a tenth of total volume — so a daily-close strategy that submits MOC orders is trading when depth is best. That is a rare case where the realistic implementation is also the convenient one.

How Quant Buffet executes in code

python
def on_day(engine, dt):
    # Target 60% SPY, 40% TLT — engine sells and buys to reach it
    engine.set_target_weights(dt, {"SPY": 0.6, "TLT": 0.4})
  • Rebalance uses the daily close as the reference price.
  • Slippage of 2 bps makes buys slightly more expensive and sells slightly cheaper.
  • Commission of 5 bps is charged on the notional of every fill.
  • Sells execute before buys, so cash is available for the purchases.
  • Weights are clamped to zero or below — negatives become 0 (long-only).
  • Weights summing above 1.0 are scaled down proportionally, so you cannot accidentally use leverage.
  • Unchanged targets are skipped entirely, which is why a daily-called strategy does not pay daily costs.

A fully worked rebalance

Start with $100,000 in cash. SPY closes at $500, TLT at $90. Your target is 60/40.

StepSPYTLT
Target notional$60,000$40,000
Execution price (+2 bps slippage)$500.10$90.018
Shares bought≈ 119.98≈ 444.35
Commission at 5 bps≈ $30≈ $20

Total friction on this rebalance is roughly $62 on $100,000, or about 6.2 bps — a one-off cost of about 0.06%. That is negligible. Now suppose your signal flips completely every single day.

Real example: how turnover kills a good signal

Strategy styleRound trips per yearApprox. annual cost drag
200-day SMA trend on SPY2–6≈ 0.03–0.09%
Monthly momentum rotation≈ 12≈ 0.2%
Weekly rotation≈ 50≈ 0.7%
Daily mean reversion, full flip≈ 250≈ 3.5% or more

The arithmetic is brutally simple: each full round trip costs roughly 14 bps in the lab's model (5 bps commission plus 2 bps slippage, on both the sell and the buy). Two hundred and fifty of those is about 3.5% per year, before taxes. Many published mean-reversion edges are worth 2–4% per year gross — which means execution cost alone can consume the entire signal. Whenever a daily strategy looks weak, check turnover before you blame the idea.

python
# Cheap way to cut turnover: only trade when the change is material
state = {"weights": {}}

def on_day(engine, dt):
    target = compute_weights(dt)   # your signal
    current = state["weights"]     # what you asked for last time

    keys = set(target) | set(current)
    drift = sum(abs(target.get(s, 0.0) - current.get(s, 0.0)) for s in keys)
    if drift < 0.05:               # less than 5% total drift
        return                     # skip the rebalance entirely

    state["weights"] = dict(target)
    engine.set_target_weights(dt, target)

Long-only weight math

WeightsInterpretation
{"SPY": 1.0}100% in SPY, 0% cash
{"SPY": 0.5, "TLT": 0.5}Fully invested, equal split
{"SPY": 0.6, "TLT": 0.3}90% invested, 10% cash drag
{"SPY": 0.8, "TLT": 0.8}Scaled to 50/50 — no leverage allowed
{"SPY": -0.5}Clamped to 0 — shorting is not supported
{} or all zerosLiquidate to cash

Before Lesson 6 — you should be able to

  • Explain what each of the four order types trades away.
  • Compute the cost of a rebalance from weights, price, slippage, and commission.
  • Estimate annual cost drag from round trips per year.
  • Reduce turnover with a drift threshold instead of rebalancing blindly.