pmquant 0.4.4__tar.gz → 0.4.5__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.
- pmquant-0.4.4/.hypothesis/constants/3687cdf4cf8f7af3 → pmquant-0.4.5/.hypothesis/constants/94e671d33554da02 +1 -1
- pmquant-0.4.5/.hypothesis/constants/e9435504a7ce4829 +4 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.hypothesis/unicode_data/15.0.0/charmap.json.gz +0 -0
- pmquant-0.4.5/.hypothesis/unicode_data/15.0.0/codec-utf-8.json.gz +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/CHANGELOG.md +13 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/PKG-INFO +9 -3
- {pmquant-0.4.4 → pmquant-0.4.5}/README.md +8 -2
- {pmquant-0.4.4 → pmquant-0.4.5}/docs/rounding-study.md +18 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/pyproject.toml +1 -1
- {pmquant-0.4.4 → pmquant-0.4.5}/src/pmq/__init__.py +1 -1
- {pmquant-0.4.4 → pmquant-0.4.5}/src/pmq/executor.py +32 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/tests/test_executor.py +14 -0
- pmquant-0.4.4/.hypothesis/constants/07a2a0eac57d1dd0 +0 -4
- pmquant-0.4.4/.hypothesis/unicode_data/15.0.0/codec-utf-8.json.gz +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.githooks/pre-push +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.github/dependabot.yml +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.github/workflows/canary.yml +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.github/workflows/codeql.yml +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.github/workflows/mcp-publish.yml +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.github/workflows/publish.yml +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.github/workflows/scorecard.yml +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.github/workflows/test.yml +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.gitignore +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.hypothesis/.gitignore +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.hypothesis/constants/6c9ffb0a1efc27b6 +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.hypothesis/constants/855d9c2e5b4693f1 +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/.hypothesis/constants/ef909bf87e6ac33f +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/AGENTS.md +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/CLAUDE.md +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/CONTRIBUTING.md +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/LICENSE +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/SECURITY.md +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/bot-template/README.md +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/bot-template/bot.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/bot-template/dash/bot_dash.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/bot-template/dash/dash.html +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/bot-template/pmq-bot.service +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/bot-template/strategy.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/docs/assets/pmq-doctor.svg +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/docs/recipes.md +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/docs/war-story.md +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/examples/fak_buy_guarded.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/examples/read_market.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/llms.txt +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/server.json +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/src/pmq/data.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/src/pmq/doctor.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/src/pmq/exceptions.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/src/pmq/mcp.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/src/pmq/py.typed +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/tests/test_canary_live.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/tests/test_data.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/tests/test_doctor.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/tests/test_fill_fuzz.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/tests/test_mcp.py +0 -0
- {pmquant-0.4.4 → pmquant-0.4.5}/tests/test_template_engine.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/pmq/pmq/src/pmq/__init__.py
|
|
2
2
|
# hypothesis_version: 6.156.1
|
|
3
3
|
|
|
4
|
-
['0.4.
|
|
4
|
+
['0.4.5', 'DEFAULT_BUILDER_CODE', 'FEE_RATES', 'Fill', 'OrderUncertain', 'PmqError', 'PolymarketExecutor', '__version__', 'band_ask_depth_usd', 'best_bid_ask', 'book_inferred_winner', 'book_meta', 'event_markets', 'fee', 'get_book', 'get_market', 'get_tape', 'http_get_json', 'parse_market', 'positions', 'resolved_winner']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/pmq/pmq/src/pmq/executor.py
|
|
2
|
+
# hypothesis_version: 6.156.1
|
|
3
|
+
|
|
4
|
+
[0.0, 0.933, 0.983, 0.985, 4.97, 5.35, 9.98, 10.13, 1000000.0, 100, 137, 300, 400, 500, 10000, '0', '0.01', '0x[0-9a-fA-F]{64}', '; ', 'AssetType', 'BUY', 'FAILED', 'FAK', 'MAKER', 'MarketOrderArgs', 'MarketOrderArgsV2', 'OpenOrderParams', 'OrderArgs', 'OrderArgsV2', 'OrderType', 'POLY_BUILDER_CODE', 'POLY_FUNDER', 'POLY_PRIVATE_KEY', 'POLY_SIG_TYPE', 'SELL', 'TradeParams', '_pmq_taker4', 'amount', 'balance', 'builder_code', 'cancel_market_orders', 'crypto', 'error_msg', 'fd', 'get_open_orders', 'get_trades', 'makingAmount', 'off', 'on', 'orderID', 'order_args', 'order_type', 'params', 'payload', 'pmq', 'price', 'r', 'side', 'size', 'status', 'status_code', 'success', 'takingAmount', 'token_id', 'trader_side']
|
|
Binary file
|
|
Binary file
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.5 (2026-07-04)
|
|
4
|
+
|
|
5
|
+
* Startup guard against the fine-tick market-order rejection class: the
|
|
6
|
+
introspection now exercises the installed builder's amount arithmetic
|
|
7
|
+
across every rounding config and refuses to construct an executor that
|
|
8
|
+
would sign a market pair above the exchange caps (2 decimal maker,
|
|
9
|
+
4 decimal taker). A client build that slips past the 0.4.3 clamp fails
|
|
10
|
+
at deploy time, before any order.
|
|
11
|
+
* Honesty pass after the 2026-07-04 production halt: the README rounding
|
|
12
|
+
bullet now states the fine-tick failure mode and its dates, and
|
|
13
|
+
docs/rounding-study.md gains an addendum scoping the July 3 conclusions
|
|
14
|
+
to ticks >= 0.01.
|
|
15
|
+
|
|
3
16
|
## 0.4.4 (2026-07-04)
|
|
4
17
|
|
|
5
18
|
* Harden: json.loads accepts NaN and Infinity, so a drifted or hostile
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pmquant
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5
|
|
4
4
|
Summary: Fail-closed execution and market-data layer for Polymarket CLOB V2: local signing, confirmed fills only, fee-correct math, working deposit-wallet (POLY_1271) support.
|
|
5
5
|
Project-URL: Homepage, https://github.com/crp4222/pmq
|
|
6
6
|
Project-URL: Issues, https://github.com/crp4222/pmq/issues
|
|
@@ -71,8 +71,14 @@ examples leave several traps undocumented. Every line of pmq was paid for with
|
|
|
71
71
|
a real error in live trading:
|
|
72
72
|
|
|
73
73
|
* `invalid amounts, the market buy orders maker amount supports a max accuracy
|
|
74
|
-
of 2 decimals`: the CLOB treats FAK/FOK
|
|
75
|
-
|
|
74
|
+
of 2 decimals, taker amount a max of 4 decimals`: the CLOB treats FAK/FOK
|
|
75
|
+
buys as **market orders** and caps their signed amounts at 2 decimals
|
|
76
|
+
(maker) / 4 decimals (taker) whatever the tick size. The official client's
|
|
77
|
+
rounding table allows 5-6 taker decimals on ticks finer than 0.01, so every
|
|
78
|
+
market order there is rejected; this cost us a production halt on
|
|
79
|
+
2026-07-04. pmq clamps the signed pair to the exchange caps (0.4.3) and
|
|
80
|
+
refuses at startup any client build that would still sign a rejectable
|
|
81
|
+
pair (0.4.5).
|
|
76
82
|
* `no orders found to match with FAK order` (HTTP 400, yet with an `orderID`):
|
|
77
83
|
a clean no-fill, not an error. pmq returns an empty `Fill` instead of crashing
|
|
78
84
|
or, worse, retrying blindly.
|
|
@@ -36,8 +36,14 @@ examples leave several traps undocumented. Every line of pmq was paid for with
|
|
|
36
36
|
a real error in live trading:
|
|
37
37
|
|
|
38
38
|
* `invalid amounts, the market buy orders maker amount supports a max accuracy
|
|
39
|
-
of 2 decimals`: the CLOB treats FAK/FOK
|
|
40
|
-
|
|
39
|
+
of 2 decimals, taker amount a max of 4 decimals`: the CLOB treats FAK/FOK
|
|
40
|
+
buys as **market orders** and caps their signed amounts at 2 decimals
|
|
41
|
+
(maker) / 4 decimals (taker) whatever the tick size. The official client's
|
|
42
|
+
rounding table allows 5-6 taker decimals on ticks finer than 0.01, so every
|
|
43
|
+
market order there is rejected; this cost us a production halt on
|
|
44
|
+
2026-07-04. pmq clamps the signed pair to the exchange caps (0.4.3) and
|
|
45
|
+
refuses at startup any client build that would still sign a rejectable
|
|
46
|
+
pair (0.4.5).
|
|
41
47
|
* `no orders found to match with FAK order` (HTTP 400, yet with an `orderID`):
|
|
42
48
|
a clean no-fill, not an error. pmq returns an empty `Fill` instead of crashing
|
|
43
49
|
or, worse, retrying blindly.
|
|
@@ -50,3 +50,21 @@ amounts against requested size, cross-checked with `get_trades`.
|
|
|
50
50
|
4. The per-market minimum size is enforced server-side with a clean 400, and
|
|
51
51
|
is readable in advance from the book response (`min_order_size`, exposed
|
|
52
52
|
by `pmq.book_meta`).
|
|
53
|
+
|
|
54
|
+
## Addendum (2026-07-04): fine-tick market orders
|
|
55
|
+
|
|
56
|
+
The study above ran on a 0.01-tick market and point 1 turns out to hold
|
|
57
|
+
only there. On 2026-07-04 a 0.001-tick market rejected every market buy
|
|
58
|
+
with `invalid amounts ... taker amount a max of 4 decimals`: the client's
|
|
59
|
+
ROUNDING_CONFIG allows amount decimals = price decimals + 2 (5 for tick
|
|
60
|
+
0.001, 6 for 0.0025 and 0.0001). That is right for LIMIT orders, whose
|
|
61
|
+
amounts are exact price×size products, but MARKET-order takers are capped
|
|
62
|
+
by the server at a flat 4 decimals whatever the tick, so on fine ticks the
|
|
63
|
+
maker/price division leaves a 5th decimal and the order can never be
|
|
64
|
+
accepted. Normalization still happens client-side; it just normalizes to a
|
|
65
|
+
precision the server refuses.
|
|
66
|
+
|
|
67
|
+
pmq 0.4.3 clamps the market path to 4 decimals (round-down: the budget
|
|
68
|
+
contract is intact, the dust given up is under 0.0001 share). pmq 0.4.5
|
|
69
|
+
also refuses at startup any client build that would still sign such a
|
|
70
|
+
pair, so this class of failure stops at deploy time, not at trade time.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pmquant"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.5"
|
|
8
8
|
description = "Fail-closed execution and market-data layer for Polymarket CLOB V2: local signing, confirmed fills only, fee-correct math, working deposit-wallet (POLY_1271) support."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -25,7 +25,7 @@ from .data import (
|
|
|
25
25
|
)
|
|
26
26
|
from .exceptions import IntrospectionMismatch, OrderUncertain, PmqError
|
|
27
27
|
|
|
28
|
-
__version__ = "0.4.
|
|
28
|
+
__version__ = "0.4.5"
|
|
29
29
|
__all__ = [
|
|
30
30
|
"FEE_RATES", "band_ask_depth_usd", "best_bid_ask", "book_inferred_winner",
|
|
31
31
|
"book_meta", "event_markets", "fee", "get_book", "get_market", "get_tape",
|
|
@@ -218,8 +218,40 @@ class PolymarketExecutor:
|
|
|
218
218
|
drifts += [f"{label} lost field {p}" for p in expected if p not in have]
|
|
219
219
|
if not hasattr(self._t["OrderType"], "FAK"):
|
|
220
220
|
drifts.append("OrderType.FAK missing")
|
|
221
|
+
drifts += self._amount_precision_drifts()
|
|
221
222
|
return drifts
|
|
222
223
|
|
|
224
|
+
def _amount_precision_drifts(self) -> list[str]:
|
|
225
|
+
"""Behavioral guard: the exchange caps signed MARKET-order amounts at
|
|
226
|
+
2 decimals (maker) and 4 (taker) whatever the tick size. Exercise the
|
|
227
|
+
installed builder's arithmetic on awkward pairs across every rounding
|
|
228
|
+
config; a client build that would sign a rejectable pair is refused
|
|
229
|
+
here, at startup, instead of failing on the first fine-tick order."""
|
|
230
|
+
try:
|
|
231
|
+
from py_clob_client_v2.order_builder.builder import (
|
|
232
|
+
ROUNDING_CONFIG,
|
|
233
|
+
OrderBuilder,
|
|
234
|
+
)
|
|
235
|
+
from py_clob_client_v2.order_builder.constants import BUY, SELL
|
|
236
|
+
except ImportError:
|
|
237
|
+
return []
|
|
238
|
+
out: set[str] = set()
|
|
239
|
+
probe = object.__new__(OrderBuilder)
|
|
240
|
+
cases = ((BUY, 9.98, 0.985), (BUY, 4.97, 0.983),
|
|
241
|
+
(SELL, 10.13, 0.985), (SELL, 5.35, 0.933))
|
|
242
|
+
for tick, rc in ROUNDING_CONFIG.items():
|
|
243
|
+
for side, amount, price in cases:
|
|
244
|
+
try:
|
|
245
|
+
_, mk, tk = OrderBuilder.get_market_order_amounts(
|
|
246
|
+
probe, side, amount, price, rc)
|
|
247
|
+
except Exception as e:
|
|
248
|
+
out.add(f"market amounts builder failed at tick {tick}: {e}")
|
|
249
|
+
continue
|
|
250
|
+
if int(mk) % 10_000 or int(tk) % 100:
|
|
251
|
+
out.add(f"market order would sign >2dp maker or >4dp taker "
|
|
252
|
+
f"at tick {tick}")
|
|
253
|
+
return sorted(out)
|
|
254
|
+
|
|
223
255
|
def _verify_client_surface(self) -> None:
|
|
224
256
|
drifts = self._surface_drifts()
|
|
225
257
|
if drifts:
|
|
@@ -362,3 +362,17 @@ def test_market_taker_amounts_clamped_to_4dp_on_fine_ticks():
|
|
|
362
362
|
assert int(tk) % 10**2 == 0, f"taker >4dp at tick {tick}"
|
|
363
363
|
_, mk_s, tk_s = fn(b, SELL, 10.13, 0.985, ROUNDING_CONFIG[tick])
|
|
364
364
|
assert int(mk_s) % 10**4 == 0 and int(tk_s) % 10**2 == 0
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def test_startup_refuses_client_signing_dirty_market_amounts(monkeypatch):
|
|
368
|
+
"""If a client build slips past the 4dp clamp (new code path, future
|
|
369
|
+
regression), the startup introspection must refuse to trade at all."""
|
|
370
|
+
from py_clob_client_v2.order_builder import builder as b
|
|
371
|
+
|
|
372
|
+
def dirty(self, side, amount, price, round_config):
|
|
373
|
+
return side, 9980000, 10131970 # taker 10.13197: 5 decimals
|
|
374
|
+
|
|
375
|
+
dirty._pmq_taker4 = True # defeat the pmq wrapper
|
|
376
|
+
monkeypatch.setattr(b.OrderBuilder, "get_market_order_amounts", dirty)
|
|
377
|
+
with pytest.raises(IntrospectionMismatch):
|
|
378
|
+
make(FakeClient())
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
# file: /home/runner/work/pmq/pmq/src/pmq/executor.py
|
|
2
|
-
# hypothesis_version: 6.156.1
|
|
3
|
-
|
|
4
|
-
[0.0, 1000000.0, 137, 300, 400, 500, '0', '0.01', '0x[0-9a-fA-F]{64}', '; ', 'AssetType', 'BUY', 'FAILED', 'FAK', 'MAKER', 'MarketOrderArgs', 'MarketOrderArgsV2', 'OpenOrderParams', 'OrderArgs', 'OrderArgsV2', 'OrderType', 'POLY_BUILDER_CODE', 'POLY_FUNDER', 'POLY_PRIVATE_KEY', 'POLY_SIG_TYPE', 'SELL', 'TradeParams', '_pmq_taker4', 'amount', 'balance', 'builder_code', 'cancel_market_orders', 'crypto', 'error_msg', 'fd', 'get_open_orders', 'get_trades', 'makingAmount', 'off', 'on', 'orderID', 'order_args', 'order_type', 'params', 'payload', 'pmq', 'price', 'r', 'side', 'size', 'status', 'status_code', 'success', 'takingAmount', 'token_id', 'trader_side']
|
|
Binary file
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|