pmwallets 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pmwallets-0.3.0 → pmwallets-0.4.0}/PKG-INFO +2 -2
- {pmwallets-0.3.0 → pmwallets-0.4.0}/README.md +1 -1
- {pmwallets-0.3.0 → pmwallets-0.4.0}/README.zh.md +1 -1
- {pmwallets-0.3.0 → pmwallets-0.4.0}/pyproject.toml +1 -1
- {pmwallets-0.3.0 → pmwallets-0.4.0}/src/pmwallets/client.py +11 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/tests/test_client.py +14 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/uv.lock +1 -1
- {pmwallets-0.3.0 → pmwallets-0.4.0}/.github/workflows/ci.yml +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/.github/workflows/publish.yml +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/.gitignore +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/LICENSE +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/src/pmwallets/__init__.py +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/src/pmwallets/stream.py +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/src/pmwallets/types.py +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/src/pmwallets/webhook.py +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/tests/test_real_socket.py +0 -0
- {pmwallets-0.3.0 → pmwallets-0.4.0}/tests/test_stream.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pmwallets
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Official PMWallets SDK: Polymarket smart-money leaderboard, real-time wallet fills over WebSocket with gap replay, webhooks and exports.
|
|
5
5
|
Project-URL: Homepage, https://pmwallets.com/docs
|
|
6
6
|
Project-URL: Repository, https://github.com/polymarketwallets/pmwallets-python
|
|
@@ -65,7 +65,7 @@ asyncio.run(main())
|
|
|
65
65
|
|
|
66
66
|
| | |
|
|
67
67
|
|---|---|
|
|
68
|
-
| `Client` / `AsyncClient` | leaderboard, entities, address unlocks, subscriptions, fills replay (`fills`, `fills_since`), trade-history exports |
|
|
68
|
+
| `Client` / `AsyncClient` | leaderboard, entities, address unlocks, your unlocked addresses (`my_addresses`), subscriptions, fills replay (`fills`, `fills_since`), trade-history exports |
|
|
69
69
|
| `FillStream` | asyncio WebSocket with reconnect and keep-alive; detects missed frames by `session`/`seq` and replays them from the last fill delivered; de-duplicates by `eventId`; anchors behind the chain head on first start; persisted cursor |
|
|
70
70
|
| `verify_webhook(raw_body, signature, secret)` | checks `x-pmw-signature` (HMAC-SHA256 of the raw body) |
|
|
71
71
|
|
|
@@ -47,7 +47,7 @@ asyncio.run(main())
|
|
|
47
47
|
|
|
48
48
|
| | |
|
|
49
49
|
|---|---|
|
|
50
|
-
| `Client` / `AsyncClient` | leaderboard, entities, address unlocks, subscriptions, fills replay (`fills`, `fills_since`), trade-history exports |
|
|
50
|
+
| `Client` / `AsyncClient` | leaderboard, entities, address unlocks, your unlocked addresses (`my_addresses`), subscriptions, fills replay (`fills`, `fills_since`), trade-history exports |
|
|
51
51
|
| `FillStream` | asyncio WebSocket with reconnect and keep-alive; detects missed frames by `session`/`seq` and replays them from the last fill delivered; de-duplicates by `eventId`; anchors behind the chain head on first start; persisted cursor |
|
|
52
52
|
| `verify_webhook(raw_body, signature, secret)` | checks `x-pmw-signature` (HMAC-SHA256 of the raw body) |
|
|
53
53
|
|
|
@@ -20,7 +20,7 @@ pip install pmwallets # Python ≥ 3.10
|
|
|
20
20
|
|
|
21
21
|
| | |
|
|
22
22
|
|---|---|
|
|
23
|
-
| `Client` / `AsyncClient` |
|
|
23
|
+
| `Client` / `AsyncClient` | 排行榜、实体、地址解锁、我的地址(`my_addresses`)、订阅、成交补发(`fills`、`fills_since`)、交易历史导出 |
|
|
24
24
|
| `FillStream` | asyncio WebSocket,自动重连与保活;按 `session`/`seq` 发现漏帧并从最后送达的成交补发;按 `eventId` 去重;首次启动锚定在链头之后;游标持久化 |
|
|
25
25
|
| `verify_webhook(raw_body, signature, secret)` | 校验 `x-pmw-signature`(原始 body 的 HMAC-SHA256) |
|
|
26
26
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pmwallets"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Official PMWallets SDK: Polymarket smart-money leaderboard, real-time wallet fills over WebSocket with gap replay, webhooks and exports."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -102,6 +102,13 @@ class Client(_Base):
|
|
|
102
102
|
def leaderboard(self, **query: Any) -> Leaderboard:
|
|
103
103
|
return self.request("GET", "/v1/leaderboard", query)
|
|
104
104
|
|
|
105
|
+
def my_addresses(self, **query: Any) -> Leaderboard:
|
|
106
|
+
"""Every address you have unlocked (bought, free, or with your plan) as board rows. Each row has
|
|
107
|
+
`unlockedBy` ('paid' / 'free' / 'plan', or None when a free or plan unlock lapsed and is masked again)
|
|
108
|
+
and `pausedReason` when your push subscription to it is paused; `outside` (first page only) lists
|
|
109
|
+
unlocked addresses with no fill in the window. Spends no board rows."""
|
|
110
|
+
return self.request("GET", "/v1/leaderboard", {**query, "mine": 1})
|
|
111
|
+
|
|
105
112
|
def entity(self, entity_id: str, period: Optional[str] = None) -> dict[str, Any]:
|
|
106
113
|
"""One entity by handle or address. The full address comes back only for entities you own."""
|
|
107
114
|
return self.request("GET", f"/v1/entities/{_q(entity_id)}", {"period": period})
|
|
@@ -205,6 +212,10 @@ class AsyncClient(_Base):
|
|
|
205
212
|
async def leaderboard(self, **query: Any) -> Leaderboard:
|
|
206
213
|
return await self.request("GET", "/v1/leaderboard", query)
|
|
207
214
|
|
|
215
|
+
async def my_addresses(self, **query: Any) -> Leaderboard:
|
|
216
|
+
"""See Client.my_addresses."""
|
|
217
|
+
return await self.request("GET", "/v1/leaderboard", {**query, "mine": 1})
|
|
218
|
+
|
|
208
219
|
async def entity(self, entity_id: str, period: Optional[str] = None) -> dict[str, Any]:
|
|
209
220
|
return await self.request("GET", f"/v1/entities/{_q(entity_id)}", {"period": period})
|
|
210
221
|
|
|
@@ -38,6 +38,20 @@ async def test_walks_every_page():
|
|
|
38
38
|
assert "sinceBlock=2&sinceLogIndex=0&limit=2" in str(calls[1].url)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def test_my_addresses_asks_the_board_for_mine():
|
|
42
|
+
calls = []
|
|
43
|
+
c = Client("k", "https://api.example.com", http=httpx.Client(transport=transport([{"periodId": "30d", "tracked": 2, "rows": []}], calls=calls)))
|
|
44
|
+
assert c.my_addresses(sort="realizedPnl", offset=15)["tracked"] == 2
|
|
45
|
+
assert str(calls[0].url) == "https://api.example.com/v1/leaderboard?sort=realizedPnl&offset=15&mine=1"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def test_async_my_addresses():
|
|
49
|
+
calls = []
|
|
50
|
+
c = AsyncClient("k", "https://api.example.com", http=httpx.AsyncClient(transport=transport([{"tracked": 0, "rows": []}], calls=calls)))
|
|
51
|
+
await c.my_addresses()
|
|
52
|
+
assert str(calls[0].url) == "https://api.example.com/v1/leaderboard?mine=1"
|
|
53
|
+
|
|
54
|
+
|
|
41
55
|
def test_non_2xx_is_pmw_error():
|
|
42
56
|
calls = []
|
|
43
57
|
c = Client("k", http=httpx.Client(transport=transport([{"statusCode": 402, "message": "insufficient balance"}], 402, calls)))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|