test(krakenfutures): align tests with simplified stoploss handling

- Add test for stoploss_query_requires_stop_flag in _ft_has.
- Replace _fix_trigger_order_id tests with base class integration tests
  (verify stop=True is passed by fetch_stoploss_order/cancel_stoploss_order).
- Add tests for stop param flow: passed to history endpoints, stripped
  from open orders query.
- Remove obsolete cancel_stoploss_order/fetch_stoploss_order override tests.
pull/12706/head
matstedt 3 months ago committed by Matthias
parent 086e1d8bad
commit f0aedc8d23

@ -29,6 +29,7 @@ def test_krakenfutures_ft_has_overrides():
ft_has = Krakenfutures._ft_has
assert ft_has["stoploss_on_exchange"] is True
assert ft_has["stoploss_order_types"] == {"limit": "limit", "market": "market"}
assert ft_has["stoploss_query_requires_stop_flag"] is True
assert ft_has["stop_price_param"] == "triggerPrice"
assert ft_has["stop_price_type_field"] == "triggerSignal"
@ -104,19 +105,20 @@ def test_krakenfutures_fetch_order_returns_direct_ccxt_result(mocker, default_co
fallback.assert_not_called()
def test_krakenfutures_fetch_stoploss_order_strips_trigger_from_status_query(mocker, default_conf):
"""Direct fetch_order status lookup should not receive trigger params."""
def test_krakenfutures_fetch_order_strips_stop_from_status_query(mocker, default_conf):
"""Direct CCXT fetch_order status lookup should not receive stop/trigger params."""
conf = dict(default_conf)
conf["dry_run"] = False
ex = get_patched_exchange(mocker, conf, exchange="krakenfutures")
ccxt_order = {"id": "trigger-raw-1", "symbol": "BTC/USD:USD", "status": "open"}
ccxt_order = {"id": "order-1", "symbol": "BTC/USD:USD", "status": "open"}
fetch_order = mocker.patch.object(ex._api, "fetch_order", return_value=ccxt_order)
res = ex.fetch_stoploss_order("trigger-raw-1", "BTC/USD:USD")
# Simulate call from base fetch_stoploss_order which adds stop=True
ex.fetch_order("order-1", "BTC/USD:USD", params={"stop": True})
assert res["id"] == "trigger-raw-1"
fetch_order.assert_called_once_with("trigger-raw-1", "BTC/USD:USD", params={})
# stop should be stripped from the direct CCXT status call
fetch_order.assert_called_once_with("order-1", "BTC/USD:USD", params={})
def test_krakenfutures_fetch_order_raises_invalid_when_not_found(mocker, default_conf):
@ -212,69 +214,63 @@ def test_krakenfutures_fetch_order_dry_run(mocker, default_conf):
assert res["id"] == "dry-123"
def test_krakenfutures_fetch_order_finds_trigger_order(mocker, default_conf):
"""Test fetch_order finds trigger orders (stoplosses) via closed orders fallback."""
def test_krakenfutures_fetch_order_finds_stoploss_via_stop_param(mocker, default_conf):
"""Test fetch_order finds stoploss orders via closed orders fallback with stop=True."""
conf = dict(default_conf)
conf["dry_run"] = False
ex = get_patched_exchange(mocker, conf, exchange="krakenfutures")
mocker.patch.object(ex._api, "fetch_order", side_effect=ccxt.OrderNotFound("not found"))
# Open orders returns empty, closed orders returns empty for regular,
# but returns the trigger order when trigger=True
mocker.patch.object(ex._api, "fetch_open_orders", return_value=[], create=True)
# With stop=True, CCXT queries trigger history endpoint
mocker.patch.object(
ex._api,
"fetch_closed_orders",
side_effect=[
[], # Regular closed orders
[{"id": "trigger-123", "symbol": "BTC/USD:USD", "status": "closed"}], # Trigger orders
return_value=[
{"id": "trigger-123", "symbol": "BTC/USD:USD", "status": "closed"},
],
create=True,
)
res = ex.fetch_order("trigger-123", "BTC/USD:USD")
# Simulate what base class fetch_stoploss_order does (adds stop=True)
res = ex.fetch_order("trigger-123", "BTC/USD:USD", params={"stop": True})
assert res["id"] == "trigger-123"
def test_krakenfutures_fetch_stoploss_order_prefers_open_orders_without_trigger_param(
mocker, default_conf
):
"""Stoploss lookup should query open orders without trigger flags and match nested orderId."""
conf = dict(default_conf)
conf["dry_run"] = False
ex = get_patched_exchange(mocker, conf, exchange="krakenfutures")
def test_krakenfutures_fetch_order_fallback_passes_stop_to_history(mocker, default_conf):
"""Stoploss query (stop=True) should pass through to closed/canceled endpoints."""
ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures")
trigger_id = "trigger-open-123"
mocker.patch.object(ex._api, "fetch_open_orders", return_value=[], create=True)
mocker.patch.object(ex._api, "fetch_order", side_effect=ccxt.OrderNotFound("not found"))
closed_order = {"id": "sl-123", "symbol": "BTC/USD:USD", "status": "closed"}
closed_fetch = mocker.patch.object(
ex._api,
"fetch_closed_orders",
return_value=[closed_order],
create=True,
)
def fetch_open(symbol, params=None):
assert symbol is None
assert params == {}
return [
{
"id": None,
"symbol": "BTC/USD:USD",
"status": "open",
"info": {"order": {"orderId": trigger_id}},
}
]
res = ex._fetch_order_fallback("sl-123", "BTC/USD:USD", {"stop": True})
open_fetch = mocker.patch.object(
ex._api, "fetch_open_orders", side_effect=fetch_open, create=True
)
closed_fetch = mocker.patch.object(ex._api, "fetch_closed_orders", return_value=[], create=True)
canceled_fetch = mocker.patch.object(
ex._api, "fetch_canceled_orders", return_value=[], create=True
)
assert res is not None
assert res["id"] == "sl-123"
# Verify stop=True was passed to closed orders (CCXT maps stop→trigger)
closed_fetch.assert_called_once_with("BTC/USD:USD", params={"stop": True})
res = ex.fetch_stoploss_order(trigger_id, "BTC/USD:USD")
assert res["id"] == trigger_id
assert res["status"] == "open"
open_fetch.assert_called_once()
closed_fetch.assert_not_called()
canceled_fetch.assert_not_called()
def test_krakenfutures_fetch_order_fallback_strips_stop_from_open_orders(mocker, default_conf):
"""Open orders query should not receive stop/trigger flags."""
ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures")
open_fetch = mocker.patch.object(ex._api, "fetch_open_orders", return_value=[], create=True)
mocker.patch.object(ex._api, "fetch_closed_orders", return_value=[], create=True)
mocker.patch.object(ex._api, "fetch_canceled_orders", return_value=[], create=True)
ex._fetch_order_fallback("abc", "BTC/USD:USD", {"stop": True})
# stop should be stripped from open orders call
open_fetch.assert_called_once_with(None, params={})
def test_krakenfutures_fetch_order_propagates_exchange_errors_from_fallback(mocker, default_conf):
@ -635,94 +631,40 @@ def test_krakenfutures_safe_float():
assert Krakenfutures._safe_float({}) is None
# --- Stoploss cancel tests ---
def test_krakenfutures_cancel_stoploss_order_fixes_id(mocker, default_conf):
"""Test cancel_stoploss_order extracts order ID from info when top-level id is None."""
ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures")
# CCXT returns 'id': None for trigger orders, but orderId is in info.order
ccxt_response = {
"id": None,
"status": "canceled",
"info": {
"order": {
"orderId": "a10258a9-01ea-44c4-a38f-66165678926e",
"type": "TRIGGER_ORDER",
"symbol": "PF_XBTUSD",
},
"status": "CANCELLED",
},
}
mocker.patch.object(ex, "cancel_order", return_value=ccxt_response)
result = ex.cancel_stoploss_order("a10258a9-01ea-44c4-a38f-66165678926e", "BTC/USD:USD")
# ID should be extracted from info.order.orderId
assert result["id"] == "a10258a9-01ea-44c4-a38f-66165678926e"
assert result["status"] == "canceled"
def test_krakenfutures_cancel_stoploss_order_preserves_existing_id(mocker, default_conf):
"""Test cancel_stoploss_order doesn't overwrite existing id."""
ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures")
# Normal response with id already set
ccxt_response = {
"id": "existing-order-id",
"status": "canceled",
"info": {},
}
mocker.patch.object(ex, "cancel_order", return_value=ccxt_response)
result = ex.cancel_stoploss_order("existing-order-id", "BTC/USD:USD")
assert result["id"] == "existing-order-id"
# --- Stoploss via base class (stoploss_query_requires_stop_flag) ---
# --- Stoploss fetch tests ---
def test_krakenfutures_fetch_stoploss_order_fixes_id(mocker, default_conf):
"""Test fetch_stoploss_order extracts order ID from info when top-level id is None."""
def test_krakenfutures_fetch_stoploss_order_uses_base_class(mocker, default_conf):
"""Base class fetch_stoploss_order should add stop=True and delegate to fetch_order."""
conf = dict(default_conf)
conf["dry_run"] = False
ex = get_patched_exchange(mocker, conf, exchange="krakenfutures")
# CCXT returns 'id': None for trigger orders, but orderId is in info.order
ccxt_response = {
"id": None,
"status": "open",
"info": {
"order": {
"orderId": "trigger-order-123",
"type": "TRIGGER_ORDER",
"symbol": "PF_XBTUSD",
},
},
}
mocker.patch.object(ex, "fetch_order", return_value=ccxt_response)
expected_order = {"id": "sl-order-1", "status": "open", "info": {}}
fetch_order = mocker.patch.object(ex, "fetch_order", return_value=expected_order)
result = ex.fetch_stoploss_order("trigger-order-123", "BTC/USD:USD")
result = ex.fetch_stoploss_order("sl-order-1", "BTC/USD:USD")
# ID should be extracted from info.order.orderId
assert result["id"] == "trigger-order-123"
assert result["id"] == "sl-order-1"
# Base class should pass stop=True
fetch_order.assert_called_once()
call_params = fetch_order.call_args[0][2] if len(fetch_order.call_args[0]) > 2 else {}
assert call_params.get("stop") is True
def test_krakenfutures_fetch_stoploss_order_passes_trigger_param(mocker, default_conf):
"""Test fetch_stoploss_order passes trigger=True to fetch_order."""
def test_krakenfutures_cancel_stoploss_order_uses_base_class(mocker, default_conf):
"""Base class cancel_stoploss_order should add stop=True and delegate to cancel_order."""
conf = dict(default_conf)
conf["dry_run"] = False
ex = get_patched_exchange(mocker, conf, exchange="krakenfutures")
mock_fetch = mocker.patch.object(
ex, "fetch_order", return_value={"id": "order-123", "status": "open", "info": {}}
)
expected_order = {"id": "sl-cancel-1", "status": "canceled"}
cancel_order = mocker.patch.object(ex, "cancel_order", return_value=expected_order)
ex.fetch_stoploss_order("order-123", "BTC/USD:USD")
result = ex.cancel_stoploss_order("sl-cancel-1", "BTC/USD:USD")
# Verify trigger=True was passed
mock_fetch.assert_called_once()
call_params = mock_fetch.call_args[0][2] # third positional arg is params
assert call_params.get("trigger") is True
assert result["id"] == "sl-cancel-1"
# Base class should pass stop=True
cancel_order.assert_called_once()
call_params = cancel_order.call_args[0][2] if len(cancel_order.call_args[0]) > 2 else {}
assert call_params.get("stop") is True

Loading…
Cancel
Save