Add prep functions to exchange
This commit is contained in:
		| @@ -1,6 +1,6 @@ | ||||
| """ Binance exchange subclass """ | ||||
| import logging | ||||
| from typing import Dict | ||||
| from typing import Dict, Optional | ||||
|  | ||||
| import ccxt | ||||
|  | ||||
| @@ -89,3 +89,104 @@ class Binance(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 transfer(self, asset: str, amount: float, frm: str, to: str, pair: Optional[str]): | ||||
|         res = self._api.sapi_post_margin_isolated_transfer({ | ||||
|             "asset": asset, | ||||
|             "amount": amount, | ||||
|             "transFrom": frm, | ||||
|             "transTo": to, | ||||
|             "symbol": pair | ||||
|         }) | ||||
|         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 | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| """ Bittrex exchange subclass """ | ||||
| import logging | ||||
| from typing import Dict | ||||
| from typing import Dict, Optional | ||||
|  | ||||
| from freqtrade.exchange import Exchange | ||||
| from freqtrade.exceptions import OperationalException | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
| @@ -23,3 +24,23 @@ 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") | ||||
|   | ||||
| @@ -184,6 +184,7 @@ class Exchange: | ||||
|             'secret': exchange_config.get('secret'), | ||||
|             'password': exchange_config.get('password'), | ||||
|             'uid': exchange_config.get('uid', ''), | ||||
|             'options': exchange_config.get('options', {}) | ||||
|         } | ||||
|         if ccxt_kwargs: | ||||
|             logger.info('Applying additional ccxt config: %s', ccxt_kwargs) | ||||
| @@ -524,8 +525,9 @@ class Exchange: | ||||
|         else: | ||||
|             return 1 / pow(10, precision) | ||||
|  | ||||
|     def get_min_pair_stake_amount(self, pair: str, price: float, | ||||
|                                   stoploss: float) -> Optional[float]: | ||||
|     def get_min_pair_stake_amount(self, pair: str, price: float, stoploss: float, | ||||
|                                   leverage: Optional[float] = 1.0) -> Optional[float]: | ||||
|         # TODO-mg: Using leverage makes the min stake amount lower (on binance at least) | ||||
|         try: | ||||
|             market = self.markets[pair] | ||||
|         except KeyError: | ||||
| @@ -559,7 +561,20 @@ class Exchange: | ||||
|         # The value returned should satisfy both limits: for amount (base currency) and | ||||
|         # for cost (quote, stake currency), so max() is used here. | ||||
|         # See also #2575 at github. | ||||
|         return max(min_stake_amounts) * amount_reserve_percent | ||||
|         return self.apply_leverage_to_stake_amount( | ||||
|             max(min_stake_amounts) * amount_reserve_percent, | ||||
|             leverage or 1.0 | ||||
|         ) | ||||
|  | ||||
|     def apply_leverage_to_stake_amount(self, stake_amount: float, leverage: float): | ||||
|         """ | ||||
|         #* Should be implemented by child classes if leverage affects the stake_amount | ||||
|         Takes the minimum stake amount for a pair with no leverage and returns the minimum | ||||
|         stake amount when leverage is considered | ||||
|         :param stake_amount: The stake amount for a pair before leverage is considered | ||||
|         :param leverage: The amount of leverage being used on the current trade | ||||
|         """ | ||||
|         return stake_amount | ||||
|  | ||||
|     # Dry-run methods | ||||
|  | ||||
| @@ -686,6 +701,15 @@ class Exchange: | ||||
|             raise InvalidOrderException( | ||||
|                 f'Tried to get an invalid dry-run-order (id: {order_id}). Message: {e}') from e | ||||
|  | ||||
|     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 | ||||
|         """ | ||||
|  | ||||
|         raise OperationalException(f"Leverage is not available on {self.name} using freqtrade") | ||||
|         return 1.0 | ||||
|  | ||||
|     # Order handling | ||||
|  | ||||
|     def create_order(self, pair: str, ordertype: str, side: str, amount: float, | ||||
| @@ -709,6 +733,7 @@ class Exchange: | ||||
|             order = self._api.create_order(pair, ordertype, side, | ||||
|                                            amount, rate_for_order, params) | ||||
|             self._log_exchange_response('create_order', order) | ||||
|  | ||||
|             return order | ||||
|  | ||||
|         except ccxt.InsufficientFunds as e: | ||||
| @@ -729,6 +754,26 @@ 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) -> bool: | ||||
|         """ | ||||
|         Verify stop_loss against stoploss-order value (limit or price) | ||||
| @@ -1492,6 +1537,9 @@ 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 is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool: | ||||
|     return exchange_name in ccxt_exchanges(ccxt_module) | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| """ Kraken exchange subclass """ | ||||
| import logging | ||||
| from typing import Any, Dict | ||||
| from typing import Any, Dict, Optional | ||||
|  | ||||
| import ccxt | ||||
|  | ||||
| @@ -124,3 +124,23 @@ 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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user