> ## Documentation Index
> Fetch the complete documentation index at: https://docs.testnet.dev.adipredictstreet.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Up/Down windows

> Short-dated crypto markets on a recurring window grid: series navigation, the cryptoWindow block, the frozen open price, and settlement.

An **Up/Down window** is a short-dated crypto market that asks one
question: was the asset's price at the end of this window above the
price pinned at its start? A 15-minute BTC window opens at `17:30`,
closes at `17:45`, and resolves `Up` or `Down` against a **frozen open
price** the oracle pins seconds after the window starts.

Windows are **machine-minted**, never hand-listed. A *series* (`BTC15M`)
describes the recurring schedule; a factory materialises one market per
window on that grid, and groups a whole UTC day of them under one event.

```
series  BTC15M  (cadence 900 s, window 900 s, BTC-USD, 18 decimals)
  └── event  crypto-updown-btc15m-20260729     ← one UTC day
        ├── market BTC15M-20260729T1715   17:15 → 17:30   RESOLVED (Down)
        ├── market BTC15M-20260729T1730   17:30 → 17:45   OPEN
        └── market BTC15M-20260729T1745   17:45 → 18:00   PRE_MARKET
```

Everything else is a normal [binary market](/concepts/markets/binary):
two outcome tokens, the same orderbook, the same EIP-712 order, the same
redemption. `outcomeLabels` is `["Up", "Down"]` — `Up` is outcome index
`0` (the YES slot), `Down` is index `1`.

## Series on the event

A crypto Up/Down event carries a `cryptoSeries` block; every other event
carries `null`. It is the entry point — the FE should navigate the
series, not the event's `markets[]` array, because a day at 15-minute
cadence is \~96 markets and the event only nests the currently tradeable
ones.

```json theme={null}
{
  "slug": "crypto-updown-btc15m-20260729",
  "vertical": "crypto",
  "cryptoSeries": {
    "id":             "04929134-5c9b-4d58-883e-1b669f286ac1",
    "symbolPrefix":   "BTC15M",
    "asset":          "BTC",
    "instrumentId":   "BTC-USD",
    "cadenceSeconds": 900,
    "windowSeconds":  900,
    "streamDecimals": 18,
    "titleTemplate":  "{asset} Up or Down: {windowStart}-{windowEnd} UTC"
  },
  "markets": [ { "...": "MarketSummary" } ]
}
```

| Field            | Meaning                                                                                                                                                                                                                                                         |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`             | Series id — the key for `GET /api/crypto-series/{id}/windows`.                                                                                                                                                                                                  |
| `symbolPrefix`   | Symbol namespace for this series' windows (`BTC15M-20260729T1730`).                                                                                                                                                                                             |
| `asset`          | Underlying ticker (`BTC`).                                                                                                                                                                                                                                      |
| `instrumentId`   | Price-catalog id (`BTC-USD`) — the key for the [price-history endpoint](/concepts/markets/crypto#underlying-price-curve) and the [`crypto_price_tick`](/concepts/websocket/crypto-price) channel. `null` on legacy series whose windows predate the rule write. |
| `cadenceSeconds` | Seconds between consecutive window **starts**.                                                                                                                                                                                                                  |
| `windowSeconds`  | Length of one window. Equal to `cadenceSeconds` on a back-to-back schedule — but they are independent columns, so don't derive one from the other.                                                                                                              |
| `streamDecimals` | Exponent for every unscaled price on this series (`cryptoWindow.openPrice`, `crypto_price_tick.price`).                                                                                                                                                         |
| `titleTemplate`  | Operator-authored title pattern the window titles are rendered from.                                                                                                                                                                                            |

Operator state (enabled / paused / delay counters / grace settings) is
deliberately not projected — this is the public surface.

## The `cryptoWindow` block

On a window market, `cryptoWindow` carries the window facts. It is
`null` on every other market, including long-dated crypto markets.

```json theme={null}
{
  "symbol": "BTC15M-20260729T1730",
  "slug":   "btc15m-20260729t1730",
  "title":  "BTC Up or Down: 17:30-17:45 UTC",
  "status": "OPEN",
  "outcomeLabels": ["Up", "Down"],
  "tradingWindow": {
    "preMarketStartsAt": "2026-07-29T17:00:30.940Z",
    "opensAt":           "2026-07-29T17:28:27.643Z",
    "closesAt":          "2026-07-29T17:45:00.000Z"
  },
  "crypto": {
    "productType":  "UP_DOWN_WINDOW",
    "direction":    null,
    "targetPrice":  null,
    "instrumentId": "BTC-USD",
    "deadlineAt":   null,
    "month":        null
  },
  "cryptoWindow": {
    "seriesId":         "04929134-5c9b-4d58-883e-1b669f286ac1",
    "windowStartAt":    "2026-07-29T17:30:00.000Z",
    "windowEndAt":      "2026-07-29T17:45:00.000Z",
    "openPrice":        "63672459298735910000000",
    "streamDecimals":   18,
    "openPriceIsFinal": true,
    "outcome":          null
  }
}
```

| Field                           | Meaning                                                                                                                                                                                                                                                                                                                  |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `seriesId`                      | Parent series — same id as `event.cryptoSeries.id`.                                                                                                                                                                                                                                                                      |
| `windowStartAt` / `windowEndAt` | The economic period, ISO-8601 UTC. `windowEndAt` equals the market's `tradingWindow.closesAt`.                                                                                                                                                                                                                           |
| `openPrice`                     | The **frozen open** — the price to beat. Unscaled integer as a decimal string, scaled by `streamDecimals`. `null` until the oracle pins it (\~45 s after `windowStartAt` on the shipped 15-minute series — the pin waits out a collection grace so the report set is complete), and on every window that hasn't started. |
| `streamDecimals`                | Exponent for `openPrice`.                                                                                                                                                                                                                                                                                                |
| `openPriceIsFinal`              | `true` once the pin exists. The pin is write-once, so an open that exists is already the value settlement will bind.                                                                                                                                                                                                     |
| `outcome`                       | `"UP"` / `"DOWN"` once the market is `RESOLVED`; `null` while a proposal is still challengeable. A proposed direction is not a settled one.                                                                                                                                                                              |

<Warning>
  **`openPrice` is a string and must stay one.** At 18 decimals it
  exceeds `Number.MAX_SAFE_INTEGER` — `Number(openPrice)` silently loses
  precision. Render it as `openPrice / 10 ** streamDecimals` with a
  bigint or decimal helper. Same rule for every price on the
  [`crypto_price_tick`](/concepts/websocket/crypto-price) channel.
</Warning>

A window market carries **both** `crypto` (with
`productType: "UP_DOWN_WINDOW"`) and `cryptoWindow`. Long-dated crypto
markets carry `crypto` only. `cryptoWindow !== null` is the reliable
"this is an Up/Down window" test.

## Navigating a series

```http theme={null}
GET /api/crypto-series/{id}/windows?state=current
GET /api/crypto-series/{id}/windows?state=next&limit=8
GET /api/crypto-series/{id}/windows?state=all&from=2026-07-29&to=2026-07-30
```

Each entry is the **same `MarketSummary` shape** the markets endpoints
return, `cryptoWindow` included — so one renderer serves the list and
the detail page.

| Parameter     | Required | Meaning                                                                                                                                                                                                                                                                                   |
| ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state`       | ✅        | `current` \| `next` \| `past` \| `all`. No default — an unrecognised value is `400 invalid_state`, never a silent fallback.                                                                                                                                                               |
| `limit`       | —        | `1`..`500`. Default `50`, or `500` for `state=all` (a 15-minute day is 96 chips, so the generic default would cut the strip at noon).                                                                                                                                                     |
| `offset`      | —        | `≥ 0`, default `0`.                                                                                                                                                                                                                                                                       |
| `from` / `to` | —        | Bounds on the window **START**: `from` inclusive, `to` exclusive, so day queries never double-count the window that straddles midnight. ISO-8601 with an explicit offset (`2026-07-29T00:00:00Z`) or a bare date (`2026-07-29`); a naive date-time is rejected. At most **31 days** wide. |

```json theme={null}
{
  "markets": [ { "...": "MarketSummary" } ],
  "hasMore": false
}
```

`hasMore` distinguishes a truncated page from end-of-data — a capped
chip strip is otherwise indistinguishable from a short day.

**Buckets are computed from the window bounds against the current time,
not from lifecycle status.** A window can be `CLOSED` while its period
is still `current` (settlement lag). `current` is normally one window
but is not guaranteed to be — treat it as a list.

| `state`   | Selects                                | Order                                                   |
| --------- | -------------------------------------- | ------------------------------------------------------- |
| `current` | `windowStartAt ≤ now < windowEndAt`    | start DESC                                              |
| `next`    | `windowStartAt > now`                  | start ASC (soonest first)                               |
| `past`    | `windowEndAt ≤ now`                    | end DESC (most recent first)                            |
| `all`     | every bounded window (add `from`/`to`) | start ASC (chronological — for the window-picker strip) |

| Status | Code                                                                                                                    |
| ------ | ----------------------------------------------------------------------------------------------------------------------- |
| `400`  | `invalid_series_id`, `invalid_state`, `invalid_limit`, `invalid_offset`, `invalid_from`, `invalid_to`, `range_too_wide` |

Public endpoint, `600 req/min` per IP, `Cache-Control: public, max-age=5`
— `current` flips on the minute, so anything longer would serve a stale
active window right after a rollover.

## Window lifecycle

| Phase         | What happens                                                                | What you see                                                                                                                                                                                                                                                                    |
| ------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Minted        | Factory materialises the market ahead of the window and deploys it on chain | Appears under `state=next`; `status: PRE_MARKET` once past `tradingWindow.preMarketStartsAt`, `cryptoWindow.openPrice: null`                                                                                                                                                    |
| Trading opens | `tradingWindow.opensAt`                                                     | `status: OPEN`, `tradingMode: "open"`                                                                                                                                                                                                                                           |
| Window starts | `cryptoWindow.windowStartAt`                                                | Oracle freezes the open price \~45 s later → `openPrice` populated, `openPriceIsFinal: true`, and a [`crypto_window_opened`](/concepts/websocket/crypto-price#crypto_window_opened) frame is pushed                                                                             |
| Window closes | `windowEndAt` = `tradingWindow.closesAt`                                    | `status: CLOSED` → `PENDING_RESOLUTION`; orders rejected                                                                                                                                                                                                                        |
| Proposed      | Oracle proposes the direction on chain, \~1–2 min after the close           | `status: PENDING_RESOLUTION`; `resolution.proposedAt`, `resolution.proposedOutcome` (`"Up"` / `"Down"`), `resolution.proposedOutcomeIndex` (`0` / `1`) and `resolution.challengeWindowClosesAt` populated. `cryptoWindow.outcome` stays `null` — a proposal is not a settlement |
| Finalized     | Challenge window elapses (or is zero, on the fast path)                     | `status: RESOLVED`, `cryptoWindow.outcome: "UP"` \| `"DOWN"`, `payoutNumerators` `[1,0]` (Up) or `[0,1]` (Down); redemption unlocked                                                                                                                                            |

Settlement latency is decided by the challenge window that applies to
the market. By default that is the platform-wide window (2 h on dev
today), so a 15-minute window resolves about two hours after it closes.
Windows armed for the fast on-chain path carry a per-market window of
zero and finalize right after the proposal instead. **Read
`resolution.challengeWindowClosesAt`** rather than assuming either — it
is the exact instant `finalize` becomes legal. See
[Settlement flow](/concepts/settlement/flow#crypto-markets).

## Live data

| You want                                   | Use                                                                                                                                                                                                       |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The underlying price line                  | [`GET /api/markets/{symbol}/crypto-price-history?range=10m`](/concepts/markets/crypto#underlying-price-curve) for the curve, then [`crypto_price_tick`](/concepts/websocket/crypto-price) for live points |
| The price to beat, the moment it is pinned | [`crypto_window_opened`](/concepts/websocket/crypto-price#crypto_window_opened) on `market_lifecycle`, or `cryptoWindow.openPrice` on refetch                                                             |
| Order flow / book                          | The usual `token_book`, `token_trade_matches`, `token_ohlc` channels keyed by the window's outcome `tokenId`s                                                                                             |

<Note>
  The chart line and the settlement price are **different Chainlink
  products** on a window market: the curve is built from one-minute
  candlestick data, while the frozen open and the live tick come from
  Data Streams. They track the same asset but are not byte-identical, so
  the line can sit a few dollars off the open line at the boundary
  instant. The curve is a display surface — settlement binds
  `cryptoWindow.openPrice`.
</Note>
