diff --git a/src/polymarket/_internal/actions/orders/limit.py b/src/polymarket/_internal/actions/orders/limit.py index c2c4b7e..03edbbd 100644 --- a/src/polymarket/_internal/actions/orders/limit.py +++ b/src/polymarket/_internal/actions/orders/limit.py @@ -64,7 +64,7 @@ def validate_limit_order_params( if expiration < 0: raise UserInputError("expiration must be a non-negative integer.") minimum = int(time.time()) + _MIN_EXPIRATION_BUFFER_S - if expiration <= minimum: + if expiration < minimum: raise UserInputError( f"expiration must be at least {_MIN_EXPIRATION_BUFFER_S} seconds in the future." ) diff --git a/src/polymarket/clients/async_secure.py b/src/polymarket/clients/async_secure.py index 447ade1..ad2b662 100644 --- a/src/polymarket/clients/async_secure.py +++ b/src/polymarket/clients/async_secure.py @@ -1595,6 +1595,10 @@ async def create_limit_order( Use :meth:`post_order` to submit the returned signed order, or :meth:`place_limit_order` to create and post in one call. + + When ``expiration`` is provided, it must be a Unix timestamp at least + 60 seconds in the future. Use extra buffer for immediate submissions to + account for latency and clock skew. """ params = validate_limit_order_params( token_id=token_id, @@ -1690,7 +1694,12 @@ async def place_limit_order( expiration: int | None = None, builder_code: str | None = None, ) -> OrderResponse: - """Create, sign, and post a limit order.""" + """Create, sign, and post a limit order. + + When ``expiration`` is provided, it must be a Unix timestamp at least + 60 seconds in the future. Use extra buffer for immediate submissions to + account for latency and clock skew. + """ signed = await self.create_limit_order( token_id=token_id, price=price, diff --git a/src/polymarket/clients/secure.py b/src/polymarket/clients/secure.py index 8ab9aac..ef978a1 100644 --- a/src/polymarket/clients/secure.py +++ b/src/polymarket/clients/secure.py @@ -1460,6 +1460,10 @@ def create_limit_order( Use :meth:`post_order` to submit the returned signed order, or :meth:`place_limit_order` to create and post in one call. + When ``expiration`` is provided, it must be a Unix timestamp at least + 60 seconds in the future. Use extra buffer for immediate submissions to + account for latency and clock skew. + Raises: UserInputError: If order parameters are invalid. SigningError: If the order cannot be signed. @@ -1540,6 +1544,10 @@ def place_limit_order( ) -> OrderResponse: """Create, sign, and post a limit order. + When ``expiration`` is provided, it must be a Unix timestamp at least + 60 seconds in the future. Use extra buffer for immediate submissions to + account for latency and clock skew. + Raises: UserInputError: If order parameters are invalid. InsufficientAllowanceError: If required allowance cannot be recovered. diff --git a/tests/unit/test_order_limit.py b/tests/unit/test_order_limit.py index 570d119..a21f1a8 100644 --- a/tests/unit/test_order_limit.py +++ b/tests/unit/test_order_limit.py @@ -164,6 +164,36 @@ def test_validate_limit_order_params_rejects_near_expiration() -> None: ) +def test_validate_limit_order_params_rejects_expiration_below_minimum( + monkeypatch: pytest.MonkeyPatch, +) -> None: + now = 1_700_000_000 + monkeypatch.setattr("polymarket._internal.actions.orders.limit.time.time", lambda: now) + with pytest.raises(UserInputError, match="60 seconds"): + validate_limit_order_params( + token_id="8501497", + price="0.5", + size="10", + side="BUY", + expiration=now + 59, + ) + + +def test_validate_limit_order_params_accepts_minimum_expiration_boundary( + monkeypatch: pytest.MonkeyPatch, +) -> None: + now = 1_700_000_000 + monkeypatch.setattr("polymarket._internal.actions.orders.limit.time.time", lambda: now) + params = validate_limit_order_params( + token_id="8501497", + price="0.5", + size="10", + side="BUY", + expiration=now + 60, + ) + assert params.expiration == now + 60 + + def test_validate_limit_order_params_accepts_far_expiration() -> None: params = validate_limit_order_params( token_id="8501497",