krakenfutures: rely on ft_has stoploss params, update tests/docs
This commit is contained in:
+6
-13
@@ -227,8 +227,11 @@ Kraken Futures uses the exchange id `krakenfutures` and supports isolated future
|
|||||||
"key": "your_exchange_key",
|
"key": "your_exchange_key",
|
||||||
"secret": "your_exchange_secret",
|
"secret": "your_exchange_secret",
|
||||||
"ccxt_config": {"enableRateLimit": true},
|
"ccxt_config": {"enableRateLimit": true},
|
||||||
"ccxt_async_config": {"enableRateLimit": true},
|
"ccxt_async_config": {"enableRateLimit": true}
|
||||||
"triggerSignal": "mark" // "mark" (default), "last", or "index"
|
},
|
||||||
|
"order_types": {
|
||||||
|
"stoploss": "market",
|
||||||
|
"stoploss_price_type": "mark" // "mark" (default), "last", or "index"
|
||||||
},
|
},
|
||||||
"trading_mode": "futures",
|
"trading_mode": "futures",
|
||||||
"margin_mode": "isolated",
|
"margin_mode": "isolated",
|
||||||
@@ -237,7 +240,7 @@ Kraken Futures uses the exchange id `krakenfutures` and supports isolated future
|
|||||||
|
|
||||||
!!! Tip "Stoploss on Exchange"
|
!!! Tip "Stoploss on Exchange"
|
||||||
Kraken Futures supports `stoploss_on_exchange` with both `limit` and `market` stop orders.
|
Kraken Futures supports `stoploss_on_exchange` with both `limit` and `market` stop orders.
|
||||||
Use `exchange.triggerSignal` to select the trigger price source (`mark`, `last`, or `index`).
|
Use `order_types.stoploss_price_type` to select the trigger price source (`mark`, `last`, or `index`).
|
||||||
|
|
||||||
!!! Note "Collateral"
|
!!! Note "Collateral"
|
||||||
Kraken Futures is USD-settled. Kraken allows EUR collateral, but USD is the recommended stake currency.
|
Kraken Futures is USD-settled. Kraken allows EUR collateral, but USD is the recommended stake currency.
|
||||||
@@ -245,16 +248,6 @@ Kraken Futures uses the exchange id `krakenfutures` and supports isolated future
|
|||||||
!!! Note "Pair format"
|
!!! Note "Pair format"
|
||||||
Futures pairs use CCXT symbols, for example `BTC/USD:USD`.
|
Futures pairs use CCXT symbols, for example `BTC/USD:USD`.
|
||||||
|
|
||||||
### Data download
|
|
||||||
|
|
||||||
Kraken Futures uses normal OHLCV downloads.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
freqtrade download-data --exchange krakenfutures --trading-mode futures --pairs BTC/USD:USD --timeframes 1m 5m
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: OHLCV requests are capped at 2000 candles per call, so large downloads may take longer.
|
|
||||||
|
|
||||||
## Kucoin
|
## Kucoin
|
||||||
|
|
||||||
Kucoin requires a passphrase for each api key, you will therefore need to add this key into the configuration so your exchange section looks as follows:
|
Kucoin requires a passphrase for each api key, you will therefore need to add this key into the configuration so your exchange section looks as follows:
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from typing import Any
|
|||||||
|
|
||||||
from ccxt.base.errors import NotSupported, OrderNotFound
|
from ccxt.base.errors import NotSupported, OrderNotFound
|
||||||
|
|
||||||
from freqtrade.constants import BuySell
|
|
||||||
from freqtrade.enums import MarginMode, PriceType, TradingMode
|
from freqtrade.enums import MarginMode, PriceType, TradingMode
|
||||||
from freqtrade.exceptions import ExchangeError, InvalidOrderException
|
from freqtrade.exceptions import ExchangeError, InvalidOrderException
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
@@ -537,28 +536,3 @@ class Krakenfutures(Exchange):
|
|||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
params = self._filter_params_for_open_closed(params or {})
|
params = self._filter_params_for_open_closed(params or {})
|
||||||
return self._api.fetch_closed_orders(pair, since, limit, params)
|
return self._api.fetch_closed_orders(pair, since, limit, params)
|
||||||
|
|
||||||
def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict[str, Any]:
|
|
||||||
params: dict[str, Any] = super()._get_stop_params(
|
|
||||||
side=side, ordertype=ordertype, stop_price=stop_price
|
|
||||||
)
|
|
||||||
|
|
||||||
# Force Kraken Futures naming
|
|
||||||
params.setdefault("triggerPrice", stop_price)
|
|
||||||
|
|
||||||
trigger_signal = self._get_trigger_signal()
|
|
||||||
if trigger_signal is not None:
|
|
||||||
params.setdefault("triggerSignal", trigger_signal)
|
|
||||||
|
|
||||||
if getattr(self, "trading_mode", None) == TradingMode.FUTURES:
|
|
||||||
params.setdefault("reduceOnly", True)
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
def _get_trigger_signal(self) -> str | None:
|
|
||||||
ex_conf = self._config.get("exchange")
|
|
||||||
if isinstance(ex_conf, dict):
|
|
||||||
v = ex_conf.get("triggerSignal") or ex_conf.get("trigger_signal")
|
|
||||||
if isinstance(v, str) and v.strip():
|
|
||||||
return v.strip()
|
|
||||||
return "mark"
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from ccxt.base.errors import NotSupported
|
from ccxt.base.errors import NotSupported
|
||||||
@@ -11,7 +12,7 @@ from freqtrade.enums import CandleType, MarginMode, TradingMode
|
|||||||
from freqtrade.exceptions import InvalidOrderException
|
from freqtrade.exceptions import InvalidOrderException
|
||||||
from freqtrade.exchange.exchange import Exchange
|
from freqtrade.exchange.exchange import Exchange
|
||||||
from freqtrade.exchange.krakenfutures import Krakenfutures
|
from freqtrade.exchange.krakenfutures import Krakenfutures
|
||||||
from tests.conftest import get_patched_exchange
|
from tests.conftest import EXMS, get_patched_exchange
|
||||||
|
|
||||||
|
|
||||||
def test_krakenfutures_ft_has_overrides():
|
def test_krakenfutures_ft_has_overrides():
|
||||||
@@ -169,20 +170,32 @@ def test_krakenfutures_fetch_order_falls_back_to_history_orders(mocker, default_
|
|||||||
assert res["filled"] == 0.0
|
assert res["filled"] == 0.0
|
||||||
|
|
||||||
|
|
||||||
def test_krakenfutures_get_stop_params_adds_triggerprice_signal_and_reduceonly(
|
def test_krakenfutures_create_stoploss_uses_trigger_price_type(mocker, default_conf):
|
||||||
mocker, default_conf
|
"""Test create_stoploss uses triggerPrice, triggerSignal, and reduceOnly."""
|
||||||
):
|
api_mock = MagicMock()
|
||||||
"""Test _get_stop_params adds triggerPrice, triggerSignal, and reduceOnly."""
|
api_mock.create_order = MagicMock(return_value={"id": "order-id", "info": {"foo": "bar"}})
|
||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
|
conf["dry_run"] = False
|
||||||
conf["trading_mode"] = TradingMode.FUTURES
|
conf["trading_mode"] = TradingMode.FUTURES
|
||||||
conf["margin_mode"] = MarginMode.ISOLATED
|
conf["margin_mode"] = MarginMode.ISOLATED
|
||||||
|
|
||||||
if isinstance(conf.get("exchange"), dict):
|
mocker.patch(f"{EXMS}.amount_to_precision", lambda s, x, y: y)
|
||||||
conf["exchange"]["triggerSignal"] = "mark"
|
mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y, **kwargs: y)
|
||||||
|
|
||||||
ex = get_patched_exchange(mocker, conf, exchange="krakenfutures")
|
ex = get_patched_exchange(mocker, conf, api_mock, exchange="krakenfutures")
|
||||||
|
|
||||||
params = ex._get_stop_params(side="sell", ordertype="market", stop_price=90000.0)
|
ex.create_stoploss(
|
||||||
|
pair="ETH/BTC",
|
||||||
|
amount=1,
|
||||||
|
stop_price=90000.0,
|
||||||
|
side="sell",
|
||||||
|
order_types={"stoploss": "market", "stoploss_price_type": "mark"},
|
||||||
|
leverage=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
call_args = api_mock.create_order.call_args
|
||||||
|
params = call_args[1].get("params") if call_args[1] else call_args[0][5]
|
||||||
|
|
||||||
assert params["triggerPrice"] == 90000.0
|
assert params["triggerPrice"] == 90000.0
|
||||||
assert params["triggerSignal"] == "mark"
|
assert params["triggerSignal"] == "mark"
|
||||||
|
|||||||
Reference in New Issue
Block a user