0.3.2 - 2026-07-25

Fixed

  • Live CLOB book timestamps in milliseconds. OrderBook.from_clob_book/2 treated a string "timestamp" as Unix seconds, but the live /book endpoint sends milliseconds as a string (e.g. "1781577793787") — DateTime.from_unix!/1 then raised invalid Unix time and crashed the consuming snapshotter (and, in the reference bot, the whole app on boot). The string path now applies the same ms-vs-seconds heuristic as the integer path, and a malformed timestamp falls back to DateTime.utc_now/0 instead of raising. Surfaced by a real paper-trading run; 2 regression tests.

0.3.1 (unreleased)

Added

  • Integration test for PaperEx.Engine.advance_pending/3 flowing through PaperExPolymarket.Adapter: a limit pending created by the adapter is resolved by a fresh snapshot end-to-end, and a non-crossing fresh snapshot leaves the pending in place with no ledger churn.
  • No adapter code change — advance_pending/3 is generic in the engine and consumes the adapter's existing simulate_fill/3.

0.3.0 (unreleased)

Added

  • PaperExPolymarket.Snapshot — convenience module closing the loop from a Polymarket CLOB /book response to a PaperEx.MarketSnapshot:
    • from_clob_book/3 — normalize an already-fetched body for a token id, {:token_id, …} tuple, or pre-resolved PaperEx.Instrument.
    • fetch_order_book_snapshot/3 — same, with an injected fetcher (1-arity function or {module, function, args} tuple). The package never owns network IO; fetchers are caller-supplied.
  • 18 new tests covering: fixture-backed conversion, token-id / tuple / instrument refs, malformed inputs returning bounded reason codes (:polymarket_invalid_market_payload, :polymarket_invalid_book), function and MFA fetcher success paths, error pass-through, instrument_opts forwarding.

Notes

  • This module exists in paper_ex_polymarket rather than polymarket to preserve the dependency direction: polymarket stays upstream of paper packages. Any helper returning PaperEx.MarketSnapshot belongs here or in polymarket_bot.

0.2.0 (unreleased)

Added

  • Hand-authored CLOB/Data-API fixture set under test/fixtures/ (clob_market.json, clob_book.json, data_api_trade.json) modeling realistic Polymarket payload shapes from the v1 bot's observed responses. See test/fixtures/README.md for the shape rationale.
  • PaperExPolymarket.Fixtures — test-support helper for loading the fixture set (not compiled into the library).
  • 28 new fixture-backed integration tests exercising Market.from_clob_market/2, OrderBook.from_clob_book/2, Adapter.normalize_instrument/1, Adapter.normalize_snapshot/2, Adapter.normalize_fill/2, and Execution.simulate_fill/3 against the realistic payload shapes. Covers happy paths, malformed payloads (non-map instrument, missing book keys, partial-parse prices, partial-parse timestamps, zero-size levels) and the bounded-reason-code contract.
  • 6 new pending-integration tests verifying that the :pending_remainder policy added to PaperEx.Engine flows cleanly through the Polymarket adapter, that market FAK partials never record :pending, and that Engine.cancel_pending/3 and LiveMirror.record_pending/3 coexist without colliding.

Notes

  • No code change to PaperExPolymarket.Adapter was required: the pending-remainder policy is implemented generically in PaperEx.Engine and the adapter's simulate_fill/3 already returns the {:ok, :partial, fills} shape the engine consumes.

0.1.0 (unreleased)

Initial scaffold of the Polymarket adapter for paper_ex.

Added

  • PaperExPolymarket top-level module with version/0 and moduledoc explaining the adapter package role and non-goals.
  • PaperExPolymarket.Market — token id ↔ PaperEx.Instrument conversion, including outcome label/index lookup against CLOB "tokens" lists and tick-size parsing.
  • PaperExPolymarket.OrderBook — CLOB /book body ↔ PaperEx.MarketSnapshot. Parses string-encoded prices/sizes, filters zero-size levels, surfaces malformed payloads as :invalid_book.
  • PaperExPolymarket.ActivityMapper — Data-API and RTDS trade event ↔ PaperEx.Fill. Enforces instrument id match, parses second-/ms- precision timestamps, surfaces maker/taker liquidity hints.
  • PaperExPolymarket.Execution — Polymarket-flavored fill simulator (:market orders modeled as FAK, :limit modeled as GTC). Walks the book, produces :filled / :partial / :missed outcomes with bounded reason codes (:no_liquidity, :limit_not_crossed).
  • PaperExPolymarket.Fees — bps-based fee helper for take-fee modeling via adapter_opts: [fee_bps: <bps>]. Defaults to zero.
  • PaperExPolymarket.Adapter — implements every PaperEx.Adapter callback. Returns bounded Polymarket-tagged reason atoms (:polymarket_invalid_market_payload, :polymarket_invalid_book, :polymarket_invalid_trade_payload, :polymarket_instrument_mismatch) on top of the generic engine codes from PaperEx.ReasonCodes.engine_codes/0.
  • PaperExPolymarket.LiveMirror — live-mirror lifecycle helpers:
    • simulate_intent/4 — engine apply with :mode = :live_mirror.
    • mirror_actual_fill/4 — record an observed exchange trade as a :filled execution.
    • record_pending/3 — record a resting :pending execution.
    • resolve_pending/4 — move a pending into :filled or :cancelled, preserving both ledger entries.
  • 71 tests covering: CLOB market normalization, CLOB book parsing (happy + malformed + zero-size), activity-event mapping (BUY / SELL / liquidity / ms timestamps / instrument mismatch / zero size / non-numeric), fill simulation across :market / :limit / :buy / :sell / partial / missed / limit-not-crossed paths, adapter behaviour conformance, end-to-end PaperEx.Engine.apply_order/4 through the adapter, fee application via adapter_opts, full live-mirror pending → filled / cancelled lifecycle, and live-mirror ledger preservation for misses and skips that never produced a position.

Deferred

  • Auto-fetching CLOB market metadata via polymarket_clob. Callers pass metadata in.
  • Polymarket FAK partial-remainder lifecycle (:pending execution for the unfilled remainder of a market order).
  • A built-in price-history loader.