Skip to content

Releases: pmxt-dev/pmxt

v2.49.1

08 Jun 20:16
805c3b4

Choose a tag to compare

Positioning-shift patch on top of 2.49.0 — the hosted trading mode shipped in 2.49.0 but the docs, READMEs, and OpenAPI schemas still defaulted to the self-hosted sidecar path. This release flips the default everywhere the SDK + docs surface a customer hits: hosted PMXT is the primary experience; self-hosting becomes the advanced escape hatch. No SDK runtime behavior changes — pure documentation, schema, and copy work. Marketing-site changes ship separately in a sibling pmxt-website PR.

Added

  • Docs: 11 new MDX pages on the Mintlify site covering the hosted trading mode end-to-end — trading-quickstart (60-second walkthrough), concepts/hosted-trading (feature landing), concepts/hosted-vs-self-hosted (one-pager comparison), concepts/catalog-uuid-vs-venue-id (the UUID/venue-id gotcha), guides/escrow-lifecycle (PreFundedEscrow walkthrough), guides/signing (EthAccountSigner / EthersSigner + EIP-712), guides/hosted-errors (the 5 most-common subclasses with try/except cookbook), guides/migrate-to-hosted-trading (ported MIGRATION.md content with language tabs), guides/self-hosted (consolidated local-sidecar story), api-reference/errors (full HostedTradingError tree with dual-parent semantic-map), api-reference/configuration (ExchangeOptions + env vars + base-URL resolution).
  • Docs: New "Hosted Trading" and "Self-host" sidebar groups in docs.json, plus a "Reference" group at the top of the API Reference tab. sdk/server moved out of the previous "SDK" group into "Self-host" (without slug rename — link-stability preserved for this release).
  • Core: New ExchangeOptions component schema in core/src/server/openapi.yaml documenting constructor-level options (pmxtApiKey, walletAddress, signer, privateKey, baseUrl, etc.) — previously only ExchangeCredentials (per-request body credentials) existed at the schema level.
  • Core: BuiltOrder.expiry field added to the OpenAPI schema (the TTL that triggers BuiltOrderExpired at submit time was implicit in the SDK and undocumented at the spec level).

Changed

  • Docs: introduction.mdx "It runs two ways" bullet order inverted — hosted listed first as the default, self-hosted second as the advanced path. First code block swapped from a dual-variant local/hosted snippet to a single hosted-default pmxt.Polymarket(pmxt_api_key=...) constructor.
  • Docs: authentication.mdx venue-credentials section reframed — "Hosted writes (recommended)" subsection added on top showing the pmxt_api_key + wallet_address + private_key constructor with a one-line client.escrow.deposit() example. The raw-private-key prose was preserved but relabeled as "Self-hosted / direct venue credentials (advanced)". Status/body/meaning error table picked up a fourth "SDK exception" column cross-linking to the new /api-reference/errors page.
  • Docs: security.mdx "Run pmxt locally" callout downgraded from <Warning> to <Note> and reworded — self-hosting is positioned as one option among several rather than the implicit "safer choice". PreFundedEscrow custody surfaced as the hosted alternative.
  • Docs: sdk/server.mdx got a top <Note> banner clarifying that the page applies to self-hosted mode only and hosted-mode users can skip it. (File location and slug intentionally not renamed in this release to preserve external links.)
  • Docs: concepts/venues.mdx gained a third table at the bottom — "Hosted-trading venues" — listing Polymarket and Opinion with custody type, cross-chain support, and minimum-order-size columns.
  • READMEs (root, Python, TypeScript): All three flipped to hosted-default. Subtitles, "Why pmxt?" bullets, Quick Start, and Trading sections now lead with pmxt.Polymarket(pmxt_api_key=...). Per-venue raw-credentials blocks preserved verbatim but moved into "Self-hosted trading (advanced)" subsections. Root README's "No API key required" bullet (actively anti-hosted-positioning) replaced with a "Hosted API" lead bullet. Net +176 lines across the three files.

Fixed

  • Core: Order schema in core/src/server/openapi.yaml (and the generated docs/api-reference/openapi.json) now includes the nullable txHash, chain, and blockNumber fields the SDK has been returning in hosted mode since 2.49.0. Previous spec was silent on these and downstream codegen consumers missed them.
  • Core: UserTrade schema gained the same txHash / chain / blockNumber nullable trio.
  • Core: Position schema — required list trimmed from [marketId, outcomeId, outcomeLabel, size, entryPrice, currentPrice, unrealizedPnL] to [marketId, outcomeId, size]. The other four became optional in 2.49.0 when the SDK stopped fabricating mark-to-market defaults for positions without a known current price; the schema kept claiming they were required, so generated clients with strict-null checking were rejecting valid responses. New currentValue field added (size * currentPrice when available). txHash / chain / blockNumber enrichment added.
  • Core: Balance schema gained the optional venue field that hosted-mode responses already carry on multi-venue queries.
  • Core: ErrorDetail schema expanded from { message: string } to the full envelope shipping in production responses — code (with a populated enum covering all HostedTradingError codes plus the pre-existing tree), retryable: boolean, optional exchange, optional free-form detail object. Downstream codegen can now branch on code.

Docs

  • docs.json: First-time redirects array added (empty for this release; reserves the structure for future slug renames).

Installation

npm:

npm install pmxtjs@2.49.1
npm install -g @pmxt/cli@2.49.1

PyPI:

pip install pmxt==2.49.1

Links

What's Changed

  • feat(core): openapi schema for hosted-mode enrichment fields (v2.49.1) by @realfishsam in #978
  • docs: hosted-default positioning + 11 new pages + READMEs + changelog (v2.49.1) by @realfishsam in #977

Full Changelog: v2.49.0f...v2.49.1f

v2.49.0

08 Jun 19:07

Choose a tag to compare

Added

  • SDK (Python + TypeScript): Hosted trading mode now works end-to-end against trade.pmxt.dev. Constructing the client with a pmxt_api_key / pmxtApiKey switches every Group A public method — create_order / createOrder, build_order / buildOrder, submit_order / submitOrder, cancel_order / cancelOrder, fetch_balance / fetchBalance, fetch_positions / fetchPositions, fetch_open_orders / fetchOpenOrders, fetch_my_trades / fetchMyTrades, fetch_order / fetchOrder — to dispatch through PMXT's PreFundedEscrow custody on trade.pmxt.dev/v0/* instead of the local sidecar. Read methods that require a wallet raise MissingWalletAddress locally before any network call when neither an explicit address argument nor wallet_address on the client is set. fetch_closed_orders and fetch_all_orders raise NotSupported in hosted mode (settled orders are modeled as trades; callers should use fetch_my_trades). Both SDKs auto-wrap a raw private_key / privateKey into the venue signer (EthAccountSigner for Python via eth-account, EthersSigner for TypeScript via the optional ethers >= 6 peer dep) so the user never has to construct a signer manually.
  • SDK (Python + TypeScript): New Escrow namespace on hosted-mode Polymarket clients (client.escrow.build_approve_tx(...), build_deposit_tx, build_withdraw_tx, withdrawals(...)) for the PreFundedEscrow deposit/withdraw flow. Mirrors the /v0/escrow/* surface; only instantiated on hosted-trading-allowlisted venues.
  • SDK (Python + TypeScript): New hosted-mode error hierarchy (HostedTradingError, InsufficientEscrowBalance, OrderSizeTooSmall, InvalidApiKey, OutcomeNotFound, CatalogUnavailable, BuiltOrderExpired, InvalidSignature, NoLiquidity, MissingWalletAddress). Each hosted error keeps a semantic parent so existing catch-sites still work — e.g. InsufficientEscrowBalance extends InsufficientFunds, OutcomeNotFound extends NotFoundError, CatalogUnavailable extends ExchangeNotAvailable. Python uses true multi-inheritance; TypeScript uses a static isHostedError = true flag plus an isHostedError(e) helper to compensate for single-inheritance. The mapper (raise_from_response / raiseFromResponse) translates trade.pmxt.dev status codes and detail strings to the right subclass.
  • SDK (Python): New tests/e2e/hosted_driver.py — runnable live driver that proves URL routing against prod. Hits trade.pmxt.dev/v0/* with a deliberately-bogus key so the server returns 401, captures the URL for every public method via an httpx-level transport hook, and asserts each URL starts with https://trade.pmxt.dev/v0/. Also verifies local-only failure paths (MissingWalletAddress, NotSupported, InvalidOrder, InvalidSignature) raise before any network call.
  • SDK (TypeScript): New tests/e2e/hosted-driver.ts — equivalent live driver (tsx-runnable) covering the same routing and local-raise assertions, using global.fetch instrumentation.
  • SDK (Python + TypeScript): 87 new in-process integration tests (test_hosted_dispatch.py + test_hosted_error_mapping.py in Python, hosted-dispatch.test.ts + hosted-error-mapping.test.ts in TypeScript). These mock the lowest reasonable HTTP layer (httpx.MockTransport / jest.spyOn(global, 'fetch')), construct a hosted client, call the public method, and assert exact URL / verb / body shape / response mapping for every Group A method plus the upstream status → SDK exception mapping.
  • SDK (Python + TypeScript): Feed listing surface on SDK clients — callers can now enumerate available data feeds from the unified client instead of reaching into the internal feed-client submodule. (#869)

Changed

  • SDK (Python): pmxt 2.17.1 → 2.18.0. New constructor kwargs wallet_address: str | None and signer: Signer | None on every Exchange subclass; both are pass-through to the base class. Existing non-hosted (sidecar) callers see no behavior change.
  • SDK (TypeScript): pmxtjs 2.17.1 → 2.18.0. New walletAddress / signer / privateKey fields on ExchangeOptions. ethers >= 6.0.0 < 7.0.0 declared as an optional peerDependency (only required for hosted writes; hosted reads work without it).
  • SDK (Python + TypeScript): Order, UserTrade, Position, and Balance now carry optional tx_hash / txHash, chain, and block_number / blockNumber fields, populated in hosted mode after the trade settles on-chain. Non-hosted callers see None / undefined for these — unchanged behavior.
  • SDK (Python + TypeScript): Position mark-to-market fields (outcome_label, entry_price, current_price, current_value) are now all Optional. Hosted endpoints populate outcome_label and entry_price from operator-side cost-basis enrichment when available, but downstream consumers must handle the missing case rather than relying on fabricated defaults.
  • SDK (Python + TypeScript): Drift parity sweep across the two SDKs — model shapes, method signatures, capability flags, and generated outputs reconciled so the same call against the same venue returns identically-shaped objects regardless of which SDK you use. (#867)
  • SDK (Python + TypeScript): Missing event/order parameters propagated through both SDK models so the full set of fields the core surface produces is actually reachable on the SDK objects. (#872)
  • SDK (Python): Type annotations tightened across the Python SDK — narrower union types and Optional markers replacing implicit Any in several public signatures. (#868)
  • Core: Cached exchange specs (the test fixtures used to detect upstream API drift) reconciled with current live payloads from each venue. (#866)
  • Core: Magic chain IDs (137, 56, etc.) replaced with named constants throughout the codebase. (#878)
  • Deps: npm dependency refresh to clear outstanding security advisories. (#864)
  • Deps: Python security dependency floors raised to clear outstanding security advisories. (#865)

Fixed

  • SDK (Python): Order dataclass field ordering — filled_shares: Optional[float] = None was declared before the non-default fields remaining: float and timestamp: int, which Python 3.13 rejects with TypeError: non-default argument 'remaining' follows default argument 'filled_shares' on first instantiation. Moved filled_shares below the required fields.
  • SDK (Python): _error_detail_from_success_payload no longer treats a successful 2xx response with a list or scalar JSON payload as an error envelope. Endpoints like /v0/user/{addr}/balances return JSON arrays ([{"currency": "USDC", "amount": 12.5}]); the previous logic stringified the array and re-raised it as HostedTradingError, so every successful read crashed. Only 2xx Mappings with explicit error / errors / success: false markers now count as an error envelope.
  • SDK (Python): Duplicate NotSupported class in _hosted_errors.py was shadowing the canonical one in errors.py. Tests that did from pmxt._hosted_errors import NotSupported failed to catch raises from client.py that used from .errors import NotSupported, because the two classes were unrelated. _hosted_errors.py now re-exports the canonical NotSupported from errors.py.
  • SDK (TypeScript): _hostedBuildOrderBody was writing the user's wallet to body["wallet_address"], but trade.pmxt.dev's BuildOrderV0Req expects the field as user_address. Every createOrder / buildOrder via the TS SDK previously 422-ed on a "missing user_address" Pydantic validation error before reaching the chain. Python SDK was already correct.
  • SDK (TypeScript): Nine HOSTED_METHOD_ROUTES.get("…") lookups in client.ts used snake_case keys ("submit_order", "fetch_balance", etc.) against a Map whose keys are camelCase ("submitOrder", "fetchBalance", etc.). Every hosted call would have thrown TypeError: Cannot read properties of undefined (reading 'method') at runtime. tsc and Jest didn't catch this because the existing unit tests stub out the lookup. Fixed by switching all nine sites to the camelCase keys actually defined in the map.
  • SDK (TypeScript): Removed the errors.ts → hosted-errors.ts re-export block that created a circular import. tsc and ts-jest tolerated the cycle, but tsx / Node CJS crashed at module load with ReferenceError: Cannot access 'PmxtError' before initialization because errors.ts's body re-exports from hosted-errors.ts, which extends PmxtError defined later in the same errors.ts body. Hosted error classes are now re-exported once from index.ts instead of via errors.ts. The public surface is unchanged for consumers importing from pmxtjs.
  • Server (Python sidecar): Bare and overly-broad except: handlers in the sidecar manager tightened to specific exception types, so genuine bugs surface instead of getting swallowed and reported as opaque "server failed to start" errors. Closes #813-#821. (#871)
  • Server: WebSocket and feed-client hygiene issues surfaced rather than swallowed — disconnects, malformed frames, and feed-side errors now propagate to the caller instead of silently dropping events. (#870)
  • Core: Exchange normalizers across all venues realigned with current live payload shapes; addresses cumulative drift that had been quietly producing inconsistent unified objects between SDKs and the server. (#873)
  • Kalshi: Pagination capped to prevent unbounded scrolling on fetchMarkets / fetchEvents, and status=all is now serialized as a single value rather than the array form that some Kalshi endpoints reject. (#874)
  • Limitless: Explicit fetch timeouts on every outbound HTTP call (and the local test server) so a slow upstream can no longer hang the entire SDK request indefinitely. (#875, #876)
  • Limitless: Throttler now rejects new work when its queue overflows instead of growing the queue unbounded — prevents memor...
Read more

v2.48.6

04 Jun 11:42

Choose a tag to compare

Fixed

  • Opinion: resolutionDate on Opinion markets no longer collapses to 1970-01-01T00:00:00Z when the upstream cutoffAt is missing or 0. Root cause: toMillis(0) returned 0, which the normalizer then wrapped in new Date(0) and emitted as a valid-looking past date. Categorical child markets (e.g. 2026 FIFA World Cup Winner - Spain) are the common case — Opinion publishes cutoffAt only on the parent, not on each child — so every child silently inherited epoch and was filtered out by any downstream closes_at > now() guard. Concretely, hosted-pmxt's fetchMarketMatches was dropping all Opinion ↔ Polymarket pairs (407 in the catalog, 13 FIFA-specific) because the Opinion side looked already-closed.
  • Opinion: normalizeChildMarket now inherits parent.cutoffAt via child.cutoffAt || parent.cutoffAt, so the fallback introduced in commit 6ac8cd1 actually fires for the cutoffAt = 0 case (the upstream literal, not a missing field).
  • Core: toMillis(ts) in opinion/utils.ts now returns null for falsy input instead of 0, so callers can distinguish "no timestamp" from "epoch" and stop materializing bogus 1970 dates. Trade/order normalizers preserve old behavior with ?? 0.
  • Core: UnifiedMarket.resolutionDate is now ?: Date. Not every venue publishes a resolution date on every market, and the optional type lets normalizers emit undefined instead of fabricating an epoch sentinel. BaseExchange.filterByCriteria and the Baozi normalizer handle the optional case (markets without a known resolution date pass an active-status filter, fail a closed-status filter, and sort last under sort=newest).

Installation

npm:

npm install pmxtjs@2.48.6
npm install -g @pmxt/cli@2.48.6

PyPI:

pip install pmxt==2.48.6

Links

Full Changelog: v2.48.5f...v2.48.6f

v2.48.5

02 Jun 16:37

Choose a tag to compare

Fixed

  • Opinion: outcome.metadata on every market returned by fetchEvents / fetchMarkets / fetchMarket now carries opinionMarketId (Opinion's source-native integer market id), mirroring the clobTokenId shape Polymarket already exposes. Downstream consumers (notably pmxt-trading's /trade/build-order, which keys Opinion orders by integer marketId) can now recover the id from a unified outcome without bypassing pmxt-api. (#838)
  • Opinion: fetchMarkets({ marketId }) now rejects non-integer values (e.g. accidentally passing a pmxt UUID) with a BAD_REQUEST instead of silently returning an unrelated market. (#838)

Installation

npm:

npm install pmxtjs@2.48.5
npm install -g @pmxt/cli@2.48.5

PyPI:

pip install pmxt==2.48.5

Links

Full Changelog: v2.48.4f...v2.48.5f

v2.48.4

02 Jun 10:49

Choose a tag to compare

Fixed

  • Python SDK: FeedClient is now exported from the top-level pmxt package, so from pmxt import FeedClient (and pmxt.FeedClient(...)) work without reaching into the internal pmxt.feed_client submodule. (#835)
  • TypeScript SDK: FeedClient is now exported from the top-level pmxtjs package alongside its related types (Ticker, Tickers, OHLCV, OracleRound, FeedClientOptions), and is exposed on the default pmxt object. Consumers can now import { FeedClient } from 'pmxtjs' or call pmxt.FeedClient(...) directly. (#835)

Installation

npm:

npm install pmxtjs@2.48.4
npm install -g @pmxt/cli@2.48.4

PyPI:

pip install pmxt==2.48.4

Links

What's Changed

Full Changelog: v2.48.3f...v2.48.4f

v2.48.3

01 Jun 13:57

Choose a tag to compare

Fixed

  • SDK (TS + Python): sourceMetadata is now declared on UnifiedMarket and UnifiedEvent model classes in both SDKs (it was previously declared only on UnifiedSeries). Closes a schema-drift gap so the venue-specific raw metadata that core already attaches via buildSourceMetadata actually surfaces on the SDK objects rather than being dropped at the model boundary.
  • Core (addBinaryOutcomes): Promoting Yes/No labels to the market title now mutates the existing outcome object instead of replacing it with a spread copy. This restores reference identity between market.yes / market.no and market.outcomes[0] / market.outcomes[1] — an invariant the unified-market contract assumes. Consumers diffing by object equality no longer see split snapshots after title promotion.
  • Kalshi: The event-title contamination heuristic now also counts sub-market ticker tails (e.g. PSG from KXUCL-26-PSG) as candidate labels. Previously, titles like "Champions League Winner: PSG vs Arsenal" only matched a single full label (Arsenal) and fell short of the >= 2-match threshold, so the contaminated title was kept instead of falling back to the series title. The threshold itself is unchanged.

Installation

npm:

npm install pmxtjs@2.48.3
npm install -g @pmxt/cli@2.48.3

PyPI:

pip install pmxt==2.48.3

Links

What's Changed

Full Changelog: v2.48.2f...v2.48.3f

v2.48.2

01 Jun 12:10

Choose a tag to compare

Fixed

  • Limitless: fetchBalance no longer loses precision when raw on-chain balances exceed Number.MAX_SAFE_INTEGER (≈ 9 × 10¹⁵ USDC raw units). Replaced parseFloat(rawBalance.toString()) / Math.pow(10, decimals) with a scaledIntegerToNumber helper that performs integer division/modulo in bigint before converting to a JS number. Affects LimitlessExchange.fetchBalance and LimitlessClient.getBalance. (#683)
  • Python SDK: Server auto-start failure message now points users at the correct package (pmxt-core) instead of the stale pmxtjs (the TypeScript SDK package, which does not provide the sidecar). Added a regression test that scans client.py source for any future pmxtjs reintroduction. Also cleaned up the same stale reference in QUICKREF.py. (#764)
  • Python SDK: pmxt.SuiBets is now exported with the matching cross-SDK casing (was Suibets). The fix lives in core/scripts/generate-python-exchanges.js via a new className override pattern so it survives regeneration; pmxt.Suibets remains as a backwards-compatible alias. (#774)

Installation

npm:

npm install pmxtjs@2.48.2
npm install -g @pmxt/cli@2.48.2

PyPI:

pip install pmxt==2.48.2

Links

What's Changed

  • fix: update Python sidecar package guidance by @nanookclaw in #764
  • fix: avoid float precision loss in Limitless balances by @00anon0X in #683
  • fix(python-sdk): rename Suibets to SuiBets for cross-SDK consistency by @PrettyFox0 in #774

New Contributors

Full Changelog: v2.48.1f...v2.48.2f

v2.48.1

30 May 14:06
8b9f7ed

Choose a tag to compare

Fixed

  • Server: POST /api/<exchange>/<method> now tolerates flat-body requests like {"slug":"wta"} in addition to the existing {"args":[{"slug":"wta"}]} envelope. Previously, flat bodies caused all filter parameters to be silently dropped (the method was invoked with no arguments). The Python and TypeScript SDKs were not affected — they always wrap params in args — but raw curl callers and documentation examples hit this. Empty bodies still behave as args:[].

Installation

npm:

npm install pmxtjs@2.48.1
npm install -g @pmxt/cli@2.48.1

PyPI:

pip install pmxt==2.48.1

Links

What's Changed

  • fix: tolerate flat POST bodies (treat as positional args[0]) by @realfishsam in #767

Full Changelog: v2.48.0f...v2.48.1f

v2.48.0

30 May 13:36
a0b3f78

Choose a tag to compare

Added

  • Core: New UnifiedSeries type representing recurring event groupings — the fourth tier above Event -> Market -> Outcome. Examples: Kalshi KXATPMATCH (every ATP match), Polymarket wta (every WTA match).
  • Core: fetchSeries(params?) method on BaseExchange with vendor implementations for Kalshi (GetSeriesList), Polymarket and Polymarket US (Gamma /series + /series/{id}), Opinion (emulated from raw collection field), and Gemini-Titan (emulated from raw series field). Venues without a series concept return [] and report has.fetchSeries: false.
  • Core: New series?: string parameter on fetchEvents for filtering by venue-native series id / ticker / slug. Passes through to vendor APIs where supported (Kalshi ?series_ticker=, Polymarket Gamma ?series_id=); venues without one return [] rather than silently ignore the filter.
  • Core: Router.fetchSeries() and Router.fetchEvents({series}) for cross-venue queries by normalized PMXT series id (e.g. tennis-atp-match, nfl, crypto-btc-15m). Backed by a curated venue-id map at core/src/router/series-map.ts covering tennis, American sports, soccer, esports, and crypto.
  • Core: ExchangeHas.fetchSeries capability flag.

Installation

npm:

npm install pmxtjs@2.48.0
npm install -g @pmxt/cli@2.48.0

PyPI:

pip install pmxt==2.48.0

Links

What's Changed

  • feat: UnifiedSeries — fetchSeries across venues, Router cross-venue series, both SDKs by @realfishsam in #766

Full Changelog: v2.47.0f...v2.48.0f

v2.47.0

30 May 12:21
3e59bc3

Choose a tag to compare

Added

  • Core: Optional sourceMetadata field on UnifiedEvent and UnifiedMarket (Record<string, unknown>) — captures venue-specific raw fields that are not promoted to first-class unified columns. Populated by every exchange normalizer (Kalshi, Polymarket, Polymarket US, Limitless, Smarkets, Opinion, Myriad, Probable, Metaculus, Baozi, Gemini-Titan, Hyperliquid, SuiBets) via a shared buildSourceMetadata helper at core/src/utils/metadata.ts. Includes recurring-series identifiers where the venue exposes them (Kalshi series_ticker/series_title, Polymarket series/seriesSlug when present, Opinion collection, Gemini-Titan series).

Installation

npm:

npm install pmxtjs@2.47.0
npm install -g @pmxt/cli@2.47.0

PyPI:

pip install pmxt==2.47.0

Links

What's Changed

  • feat: add sourceMetadata to UnifiedEvent and UnifiedMarket by @realfishsam in #765

Full Changelog: v2.46.14f...v2.47.0f