Skip to content
Open
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
22 changes: 21 additions & 1 deletion src/polymarket/_internal/actions/gamma.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,26 @@ def _coerce_timestamp_filter(value: TimestampFilter | None) -> str | None:
return value.isoformat()


# The service keeps market ``volume`` and ``liquidity`` as text columns (a leftover
# of the original ingestion writing ``toFixed(2)`` strings) next to numeric twins
# holding the same value, and orders by the raw column, so ``order="volume"`` sorts
# lexicographically. Callers mean the number: each token is sent as its numeric
# twin. The service already does this itself for ``liquidity``, so that entry is
# idempotent with upstream and only ``volume`` is load-bearing today. Every other
# token is forwarded untouched; unknown names are still rejected by the service.
_MARKET_ORDER_ALIASES: dict[str, str] = {
"volume": "volumeNum",
"liquidity": "liquidityNum",
}


def _normalize_market_order(order: str | None) -> str | None:
if order is None:
return None
tokens = [token.strip() for token in order.split(",")]
return ",".join(_MARKET_ORDER_ALIASES.get(token, token) for token in tokens)


def _add_optional_seq(
params: dict[str, QueryParamValue],
key: str,
Expand Down Expand Up @@ -550,7 +570,7 @@ def list_markets_spec(
_add_optional(params, "liquidity_num_min", liquidity_num_min)
_add_optional(params, "locale", locale)
_add_optional_seq(params, "market_maker_address", market_maker_addresses)
_add_optional(params, "order", order)
_add_optional(params, "order", _normalize_market_order(order))
_add_optional_seq(params, "position_ids", position_ids)
_add_optional_seq(params, "question_ids", question_ids)
_add_optional(params, "related_tags", related_tags)
Expand Down
7 changes: 7 additions & 0 deletions src/polymarket/clients/async_public.py
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,13 @@ def list_markets(
Markets that cannot be represented by the binary Market model are
omitted from results.

``order`` takes one or more comma-separated market field names, with
``ascending`` setting the direction (for example ``"volume"``,
``"liquidity"``, ``"volume24hr"``, ``"startDate"``, ``"endDate"``,
``"createdAt"``, ``"slug"``). ``"volume"`` and ``"liquidity"`` sort by
their numeric values. A cursor obtained from an earlier release with
``order="volume"`` or ``order="liquidity"`` cannot be resumed.

Returns:
An async paginator over matching markets.

Expand Down
7 changes: 7 additions & 0 deletions src/polymarket/clients/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,13 @@ def list_markets(
Markets that cannot be represented by the binary Market model are
omitted from results.

``order`` takes one or more comma-separated market field names, with
``ascending`` setting the direction (for example ``"volume"``,
``"liquidity"``, ``"volume24hr"``, ``"startDate"``, ``"endDate"``,
``"createdAt"``, ``"slug"``). ``"volume"`` and ``"liquidity"`` sort by
their numeric values. A cursor obtained from an earlier release with
``order="volume"`` or ``order="liquidity"`` cannot be resumed.

Returns:
A paginator over matching markets.

Expand Down
47 changes: 47 additions & 0 deletions tests/unit/test_gamma_paginated_specs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

from polymarket._internal.actions import gamma as gamma_actions
from polymarket._internal.pagination import fingerprint_query
from polymarket._internal.request import (
KeysetPaginatedSpec,
OffsetPaginatedSpec,
Expand Down Expand Up @@ -85,6 +86,52 @@ def test_list_markets_spec_collects_array_params() -> None:
}


@pytest.mark.parametrize(
("order", "expected"),
[
("volume", "volumeNum"),
("liquidity", "liquidityNum"),
("volume,id", "volumeNum,id"),
("createdAt, volume", "createdAt,volumeNum"),
],
)
def test_list_markets_spec_sends_numeric_twins_for_text_sorted_fields(
order: str, expected: str
) -> None:
spec = gamma_actions.list_markets_spec(order=order)

assert spec.base_params == {"order": expected}


@pytest.mark.parametrize("order", ["volumeNum", "liquidityNum", "volume24hr", "startDate", "id"])
def test_list_markets_spec_forwards_other_order_fields_unchanged(order: str) -> None:
spec = gamma_actions.list_markets_spec(order=order, ascending=False)

assert spec.base_params == {"ascending": False, "order": order}


def test_list_markets_spec_order_alias_keeps_cursors_interchangeable() -> None:
# Pagination cursors carry a fingerprint of the query; a cursor issued while
# ordering by "volume" must resume when the caller spells it "volumeNum".
aliased = gamma_actions.list_markets_spec(order="volume", closed=False)
explicit = gamma_actions.list_markets_spec(order="volumeNum", closed=False)

assert fingerprint_query(aliased.base_params) == fingerprint_query(explicit.base_params)


def test_list_markets_spec_order_alias_leaves_empty_tokens_alone() -> None:
spec = gamma_actions.list_markets_spec(order="volume,")

assert spec.base_params == {"order": "volumeNum,"}


def test_list_events_spec_does_not_alias_order() -> None:
# Events store volume as a number upstream, so the alias is markets-only.
spec = gamma_actions.list_events_spec(order="volume")

assert spec.base_params == {"closed": False, "order": "volume"}


def test_list_markets_parser_skips_non_binary_markets_and_keeps_cursor() -> None:
spec = gamma_actions.list_markets_spec()

Expand Down
Loading