Time-in-Force & Sessions¶
Objective¶
Understand how Time-in-Force (TIF) controls order lifetime across session phases, and how to use the scheduler to drive the exchange through a full trading day.
Background reading
Prerequisites¶
- Chapters 01–05 completed.
pm-engine, at least one trader gateway and the operator console running. Exercise 1 runspm-scheduleronce to show the phases; after that the chapter controls them by hand (see the Freeze/Advance Procedure below).
Background¶
Session phases progress as:
Different TIF values control when orders are active and when they expire.
| TIF | Meaning | Valid During |
|---|---|---|
DAY |
Lives until the end of the trading day (expires at CLOSED) | PRE_OPEN, OPENING_AUCTION, CONTINUOUS, CLOSING_AUCTION |
GTC |
Good-Till-Cancelled; survives session boundaries | Any |
ATO |
At-The-Open only; participates in opening auction | OPENING_AUCTION |
ATC |
At-The-Close only; participates in closing auction | CLOSING_AUCTION |
A DAY order's \"end of session\" is not the same as \"the engine process exits\"
The table above describes the session-scheduler's rule: pm-scheduler
driving the session into CLOSED cancels every resting DAY order at
that moment, published as order.expired. That is unchanged.
Separately, an engine restart is not itself a day boundary. If you
stop and restart pm-engine mid-session (a config reload, a crash
recovery) while still on the same business day, a resting DAY order
now survives that restart, exactly like a GTC order would — it is only
discarded, at the next startup, once its own timestamp is from a prior
business day. See Persistence
for the full restart-vs-session-close distinction, and chapter
16 — Persistence & Recovery for a hands-on
exercise.
Exercise 1: Observe Session Phases¶
Start the scheduler in rapid-fire mode to see all transitions quickly:
Watch the transitions. The trader console does not subscribe to session
broadcasts — it only answers an explicit SESSION query — so use the operator
console, which does:
Run it after each transition, or watch pm-audit --terminal in another
terminal for the live session.state events:
Checkpoint: you see at least one session state transition.
Freeze/Advance Procedure (Deterministic Session Control)¶
The exercises below need the exchange sitting in a specific, known phase
before you place an order — otherwise a TIF exercise may pass or fail purely
by timing luck. Instead of running pm-scheduler continuously, use the admin
gateway to freeze on a phase and advance only when you are ready:
- Do not start
pm-scheduler --now(which free-runs through all phases). Start the engine and gateways as usual, but leave the scheduler stopped. Withsessions_enabled: trueand no scheduler, the engine starts inCLOSEDand stays there — so every order is rejected with "Market is closed" until you move it yourself in step 2. That is expected, not a fault. - From the admin gateway, move to the exact phase you need for the next
exercise. The engine only accepts the transitions in the session
sequence, and from
CLOSEDthe only legal move is toPRE_OPEN— so to reachCONTINUOUSgo in two steps:
An illegal jump (for example CLOSED straight to CONTINUOUS) is
silently ignored by the engine, and the console just prints TIMEOUT
with no reason. If you see that, run SESSION_STATUS and check which
phase you are really in.
- Confirm the change took effect with:
- Run the TIF exercise's orders while frozen in that phase.
- When the exercise calls for a transition (e.g., DAY expiry at
CLOSED, orATO/ATCat auction phases), advance deliberately:
Using SESSION|STATE= this way means each exercise below runs in a
reproducible phase instead of racing a free-running scheduler.
Exercise 2: DAY Order Expiry¶
During CONTINUOUS (freeze there with SESSION|STATE=CONTINUOUS per the
procedure above), place a DAY order:
Advance the session to CLOSED — from CONTINUOUS that is two steps, through the closing auction:
Watch the trader console right after the second transition: the order is
reported EXPIRED.
Operational note: DAY is best for intraday intent where stale overnight orders must not persist.
Checkpoint: DAY order expired at session close.
Exercise 3: GTC Order Survives¶
Place a GTC order:
This order survives the CLOSED phase and will still be resting when the next session opens.
Operational note: GTC is used for standing instructions that remain valid until explicitly cancelled.
Checkpoint: GTC order remains after session restart.
Exercise 4: ATO Order¶
Freeze in OPENING_AUCTION ([GW_ADMIN|ADMIN]> SESSION|STATE=OPENING_AUCTION — from
PRE_OPEN, this is a valid direct transition), then place an At-The-Open
order:
ATO is rejected during PRE_OPEN, not just outside auction hours
The engine only accepts TIF=ATO orders while the session state is
exactly OPENING_AUCTION — PRE_OPEN accepts orders generally (it is
not CLOSED), but an ATO order submitted while still in PRE_OPEN is
rejected with "ATO orders only accepted during opening auction". Make
sure you have actually transitioned to OPENING_AUCTION (confirm with
SESSION_STATUS) before running this exercise.
This order only participates in the opening auction. If not filled during the
auction, it expires the moment the session leaves OPENING_AUCTION
(e.g. when CONTINUOUS begins).
Checkpoint: ATO order participates in the auction or expires.
Exercise 5: ATC Order¶
First try an At-The-Close order during CONTINUOUS:
It is rejected: ATC orders only accepted during closing auction. An ATC
order is not parked until the close — it can only be entered during the
closing auction. Freeze there and send it again:
[GW_ADMIN|ADMIN]> SESSION|STATE=CLOSING_AUCTION
[TRADER01]> NEW|SYM=AAPL|SIDE=SELL|TYPE=LIMIT|QTY=100|PRICE=149.00|TIF=ATC
Now it is accepted and waits in the auction; it can only trade when the
closing auction uncrosses, on the move to CLOSED.
Checkpoint: ATC is rejected in CONTINUOUS and accepted in CLOSING_AUCTION.
Exercise 6: Rejected TIF¶
Try submitting an ATO order during CONTINUOUS:
Expected: rejection — ATO orders only accepted during opening auction. The
phase must be exactly OPENING_AUCTION; PRE_OPEN is not enough, even
though PRE_OPEN accepts orders generally. This matches the note in the
summary below and the rule you saw in Exercise 4.
Checkpoint: engine rejects out-of-phase TIF correctly.
Summary¶
| Phase | Accepts | Cancels on entry |
|---|---|---|
| PRE_OPEN | DAY, GTC (not ATO — see Exercise 4) | — |
| OPENING_AUCTION | DAY, GTC, ATO | ATO (unfilled, on exit to any other phase) |
| CONTINUOUS | DAY, GTC (not ATO or ATC) | — |
| CLOSING_AUCTION | DAY, GTC, ATC | ATC (unfilled, on exit to CLOSED) |
| CLOSED | — (no new orders accepted) | DAY (all, on entry to CLOSED) |
Notes:
- The four values above are the whole of
TIF=: DAY, GTC, ATO, ATC.IOCandFOKare order types (TYPE=), not time-in-force values — a common confusion, since all six describe "how long does this live". ATOis only accepted while the session state is exactlyOPENING_AUCTION— not duringPRE_OPEN, even thoughPRE_OPENaccepts orders generally.ATCis only accepted while the session state is exactlyCLOSING_AUCTION.- Unfilled
ATO/ATCorders are expired the moment the session leaves their respective auction phase (not on any other transition). - All resting
DAYorders (regardless of when they were placed) are expired the moment the session transitions intoCLOSED.
Reflection¶
Why does GTC survive every phase transition while DAY does not? If you were running an overnight risk desk, what would go wrong if all your hedge orders were accidentally submitted as DAY instead of GTC?
Further Reading¶
Next: 07 — Auctions