"""OKX 中控委托:须为 OCO 条件单,不得带 reduceOnly 或分两笔 market。""" from __future__ import annotations import sys import unittest from pathlib import Path from unittest.mock import MagicMock, patch ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "manual_trading_hub")) from exchange_orders import _okx_place_tp_sl # noqa: E402 class TestHubOkxPlaceTpsl(unittest.TestCase): def test_okx_place_tpsl_single_oco_without_reduce_only(self): captured: list[dict] = [] def fake_create_order(symbol, order_type, side, amount, price, params): captured.append( { "symbol": symbol, "type": order_type, "side": side, "amount": amount, "params": dict(params or {}), } ) return {"id": "algo-1"} ex = MagicMock() ex.create_order = fake_create_order ex.load_markets = MagicMock() ex.amount_to_precision = lambda sym, amt: str(amt) ex.price_to_precision = lambda sym, px: str(px) with patch.dict( "os.environ", {"OKX_POS_MODE": "hedge", "OKX_TD_MODE": "cross"}, clear=False, ): _okx_place_tp_sl( ex, "HYPE/USDT:USDT", "short", 6.0, 75.5, 70.2, ) self.assertEqual(len(captured), 1, captured) call = captured[0] self.assertEqual(call["type"], "oco") self.assertEqual(call["side"], "buy") params = call["params"] self.assertNotIn("reduceOnly", params) self.assertEqual(params.get("posSide"), "short") self.assertEqual(params.get("positionSide"), "short") self.assertEqual(params.get("stopLossPrice"), 75.5) self.assertEqual(params.get("takeProfitPrice"), 70.2) self.assertEqual(params.get("tpOrdPx"), "-1") self.assertEqual(params.get("slOrdPx"), "-1") self.assertNotIn("stopLoss", params) if __name__ == "__main__": unittest.main()