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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,23 @@ do not amend them.
| `portfolio.positions(params?)` | Get trading positions |
| `portfolio.activities(params?)` | Get activity history |

Filter activity history by time and reward type:

```python
activities = client.portfolio.activities(
{
"startTime": "2026-10-01T00:00:00Z",
"endTime": "2026-10-02T00:00:00Z",
"types": ["ACTIVITY_TYPE_TAKER_FEE_REBATE", "ACTIVITY_TYPE_LIQUIDITY_PROGRAM"],
}
)
```

Both time filters are optional RFC 3339 strings and include their endpoints.
When supplied together, `startTime` must be before `endTime`. For later pages,
pass `nextCursor` as `cursor` with the same filters until `eof` is true.
The async client accepts the same parameters with `await`.

### Account (Authenticated)

| Method | Description |
Expand Down
4 changes: 4 additions & 0 deletions polymarket_us/types/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"ACTIVITY_TYPE_ACCOUNT_WITHDRAWAL",
"ACTIVITY_TYPE_REFERRAL_BONUS",
"ACTIVITY_TYPE_TRANSFER",
"ACTIVITY_TYPE_TAKER_FEE_REBATE",
"ACTIVITY_TYPE_LIQUIDITY_PROGRAM",
]

SortOrder = Literal["SORT_ORDER_DESCENDING", "SORT_ORDER_ASCENDING"]
Expand Down Expand Up @@ -122,6 +124,8 @@ class GetActivitiesParams(TypedDict, total=False):
marketSlug: str
types: list[ActivityType]
sortOrder: SortOrder
startTime: str
endTime: str


class GetActivitiesResponse(TypedDict, total=False):
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.4.1"
version = "2.4.2"
description = "Polymarket US Python SDK"
readme = "README.md"
license = "MIT"
Expand Down
67 changes: 67 additions & 0 deletions tests/test_activity_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Activity filters pass through both clients without losing timestamp or type values."""

from copy import deepcopy

import pytest
from pytest_httpx import HTTPXMock

from polymarket_us import AsyncPolymarketUS, PolymarketUS
from polymarket_us.types import GetActivitiesParams, GetActivitiesResponse
from tests.types.activity_history import activities, params

TEST_SECRET_KEY = "nWGxne/9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A="

QUERIES: list[GetActivitiesParams | None] = [
None,
{
"startTime": "2026-10-01T00:00:00.123456789Z",
"types": ["ACTIVITY_TYPE_TAKER_FEE_REBATE"],
},
{
"endTime": "2026-10-02T00:00:00.123456789+05:30",
"types": ["ACTIVITY_TYPE_LIQUIDITY_PROGRAM"],
},
params,
]


@pytest.mark.parametrize("asynchronous", [False, True], ids=["sync", "async"])
@pytest.mark.parametrize("query", QUERIES, ids=["omitted", "start-only", "end-only", "combined"])
async def test_activity_history_filters(
httpx_mock: HTTPXMock, asynchronous: bool, query: GetActivitiesParams | None
) -> None:
response: GetActivitiesResponse = {
"activities": activities,
"nextCursor": "next+/=cursor",
"eof": False,
}
httpx_mock.add_response(json=response)
original_query = deepcopy(query)

if asynchronous:
async with AsyncPolymarketUS(
key_id="offline", secret_key=TEST_SECRET_KEY, api_base_url="https://api.test"
) as client:
result = await client.portfolio.activities(query)
else:
with PolymarketUS(
key_id="offline", secret_key=TEST_SECRET_KEY, api_base_url="https://api.test"
) as sync_client:
result = sync_client.portfolio.activities(query)

assert result == response
assert query == original_query
requests = httpx_mock.get_requests()
assert len(requests) == 1
request = requests[0]
assert request.method == "GET"
assert request.url.host == "api.test"
assert request.url.path == "/v1/portfolio/activities"
assert request.content == b""
assert request.headers["X-PM-Access-Key"] == "offline"
expected = original_query or {}
assert set(request.url.params) == set(expected)
for key, value in expected.items():
assert request.url.params.get_list(key) == (
value if isinstance(value, list) else [str(value)]
)
27 changes: 27 additions & 0 deletions tests/types/activity_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Typed consumers of activity time filters and reward categories."""

from polymarket_us import AsyncPolymarketUS, PolymarketUS
from polymarket_us.types import Activity, ActivityType, GetActivitiesParams, GetActivitiesResponse

reward_types: list[ActivityType] = [
"ACTIVITY_TYPE_TAKER_FEE_REBATE",
"ACTIVITY_TYPE_LIQUIDITY_PROGRAM",
]
params: GetActivitiesParams = {
"startTime": "2026-10-01T00:00:00.123456789Z",
"endTime": "2026-10-02T00:00:00.123456789+00:00",
"types": reward_types,
"limit": 2,
"cursor": "opaque+/=cursor",
"marketSlug": "example-market",
"sortOrder": "SORT_ORDER_ASCENDING",
}
activities: list[Activity] = [{"type": value} for value in reward_types]


def history(client: PolymarketUS) -> GetActivitiesResponse:
return client.portfolio.activities(params)


async def async_history(client: AsyncPolymarketUS) -> GetActivitiesResponse:
return await client.portfolio.activities(params)
Loading