Skip to main content

Time in Force

Control how long an order remains active.

ValueNameDescription
dayDay OrderExpires at end of trading day
gtcGood Til CancelledRemains active until filled or cancelled
gtdGood Til DateRemains active until specified date
opgMarket on OpenExecutes at market open
clsMarket on CloseExecutes at market close (MOC)
iocImmediate or CancelFill immediately or cancel
fokFill or KillFill entirely immediately or cancel

Examples​

Day Order (Default)​

{
"timeInForce": "day"
}

Good Til Cancelled​

{
"timeInForce": "gtc"
}

Market on Close​

{
"timeInForce": "cls"
}

Good Til Date​

gtd keeps the order live until a specific moment, then cancels it. Pair timeInForce: "gtd" with expiresAt and a timezone.

{
"timeInForce": "gtd",
"expiresAt": "2026-08-15T16:00:00",
"timezone": "America/New_York"
}

Validation rules on the create and update signal endpoints:

  • expiresAt is required when timeInForce is gtd, and only valid in that case.
  • timezone is required when expiresAt is supplied.
  • expiresAt must be in the future when interpreted in the supplied timezone.
  • expiresAt must be within 90 days from now.

How gtd is enforced depends on whether the broker supports a native GTD time-in-force on order submit. UTM picks the path automatically and records which one ran on the Order.gtdMode field so callers can detect the reliability difference without having to know broker-by-broker behaviour.

BrokerGTD modeResilience to UTM outage at expiry
TradeStationBroker-nativeBroker cancels the order at expiresAt even if UTM is down.
AlpacaUTM-emulatedUTM submits as gtc and schedules a server-side cancel. If UTM is down at the expiry moment, the cancel does not fire.

The caller-facing contract is the same regardless of mode: Order.timeInForce keeps the original gtd value and Order.expiresAt carries the requested expiry. On the emulated path UTM also writes Order.gtdCancellationJobId referencing the queued cancel; on the native path that field stays null.

Order.gtdMode​

Order.gtdMode is set by UTM at order-submit time. It can take one of:

  • "broker" - the broker accepted a native GTD time-in-force with an explicit expiry. The broker cancels at expiry regardless of UTM uptime.
  • "emulated" - UTM submitted the order as gtc and scheduled a server-side cancel at expiresAt. UTM must be up at the expiry moment for the cancel to fire.
  • null - the order is not gtd, so the field is not applicable.

The signal-create response does not carry gtdMode; the mode is determined when the order is placed at the broker, not when the signal is created. Callers that need the mode should read it from the order detail endpoint.

Broker submission cutoffs for CLS​

UTM sends cls to Alpaca as cls and to TradeStation as CLO, TradeStation's own at-close duration. TradeStation has no CLS duration. cls orders must reach the broker before its submission cutoff. Orders submitted past the cutoff are rejected. Cutoffs are expressed as minutes before market close so they apply correctly on short trading days (e.g. half-day Thanksgiving session) without special handling.

BrokerCLS cutoff (minutes before close)Regular session ET equivalent
Alpaca103:50 PM
TradeStation103:50 PM

Alpaca additionally requires cls orders to be submitted after 7:00 PM ET on the prior session — that is broker-side window opening behaviour, separate from the UTM cutoff guard described below.

UTM at-close safety behaviour​

When strategy auto-close, or an exit rule with exitOrderType: "moc", sends an at-close order, UTM checks the broker's cutoff before sending. Paper and SIM accounts follow exactly the same rule as live accounts, on every broker: Alpaca paper gets cls and TradeStation SIM gets CLO.

  • Before the cutoff, the order is sent at-close.
  • At or after the cutoff (see table above), UTM downgrades the order from cls to day. The order then fills against the order book at close rather than being rejected. UTM logs a warning and writes an order.moc_downgrade audit row on the trade, with reason past_cutoff, when the downgraded order is sent. The trade's audit timeline shows it as "Sent as market". An order UTM stops before the broker (for example the live trading guard) gets no such row, and neither does an order sent at-close.

If a broker refuses an at-close order, the order is recorded as rejected like any other refusal. UTM does not retry it as a market order.

Paper fills may not match the closing auction. Alpaca's paper account accepts cls, but Alpaca says paper fills against the current quote rather than the closing print, so compare paper results with the fill price recorded on the order.

Signals submitted manually or via webhook are sent with whatever timeInForce the caller specifies and must meet broker submission windows independently.

To stay clear of the cutoff on live accounts, configure autoCloseMinutesBefore on your strategy with at least 15 minutes of buffer. The 15-minute floor exists because it sits at or above every supported broker's CLS cutoff. The default is 30 minutes, which gives adequate margin for all supported brokers.