Packages
paper_ex_polymarket
0.3.2
Adapter between paper_ex (generic paper trading) and polymarket (Polymarket SDK). Translates Polymarket CLOB books, market metadata, and trade activity into the normalized paper_ex domain. Not a trading bot.
Current section
Files
Jump to
Current section
Files
paper_ex_polymarket
CHANGELOG.md
CHANGELOG.md
# Changelog
## 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.