diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 48730fa98..a5f920787 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -110,98 +110,5 @@ class Binance(Exchange): }) logger.info(f"Transfer response: {res}") - def borrow(self, asset: str, amount: float, pair: str): - res = self._api.sapi_post_margin_loan({ - "asset": asset, - "isIsolated": True, - "symbol": pair, - "amount": amount - }) # borrow from binance - logger.info(f"Borrow response: {res}") - - def repay(self, asset: str, amount: float, pair: str): - res = self._api.sapi_post_margin_repay({ - "asset": asset, - "isIsolated": True, - "symbol": pair, - "amount": amount - }) # borrow from binance - logger.info(f"Borrow response: {res}") - - def setup_leveraged_enter( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - if not quote_currency or not is_short: - raise OperationalException( - "quote_currency and is_short are required arguments to setup_leveraged_enter" - " when trading with leverage on binance" - ) - open_rate = 2 # TODO-mg: get the real open_rate, or real stake_amount - stake_amount = amount * open_rate - if is_short: - borrowed = stake_amount * ((leverage-1)/leverage) - else: - borrowed = amount - - self.transfer( # Transfer to isolated margin - asset=quote_currency, - amount=stake_amount, - frm='SPOT', - to='ISOLATED_MARGIN', - pair=pair - ) - - self.borrow( - asset=quote_currency, - amount=borrowed, - pair=pair - ) # borrow from binance - - def complete_leveraged_exit( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - - if not quote_currency or not is_short: - raise OperationalException( - "quote_currency and is_short are required arguments to setup_leveraged_enter" - " when trading with leverage on binance" - ) - - open_rate = 2 # TODO-mg: get the real open_rate, or real stake_amount - stake_amount = amount * open_rate - if is_short: - borrowed = stake_amount * ((leverage-1)/leverage) - else: - borrowed = amount - - self.repay( - asset=quote_currency, - amount=borrowed, - pair=pair - ) # repay binance - - self.transfer( # Transfer to isolated margin - asset=quote_currency, - amount=stake_amount, - frm='ISOLATED_MARGIN', - to='SPOT', - pair=pair - ) - def apply_leverage_to_stake_amount(self, stake_amount: float, leverage: float): return stake_amount / leverage - - def get_isolated_liq(self, pair: str, open_rate: float, - amount: float, leverage: float, is_short: bool) -> float: - # TODO-mg: implement - return 0.0 diff --git a/freqtrade/exchange/bittrex.py b/freqtrade/exchange/bittrex.py index d9dc9287b..69e2f2b8d 100644 --- a/freqtrade/exchange/bittrex.py +++ b/freqtrade/exchange/bittrex.py @@ -1,8 +1,7 @@ """ Bittrex exchange subclass """ import logging -from typing import Dict, Optional +from typing import Dict -from freqtrade.exceptions import OperationalException from freqtrade.exchange import Exchange @@ -24,28 +23,3 @@ class Bittrex(Exchange): }, "l2_limit_range": [1, 25, 500], } - - def setup_leveraged_enter( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - raise OperationalException("Bittrex does not support leveraged trading") - - def complete_leveraged_exit( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - raise OperationalException("Bittrex does not support leveraged trading") - - def get_isolated_liq(self, pair: str, open_rate: float, - amount: float, leverage: float, is_short: bool) -> float: - # TODO-mg: implement - raise OperationalException("Bittrex does not support margin trading") diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index b4cd01ef8..3e726a9e1 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -707,8 +707,7 @@ class Exchange: def get_max_leverage(self, pair: str, stake_amount: float, price: float) -> float: """ - Gets the maximum leverage available on this pair that is below the config leverage - but higher than the config min_leverage + Gets the maximum leverage available on this pair """ raise OperationalException(f"Leverage is not available on {self.name} using freqtrade") @@ -758,26 +757,6 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def setup_leveraged_enter( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - raise OperationalException(f"Leverage is not available on {self.name} using freqtrade") - - def complete_leveraged_exit( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - raise OperationalException(f"Leverage is not available on {self.name} using freqtrade") - def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool: """ Verify stop_loss against stoploss-order value (limit or price) @@ -1542,19 +1521,13 @@ class Exchange: self._async_get_trade_history(pair=pair, since=since, until=until, from_id=from_id)) - def transfer(self, asset: str, amount: float, frm: str, to: str, pair: Optional[str]): - self._api.transfer(asset, amount, frm, to) - - def get_isolated_liq(self, pair: str, open_rate: float, - amount: float, leverage: float, is_short: bool) -> float: - raise OperationalException( - f"Isolated margin is not available on {self.name} using freqtrade" - ) - def get_interest_rate(self, pair: str, open_rate: float, is_short: bool) -> float: # TODO-mg: implement return 0.0005 + def set_leverage(self, pair, leverage): + self._api.set_leverage(symbol=pair, leverage=leverage) + def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool: return exchange_name in ccxt_exchanges(ccxt_module) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 4f7f19579..50ea28079 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -160,9 +160,3 @@ class Ftx(Exchange): if order['type'] == 'stop': return safe_value_fallback2(order, order, 'id_stop', 'id') return order['id'] - - def get_isolated_liq(self, pair: str, open_rate: float, - amount: float, leverage: float, is_short: bool) -> float: - # TODO-mg: implement - raise OperationalException( - "Isolated margin trading not yet implemented on FTX, would you like to implement it?") diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index ef7cfa17b..a84b0099b 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -1,6 +1,6 @@ """ Kraken exchange subclass """ import logging -from typing import Any, Dict, Optional +from typing import Any, Dict import ccxt @@ -132,28 +132,3 @@ class Kraken(Exchange): f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: raise OperationalException(e) from e - - def setup_leveraged_enter( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - return - - def complete_leveraged_exit( - self, - pair: str, - leverage: float, - amount: float, - quote_currency: Optional[str], - is_short: Optional[bool] - ): - return - - def get_isolated_liq(self, pair: str, open_rate: float, - amount: float, leverage: float, is_short: bool) -> float: - # TODO-mg: implement - raise OperationalException("Kraken only supports cross margin trading") diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d590c6558..01fb729b2 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -532,6 +532,42 @@ class FreqtradeBot(LoggingMixin): logger.info(f"Bids to asks delta for {pair} does not satisfy condition.") return False + def leverage_prep( + self, + pair: str, + open_rate: float, + amount: float, + leverage: float, + is_short: bool + ): # -> (float, Optional[float]): + + interest_rate = 0.0 + isolated_liq = None + + if leverage > 1.0: + interest_rate = self.exchange.get_interest_rate( + pair=pair, + open_rate=open_rate, + is_short=is_short + ) + + if self.trading_mode == TradingMode.ISOLATED_MARGIN or \ + self.trading_mode == TradingMode.ISOLATED_FUTURES: + + isolated_liq = self.exchange.liq_formula( + trading_mode=self.trading_mode, + open_rate=open_rate, + amount=amount, + leverage=leverage, + is_short=is_short + ) + + if self.trading_mode == TradingMode.CROSS_FUTURES or \ + self.trading_mode == TradingMode.ISOLATED_FUTURES: + self.exchange.set_leverage(pair=pair, leverage=leverage) + + return interest_rate, isolated_liq + def execute_enter( self, pair: str, @@ -598,6 +634,7 @@ class FreqtradeBot(LoggingMixin): logger.info(f"User requested abortion of {name.lower()}ing {pair}") return False amount = self.exchange.amount_to_precision(pair, amount) + order = self.exchange.create_order(pair=pair, ordertype=order_type, side=side, amount=amount, rate=enter_limit_requested, time_in_force=time_in_force) @@ -638,26 +675,13 @@ class FreqtradeBot(LoggingMixin): amount = safe_value_fallback(order, 'filled', 'amount') enter_limit_filled_price = safe_value_fallback(order, 'average', 'price') - interest_rate = 0.0 - isolated_liq = None - - if leverage > 1.0: - interest_rate = self.exchange.get_interest_rate( - pair=pair, - open_rate=enter_limit_filled_price, - is_short=is_short - ) - - if self.trading_mode == TradingMode.ISOLATED_MARGIN or \ - self.trading_mode == TradingMode.ISOLATED_FUTURES: - - isolated_liq = self.exchange.liq_formula( - trading_mode=self.trading_mode, - open_rate=enter_limit_filled_price, - amount=amount, - leverage=leverage, - is_short=is_short - ) + interest_rate, isolated_liq = self.leverage_prep( + leverage=leverage, + pair=pair, + amount=amount, + open_rate=enter_limit_filled_price, + is_short=is_short + ) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')