Skip to content

API version 2026-10-01: error codes, has_more, capabilities, side and triggered filters, L4 replay on every product - #20

Merged
0xFantomMenace merged 4 commits into
masterfrom
feat/api-contract-2026-10
Oct 5, 2026
Merged

0xFantomMenace merged 4 commits into
masterfrom
feat/api-contract-2026-10

Conversation

@0xFantomMenace

Copy link
Copy Markdown
Member

Adopts API version 2026-10-01 in the Python SDK. Everything is folded into the unreleased 1.12.0 entry of the changelog; the version stays 1.12.0.

What changes for users

Version selector. Every REST request, sync and async, sends 0xArchive-Version: 2026-10-01 (oxarchive.API_VERSION), and OxArchiveWs connects with version=2026-10-01. The SDK parses the shapes this version selects:

  • Data quality, the public coverage summary (/v1/status/coverage, new data_quality.status_coverage()) and /v1/symbols answer with {success, data, meta}; the methods return the same models as before.
  • CVD buckets, the HIP-3 oracle, Lighter and Robinhood Chain liquidations and liquidation volume have timestamp as a UTC datetime plus timestamp_ms; liquidation and trigger levels add snapshot_ts_ms; L4 snapshot resting orders carry an RFC 3339 timestamp with timestamp_ms.
  • Lighter and Robinhood Chain replay messages have the live shapes. The new on_replay_data() handler receives (channel, coin, timestamp, record) and decodes them with the live parsers (decode_lighter_payload() is public too).

Errors. OxArchiveError exposes error_code, status, request_id, param, valid_values and details (the whole error body, such as available_on). A body without a code gets one derived from the HTTP status. ERROR_CODES / ErrorCode export the public set; WEBSOCKET_ERROR_CODES names slow_consumer and endpoint_unsupported. WsError.error_code is typed.

Pagination. CursorResponse.has_more (from meta.has_more, or from next_cursor where a route does not send it). The iterators stop when it is false.

Response meta. Every paged method returns meta, with symbol and venue on per-symbol routes. Records a method returns on its own (order book, funding, open interest, instruments, freshness, summary, levels, oracle, HIP-4 outcomes and questions, data quality results) carry it as response_meta.

Capabilities. client.capabilities() / acapabilities() return one typed Capability per venue and datatype.

Filters the API applies. side="buy"|"sell" on every trades method including recent(), on all venues; triggered on order history (core, HIP-3, HIP-4); depth on full-depth L2 history. depth on order book history is documented for HIP-3, HIP-4 and spot. L3 history still refuses granularity (and depth) before sending.

WebSocket replay. L4 replay for HIP-3, HIP-4 and spot, and full-depth replay (orderbook_full, hip3_orderbook_full), are no longer refused. oxarchive.websocket.WS_CHANNELS mirrors /v1/capabilities (live, replay, bulk replay per channel); the client refuses before sending a replay of a live-only channel, a live subscription to a replay-only channel (now including candles and hip3_candles), and a bulk channel inside multi_replay().

Verbs. Aliases that follow get/current/history/list: trades.history() (= list()), spot.twap.history() (= by_symbol()), hip4.get_price_history() (= get_prices()). Every existing name keeps working.

Naming. The venue is "Lighter" (not "Lighter.xyz") in the README, docstrings, messages and package keywords.

The README gains sections on the API version, pagination, response metadata, capabilities and error codes, and its channel tables now mirror /v1/capabilities. The spot coverage row lists each dataset's first date.

Tests

  • pytest: 476 passed, 17 skipped (the live module, which runs only with OXARCHIVE_LIVE_TESTS=1 and an API key).
  • New tests/test_api_contract.py: mock-transport tests for every item above (headers on sync and async requests, the versioned WebSocket URL, both envelope shapes, time fields, error parsing, has_more and iterators, meta on every paged method, capabilities, filters on every venue, the channel table against a capabilities snapshot, replay refusals and aliases).
  • New tests/test_api_contract_live.py: read-only checks against the live API: 15 passed. The Lighter side checks are marked expected-to-fail while the API does not serve that filter.
  • python -m build and the package-contents allowlist check pass: the wheel holds only oxarchive/ and metadata.
  • No new ruff or mypy findings against master.

… triggered filters, L4 replay on every product

Every REST request sends 0xArchive-Version: 2026-10-01 and the WebSocket
client connects with version=2026-10-01. The SDK parses the shapes this
version selects: the standard envelope on data quality, the public coverage
summary and /v1/symbols; RFC 3339 times with integer *_ms companions on CVD,
the HIP-3 oracle, Lighter liquidations and volume, and levels snapshots; and
the live message shapes on Lighter and Robinhood Chain replay.

- OxArchiveError exposes error_code, status, param, valid_values and the
  whole error body; WsError exposes error_code. ERROR_CODES and ErrorCode
  export the public set, including slow_consumer and endpoint_unsupported.
- CursorResponse.has_more; iterators stop when it is false. Every paged
  method returns meta, with symbol and venue on per-symbol routes; records
  returned on their own carry it as response_meta.
- client.capabilities() and acapabilities() for GET /v1/capabilities, and
  data_quality.status_coverage() for /v1/status/coverage.
- side on every trades method (all venues), triggered on order history,
  depth on full-depth L2 history.
- WebSocket: WS_CHANNELS mirrors /v1/capabilities. HIP-3, HIP-4 and spot L4
  and both full-depth channels replay (bulk, single-channel); channels
  without replay or live data are refused before sending. on_replay_data()
  decodes Lighter replay records with the live parsers.
- Aliases for consistent verbs: trades.history(), spot.twap.history(),
  hip4.get_price_history(). Existing names keep working.
- The venue is named Lighter throughout.
@nuttykenzo

Copy link
Copy Markdown
Contributor

Target date: no date → 2026-10-08

Date status: Proposed. This is a proposed delivery target; owner confirmation is still required.

Why: K requested a Target date on every active tracked item. This copies the already recorded planning/review date; it does not create owner agreement or change scope, status, priority, ownership, implementation/deployment/sending permission, or any release hold.

- import oxarchive raised TypeError on Python 3.9, which the package
  declares as supported: two SymbolDataTypeCoverage fields used the
  X | None annotation syntax, which pydantic evaluates at import time.
  They use Optional now. The tests use typing constructs too, and the
  suite passes on Python 3.9, 3.12 and 3.13.
- The package ships a py.typed marker in the wheel and the sdist, as the
  Typing :: Typed classifier already declared.
- tests/test_slug_encoding.py imports the shared mock the way the other
  tests do, so plain pytest collects it against an installed package.
…eplay only, spot TWAP over REST

/v1/capabilities lists hip4_orderbook and hip4_open_interest as replay
only, and spot TWAP as REST only. The server accepts those subscriptions
but sends nothing on them, so the SDK now says so instead of waiting:

- WS_CHANNELS marks hip4_orderbook and hip4_open_interest live=False.
  subscribe() and the subscribe_hip4_* helpers raise ValueError before
  sending; replay() is unchanged.
- spot_twap is listed with neither live nor replay. subscribe_spot_twap()
  and replay() raise ValueError with REST_ONLY_ERROR, which points to
  client.spot.twap.
- The WsChannel docstring, the helper docstrings, the README channel
  tables and the CHANGELOG list each channel as capabilities does. The
  HIP-4 WebSocket example finds an open outcome instead of pinning one.
- Tests: the expected table matches the live endpoint, with spot_twap
  checked against the spot TWAP row. The Lighter side-filter tests are no
  longer expected to fail; the API filters side on every venue.
…age dates, examples inside coverage

- Quick Start: no placeholder wallet (it finds one from the BTC position
  listing), an active HIP-3 market (xyz:TSLA) instead of an empty one, and
  order book history for the last hour instead of a fixed 2024 window.
  README and docstring examples use the same HIP-3 market.
- Coverage table and docstrings give each dataset's first served instant
  as /v1/capabilities reports it, replacing month-only dates and stale
  starts: spot L4 from 2026-05-05 22:57 UTC (PURR-USDC 2026-03-11 01:03
  UTC), spot candles from 2025-03-22 10:50 UTC, Hyperliquid liquidations
  from 2025-12-22, Robinhood Chain candles from 2026-06-26 20:10 UTC
  (served, no longer "once enabled"). Market counts are not pinned; the
  listing calls return the current sets.
- Examples read the last hour, day or week, which every plan can reach,
  instead of fixed dates that fell before coverage (candles, the Lighter
  order book, L2, L3, L4, order history, liquidations) or outside the Free
  plan's 30-day history. HIP-4 examples find an open outcome.
- The Lighter tick-level example uses orderbook.history_tick();
  orderbook.history() returns snapshots, and its docstring now says so.
- The positions examples no longer reuse the name now for a response.
- CHANGELOG: an "Upgrading from 1.7" note at the top of 1.12.0 lists the
  changes that can break code written against the last PyPI release.
@0xFantomMenace
0xFantomMenace merged commit dbfbfc2 into master Oct 5, 2026
1 check passed
@0xFantomMenace
0xFantomMenace deleted the feat/api-contract-2026-10 branch October 5, 2026 00:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants