|
|
@@ -221,6 +221,23 @@ def positions_with_zero_size_malformed_avg_price_response() -> DummyResponse:
|
|
|
)
|
|
|
|
|
|
|
|
|
+def positions_with_non_string_identity_response() -> DummyResponse:
|
|
|
+ return DummyResponse(
|
|
|
+ {
|
|
|
+ "code": "0",
|
|
|
+ "msg": "",
|
|
|
+ "data": [
|
|
|
+ {
|
|
|
+ "instId": None,
|
|
|
+ "posSide": ["long"],
|
|
|
+ "pos": "3",
|
|
|
+ "avgPx": "24900",
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
def market_long_signal() -> TradeSignal:
|
|
|
return TradeSignal(
|
|
|
action="long",
|
|
|
@@ -302,6 +319,24 @@ def test_build_contract_size_fails_below_min_size():
|
|
|
build_contract_size(notional=250, price=25_100, metadata=metadata)
|
|
|
|
|
|
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ ("price", "metadata"),
|
|
|
+ [
|
|
|
+ (0, InstrumentMeta(ct_val=0.01, lot_sz=1, min_sz=1)),
|
|
|
+ (-1, InstrumentMeta(ct_val=0.01, lot_sz=1, min_sz=1)),
|
|
|
+ (25_000, InstrumentMeta(ct_val=0, lot_sz=1, min_sz=1)),
|
|
|
+ (25_000, InstrumentMeta(ct_val=-0.01, lot_sz=1, min_sz=1)),
|
|
|
+ (25_000, InstrumentMeta(ct_val=0.01, lot_sz=0, min_sz=1)),
|
|
|
+ (25_000, InstrumentMeta(ct_val=0.01, lot_sz=-1, min_sz=1)),
|
|
|
+ (25_000, InstrumentMeta(ct_val=0.01, lot_sz=1, min_sz=0)),
|
|
|
+ (25_000, InstrumentMeta(ct_val=0.01, lot_sz=1, min_sz=-1)),
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_build_contract_size_rejects_non_positive_inputs(price, metadata):
|
|
|
+ with pytest.raises(ValueError, match="contract sizing inputs are invalid"):
|
|
|
+ build_contract_size(notional=250, price=price, metadata=metadata)
|
|
|
+
|
|
|
+
|
|
|
def test_market_order_fetches_latest_price_before_sizing():
|
|
|
session = DummySession(
|
|
|
[
|
|
|
@@ -561,3 +596,11 @@ def test_get_positions_ignores_malformed_fields_on_zero_size_rows():
|
|
|
assert len(positions) == 1
|
|
|
assert positions[0].pos_side == "short"
|
|
|
assert positions[0].avg_price == 24900.0
|
|
|
+
|
|
|
+
|
|
|
+def test_get_positions_rejects_non_string_inst_id_and_pos_side():
|
|
|
+ session = DummySession([positions_with_non_string_identity_response()])
|
|
|
+ client = OkxClient(config=sample_config(), session=session)
|
|
|
+
|
|
|
+ with pytest.raises(ValueError, match="okx response payload is invalid"):
|
|
|
+ client.get_positions(symbol="BTC-USDT-SWAP")
|