|
|
@@ -3,7 +3,8 @@ import hashlib
|
|
|
import hmac
|
|
|
import json
|
|
|
from datetime import UTC, datetime
|
|
|
-from decimal import Decimal, ROUND_DOWN
|
|
|
+from decimal import Decimal, InvalidOperation, ROUND_DOWN
|
|
|
+from math import isfinite
|
|
|
from typing import TypeAlias
|
|
|
from urllib.parse import urlencode
|
|
|
|
|
|
@@ -14,14 +15,34 @@ from okx_codex_trader.models import Candle, InstrumentMeta, OrderResult, Positio
|
|
|
OkxRow: TypeAlias = dict[str, object] | list[object]
|
|
|
|
|
|
|
|
|
+def _parse_finite_decimal(value: object) -> Decimal:
|
|
|
+ try:
|
|
|
+ parsed = Decimal(str(value))
|
|
|
+ except (InvalidOperation, TypeError, ValueError):
|
|
|
+ raise ValueError("contract sizing inputs are invalid") from None
|
|
|
+ if not parsed.is_finite():
|
|
|
+ raise ValueError("contract sizing inputs are invalid")
|
|
|
+ return parsed
|
|
|
+
|
|
|
+
|
|
|
+def _parse_finite_float(value: object) -> float:
|
|
|
+ try:
|
|
|
+ parsed = float(value)
|
|
|
+ except (TypeError, ValueError):
|
|
|
+ raise ValueError("okx response payload is invalid") from None
|
|
|
+ if not isfinite(parsed):
|
|
|
+ raise ValueError("okx response payload is invalid")
|
|
|
+ return parsed
|
|
|
+
|
|
|
+
|
|
|
def build_contract_size(notional: float, price: float, metadata: InstrumentMeta) -> float:
|
|
|
- price_decimal = Decimal(str(price))
|
|
|
- ct_val_decimal = Decimal(str(metadata.ct_val))
|
|
|
- lot_size = Decimal(str(metadata.lot_sz))
|
|
|
- min_size = Decimal(str(metadata.min_sz))
|
|
|
+ price_decimal = _parse_finite_decimal(price)
|
|
|
+ ct_val_decimal = _parse_finite_decimal(metadata.ct_val)
|
|
|
+ lot_size = _parse_finite_decimal(metadata.lot_sz)
|
|
|
+ min_size = _parse_finite_decimal(metadata.min_sz)
|
|
|
if price_decimal <= 0 or ct_val_decimal <= 0 or lot_size <= 0 or min_size <= 0:
|
|
|
raise ValueError("contract sizing inputs are invalid")
|
|
|
- raw_size = Decimal(str(notional)) / (price_decimal * ct_val_decimal)
|
|
|
+ raw_size = _parse_finite_decimal(notional) / (price_decimal * ct_val_decimal)
|
|
|
size = (raw_size / lot_size).to_integral_value(rounding=ROUND_DOWN) * lot_size
|
|
|
if size < min_size:
|
|
|
raise ValueError("contract size below minimum")
|
|
|
@@ -121,11 +142,11 @@ class OkxClient:
|
|
|
Candle(
|
|
|
symbol=symbol,
|
|
|
ts=int(entry[0]),
|
|
|
- open=float(entry[1]),
|
|
|
- high=float(entry[2]),
|
|
|
- low=float(entry[3]),
|
|
|
- close=float(entry[4]),
|
|
|
- volume=float(entry[5]),
|
|
|
+ open=_parse_finite_float(entry[1]),
|
|
|
+ high=_parse_finite_float(entry[2]),
|
|
|
+ low=_parse_finite_float(entry[3]),
|
|
|
+ close=_parse_finite_float(entry[4]),
|
|
|
+ volume=_parse_finite_float(entry[5]),
|
|
|
)
|
|
|
for entry in data
|
|
|
]
|
|
|
@@ -142,9 +163,9 @@ class OkxClient:
|
|
|
instrument = self._first_item(data)
|
|
|
try:
|
|
|
return InstrumentMeta(
|
|
|
- ct_val=float(instrument["ctVal"]),
|
|
|
- lot_sz=float(instrument["lotSz"]),
|
|
|
- min_sz=float(instrument["minSz"]),
|
|
|
+ ct_val=_parse_finite_float(instrument["ctVal"]),
|
|
|
+ lot_sz=_parse_finite_float(instrument["lotSz"]),
|
|
|
+ min_sz=_parse_finite_float(instrument["minSz"]),
|
|
|
)
|
|
|
except (KeyError, TypeError, ValueError):
|
|
|
raise self._invalid_payload() from None
|
|
|
@@ -153,7 +174,7 @@ class OkxClient:
|
|
|
data = self._request("GET", "/api/v5/market/ticker", params={"instId": symbol})
|
|
|
ticker = self._first_item(data)
|
|
|
try:
|
|
|
- return float(ticker["last"])
|
|
|
+ return _parse_finite_float(ticker["last"])
|
|
|
except (KeyError, TypeError, ValueError):
|
|
|
raise self._invalid_payload() from None
|
|
|
|
|
|
@@ -232,7 +253,7 @@ class OkxClient:
|
|
|
try:
|
|
|
positions = []
|
|
|
for entry in data:
|
|
|
- size = float(entry["pos"])
|
|
|
+ size = _parse_finite_float(entry["pos"])
|
|
|
if size == 0.0:
|
|
|
continue
|
|
|
symbol = entry["instId"]
|
|
|
@@ -244,7 +265,7 @@ class OkxClient:
|
|
|
symbol=symbol,
|
|
|
pos_side=pos_side,
|
|
|
size=size,
|
|
|
- avg_price=float(entry["avgPx"]),
|
|
|
+ avg_price=_parse_finite_float(entry["avgPx"]),
|
|
|
)
|
|
|
)
|
|
|
return positions
|