diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index 464b38967..c4bd0bf4d 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -129,10 +129,9 @@ def start_list_markets(args: Dict[str, Any], pairs_only: bool = False) -> None: quote_currencies = args.get('quote_currencies', []) try: - # TODO-lev: Add leverage amount to get markets that support a certain leverage pairs = exchange.get_markets(base_currencies=base_currencies, quote_currencies=quote_currencies, - pairs_only=pairs_only, + tradable_only=pairs_only, active_only=active_only) # Sort the pairs/markets by symbol pairs = dict(sorted(pairs.items())) @@ -152,15 +151,19 @@ def start_list_markets(args: Dict[str, Any], pairs_only: bool = False) -> None: if quote_currencies else "")) headers = ["Id", "Symbol", "Base", "Quote", "Active", - *(['Is pair'] if not pairs_only else [])] + "Spot", "Margin", "Future", "Leverage"] - tabular_data = [] - for _, v in pairs.items(): - tabular_data.append({'Id': v['id'], 'Symbol': v['symbol'], - 'Base': v['base'], 'Quote': v['quote'], - 'Active': market_is_active(v), - **({'Is pair': exchange.market_is_tradable(v)} - if not pairs_only else {})}) + tabular_data = [{ + 'Id': v['id'], + 'Symbol': v['symbol'], + 'Base': v['base'], + 'Quote': v['quote'], + 'Active': market_is_active(v), + 'Spot': 'Spot' if exchange.market_is_spot(v) else '', + 'Margin': 'Margin' if exchange.market_is_margin(v) else '', + 'Future': 'Future' if exchange.market_is_future(v) else '', + 'Leverage': exchange.get_max_leverage(v['symbol'], 20) + } for _, v in pairs.items()] if (args.get('print_one_column', False) or args.get('list_pairs_print_json', False) or diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index a2fbfba02..fed464712 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -280,7 +280,9 @@ class Exchange: timeframe, self._ft_has.get('ohlcv_candle_limit'))) def get_markets(self, base_currencies: List[str] = None, quote_currencies: List[str] = None, - pairs_only: bool = False, active_only: bool = False) -> Dict[str, Any]: + spot_only: bool = False, margin_only: bool = False, futures_only: bool = False, + tradable_only: bool = True, + active_only: bool = False) -> Dict[str, Any]: """ Return exchange ccxt markets, filtered out by base currency and quote currency if this was requested in parameters. @@ -295,8 +297,14 @@ class Exchange: markets = {k: v for k, v in markets.items() if v['base'] in base_currencies} if quote_currencies: markets = {k: v for k, v in markets.items() if v['quote'] in quote_currencies} - if pairs_only: + if tradable_only: markets = {k: v for k, v in markets.items() if self.market_is_tradable(v)} + if spot_only: + markets = {k: v for k, v in markets.items() if self.market_is_spot(v)} + if margin_only: + markets = {k: v for k, v in markets.items() if self.market_is_margin(v)} + if futures_only: + markets = {k: v for k, v in markets.items() if self.market_is_future(v)} if active_only: markets = {k: v for k, v in markets.items() if market_is_active(v)} return markets @@ -320,18 +328,25 @@ class Exchange: """ return self.markets.get(pair, {}).get('base', '') + def market_is_future(self, market: Dict[str, Any]) -> bool: + return market.get('future', False) is True + + def market_is_spot(self, market: Dict[str, Any]) -> bool: + return market.get('spot', False) is True + + def market_is_margin(self, market: Dict[str, Any]) -> bool: + return market.get('margin', False) is True + def market_is_tradable(self, market: Dict[str, Any]) -> bool: """ Check if the market symbol is tradable by Freqtrade. - By default, checks if it's splittable by `/` and both sides correspond to base / quote + Ensures that Configured mode aligns to """ - symbol_parts = market['symbol'].split('/') - return (len(symbol_parts) == 2 and - len(symbol_parts[0]) > 0 and - len(symbol_parts[1]) > 0 and - symbol_parts[0] == market.get('base') and - symbol_parts[1] == market.get('quote') - ) + return ( + (self.trading_mode == TradingMode.SPOT and self.market_is_spot(market)) + or (self.trading_mode == TradingMode.MARGIN and self.market_is_margin(market)) + or (self.trading_mode == TradingMode.FUTURES and self.market_is_future(market)) + ) def klines(self, pair_interval: Tuple[str, str], copy: bool = True) -> DataFrame: if pair_interval in self._klines: diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 2acf32ba3..4b3e765bb 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -29,15 +29,6 @@ class Ftx(Exchange): # (TradingMode.FUTURES, Collateral.CROSS) # TODO-lev: Uncomment once supported ] - def market_is_tradable(self, market: Dict[str, Any]) -> bool: - """ - Check if the market symbol is tradable by Freqtrade. - Default checks + check if pair is spot pair (no futures trading yet). - """ - parent_check = super().market_is_tradable(market) - - return (parent_check and - market.get('spot', False) is True) def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool: """