# Time in force: GTC, IOC, FOK and post only in trading APIs

URL: https://fincodelabs.com/en/blog/time-in-force · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Category: Exchange APIs · Published: 2026-11-06 · Updated: 2026-11-06

Time in force explained: GTC, IOC, FOK, GTD and post only mapped across seven exchange APIs and FIX tag 59, with failure behavior and a tested Python mapper.

**Short answer:** **Time in force** (TIF) tells a trading venue what to do with the part of an order that cannot fill at once. GTC rests until filled or canceled, GTD until a set expiry, IOC fills what it can and cancels the rest, FOK fills completely or not at all, and post only, a related instruction, refuses to take liquidity. Official documents read on 5 November 2026 spell and default these differently (FIX tag 59 means Day when absent), so multi-venue software needs one internal model and a per-venue mapping.

One order idea can become `timeInForce=GTX` on one venue, `oflags=post` on the next, `ordType=post_only` on a third and a key inside `order_configuration` on a fourth.

A field reference for developers normalizing venues, from official API documentation and FIX Latest read on 5 November 2026. Naming a venue is not a recommendation, examples are hypothetical, and no setting here is advice. General design is in [crypto exchange API integration](https://fincodelabs.com/en/blog/crypto-exchange-api-integration).

## What does time in force mean?

Time in force sets how long an order stays active, which in practice decides what happens to quantity that cannot fill on arrival.

**Hypothetical buy limit at 25.00 for 1,000 units; the book offers 600 at 25.00 and 500 at 25.05**

| Instruction | Fills now | The other quantity | Partial fill? |
| --- | ---: | --- | --- |
| GTC, good till canceled | 600 | 400 rests until filled or canceled | Yes |
| GTD, good till date | 600 | 400 rests until the expiry time | Yes |
| Day (stock markets) | 600 | 400 rests until the session ends | Yes |
| IOC, immediate or cancel | 600 | 400 canceled at once | Yes |
| FOK, fill or kill | 0 | All 1,000 canceled | No |
| Post only | 0 | All 1,000 refused, because it would take liquidity | No taker fill |

> **Fill or kill vs immediate or cancel, hypothetical:** neither rests on the book; they differ only on partial fills, so on the book above IOC ends with 600 units and FOK with none.

Binance Spot's enum page documents `EXPIRED`, not `CANCELED`, for a limit FOK with no fill and for a limit IOC or market order that fills partly, so code that waits for `CANCELED` misses them.

## Time in force across exchange APIs: one idea, seven spellings

Behavior is close across venues, but the field, the value and sometimes the request shape differ.

**Where each instruction goes, official documents read 5 November 2026 (alphabetical, not a ranking)**

| Venue | Field | GTC | IOC | FOK | Post only |
| --- | --- | --- | --- | --- | --- |
| Binance Spot | `timeInForce`, `type` | `GTC` | `IOC` | `FOK` | `type=LIMIT_MAKER`, no TIF |
| Binance USD-M | `timeInForce` | `GTC` | `IOC` | `FOK` | `GTX` |
| Bybit V5 | `timeInForce` | `GTC` | `IOC` | `FOK` | `PostOnly` |
| Coinbase Advanced Trade | `order_configuration` key | `limit_limit_gtc` | `sor_limit_ioc` | `limit_limit_fok` | `post_only: true` inside the GTC or GTD key |
| Hyperliquid | `t.limit.tif` | `Gtc` | `Ioc` | Not listed | `Alo` |
| Kraken Spot REST | `timeinforce`, `oflags` | `GTC` | `IOC` | `FOK`, [added 12 May 2026](https://docs.kraken.com/exchange/changelog) | `oflags=post` |
| OKX | `ordType` | `limit` | `ioc` | `fok` | `post_only` |

**Expiry, default and post only refusal, same documents and date**

| Venue | GTD | If omitted | Post only would cross |
| --- | --- | --- | --- |
| Binance Spot | Not in the enum | Mandatory on `LIMIT`, `-1102` if missing | Rejected: `-2010`, `Order would immediately match and take.` |
| Binance USD-M | `GTD` with [`goodTillDate`](https://developers.binance.com/en/docs/catalog/core-trading-derivatives-trading-usd-s-m-futures/api/rest-api/trade), over 600 seconds ahead | Mandatory on `LIMIT` | Rejected: [`-5022`](https://developers.binance.com/en/docs/products/derivatives-trading-usds-futures/error-code) |
| Bybit V5 | Not in the enum | GTC | Canceled; reject reason `EC_PostOnlyWillTakeLiquidity` |
| Coinbase | `limit_limit_gtd` with `end_time` | No default, the key is the policy | Failure reason `INVALID_LIMIT_PRICE_POST_ONLY` |
| Hyperliquid | None; `expiresAfter` expires the request, not a resting order | Part of the required type | Status error `BadAloPx` |
| Kraken | `GTD` with `expiretm`, up to one month | GTC | Canceled on arrival, per its FIX page; the REST page names no error |
| OKX | No such `ordType` | `ordType` is required | Canceled; `cancelSource` 31 |

## What is a post only order? Binance and other venues

A post only order asks to rest on the book as a maker and is refused if it would trade immediately, so a refusal is a normal outcome rather than a fault.

The refusal arrives in two ways:

- **Refused in the reply.** Binance Spot, Binance USD-M, Coinbase and Hyperliquid document an error or failure reason in the response, as in the table above; [Hyperliquid's message](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/error-responses) includes the best bid and offer.
- **Accepted, then canceled.** Bybit says its reply only acknowledges the request and the WebSocket confirms the status, and OKX cancels an order that would execute at placement. A success code is not proof that the order is resting.

Do not retry the same price in a loop; what happens next belongs in your own written rules. Post only controls how the order enters the book; the fee you pay remains the venue's schedule.

RPI is a separate post only type: Binance USD-M and Bybit both say it matches only orders from their app or website, not API orders. Wider error handling is in [Binance API error codes](https://fincodelabs.com/en/blog/binance-api-error-codes).

## Time in force FIX tag: FIX 59 values

FIX carries time in force in tag 59 (TimeInForce), and the FIX Latest code set, version EP312 on Orchimate, says an absent tag means Day.

**TimeInForce (59) in FIX Latest, read 5 November 2026**

| Value | Name | Meaning |
| --- | --- | --- |
| 0 | Day | Expires at the end of the trading day |
| 1 | GTC | In effect until executed or canceled |
| 2 | OPG | Executes at the opening or not at all |
| 3 | IOC | Executes in whole or part, the rest is canceled |
| 4 | FOK | Executes in full or is canceled |
| 5 | GTX | Canceled before the market enters an auction or crossing phase |
| 6 | GTD | Until ExpireDate (432) or ExpireTime (126) |
| 7, 8, 9, A, B, C | Others | At the Close, Good Through Crossing, At Crossing, Good for Time, Good for Auction, Good for this Month |

- **GTX is not post only in FIX.** Tag 59=5 concerns auction phases, while Binance USD-M uses GTX for post only. A mapper that matches on the letters sends the wrong instruction.
- **Post only is not in tag 59.** Binance's FIX API sends `LIMIT_MAKER` as `40=2|18=6`, using ExecInst value 6, Participate don't initiate, and accepts only 1, 3 and 4 in tag 59. Kraken's FIX 4.4 requires tag 59 and uses `18=P`, while the [OnixS FIX 4.4 dictionary](https://www.onixs.biz/fix-dictionary/4.4/tagNum_18.html) lists P as Market peg. On Kraken FIX, FOK and GTD are Spot only.

Session mechanics are in [broker API integration](https://fincodelabs.com/en/blog/broker-api-integration-fix-rest).

## Good til canceled: does a GTC order ever expire?

Yes: FIX describes GTC as lasting until executed or canceled, but venues and brokers can set their own limit, so store an expiry rather than assume none.

- **Binance USD-M:** its Common Definition page, last modified 30 October 2026, gives GTC a validity of 1 year from placement.
- **Alpaca:** its order documentation says an aged order policy cancels GTC orders 90 days after creation, with the date in `expires_at`.
- **US stock brokers:** the SEC's Investor.gov bulletin on order types says the limit varies by brokerage firm.

Account types and API access vary by broker and country, so read your own terms. The other venue pages read here state no limit, which does not mean none exists. A forgotten GTC order can fill long after its reason has passed, so read open orders from the venue at every restart.

## A tested Python mapper for time in force

Normalize once: one validated internal order model, one small function per venue, and a hard refusal for any combination the venue does not document.

```python
from dataclasses import dataclass
from datetime import datetime, timedelta

NOW = 1_800_000_000                     # synthetic clock

class Unsupported(ValueError):
    pass

@dataclass(frozen=True)
class Order:
    tif: str                            # GTC, IOC, FOK, POST or GTD
    expire_s: int = 0                   # unix seconds, GTD only

    def __post_init__(self):            # bad input never reaches a mapper
        if (self.tif not in ("GTC", "IOC", "FOK", "POST", "GTD") or type(self.expire_s) is not int
                or not (NOW < self.expire_s < 253_402_300_799 if self.tif == "GTD"
                        else self.expire_s == 0)):
            raise Unsupported(f"bad order {self}")

def utc(s, fmt):                        # date math: no gmtime year limit
    return (datetime(1970, 1, 1) + timedelta(seconds=s)).strftime(fmt)

def kraken_month(o):                    # GTD "up to one month"; 28 days fits any month
    if o.tif == "GTD" and o.expire_s > NOW + 28 * 86400:
        raise Unsupported("GTD over one month ahead")

def binance_spot(o):
    if o.tif == "GTD":
        raise Unsupported("no GTD in the enum")
    if o.tif == "POST":
        return {"type": "LIMIT_MAKER"}  # needs no timeInForce
    return {"type": "LIMIT", "timeInForce": o.tif}

def binance_usdm(o):
    if o.tif == "GTD":
        if o.expire_s <= NOW + 600:
            raise Unsupported("goodTillDate must be over 600 s ahead")
        return {"type": "LIMIT", "timeInForce": "GTD", "goodTillDate": o.expire_s * 1000}
    return {"type": "LIMIT", "timeInForce": {"POST": "GTX"}.get(o.tif, o.tif)}

def bybit(o):
    if o.tif == "GTD":
        raise Unsupported("no GTD in the enum")
    return {"orderType": "Limit", "timeInForce": {"POST": "PostOnly"}.get(o.tif, o.tif)}

def coinbase(o):
    key, extra = {"GTC": ("limit_limit_gtc", {"post_only": False}),
                  "POST": ("limit_limit_gtc", {"post_only": True}),
                  "IOC": ("sor_limit_ioc", {}), "FOK": ("limit_limit_fok", {}),
                  "GTD": ("limit_limit_gtd", {"end_time": utc(o.expire_s, "%Y-%m-%dT%H:%M:%SZ")})}[o.tif]
    return {"order_configuration": {key: extra}}

def hyperliquid(o):
    if o.tif in ("FOK", "GTD"):
        raise Unsupported("tif is Alo, Ioc or Gtc")
    return {"t": {"limit": {"tif": {"GTC": "Gtc", "IOC": "Ioc", "POST": "Alo"}[o.tif]}}}

def kraken(o):
    kraken_month(o)
    if o.tif == "POST":
        return {"timeinforce": "GTC", "oflags": "post"}
    return {"timeinforce": o.tif} | ({"expiretm": str(o.expire_s)} if o.tif == "GTD" else {})

def okx(o):
    if o.tif == "GTD":
        raise Unsupported("no ordType for GTD")
    return {"ordType": {"GTC": "limit", "IOC": "ioc", "FOK": "fok", "POST": "post_only"}[o.tif]}

def binance_fix(o):
    if o.tif == "GTD":
        raise Unsupported("tag 59 takes only 1, 3 or 4")
    return {18: "6"} if o.tif == "POST" else {59: {"GTC": "1", "IOC": "3", "FOK": "4"}[o.tif]}

def kraken_fix(o):                      # Spot; tag 59 is required
    kraken_month(o)
    return {"GTC": {59: "1"}, "IOC": {59: "3"}, "FOK": {59: "4"}, "POST": {59: "1", 18: "P"},
            "GTD": {59: "6", 126: utc(o.expire_s, "%Y%m%d-%H:%M:%S")}}[o.tif]

for build in (binance_spot, binance_usdm, bybit, coinbase, hyperliquid, kraken, okx,
              binance_fix, kraken_fix):
    refused = []
    for tif in ("GTC", "IOC", "FOK", "POST", "GTD"):
        try:
            build(Order(tif, NOW + 3600 if tif == "GTD" else 0))
        except Unsupported:
            refused.append(tif)
    print(f"{build.__name__:13}", build(Order("POST")), "refuses", refused or "nothing")

for build, args in ((binance_usdm, ("GTD", NOW + 60)), (okx, ("DAY",)), (okx, ("IOC", float("1e400")))):
    try:
        build(Order(*args))
    except Unsupported as err:
        print(f"{build.__name__}: {err}")
```

Output:

```
binance_spot  {'type': 'LIMIT_MAKER'} refuses ['GTD']
binance_usdm  {'type': 'LIMIT', 'timeInForce': 'GTX'} refuses nothing
bybit         {'orderType': 'Limit', 'timeInForce': 'PostOnly'} refuses ['GTD']
coinbase      {'order_configuration': {'limit_limit_gtc': {'post_only': True}}} refuses nothing
hyperliquid   {'t': {'limit': {'tif': 'Alo'}}} refuses ['FOK', 'GTD']
kraken        {'timeinforce': 'GTC', 'oflags': 'post'} refuses nothing
okx           {'ordType': 'post_only'} refuses ['GTD']
binance_fix   {18: '6'} refuses ['GTD']
kraken_fix    {59: '1', 18: 'P'} refuses nothing
binance_usdm: goodTillDate must be over 600 s ahead
okx: bad order Order(tif='DAY', expire_s=0)
okx: bad order Order(tif='IOC', expire_s=inf)
```

This ran on Python 3.11 on 5 November 2026 with synthetic orders and no network. It maps only time in force fields; symbol, side, quantity and price need the same care. No request was sent, because order endpoints need real keys, so run each path on the venue's test environment first, where it has one, as the [Binance Python guide](https://fincodelabs.com/en/blog/binance-api-python-trading-bot) shows.

Keep the refusals. Falling back to GTC when GTD was asked for leaves an order resting after you meant it to end.

## Failure cases to test before live orders

Test the paths where an order ends without trading; a happy-path demo never shows them.

**Test cases for a time in force layer**

| Case | Pass condition |
| --- | --- |
| Post only would cross | Handles a rejected reply and a later cancel, with no retry at the same price |
| FOK finds too little | No position change; `EXPIRED` on Binance Spot and `-5021` on USD-M are both handled |
| IOC fills partly | Position uses the filled quantity; the rest is not re-sent without a rule |
| GTD reaches its expiry | The venue's expiry event closes the local order, not your clock; Binance USD-M warns its auto-cancel can run late in extreme markets |
| Field left out | The mapper sends a value wherever the venue takes one, since Bybit and Kraken default to GTC and FIX to Day |
| GTC outlives a broker limit | Reconciliation marks the local order closed |

Rules depend on your jurisdiction and each platform's terms. For readers in Turkey: Article 99/A of Capital Markets Law No. 6362 treats activity by foreign crypto-asset platforms directed at Turkish residents as unauthorized. The Capital Markets Board (SPK) says being on its [operating list](https://spk.gov.tr/kurumlar/kripto-varlik-hizmet-saglayicilar/faaliyette-bulunanlar-listesi) does not mean authorization. For integration work in general, see Fincode's [exchange API integration](https://fincodelabs.com/en/services/exchange-api-integration) page; naming these venues here does not mean Fincode integrates them or has built on them. Automated orders can lose money, and tests show how software behaves, not future results.

## Frequently asked questions

### What does GTC mean in trading?

Good till canceled: the order stays active until it fills or you cancel it. Some venues and brokers still cap it, such as 1 year on Binance USD-M and 90 days at Alpaca, as read on 5 November 2026.

### What is the difference between an immediate or cancel order and a fill or kill order?

Both execute at once and never rest: IOC keeps what fills and cancels the rest, while FOK cancels everything unless the whole quantity can fill.

### Is GTX the same as post only?

On Binance USD-M, yes. In the FIX standard, no: tag 59=5 means an order canceled before an auction or crossing phase, and Binance and Kraken FIX send post only through ExecInst (18).

### Which time in force should a bot use?

That depends on rules you wrote for your own purpose; this guide recommends no setting. Software must send the value explicitly and handle every way the order can end.

## Sources

- [Binance Spot API documentation: Enum definitions (timeInForce, order types, EXPIRED)](https://github.com/binance/binance-spot-api-docs/blob/master/enums.md)
- [Binance Spot API documentation: Errors (-1102, -2010 and order placement messages)](https://github.com/binance/binance-spot-api-docs/blob/master/errors.md)
- [Binance Spot API documentation: FIX API (ExecInst, TimeInForce, LIMIT_MAKER)](https://github.com/binance/binance-spot-api-docs/blob/master/fix-api.md)
- [Binance USD-M Futures: Common Definition (time in force, last modified 30 October 2026)](https://developers.binance.com/en/docs/products/derivatives-trading-usds-futures/common-definition)
- [Bybit V5 API: Place Order](https://bybit-exchange.github.io/docs/v5/order/create-order)
- [Bybit V5 API: Enums Definitions (timeInForce, rejectReason)](https://bybit-exchange.github.io/docs/v5/enum)
- [Coinbase Advanced Trade API: Create Order (order_configuration, failure reasons)](https://docs.cdp.coinbase.com/api-reference/advanced-trade-api/rest-api/orders/create-order)
- [Hyperliquid API: Exchange endpoint (tif values, expiresAfter)](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint)
- [Kraken API: Add Order (timeinforce, oflags, expiretm)](https://docs.kraken.com/api-reference/trading/add-order)
- [Kraken API: FIX New Order Single (tags 18 and 59)](https://docs.kraken.com/exchange/api-reference/unified-fix/nos)
- [OKX API v5: Place order (ordType, cancelSource)](https://www.okx.com/docs-v5/en/)
- [FIX Latest EP312 on Orchimate, where FIX Trading Community Fiximate links now redirect: TimeInForceCodeSet](https://orchimate.org/fixtrading/fix-latest/codeSets/TimeInForceCodeSet)
- [SEC Investor.gov: Understanding Order Types, Investor Bulletin](https://www.investor.gov/introduction-investing/general-resources/news-alerts/alerts-bulletins/investor-bulletins-14)
- [Alpaca: Orders at Alpaca (time in force, aged order policy)](https://docs.alpaca.markets/us/docs/orders-at-alpaca)
- [Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish)](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.6362.pdf)

> This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the [editorial policy](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/blog/time-in-force

Cite as: Fincode, "Time in force: GTC, IOC, FOK and post only in trading APIs", updated November 6, 2026, https://fincodelabs.com/en/blog/time-in-force
