diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 7d50e2c57..ef5c65f5b 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -86,14 +86,22 @@ class Binance(Exchange): try: params = self._params.copy() params.update({'stopPrice': stop_price}) + if self.trading_mode == TradingMode.FUTURES: + params.update({'reduceOnly': True}) amount = self.amount_to_precision(pair, amount) rate = self.price_to_precision(pair, rate) - self._lev_prep(pair, leverage) - order = self._api.create_order(symbol=pair, type=ordertype, side=side, - amount=amount, price=rate, params=params) + self._lev_prep(pair, leverage, side) + order = self._api.create_order( + symbol=pair, + type=ordertype, + side=side, + amount=amount, + price=rate, + params=params + ) logger.info('stoploss limit order added for %s. ' 'stop price: %s. limit: %s', pair, stop_price, rate) self._log_exchange_response('create_stoploss_order', order) @@ -220,7 +228,7 @@ class Binance(Exchange): return try: - self._api.set_leverage(symbol=pair, leverage=leverage) + self._api.set_leverage(symbol=pair, leverage=round(leverage)) except ccxt.DDoSProtection as e: raise DDosProtection(e) from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 4c5cf0226..2b35417d3 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -877,26 +877,48 @@ class Exchange: # Order handling - def _lev_prep(self, pair: str, leverage: float): + def _lev_prep( + self, + pair: str, + leverage: float, + side: str # buy or sell + ): if self.trading_mode != TradingMode.SPOT: self.set_margin_mode(pair, self.margin_mode) self._set_leverage(leverage, pair) - def _get_params(self, ordertype: str, leverage: float, time_in_force: str = 'gtc') -> Dict: + def _get_params( + self, + ordertype: str, + leverage: float, + reduceOnly: bool, + time_in_force: str = 'gtc', + ) -> Dict: params = self._params.copy() if time_in_force != 'gtc' and ordertype != 'market': param = self._ft_has.get('time_in_force_parameter', '') params.update({param: time_in_force}) + if reduceOnly: + params.update({'reduceOnly': True}) return params - def create_order(self, pair: str, ordertype: str, side: str, amount: float, - rate: float, leverage: float = 1.0, time_in_force: str = 'gtc') -> Dict: + def create_order( + self, + pair: str, + ordertype: str, + side: str, + amount: float, + rate: float, + reduceOnly: bool = False, + leverage: float = 1.0, + time_in_force: str = 'gtc', + ) -> Dict: # TODO-lev: remove default for leverage if self._config['dry_run']: dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate, leverage) return dry_order - params = self._get_params(ordertype, leverage, time_in_force) + params = self._get_params(ordertype, leverage, reduceOnly, time_in_force) try: # Set the precision for amount and price(rate) as accepted by the exchange @@ -905,7 +927,9 @@ class Exchange: or self._api.options.get("createMarketBuyOrderRequiresPrice", False)) rate_for_order = self.price_to_precision(pair, rate) if needs_price else None - self._lev_prep(pair, leverage) + if not reduceOnly: + self._lev_prep(pair, leverage, side) + order = self._api.create_order( pair, ordertype, @@ -1786,7 +1810,7 @@ class Exchange: try: funding_history = self._api.fetch_funding_history( - pair=pair, + symbol=pair, since=since ) return sum(fee['amount'] for fee in funding_history) @@ -1861,7 +1885,7 @@ class Exchange: return try: - self._api.set_margin_mode(pair, margin_mode.value, params) + self._api.set_margin_mode(margin_mode.value, pair, params) except ccxt.DDoSProtection as e: raise DDosProtection(e) from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 88e906a56..be2e19c66 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -70,11 +70,13 @@ class Ftx(Exchange): if order_types.get('stoploss', 'market') == 'limit': # set orderPrice to place limit order, otherwise it's a market order params['orderPrice'] = limit_rate + if self.trading_mode == TradingMode.FUTURES: + params.update({'reduceOnly': True}) params['stopPrice'] = stop_price amount = self.amount_to_precision(pair, amount) - self._lev_prep(pair, leverage) + self._lev_prep(pair, leverage, side) order = self._api.create_order(symbol=pair, type=ordertype, side=side, amount=amount, params=params) self._log_exchange_response('create_stoploss_order', order) diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 308a6feab..0c3fe4e7b 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -101,6 +101,8 @@ class Kraken(Exchange): Stoploss market orders is the only stoploss type supported by kraken. """ params = self._params.copy() + if self.trading_mode == TradingMode.FUTURES: + params.update({'reduceOnly': True}) if order_types.get('stoploss', 'market') == 'limit': ordertype = "stop-loss-limit" @@ -159,10 +161,16 @@ class Kraken(Exchange): """ return - def _get_params(self, ordertype: str, leverage: float, time_in_force: str = 'gtc') -> Dict: - params = super()._get_params(ordertype, leverage, time_in_force) + def _get_params( + self, + ordertype: str, + leverage: float, + reduceOnly: bool, + time_in_force: str = 'gtc' + ) -> Dict: + params = super()._get_params(ordertype, leverage, reduceOnly, time_in_force) if leverage > 1.0: - params['leverage'] = leverage + params['leverage'] = round(leverage) return params def calculate_funding_fees( diff --git a/freqtrade/exchange/okex.py b/freqtrade/exchange/okex.py index b6252ed1e..3c774cc9a 100644 --- a/freqtrade/exchange/okex.py +++ b/freqtrade/exchange/okex.py @@ -3,7 +3,7 @@ from typing import Dict, List, Tuple from freqtrade.enums import MarginMode, TradingMode from freqtrade.exchange import Exchange - +from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) @@ -26,3 +26,22 @@ class Okex(Exchange): # (TradingMode.FUTURES, MarginMode.CROSS), # (TradingMode.FUTURES, MarginMode.ISOLATED) ] + + def _lev_prep( + self, + pair: str, + leverage: float, + side: str # buy or sell + ): + if self.trading_mode != TradingMode.SPOT: + if self.margin_mode is None: + raise OperationalException( + f"{self.name}.margin_mode must be set for {self.trading_mode.value}" + ) + self._api.set_leverage( + leverage, + pair, + params={ + "mgnMode": self.margin_mode.value, + "posSide": "long" if side == "buy" else "short", + }) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 38d80ab7a..6f39c23f7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -699,6 +699,7 @@ class FreqtradeBot(LoggingMixin): side=side, amount=amount, rate=enter_limit_requested, + reduceOnly=False, time_in_force=time_in_force, leverage=leverage ) @@ -1422,6 +1423,7 @@ class FreqtradeBot(LoggingMixin): side=trade.exit_side, amount=amount, rate=limit, + reduceOnly=self.trading_mode == TradingMode.FUTURES, time_in_force=time_in_force ) except InsufficientFundsError as e: