From 0f316076177122cdd3050f6ab12c9a4307ca0064 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Oct 2025 20:21:23 +0200 Subject: [PATCH 01/20] feat: Enable bitget futures trading --- freqtrade/exchange/bitget.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index b6b347c34..d4f294956 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -41,6 +41,12 @@ class Bitget(Exchange): "mark_ohlcv_timeframe": "4h", } + _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ + (TradingMode.SPOT, MarginMode.NONE), + (TradingMode.FUTURES, MarginMode.ISOLATED), + # (TradingMode.FUTURES, MarginMode.CROSS), + ] + def ohlcv_candle_limit( self, timeframe: str, candle_type: CandleType, since_ms: int | None = None ) -> int: From 572ce0b739500e887878df085f9556dee3ba1c1c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Oct 2025 20:22:08 +0200 Subject: [PATCH 02/20] feat: bitget liquidation price calc --- freqtrade/exchange/bitget.py | 62 +++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index d4f294956..2d173caa3 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -3,7 +3,7 @@ from datetime import timedelta import ccxt -from freqtrade.enums import CandleType +from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.exceptions import ( DDosProtection, OperationalException, @@ -132,3 +132,63 @@ class Bitget(Exchange): def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True}) + + def dry_run_liquidation_price( + self, + pair: str, + open_rate: float, + is_short: bool, + amount: float, + stake_amount: float, + leverage: float, + wallet_balance: float, + open_trades: list, + ) -> float | None: + """ + Important: Must be fetching data from cached values as this is used by backtesting! + + + https://www.bitget.com/support/articles/12560603808759 + MMR: Maintenance margin rate of the trading pair. + + CoinMainIndexPrice: The index price for Coin-M futures. For USDT-M futures, + the index price is: 1. + + TakerFeeRatio: The fee rate applied when placing taker orders. + + Position direction: The current position direction of the trading pair. + 1 indicates a long position, and -1 indicates a short position. + + Formula: + + Estimated liquidation price = [ + position margin - position size x average entry price x position direction + ] ÷ [position size x (MMR + TakerFeeRatio - position direction)] + + :param pair: Pair to calculate liquidation price for + :param open_rate: Entry price of position + :param is_short: True if the trade is a short, false otherwise + :param amount: Absolute value of position size incl. leverage (in base currency) + :param stake_amount: Stake amount - Collateral in settle currency. + :param leverage: Leverage used for this position. + :param wallet_balance: Amount of margin_mode in the wallet being used to trade + Cross-Margin Mode: crossWalletBalance + Isolated-Margin Mode: isolatedWalletBalance + :param open_trades: List of other open trades in the same wallet + """ + market = self.markets[pair] + taker_fee_rate = market["taker"] or self._api.describe().get("fees", {}).get( + "trading", {} + ).get("taker", 0.001) + mm_ratio, _ = self.get_maintenance_ratio_and_amt(pair, stake_amount) + + if self.trading_mode == TradingMode.FUTURES and self.margin_mode == MarginMode.ISOLATED: + position_direction = -1 if is_short else 1 + + return wallet_balance - (amount * open_rate * position_direction) / amount * ( + mm_ratio + taker_fee_rate - position_direction + ) + else: + raise OperationalException( + "Freqtrade only supports isolated futures for leverage trading" + ) From fcbe58e971a63fca461d96f91945ec8be309b50c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 13 Oct 2025 20:23:18 +0200 Subject: [PATCH 03/20] feat: Add bitget exchange init --- freqtrade/exchange/bitget.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 2d173caa3..3fdcc4e63 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -133,6 +133,27 @@ class Bitget(Exchange): def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True}) + @retrier + def additional_exchange_init(self) -> None: + """ + Additional exchange initialization logic. + .api will be available at this point. + Must be overridden in child methods if required. + """ + try: + if not self._config["dry_run"]: + if self.trading_mode == TradingMode.FUTURES: + position_mode = self._api.set_position_mode(False) + self._log_exchange_response("set_position_mode", position_mode) + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.OperationFailed, ccxt.ExchangeError) as e: + raise TemporaryError( + f"Error in additional_exchange_init due to {e.__class__.__name__}. Message: {e}" + ) from e + except ccxt.BaseError as e: + raise OperationalException(e) from e + def dry_run_liquidation_price( self, pair: str, From 3da36eae31764cee8465c842aa8b283453845e55 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 06:40:01 +0200 Subject: [PATCH 04/20] feat: bitget futures order parameters --- freqtrade/exchange/bitget.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 3fdcc4e63..a847df262 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -3,6 +3,7 @@ from datetime import timedelta import ccxt +from freqtrade.constants import BuySell from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.exceptions import ( DDosProtection, @@ -154,6 +155,31 @@ class Bitget(Exchange): except ccxt.BaseError as e: raise OperationalException(e) from e + def _lev_prep(self, pair: str, leverage: float, side: BuySell, accept_fail: bool = False): + if self.trading_mode != TradingMode.SPOT: + # Explicitly setting margin_mode is not necessary as marginMode can be set per order. + # self.set_margin_mode(pair, self.margin_mode, accept_fail) + self._set_leverage(leverage, pair, accept_fail) + + def _get_params( + self, + side: BuySell, + ordertype: str, + leverage: float, + reduceOnly: bool, + time_in_force: str = "GTC", + ) -> dict: + params = super()._get_params( + side=side, + ordertype=ordertype, + leverage=leverage, + reduceOnly=reduceOnly, + time_in_force=time_in_force, + ) + if self.trading_mode == TradingMode.FUTURES and self.margin_mode: + params["marginMode"] = self.margin_mode.value.lower() + return params + def dry_run_liquidation_price( self, pair: str, From badc7ed18cc6b7f27c8179d32fd8a7b61044bae4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 06:49:33 +0200 Subject: [PATCH 05/20] feat(bitget): fix liquidation formula --- freqtrade/exchange/bitget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index a847df262..94be39f42 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -232,8 +232,8 @@ class Bitget(Exchange): if self.trading_mode == TradingMode.FUTURES and self.margin_mode == MarginMode.ISOLATED: position_direction = -1 if is_short else 1 - return wallet_balance - (amount * open_rate * position_direction) / amount * ( - mm_ratio + taker_fee_rate - position_direction + return (wallet_balance - (amount * open_rate * position_direction)) / ( + amount * (mm_ratio + taker_fee_rate - position_direction) ) else: raise OperationalException( From d46af09c10164f5f847834f5125a56369797da9a Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:07:18 +0200 Subject: [PATCH 06/20] docs: add bitget to support matrix --- docs/includes/exchange-features.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/includes/exchange-features.md b/docs/includes/exchange-features.md index 7aa6e6436..6833818b7 100644 --- a/docs/includes/exchange-features.md +++ b/docs/includes/exchange-features.md @@ -5,6 +5,8 @@ | [Binance](exchanges.md#binance) | futures | isolated, cross | market, limit | | [Bingx](exchanges.md#bingx) | spot | | market, limit | | [Bitmart](exchanges.md#bitmart) | spot | | ❌ (not supported) | +| [Bitget](exchanges.md#bitget) | spot | | market, limit | +| [Bitget](exchanges.md#bitget) | futures | isolated | market, limit | | [Bybit](exchanges.md#bybit) | spot | | ❌ (not supported) | | [Bybit](exchanges.md#bybit) | futures | isolated | market, limit | | [Gate.io](exchanges.md#gateio) | spot | | limit | From eb31581c7bc506fdfbdfb53d25545fe3ad2d9ded Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:07:31 +0200 Subject: [PATCH 07/20] docs(bitget): Add futures notes for bitget --- docs/exchanges.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/exchanges.md b/docs/exchanges.md index 859a6cbaf..f71aa94f8 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -355,6 +355,12 @@ Bitget supports [time_in_force](configuration.md#understand-order_time_in_force) Bitget supports `stoploss_on_exchange` and can use both stop-loss-market and stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it. You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type of stoploss shall be used. +### Bitget Futures + +Futures trading on bitget is supported for isolated futures mode. + +On startup, freqtrade will set the position mode to "One-way Mode" for the whole (sub)account. This avoids making this call over and over again (slowing down bot operations), but means that manual changes to this setting may result in exceptions and errors. + ## Hyperliquid !!! Tip "Stoploss on Exchange" From 99566b4d9bc237128236da2aa0b5996a423df2ad Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:07:48 +0200 Subject: [PATCH 08/20] docs: slight refactor to exchanges structure --- docs/exchanges.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index f71aa94f8..b71b33ff0 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -298,7 +298,14 @@ Without these permissions, the bot will not start correctly and show errors like Bybit supports [time_in_force](configuration.md#understand-order_time_in_force) with settings "GTC" (good till cancelled), "FOK" (full-or-cancel), "IOC" (immediate-or-cancel) and "PO" (Post only) settings. -Futures trading on bybit is currently supported for isolated futures mode. +!!! Warning "Unified accounts" + Freqtrade assumes accounts to be dedicated to the bot. + We therefore recommend the usage of one subaccount per bot. This is especially important when using unified accounts. + Other configurations (multiple bots on one account, manual non-bot trades on the bot account) are not supported and may lead to unexpected behavior. + +### Bybit Futures + +Futures trading on bybit is supported for isolated futures mode. On startup, freqtrade will set the position mode to "One-way Mode" for the whole (sub)account. This avoids making this call over and over again (slowing down bot operations), but means that manual changes to this setting may result in exceptions and errors. @@ -312,10 +319,6 @@ API Keys for live futures trading must have the following permissions: We do strongly recommend to limit all API keys to the IP you're going to use it from. -!!! Warning "Unified accounts" - Freqtrade assumes accounts to be dedicated to the bot. - We therefore recommend the usage of one subaccount per bot. This is especially important when using unified accounts. - Other configurations (multiple bots on one account, manual non-bot trades on the bot account) are not supported and may lead to unexpected behavior. ## Bitmart From 7aca82b590ace18f61867d87daf277e3d94f1ea2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:10:13 +0200 Subject: [PATCH 09/20] docs: clarify exchange warning --- docs/exchanges.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/exchanges.md b/docs/exchanges.md index b71b33ff0..61d0628ec 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -487,3 +487,5 @@ For example, to test the order type `FOK` with Kraken, and modify candle limit t !!! Warning Please make sure to fully understand the impacts of these settings before modifying them. + Using `_ft_has_params` overrides may lead to unexpected behavior, and may even break your bot. + We will not be able to provide support for issues caused by custom settings in `_ft_has_params`. From 0c65c915a414aaacb3b72c6961323a2c6e665f31 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:12:36 +0200 Subject: [PATCH 10/20] feat: more settings for bitget futures --- freqtrade/exchange/bitget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 94be39f42..e6378ca7a 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -40,6 +40,7 @@ class Bitget(Exchange): } _ft_has_futures: FtHas = { "mark_ohlcv_timeframe": "4h", + "stoploss_blocks_assets": False, # Stoploss orders do not block assets } _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ From 16e9109e967ca86b1e949ff9ce85dc2e8c76f1de Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:14:41 +0200 Subject: [PATCH 11/20] test: add test for bitget exchange init --- tests/exchange/test_bitget.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index fdfaf7a7e..117c62b7b 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock import pytest -from freqtrade.enums import CandleType +from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.exceptions import RetryableOrderError from freqtrade.exchange.common import API_RETRY_COUNT from freqtrade.util import dt_now, dt_ts @@ -120,3 +120,18 @@ def test_bitget_ohlcv_candle_limit(mocker, default_conf_usdt): assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 + + +def test_additional_exchange_init_bitget(default_conf, mocker): + default_conf["dry_run"] = False + default_conf["trading_mode"] = TradingMode.FUTURES + default_conf["margin_mode"] = MarginMode.ISOLATED + api_mock = MagicMock() + api_mock.set_position_mode = MagicMock(return_value={}) + + get_patched_exchange(mocker, default_conf, exchange="bitget", api_mock=api_mock) + assert api_mock.set_position_mode.call_count == 1 + + ccxt_exceptionhandlers( + mocker, default_conf, api_mock, "bitget", "additional_exchange_init", "set_position_mode" + ) From 1f620257f99fd1ed96b29d474f1823519b7d3dc5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:16:11 +0200 Subject: [PATCH 12/20] test: test for get_params --- tests/exchange/test_exchange.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 4315f95c3..fb2b1efb5 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -5942,7 +5942,7 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers): assert exchange.get_max_leverage("TIA/USDT:USDT", 130.008) == 40 -@pytest.mark.parametrize("exchange_name", ["binance", "kraken", "gate", "okx", "bybit"]) +@pytest.mark.parametrize("exchange_name", ["binance", "kraken", "gate", "okx", "bybit", "bitget"]) def test__get_params(mocker, default_conf, exchange_name): api_mock = MagicMock() mocker.patch(f"{EXMS}.exchange_has", return_value=True) @@ -5966,6 +5966,9 @@ def test__get_params(mocker, default_conf, exchange_name): if exchange_name == "bybit": params2["position_idx"] = 0 + if exchange_name == "bitget": + params2["marginMode"] = "isolated" + assert ( exchange._get_params( side="buy", From f82d8f3b5200074b9e35a54fc19f94b37b10f8e2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 14 Oct 2025 07:19:27 +0200 Subject: [PATCH 13/20] test: refactor _get_params test for simpler reading --- tests/exchange/test_exchange.py | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index fb2b1efb5..e5fc6f574 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -5942,32 +5942,32 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers): assert exchange.get_max_leverage("TIA/USDT:USDT", 130.008) == 40 -@pytest.mark.parametrize("exchange_name", ["binance", "kraken", "gate", "okx", "bybit", "bitget"]) -def test__get_params(mocker, default_conf, exchange_name): +@pytest.mark.parametrize( + "exchange_name, add_params_spot, add_params_futures", + [ + ("binance", {}, {}), + ("kraken", {}, {"leverage": 3.0}), + ("gate", {}, {}), + ("okx", {}, {"tdMode": "isolated", "posSide": "net"}), + ("bybit", {}, {"position_idx": 0}), + ("bitget", {}, {"marginMode": "isolated"}), + ], +) +def test__get_params(mocker, default_conf, exchange_name, add_params_spot, add_params_futures): api_mock = MagicMock() mocker.patch(f"{EXMS}.exchange_has", return_value=True) exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name) exchange._params = {"test": True} params1 = {"test": True} - params2 = { + params1.update(add_params_spot) + + params_fut = { "test": True, "timeInForce": "IOC", "reduceOnly": True, } - - if exchange_name == "kraken": - params2["leverage"] = 3.0 - - if exchange_name == "okx": - params2["tdMode"] = "isolated" - params2["posSide"] = "net" - - if exchange_name == "bybit": - params2["position_idx"] = 0 - - if exchange_name == "bitget": - params2["marginMode"] = "isolated" + params_fut.update(add_params_futures) assert ( exchange._get_params( @@ -6015,7 +6015,7 @@ def test__get_params(mocker, default_conf, exchange_name): time_in_force="IOC", leverage=3.0, ) - == params2 + == params_fut ) From 02a0ce8c498b94b5ff641d6d1f6238dcf3a3ca28 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 17 Oct 2025 06:45:57 +0200 Subject: [PATCH 14/20] test: enable online bitget tests --- tests/exchange_online/conftest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/exchange_online/conftest.py b/tests/exchange_online/conftest.py index 0b5cccc8e..94ff4ac47 100644 --- a/tests/exchange_online/conftest.py +++ b/tests/exchange_online/conftest.py @@ -422,6 +422,10 @@ EXCHANGES = { "hasQuoteVolume": True, "timeframe": "1h", "candle_count": 1000, + "futures": True, + "futures_pair": "BTC/USDT:USDT", + "leverage_tiers_public": True, + "leverage_in_spot_market": True, }, "coinex": { "pair": "BTC/USDT", From d283252ac78dc786f03aaa9963bbd8175b76d96c Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 17 Oct 2025 06:56:29 +0200 Subject: [PATCH 15/20] fix: bitget only provides 100 funding rate candles per call --- freqtrade/exchange/bitget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index e6378ca7a..49c757e82 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -40,6 +40,7 @@ class Bitget(Exchange): } _ft_has_futures: FtHas = { "mark_ohlcv_timeframe": "4h", + "funding_fee_candle_limit": 100, "stoploss_blocks_assets": False, # Stoploss orders do not block assets } From 5b45f1bbc87ffc89ac989e0f7dd23ead8b61f7bf Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Oct 2025 08:52:52 +0200 Subject: [PATCH 16/20] docs: add bitget as supported exchange --- README.md | 4 +++- docs/index.md | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 80ccd871a..fcf28efe4 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,9 @@ hesitate to read the source code and understand the mechanism of this bot. Please read the [exchange specific notes](docs/exchanges.md) to learn about eventual, special configurations needed for each exchange. - [X] [Binance](https://www.binance.com/) -- [X] [Bitmart](https://bitmart.com/) - [X] [BingX](https://bingx.com/invite/0EM9RX) +- [X] [Bitget](https://www.bitget.com/) +- [X] [Bitmart](https://bitmart.com/) - [X] [Bybit](https://bybit.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) - [X] [HTX](https://www.htx.com/) @@ -41,6 +42,7 @@ Please read the [exchange specific notes](docs/exchanges.md) to learn about even ### Supported Futures Exchanges (experimental) - [X] [Binance](https://www.binance.com/) +- [X] [Bitget](https://www.bitget.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) - [X] [Hyperliquid](https://hyperliquid.xyz/) (A decentralized exchange, or DEX) - [X] [OKX](https://okx.com/) diff --git a/docs/index.md b/docs/index.md index 7276432da..a97e43365 100644 --- a/docs/index.md +++ b/docs/index.md @@ -39,6 +39,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual, - [X] [Binance](https://www.binance.com/) - [X] [BingX](https://bingx.com/invite/0EM9RX) +- [X] [Bitget](https://www.bitget.com/) - [X] [Bitmart](https://bitmart.com/) - [X] [Bybit](https://bybit.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) @@ -52,6 +53,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual, ### Supported Futures Exchanges (experimental) - [X] [Binance](https://www.binance.com/) +- [X] [Bitget](https://www.bitget.com/) - [X] [Bybit](https://bybit.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) - [X] [Hyperliquid](https://hyperliquid.xyz/) (A decentralized exchange, or DEX) From 0f60ee876324f07708bbfe208820d369e75d85c4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Oct 2025 08:53:18 +0200 Subject: [PATCH 17/20] feat: add bitget as supported exchange --- freqtrade/exchange/bitget.py | 9 ++------- freqtrade/exchange/common.py | 1 + 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 49c757e82..c85ff0d1b 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -21,13 +21,8 @@ logger = logging.getLogger(__name__) class Bitget(Exchange): - """ - Bitget exchange class. Contains adjustments needed for Freqtrade to work - with this exchange. - - Please note that this exchange is not included in the list of exchanges - officially supported by the Freqtrade development team. So some features - may still not work as expected. + """Bitget exchange class. + Contains adjustments needed for Freqtrade to work with this exchange. """ _ft_has: FtHas = { diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index 74aecacaa..d35ff4c17 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -57,6 +57,7 @@ SUPPORTED_EXCHANGES = [ "binance", "bingx", "bitmart", + "bitget", "bybit", "gate", "htx", From d1e73c19162e418e102352a5c751b7efbfa662a2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Oct 2025 09:48:48 +0200 Subject: [PATCH 18/20] test: add test for cross liquidation price not supported --- freqtrade/exchange/bitget.py | 2 +- tests/exchange/test_bitget.py | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index c85ff0d1b..398cf1803 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -234,5 +234,5 @@ class Bitget(Exchange): ) else: raise OperationalException( - "Freqtrade only supports isolated futures for leverage trading" + "Freqtrade currently only supports isolated futures for bitget" ) diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index 117c62b7b..216e953db 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -1,10 +1,11 @@ from datetime import timedelta +from sqlite3 import OperationalError from unittest.mock import MagicMock import pytest from freqtrade.enums import CandleType, MarginMode, TradingMode -from freqtrade.exceptions import RetryableOrderError +from freqtrade.exceptions import OperationalException, RetryableOrderError from freqtrade.exchange.common import API_RETRY_COUNT from freqtrade.util import dt_now, dt_ts from tests.conftest import EXMS, get_patched_exchange @@ -135,3 +136,26 @@ def test_additional_exchange_init_bitget(default_conf, mocker): ccxt_exceptionhandlers( mocker, default_conf, api_mock, "bitget", "additional_exchange_init", "set_position_mode" ) + + +def test_dry_run_liquidation_price_cross_bitget(default_conf, mocker): + default_conf["dry_run"] = True + default_conf["trading_mode"] = TradingMode.FUTURES + default_conf["margin_mode"] = MarginMode.CROSS + api_mock = MagicMock() + mocker.patch(f"{EXMS}.get_maintenance_ratio_and_amt", MagicMock(return_value=(0.005, 0.0))) + exchange = get_patched_exchange(mocker, default_conf, exchange="bitget", api_mock=api_mock) + + with pytest.raises( + OperationalException, match="Freqtrade currently only supports isolated futures for bitget" + ): + exchange.dry_run_liquidation_price( + "ETH/USDT:USDT", + 100_000, + False, + 0.1, + 100, + 10, + 100, + [], + ) From 46dc37847200fa1a2b28943e9bafe1b691e729bc Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Oct 2025 09:53:42 +0200 Subject: [PATCH 19/20] test: add bitget leverage prep test --- tests/exchange/test_bitget.py | 38 +++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index 216e953db..ca32ba536 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -1,6 +1,5 @@ from datetime import timedelta -from sqlite3 import OperationalError -from unittest.mock import MagicMock +from unittest.mock import MagicMock, PropertyMock import pytest @@ -159,3 +158,38 @@ def test_dry_run_liquidation_price_cross_bitget(default_conf, mocker): 100, [], ) + + +def test__lev_prep_bitget(default_conf, mocker): + api_mock = MagicMock() + api_mock.set_margin_mode = MagicMock() + api_mock.set_leverage = MagicMock() + type(api_mock).has = PropertyMock(return_value={"setMarginMode": True, "setLeverage": True}) + exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") + exchange._lev_prep("BTC/USDC:USDC", 3.2, "buy") + + assert api_mock.set_margin_mode.call_count == 0 + assert api_mock.set_leverage.call_count == 0 + + # test in futures mode + api_mock.set_margin_mode.reset_mock() + api_mock.set_leverage.reset_mock() + default_conf["dry_run"] = False + + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + + exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") + exchange._lev_prep("BTC/USDC:USDC", 3.2, "buy") + + assert api_mock.set_margin_mode.call_count == 0 + assert api_mock.set_leverage.call_count == 1 + api_mock.set_leverage.assert_called_with(symbol="BTC/USDC:USDC", leverage=3.2) + + api_mock.reset_mock() + + exchange._lev_prep("BTC/USDC:USDC", 19.99, "sell") + + assert api_mock.set_margin_mode.call_count == 0 + assert api_mock.set_leverage.call_count == 1 + api_mock.set_leverage.assert_called_with(symbol="BTC/USDC:USDC", leverage=19.99) From dcd9e2ef1457849a527a954603c593f022a201b8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 18 Oct 2025 13:02:33 +0200 Subject: [PATCH 20/20] feat: bitget stop orders don't block assets --- freqtrade/exchange/bitget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 398cf1803..a1d6db255 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -29,6 +29,7 @@ class Bitget(Exchange): "stoploss_on_exchange": True, "stop_price_param": "stopPrice", "stop_price_prop": "stopPrice", + "stoploss_blocks_assets": False, # Stoploss orders do not block assets "stoploss_order_types": {"limit": "limit", "market": "market"}, "ohlcv_candle_limit": 200, # 200 for historical candles, 1000 for recent ones. "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], @@ -36,7 +37,6 @@ class Bitget(Exchange): _ft_has_futures: FtHas = { "mark_ohlcv_timeframe": "4h", "funding_fee_candle_limit": 100, - "stoploss_blocks_assets": False, # Stoploss orders do not block assets } _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [