11from __future__ import annotations
22
3+ from decimal import Decimal
34from typing import Any
45
6+ from upbeat ._auth import Credentials
57from upbeat ._base import _AsyncAPIResource , _SyncAPIResource
8+ from upbeat ._errors import ValidationError
9+ from upbeat ._http import AsyncTransport , SyncTransport
610from upbeat .types .order import (
711 CancelAndNewOrderResponse ,
812 CancelResult ,
@@ -20,7 +24,54 @@ def _filter_params(**kwargs: Any) -> dict[str, Any]:
2024 return {k : v for k , v in kwargs .items () if v is not None }
2125
2226
27+ def _compute_bid_total (
28+ price : str | None , volume : str | None , ord_type : str
29+ ) -> Decimal | None :
30+ """Return the total KRW value of a bid order, or None if indeterminate."""
31+ if price is None :
32+ return None
33+ if ord_type == "limit" :
34+ return Decimal (price ) * Decimal (volume ) if volume is not None else None
35+ return Decimal (price )
36+
37+
2338class OrdersAPI (_SyncAPIResource ):
39+ _validate_min_order : bool
40+
41+ def __init__ (
42+ self ,
43+ transport : SyncTransport ,
44+ credentials : Credentials | None ,
45+ * ,
46+ validate_min_order : bool = False ,
47+ ) -> None :
48+ super ().__init__ (transport , credentials )
49+ self ._validate_min_order = validate_min_order
50+
51+ def _check_min_order (
52+ self ,
53+ market : str ,
54+ side : str ,
55+ price : str | None ,
56+ volume : str | None ,
57+ ord_type : str ,
58+ ) -> None :
59+ if not self ._validate_min_order or side != "bid" :
60+ return
61+ total = _compute_bid_total (price , volume , ord_type )
62+ if total is None :
63+ return
64+ chance = self .get_chance (market = market )
65+ if chance .market .bid is not None :
66+ min_total = Decimal (chance .market .bid .min_total )
67+ if total < min_total :
68+ raise ValidationError (
69+ f"Order total { total } is below minimum { min_total } for { market } " ,
70+ market = market ,
71+ total = str (total ),
72+ min_total = chance .market .bid .min_total ,
73+ )
74+
2475 def create (
2576 self ,
2677 * ,
@@ -33,6 +84,7 @@ def create(
3384 time_in_force : str | None = None ,
3485 smp_type : str | None = None ,
3586 ) -> OrderCreated :
87+ self ._check_min_order (market , side , price , volume , ord_type )
3688 json_body = _filter_params (
3789 market = market ,
3890 side = side ,
@@ -60,6 +112,7 @@ def create_test(
60112 time_in_force : str | None = None ,
61113 smp_type : str | None = None ,
62114 ) -> OrderCreated :
115+ self ._check_min_order (market , side , price , volume , ord_type )
63116 json_body = _filter_params (
64117 market = market ,
65118 side = side ,
@@ -244,6 +297,42 @@ def get_chance(self, *, market: str) -> OrderChance:
244297
245298
246299class AsyncOrdersAPI (_AsyncAPIResource ):
300+ _validate_min_order : bool
301+
302+ def __init__ (
303+ self ,
304+ transport : AsyncTransport ,
305+ credentials : Credentials | None ,
306+ * ,
307+ validate_min_order : bool = False ,
308+ ) -> None :
309+ super ().__init__ (transport , credentials )
310+ self ._validate_min_order = validate_min_order
311+
312+ async def _check_min_order (
313+ self ,
314+ market : str ,
315+ side : str ,
316+ price : str | None ,
317+ volume : str | None ,
318+ ord_type : str ,
319+ ) -> None :
320+ if not self ._validate_min_order or side != "bid" :
321+ return
322+ total = _compute_bid_total (price , volume , ord_type )
323+ if total is None :
324+ return
325+ chance = await self .get_chance (market = market )
326+ if chance .market .bid is not None :
327+ min_total = Decimal (chance .market .bid .min_total )
328+ if total < min_total :
329+ raise ValidationError (
330+ f"Order total { total } is below minimum { min_total } for { market } " ,
331+ market = market ,
332+ total = str (total ),
333+ min_total = chance .market .bid .min_total ,
334+ )
335+
247336 async def create (
248337 self ,
249338 * ,
@@ -256,6 +345,7 @@ async def create(
256345 time_in_force : str | None = None ,
257346 smp_type : str | None = None ,
258347 ) -> OrderCreated :
348+ await self ._check_min_order (market , side , price , volume , ord_type )
259349 json_body = _filter_params (
260350 market = market ,
261351 side = side ,
@@ -283,6 +373,7 @@ async def create_test(
283373 time_in_force : str | None = None ,
284374 smp_type : str | None = None ,
285375 ) -> OrderCreated :
376+ await self ._check_min_order (market , side , price , volume , ord_type )
286377 json_body = _filter_params (
287378 market = market ,
288379 side = side ,
0 commit comments