The Market Is Closed Is Not a Trading Rule
Suneet Malhotra
Aug 25, 2026
The market is closed is not a trading rule. It is an assumption about the calendar, and assumptions about calendars age quietly.
That distinction matters because a trading system usually treats time as metadata. The strategy has a timestamp, the broker has a timestamp, and the scheduler has a timestamp. Price is the data. Time is the label attached to it.
That hierarchy is backwards at the boundary of a session. The calendar decides whether a price can exist, whether an order can be accepted, and whether a position can be managed. A stale calendar can therefore make an otherwise correct signal impossible to execute.
The backtest has an easier calendar
My backtests tend to make session logic look clean. Load daily bars. Group by date. Generate a signal after the close. Send the next order on the next row. If the next row exists, the market was open.
That last sentence smuggles in the entire calendar model.
A historical price file often encodes only the sessions that produced observations. The absence of a row becomes the proxy for a closed market. That works for ordinary weekends and familiar holidays. It says less about early closes, exchange-specific holidays, half-days, daylight-saving transitions, and the difference between an exchange session and a broker's order window.
The backtest also gets to look backward. If the next row is Tuesday, it knows Monday had no bar. A live scheduler has to decide what to do at 09:29:59 on a day whose session metadata may be missing, stale, or expressed in the wrong timezone.
The historical dataset has an answer. The live system has a question.
That question also belongs in incident review. If an order was not sent because the calendar could not resolve, the run should be recoverable from the log without reconstructing state from screenshots or scheduler output. Operational traceability is part of execution correctness.
A timestamp is not a session
The dangerous shortcut is to compare a wall-clock time with a hardcoded interval:
'if weekday < 5 and hour >= 9.5 and hour < 16: trade()'
This is readable and wrong in several independent ways.
It does not know which exchange owns the instrument. It does not know whether the day is a holiday. It does not know whether the close is early. It does not know whether the timestamp arrived in local time, UTC, or daylight time. It does not know whether the instrument is eligible for the strategy during that particular session.
The problem is not that the conditional lacks enough exceptions. The problem is that a clock interval is being used as a substitute for a session object.
A session object can answer more useful questions: is this date a valid trading session, when does it open, when does it close, when is the last time a new position may be entered, and when must an existing position be reconciled? Those are different timestamps with different consequences.
The failure is usually not a bad fill
Calendar defects are easy to miss in performance analysis because they often produce no trade. A bad fill appears in the ledger. A skipped signal may disappear into the same bucket as an ordinary no-op.
That creates a misleading diagnosis. The report says the strategy had fewer trades than expected. The engineer checks slippage, indicators, and data quality. The missing event is filed as randomness.
The better question is: was there a signal whose execution opportunity was removed by calendar state?
That requires logging more than the order table. For every scheduled decision, I want a decision record with the intended timestamp, resolved timezone, instrument calendar, session identifier, session status, and the reason for allowing or rejecting the action. market_closed is not enough. holiday, early_close, outside_entry_window, and calendar_unavailable describe different failures and demand different fixes.
This is the same measurement trap that appears in backtests elsewhere: a metric over the rows that exist cannot explain rows that were never admitted. Trade count can tell me how many orders survived. It cannot tell me how many opportunities the calendar rejected unless the rejection is part of the dataset.
The fix is not more scheduler retries
The tempting repair is to retry until the market opens. That handles a late process and makes a missing calendar look like a transient network error. It is dangerous when the session is genuinely closed. A retry can turn a rejected decision into an order submitted on the wrong day.
The durable control is to make calendar resolution a typed input to the strategy boundary. No resolved session, no order. A resolved session with no entry permission, no order. A session close earlier than the usual close should change the exit and reconciliation deadlines, not merely produce a warning.
I would also test the calendar as data, not as plumbing: ordinary sessions, weekends, named holidays, early closes, daylight-saving transitions, and a deliberately unavailable calendar. The expected result is not always a fill. In several cases, the correct output is an explicit refusal with a reason that remains queryable after the run.
The market does not owe the scheduler a familiar shape. A price series is only the part of market state that made it into the file. Session state determines when the file could have existed in the first place.
Treating the calendar as market data does not make a strategy more profitable. It makes its absence of a trade explainable. That is a smaller promise, and a much more useful one.
Share this post
You Might Also Like
A Retry Is Not a Trading Decision
A rejected order is a decision. Retrying it without preserving the reason can turn a risk control into a duplicate trade.
Quantitative TradingEvery Trade in My Backtest Filled
Every trade in my options backtest filled. All of them. There is no line in that file where an order fails, because there is no line where an order exists.
Agentic AIThe Log Is Part of the Agent's Interface
An agent that can act but cannot leave a useful decision record is not autonomous. It is an opaque process with write access.
Agentic AIThe Agent Did Not Need More Context
When an agent edits a shared checkout, the dangerous variable is not context length. It is the boundary around what the run is allowed to write.
Latest Blog Posts
A Retry Is Not a Trading Decision
A rejected order is a decision. Retrying it without preserving the reason can turn a risk control into a duplicate trade.
The Log Is Part of the Agent's Interface
An agent that can act but cannot leave a useful decision record is not autonomous. It is an opaque process with write access.
The Agent Did Not Need More Context
When an agent edits a shared checkout, the dangerous variable is not context length. It is the boundary around what the run is allowed to write.
Related Tools & Demos
The QA Field Manual to Language Models
A free 24-chapter book. Start at βwhat is AI, really?β and finish with a small language model you built yourself β one that reads a failing Playwright test and proposes a fix you can run. Read it in your browser, or download the PDF or the Mac app.
View Source Code βMulti-Model LLM Harness
One interface to call any AI model β capability routing, fallback chains, budgets, circuit breakers, and a quality feedback loop. A practical architecture pattern write-up.
Automated Trading System
Multi-engine trading platform with real-time risk management, regime-based strategy selection, and automated order execution.
View Source Code β
Stay in the Loop
Get weekly insights on AI-driven QA, engineering leadership, and automation strategies.
No spam, ever. Unsubscribe anytime.