Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,44 @@ Handle `None` for book `stats` and `transactTime`, and BBO `bestBid`, `bestAsk`,
| `orders.preview(params)` | Preview an order |
| `orders.close_position(params)` | Close a position |

### RFQ trades (Authenticated)

`client.rfqs.trades(params=None)` returns one page of anonymous original fills
where either order originated from an RFQ, including later fills on resting
orders. API credentials and access to the retail RFQ beta are required.

```python
from polymarket_us.types import GetRFQTradesParams

params: GetRFQTradesParams = {
"limit": 100,
"startTime": "2026-10-01T00:00:00Z",
"endTime": "2026-10-02T00:00:00Z",
}
while True:
page = client.rfqs.trades(params)
for trade in page["trades"]:
print(trade["tradeId"], trade["price"], trade["qtyDecimal"])
if not page["cursor"]:
break
params["cursor"] = page["cursor"]
```

With `AsyncPolymarketUS`, use `await client.rfqs.trades(params)`. `limit` defaults
to 100 when omitted or zero and otherwise accepts 1–100. `startTime` is inclusive;
`endTime` is exclusive. Both accept RFC 3339 timestamp strings. The optional
`symbol` filter is an exact, case-sensitive instrument symbol.

Results are newest first. Keep the same filters and limit on subsequent pages,
and continue while `cursor` is nonempty, even if `trades` is empty. The SDK does
not paginate automatically. Prices and quantities remain exact decimal strings;
`executedTime` is a timestamp string or `None`.

History is eventually consistent. To recover gaps in the live RFQ stream,
requery overlapping time windows and deduplicate by `tradeId`. These anonymous
prints are not account reconciliation data; later corrections and trade busts
do not amend them.

### Portfolio (Authenticated)

| Method | Description |
Expand Down Expand Up @@ -348,7 +386,7 @@ WebSocket methods (`connect()`, `subscribe()`, `close()`) are async and must be
- `position_update` - Position changes
- `account_balance_snapshot` - Initial balance
- `account_balance_update` - Balance changes
- `rfq_event` - RFQ and quote lifecycle events
- `rfq_event` - RFQ/quote lifecycle events and anonymous RFQ trades
- `heartbeat` - Connection keepalive
- `error` - Error events
- `close` - Connection closed
Expand All @@ -359,7 +397,7 @@ aliases are retained, and both the named callback and `message` receive the
original envelope without renaming fields. An empty `error` string no longer
suppresses a successful data callback.

RFQ subscriptions deliver lifecycle events through `rfq_event` without an initial
RFQ subscriptions deliver lifecycle events and trade prints through `rfq_event` without an initial
snapshot. Market filters are not supported.

```python
Expand All @@ -372,14 +410,18 @@ def on_rfq_event(data: RFQEvent) -> None:
rfq = created["rfq"] if created is not None else None
if rfq is not None:
print(rfq["id"], rfq.get("qtyDecimal"))
traded = event.get("rfqTrade")
trade = traded["trade"] if traded is not None else None
if trade is not None:
print(trade["tradeId"], trade["price"], trade["qtyDecimal"])


private_ws.on("rfq_event", on_rfq_event)
await private_ws.subscribe_rfq("rfqs-1")
```

Other event keys are `rfqClosed`, `quoteCreated`, `quoteDeleted`, `quoteAccepted`,
`quoteConfirmed`, and `quoteExecuted`. Timestamps and nested RFQ/quote objects may
`quoteConfirmed`, and `quoteExecuted`. Timestamps and nested RFQ/quote/trade objects may
be null. Portfolio activity trades also expose `qtyDecimal` as an exact decimal
string; use it instead of the rounded `qty` when fractional quantities matter.

Expand Down
2 changes: 2 additions & 0 deletions polymarket_us/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
AsyncMarkets,
AsyncOrders,
AsyncPortfolio,
AsyncRFQs,
AsyncSearch,
AsyncSeries,
AsyncSports,
Expand Down Expand Up @@ -84,6 +85,7 @@ def __init__(
self.series = AsyncSeries(self)
self.sports = AsyncSports(self)
self.search = AsyncSearch(self)
self.rfqs = AsyncRFQs(self)
self.ws = _AsyncWebSocketFactory(self)

async def get(
Expand Down
2 changes: 2 additions & 0 deletions polymarket_us/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
Markets,
Orders,
Portfolio,
RFQs,
Search,
Series,
Sports,
Expand Down Expand Up @@ -96,6 +97,7 @@ def __init__(
self.series = Series(self)
self.sports = Sports(self)
self.search = Search(self)
self.rfqs = RFQs(self)
self.ws = _WebSocketFactory(self)

def get(
Expand Down
3 changes: 3 additions & 0 deletions polymarket_us/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from polymarket_us.resources.markets import AsyncMarkets, Markets
from polymarket_us.resources.orders import AsyncOrders, Orders
from polymarket_us.resources.portfolio import AsyncPortfolio, Portfolio
from polymarket_us.resources.rfqs import AsyncRFQs, RFQs
from polymarket_us.resources.search import AsyncSearch, Search
from polymarket_us.resources.series import AsyncSeries, Series
from polymarket_us.resources.sports import AsyncSports, Sports
Expand All @@ -20,6 +21,8 @@
"AsyncOrders",
"Portfolio",
"AsyncPortfolio",
"RFQs",
"AsyncRFQs",
"Search",
"AsyncSearch",
"Series",
Expand Down
28 changes: 28 additions & 0 deletions polymarket_us/resources/rfqs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""RFQ resource."""

from polymarket_us.resource import APIResource, AsyncAPIResource
from polymarket_us.types import GetRFQTradesParams, GetRFQTradesResponse


class RFQs(APIResource):
"""RFQ API resource (requires authentication and RFQ access)."""

def trades(self, params: GetRFQTradesParams | None = None) -> GetRFQTradesResponse:
"""Get a page of anonymous original RFQ fills, newest first."""
return self._client.get(
"/v1/rfqs/trades",
query=dict(params) if params else None,
authenticated=True,
)


class AsyncRFQs(AsyncAPIResource):
"""RFQ API resource (async, requires authentication and RFQ access)."""

async def trades(self, params: GetRFQTradesParams | None = None) -> GetRFQTradesResponse:
"""Get a page of anonymous original RFQ fills, newest first."""
return await self._client.get(
"/v1/rfqs/trades",
query=dict(params) if params else None,
authenticated=True,
)
15 changes: 14 additions & 1 deletion polymarket_us/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,17 @@
Trade,
UserPosition,
)
from polymarket_us.types.rfq import RFQ, Quote, QuoteStatus, RFQComboLeg, RFQSide, RFQStatus
from polymarket_us.types.rfq import (
RFQ,
GetRFQTradesParams,
GetRFQTradesResponse,
Quote,
QuoteStatus,
RFQComboLeg,
RFQSide,
RFQStatus,
RFQTrade,
)
from polymarket_us.types.search import SearchParams, SearchResponse
from polymarket_us.types.series import (
GetSeriesListResponse,
Expand Down Expand Up @@ -114,6 +124,9 @@
"RFQSide",
"RFQStatus",
"QuoteStatus",
"RFQTrade",
"GetRFQTradesParams",
"GetRFQTradesResponse",
# Events
"Event",
"Market",
Expand Down
30 changes: 29 additions & 1 deletion polymarket_us/types/rfq.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Private RFQ stream types."""
"""RFQ history and private stream types."""

from typing import Literal, TypedDict

Expand Down Expand Up @@ -75,3 +75,31 @@ class Quote(_OptionalQuote):
confirmationDeadline: str | None
executionDeadline: str | None
executedTime: str | None


class RFQTrade(TypedDict):
"""An anonymous original fill; later corrections and busts are not reflected."""

tradeId: str
symbol: str
price: str
qtyDecimal: str
aggressorSide: RFQSide
executedTime: str | None


class GetRFQTradesParams(TypedDict, total=False):
"""History filters; repeat the same filters and limit with each cursor."""

limit: int
cursor: str
startTime: str
endTime: str
symbol: str


class GetRFQTradesResponse(TypedDict):
"""A history page; continue nonempty cursors even when trades is empty."""

trades: list[RFQTrade]
cursor: str
16 changes: 15 additions & 1 deletion polymarket_us/websocket/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

from typing import Literal, TypedDict

from polymarket_us.types import RFQ, Amount, Execution, Order, Quote, UserBalance, UserPosition
from polymarket_us.types import (
RFQ,
Amount,
Execution,
Order,
Quote,
RFQTrade,
UserBalance,
UserPosition,
)

PrivateSubscriptionType = Literal[
"SUBSCRIPTION_TYPE_ORDER",
Expand Down Expand Up @@ -161,6 +170,10 @@ class _QuoteExecuted(_QuoteChange):
executedTime: str | None


class _RFQTradeEvent(TypedDict):
trade: RFQTrade | None


class _RFQEventPayload(TypedDict, total=False):
# The gateway emits only the selected event; unknown future events may be empty.
rfqCreated: _RFQChange
Expand All @@ -170,6 +183,7 @@ class _RFQEventPayload(TypedDict, total=False):
quoteAccepted: _QuoteAccepted
quoteConfirmed: _QuoteConfirmed
quoteExecuted: _QuoteExecuted
rfqTrade: _RFQTradeEvent


class RFQEvent(TypedDict):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "polymarket-us"
version = "2.1.0"
version = "2.3.0"
description = "Polymarket US Python SDK"
readme = "README.md"
license = "MIT"
Expand Down
Loading
Loading