From f369151e8ee5094b97068e8effbf54016e977cef Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Oct 2024 06:46:45 +0200 Subject: [PATCH] chore: update exchange config to modern typing syntax --- freqtrade/exchange/binance.py | 12 +- freqtrade/exchange/bitpanda.py | 6 +- freqtrade/exchange/bybit.py | 16 +- freqtrade/exchange/common.py | 4 +- freqtrade/exchange/exchange.py | 221 ++++++++++++++------------- freqtrade/exchange/exchange_types.py | 28 ++-- freqtrade/exchange/exchange_utils.py | 16 +- freqtrade/exchange/exchange_ws.py | 11 +- freqtrade/exchange/gate.py | 18 +-- freqtrade/exchange/htx.py | 3 +- freqtrade/exchange/hyperliquid.py | 3 +- freqtrade/exchange/kraken.py | 14 +- freqtrade/exchange/kucoin.py | 5 +- freqtrade/exchange/okx.py | 24 +-- 14 files changed, 189 insertions(+), 192 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 13582b183..c0e46c32a 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -3,7 +3,7 @@ import logging from datetime import datetime, timezone from pathlib import Path -from typing import Dict, List, Optional, Tuple +from typing import Optional import ccxt @@ -46,14 +46,14 @@ class Binance(Exchange): "ws_enabled": False, } - _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ + _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list # (TradingMode.MARGIN, MarginMode.CROSS), # (TradingMode.FUTURES, MarginMode.CROSS), (TradingMode.FUTURES, MarginMode.ISOLATED) ] - def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Tickers: + def get_tickers(self, symbols: Optional[list[str]] = None, cached: bool = False) -> Tickers: tickers = super().get_tickers(symbols=symbols, cached=cached) if self.trading_mode == TradingMode.FUTURES: # Binance's future result has no bid/ask values. @@ -145,8 +145,8 @@ class Binance(Exchange): return open_date.minute == 0 and open_date.second < 15 def fetch_funding_rates( - self, symbols: Optional[List[str]] = None - ) -> Dict[str, Dict[str, float]]: + self, symbols: Optional[list[str]] = None + ) -> dict[str, dict[str, float]]: """ Fetch funding rates for the given symbols. :param symbols: List of symbols to fetch funding rates for @@ -253,7 +253,7 @@ class Binance(Exchange): "Freqtrade only supports isolated futures for leverage trading" ) - def load_leverage_tiers(self) -> Dict[str, List[Dict]]: + def load_leverage_tiers(self) -> dict[str, list[dict]]: if self.trading_mode == TradingMode.FUTURES: if self._config["dry_run"]: leverage_tiers_path = Path(__file__).parent / "binance_leverage_tiers.json" diff --git a/freqtrade/exchange/bitpanda.py b/freqtrade/exchange/bitpanda.py index 1e93256e7..cca961cb7 100644 --- a/freqtrade/exchange/bitpanda.py +++ b/freqtrade/exchange/bitpanda.py @@ -2,7 +2,7 @@ import logging from datetime import datetime, timezone -from typing import Dict, List, Optional +from typing import Optional from freqtrade.exchange import Exchange @@ -17,8 +17,8 @@ class Bitpanda(Exchange): """ def get_trades_for_order( - self, order_id: str, pair: str, since: datetime, params: Optional[Dict] = None - ) -> List: + self, order_id: str, pair: str, since: datetime, params: Optional[dict] = None + ) -> list: """ Fetch Orders using the "fetch_my_trades" endpoint and filter them by order-id. The "since" argument passed in is coming from the database and is in UTC, diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index 967c80b7d..e69045b4c 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -2,7 +2,7 @@ import logging from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Optional import ccxt @@ -53,14 +53,14 @@ class Bybit(Exchange): }, } - _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ + _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list # (TradingMode.FUTURES, MarginMode.CROSS), (TradingMode.FUTURES, MarginMode.ISOLATED) ] @property - def _ccxt_config(self) -> Dict: + def _ccxt_config(self) -> dict: # Parameters to add directly to ccxt sync/async initialization. # ccxt defaults to swap mode. config = {} @@ -69,7 +69,7 @@ class Bybit(Exchange): config.update(super()._ccxt_config) return config - def market_is_future(self, market: Dict[str, Any]) -> bool: + def market_is_future(self, market: dict[str, Any]) -> bool: main = super().market_is_future(market) # For ByBit, we'll only support USDT markets for now. return main and market["settle"] == "USDT" @@ -126,7 +126,7 @@ class Bybit(Exchange): leverage: float, reduceOnly: bool, time_in_force: str = "GTC", - ) -> Dict: + ) -> dict: params = super()._get_params( side=side, ordertype=ordertype, @@ -220,7 +220,7 @@ class Bybit(Exchange): logger.warning(f"Could not update funding fees for {pair}.") return 0.0 - def fetch_orders(self, pair: str, since: datetime, params: Optional[Dict] = None) -> List[Dict]: + def fetch_orders(self, pair: str, since: datetime, params: Optional[dict] = None) -> list[dict]: """ Fetch all orders for a pair "since" :param pair: Pair for the query @@ -237,7 +237,7 @@ class Bybit(Exchange): return orders - def fetch_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: + def fetch_order(self, order_id: str, pair: str, params: Optional[dict] = None) -> dict: if self.exchange_has("fetchOrder"): # Set acknowledged to True to avoid ccxt exception params = {"acknowledged": True} @@ -255,7 +255,7 @@ class Bybit(Exchange): return order @retrier - def get_leverage_tiers(self) -> Dict[str, List[Dict]]: + def get_leverage_tiers(self) -> dict[str, list[dict]]: """ Cache leverage tiers for 1 day, since they are not expected to change often, and bybit requires pagination to fetch all tiers. diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index 1bb738dcb..b32216b05 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -2,7 +2,7 @@ import asyncio import logging import time from functools import wraps -from typing import Any, Callable, Dict, List, Optional, TypeVar, cast, overload +from typing import Any, Callable, Optional, TypeVar, cast, overload from freqtrade.constants import ExchangeConfig from freqtrade.exceptions import DDosProtection, RetryableOrderError, TemporaryError @@ -62,7 +62,7 @@ SUPPORTED_EXCHANGES = [ ] # either the main, or replacement methods (array) is required -EXCHANGE_HAS_REQUIRED: Dict[str, List[str]] = { +EXCHANGE_HAS_REQUIRED: dict[str, list[str]] = { # Required / private "fetchOrder": ["fetchOpenOrder", "fetchClosedOrder"], "fetchL2OrderBook": ["fetchTicker"], diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index c0b4a72e9..a0cd018ed 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -7,11 +7,12 @@ import asyncio import inspect import logging import signal +from collections.abc import Coroutine from copy import deepcopy from datetime import datetime, timedelta, timezone from math import floor, isnan from threading import Lock -from typing import Any, Coroutine, Dict, List, Literal, Optional, Tuple, Union +from typing import Any, Literal, Optional, Union import ccxt import ccxt.pro as ccxt_pro @@ -114,10 +115,10 @@ logger = logging.getLogger(__name__) class Exchange: # Parameters to add directly to buy/sell calls (like agreeing to trading agreement) - _params: Dict = {} + _params: dict = {} # Additional parameters - added to the ccxt object - _ccxt_params: Dict = {} + _ccxt_params: dict = {} # Dict to specify which options each exchange implements # This defines defaults, which can be selectively overridden by subclasses using _ft_has @@ -160,7 +161,7 @@ class Exchange: _ft_has: FtHas = {} _ft_has_futures: FtHas = {} - _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ + _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list ] @@ -181,9 +182,9 @@ class Exchange: self._api_async: ccxt_pro.Exchange self._ws_async: ccxt_pro.Exchange = None self._exchange_ws: Optional[ExchangeWS] = None - self._markets: Dict = {} - self._trading_fees: Dict[str, Any] = {} - self._leverage_tiers: Dict[str, List[Dict]] = {} + self._markets: dict = {} + self._trading_fees: dict[str, Any] = {} + self._leverage_tiers: dict[str, list[dict]] = {} # Lock event loop. This is necessary to avoid race-conditions when using force* commands # Due to funding fee fetching. self._loop_lock = Lock() @@ -193,7 +194,7 @@ class Exchange: self._config.update(config) # Holds last candle refreshed time of each pair - self._pairs_last_refresh_time: Dict[PairWithTimeframe, int] = {} + self._pairs_last_refresh_time: dict[PairWithTimeframe, int] = {} # Timestamp of last markets refresh self._last_markets_refresh: int = 0 @@ -208,19 +209,19 @@ class Exchange: self._entry_rate_cache: TTLCache = TTLCache(maxsize=100, ttl=300) # Holds candles - self._klines: Dict[PairWithTimeframe, DataFrame] = {} - self._expiring_candle_cache: Dict[Tuple[str, int], PeriodicCache] = {} + self._klines: dict[PairWithTimeframe, DataFrame] = {} + self._expiring_candle_cache: dict[tuple[str, int], PeriodicCache] = {} # Holds public_trades - self._trades: Dict[PairWithTimeframe, DataFrame] = {} + self._trades: dict[PairWithTimeframe, DataFrame] = {} # Holds all open sell orders for dry_run - self._dry_run_open_orders: Dict[str, Any] = {} + self._dry_run_open_orders: dict[str, Any] = {} if config["dry_run"]: logger.info("Instance is running with dry_run enabled") logger.info(f"Using CCXT {ccxt.__version__}") - exchange_conf: Dict[str, Any] = exchange_config if exchange_config else config["exchange"] + exchange_conf: dict[str, Any] = exchange_config if exchange_config else config["exchange"] remove_exchange_credentials(exchange_conf, config.get("dry_run", False)) self.log_responses = exchange_conf.get("log_responses", False) @@ -339,7 +340,7 @@ class Exchange: self.validate_freqai(config) def _init_ccxt( - self, exchange_config: Dict[str, Any], sync: bool, ccxt_kwargs: Dict[str, Any] + self, exchange_config: dict[str, Any], sync: bool, ccxt_kwargs: dict[str, Any] ) -> ccxt.Exchange: """ Initialize ccxt with given config and return valid ccxt instance. @@ -390,7 +391,7 @@ class Exchange: return api @property - def _ccxt_config(self) -> Dict: + def _ccxt_config(self) -> dict: # Parameters to add directly to ccxt sync/async initialization. if self.trading_mode == TradingMode.MARGIN: return {"options": {"defaultType": "margin"}} @@ -410,11 +411,11 @@ class Exchange: return self._api.id @property - def timeframes(self) -> List[str]: + def timeframes(self) -> list[str]: return list((self._api.timeframes or {}).keys()) @property - def markets(self) -> Dict[str, Any]: + def markets(self) -> dict[str, Any]: """exchange ccxt markets""" if not self._markets: logger.info("Markets were not loaded. Loading them now..") @@ -471,14 +472,14 @@ class Exchange: def get_markets( self, - base_currencies: Optional[List[str]] = None, - quote_currencies: Optional[List[str]] = None, + base_currencies: Optional[list[str]] = None, + quote_currencies: Optional[list[str]] = None, spot_only: bool = False, margin_only: bool = False, futures_only: bool = False, tradable_only: bool = True, active_only: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """ Return exchange ccxt markets, filtered out by base currency and quote currency if this was requested in parameters. @@ -503,7 +504,7 @@ class Exchange: markets = {k: v for k, v in markets.items() if market_is_active(v)} return markets - def get_quote_currencies(self) -> List[str]: + def get_quote_currencies(self) -> list[str]: """ Return a list of supported quote currencies """ @@ -518,19 +519,19 @@ class Exchange: """Return a pair's base currency (base/quote:settlement)""" return self.markets.get(pair, {}).get("base", "") - def market_is_future(self, market: Dict[str, Any]) -> bool: + def market_is_future(self, market: dict[str, Any]) -> bool: return ( market.get(self._ft_has["ccxt_futures_name"], False) is True and market.get("linear", False) is True ) - def market_is_spot(self, market: Dict[str, Any]) -> bool: + 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: + 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: + def market_is_tradable(self, market: dict[str, Any]) -> bool: """ Check if the market symbol is tradable by Freqtrade. Ensures that Configured mode aligns to @@ -578,7 +579,7 @@ class Exchange: else: return 1 - def _trades_contracts_to_amount(self, trades: List) -> List: + def _trades_contracts_to_amount(self, trades: list) -> list: if len(trades) > 0 and "symbol" in trades[0]: contract_size = self.get_contract_size(trades[0]["symbol"]) if contract_size != 1: @@ -586,7 +587,7 @@ class Exchange: trade["amount"] = trade["amount"] * contract_size return trades - def _order_contracts_to_amount(self, order: Dict) -> Dict: + def _order_contracts_to_amount(self, order: dict) -> dict: if "symbol" in order and order["symbol"] is not None: contract_size = self.get_contract_size(order["symbol"]) if contract_size != 1: @@ -620,7 +621,7 @@ class Exchange: if self._exchange_ws: self._exchange_ws.reset_connections() - async def _api_reload_markets(self, reload: bool = False) -> Dict[str, Any]: + async def _api_reload_markets(self, reload: bool = False) -> dict[str, Any]: try: return await self._api_async.load_markets(reload=reload, params={}) except ccxt.DDoSProtection as e: @@ -632,7 +633,7 @@ class Exchange: except ccxt.BaseError as e: raise TemporaryError(e) from e - def _load_async_markets(self, reload: bool = False) -> Dict[str, Any]: + def _load_async_markets(self, reload: bool = False) -> dict[str, Any]: try: markets = self.loop.run_until_complete(self._api_reload_markets(reload=reload)) @@ -734,7 +735,7 @@ class Exchange: ): raise ConfigurationError("Timeframes < 1m are currently not supported by Freqtrade.") - def validate_ordertypes(self, order_types: Dict) -> None: + def validate_ordertypes(self, order_types: dict) -> None: """ Checks if order-types configured in strategy/config are supported """ @@ -743,7 +744,7 @@ class Exchange: raise ConfigurationError(f"Exchange {self.name} does not support market orders.") self.validate_stop_ordertypes(order_types) - def validate_stop_ordertypes(self, order_types: Dict) -> None: + def validate_stop_ordertypes(self, order_types: dict) -> None: """ Validate stoploss order types """ @@ -762,7 +763,7 @@ class Exchange: f"On exchange stoploss price type is not supported for {self.name}." ) - def validate_pricing(self, pricing: Dict) -> None: + def validate_pricing(self, pricing: dict) -> None: if pricing.get("use_order_book", False) and not self.exchange_has("fetchL2OrderBook"): raise ConfigurationError(f"Orderbook not available for {self.name}.") if not pricing.get("use_order_book", False) and ( @@ -770,7 +771,7 @@ class Exchange: ): raise ConfigurationError(f"Ticker pricing not available for {self.name}.") - def validate_order_time_in_force(self, order_time_in_force: Dict) -> None: + def validate_order_time_in_force(self, order_time_in_force: dict) -> None: """ Checks if order time in force configured in strategy/config are supported """ @@ -782,7 +783,7 @@ class Exchange: f"Time in force policies are not supported for {self.name} yet." ) - def validate_orderflow(self, exchange: Dict) -> None: + def validate_orderflow(self, exchange: dict) -> None: if exchange.get("use_public_trades", False) and ( not self.exchange_has("fetchTrades") or not self._ft_has["trades_has_history"] ): @@ -1000,16 +1001,16 @@ class Exchange: amount: float, rate: float, leverage: float, - params: Optional[Dict] = None, + params: Optional[dict] = None, stop_loss: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: now = dt_now() order_id = f"dry_run_{side}_{pair}_{now.timestamp()}" # Rounding here must respect to contract sizes _amount = self._contracts_to_amount( pair, self.amount_to_precision(pair, self._amount_to_contracts(pair, amount)) ) - dry_order: Dict[str, Any] = { + dry_order: dict[str, Any] = { "id": order_id, "symbol": pair, "price": rate, @@ -1071,9 +1072,9 @@ class Exchange: def add_dry_order_fee( self, pair: str, - dry_order: Dict[str, Any], + dry_order: dict[str, Any], taker_or_maker: MakerTaker, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: fee = self.get_fee(pair, taker_or_maker=taker_or_maker) dry_order.update( { @@ -1157,8 +1158,8 @@ class Exchange: return False def check_dry_limit_order_filled( - self, order: Dict[str, Any], immediate: bool = False, orderbook: Optional[OrderBook] = None - ) -> Dict[str, Any]: + self, order: dict[str, Any], immediate: bool = False, orderbook: Optional[OrderBook] = None + ) -> dict[str, Any]: """ Check dry-run limit order fill and update fee (if it filled). """ @@ -1185,7 +1186,7 @@ class Exchange: return order - def fetch_dry_run_order(self, order_id) -> Dict[str, Any]: + def fetch_dry_run_order(self, order_id) -> dict[str, Any]: """ Return dry-run order Only call if running in dry-run mode. @@ -1221,7 +1222,7 @@ class Exchange: leverage: float, reduceOnly: bool, time_in_force: str = "GTC", - ) -> Dict: + ) -> dict: params = self._params.copy() if time_in_force != "GTC" and ordertype != "market": params.update({"timeInForce": time_in_force.upper()}) @@ -1247,7 +1248,7 @@ class Exchange: leverage: float, reduceOnly: bool = False, time_in_force: str = "GTC", - ) -> Dict: + ) -> dict: if self._config["dry_run"]: dry_order = self.create_dry_run_order( pair, ordertype, side, amount, self.price_to_precision(pair, rate), leverage @@ -1305,7 +1306,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool: + def stoploss_adjust(self, stop_loss: float, order: dict, side: str) -> bool: """ Verify stop_loss against stoploss-order value (limit or price) Returns True if adjustment is necessary. @@ -1318,8 +1319,8 @@ class Exchange: or (side == "buy" and stop_loss < float(order[price_param])) ) - def _get_stop_order_type(self, user_order_type) -> Tuple[str, str]: - available_order_Types: Dict[str, str] = self._ft_has["stoploss_order_types"] + def _get_stop_order_type(self, user_order_type) -> tuple[str, str]: + available_order_Types: dict[str, str] = self._ft_has["stoploss_order_types"] if user_order_type in available_order_Types.keys(): ordertype = available_order_Types[user_order_type] @@ -1329,7 +1330,7 @@ class Exchange: user_order_type = list(available_order_Types.keys())[0] return ordertype, user_order_type - def _get_stop_limit_rate(self, stop_price: float, order_types: Dict, side: str) -> float: + def _get_stop_limit_rate(self, stop_price: float, order_types: dict, side: str) -> float: # Limit price threshold: As limit price should always be below stop-price limit_price_pct = order_types.get("stoploss_on_exchange_limit_ratio", 0.99) if side == "sell": @@ -1351,7 +1352,7 @@ class Exchange: ) return limit_rate - def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> Dict: + def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict: params = self._params.copy() # Verify if stopPrice works for your exchange, else configure stop_price_param params.update({self._ft_has["stop_price_param"]: stop_price}) @@ -1363,10 +1364,10 @@ class Exchange: pair: str, amount: float, stop_price: float, - order_types: Dict, + order_types: dict, side: BuySell, leverage: float, - ) -> Dict: + ) -> dict: """ creates a stoploss order. requires `_ft_has['stoploss_order_types']` to be set as a dict mapping limit and market @@ -1459,7 +1460,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def fetch_order_emulated(self, order_id: str, pair: str, params: Dict) -> Dict: + def fetch_order_emulated(self, order_id: str, pair: str, params: dict) -> dict: """ Emulated fetch_order if the exchange doesn't support fetch_order, but requires separate calls for open and closed orders. @@ -1493,7 +1494,7 @@ class Exchange: raise OperationalException(e) from e @retrier(retries=API_FETCH_ORDER_RETRY_COUNT) - def fetch_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: + def fetch_order(self, order_id: str, pair: str, params: Optional[dict] = None) -> dict: if self._config["dry_run"]: return self.fetch_dry_run_order(order_id) if params is None: @@ -1522,12 +1523,12 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: + def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[dict] = None) -> dict: return self.fetch_order(order_id, pair, params) def fetch_order_or_stoploss_order( self, order_id: str, pair: str, stoploss_order: bool = False - ) -> Dict: + ) -> dict: """ Simple wrapper calling either fetch_order or fetch_stoploss_order depending on the stoploss_order parameter @@ -1539,7 +1540,7 @@ class Exchange: return self.fetch_stoploss_order(order_id, pair) return self.fetch_order(order_id, pair) - def check_order_canceled_empty(self, order: Dict) -> bool: + def check_order_canceled_empty(self, order: dict) -> bool: """ Verify if an order has been cancelled without being partially filled :param order: Order dict as returned from fetch_order() @@ -1548,7 +1549,7 @@ class Exchange: return order.get("status") in NON_OPEN_EXCHANGE_STATES and order.get("filled") == 0.0 @retrier - def cancel_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: + def cancel_order(self, order_id: str, pair: str, params: Optional[dict] = None) -> dict: if self._config["dry_run"]: try: order = self.fetch_dry_run_order(order_id) @@ -1577,8 +1578,8 @@ class Exchange: raise OperationalException(e) from e def cancel_stoploss_order( - self, order_id: str, pair: str, params: Optional[Dict] = None - ) -> Dict: + self, order_id: str, pair: str, params: Optional[dict] = None + ) -> dict: return self.cancel_order(order_id, pair, params) def is_cancel_order_result_suitable(self, corder) -> bool: @@ -1588,7 +1589,7 @@ class Exchange: required = ("fee", "status", "amount") return all(corder.get(k, None) is not None for k in required) - def cancel_order_with_result(self, order_id: str, pair: str, amount: float) -> Dict: + def cancel_order_with_result(self, order_id: str, pair: str, amount: float) -> dict: """ Cancel order returning a result. Creates a fake result if cancel order returns a non-usable result @@ -1619,7 +1620,7 @@ class Exchange: return order - def cancel_stoploss_order_with_result(self, order_id: str, pair: str, amount: float) -> Dict: + def cancel_stoploss_order_with_result(self, order_id: str, pair: str, amount: float) -> dict: """ Cancel stoploss order returning a result. Creates a fake result if cancel order returns a non-usable result @@ -1661,7 +1662,7 @@ class Exchange: raise OperationalException(e) from e @retrier - def fetch_positions(self, pair: Optional[str] = None) -> List[CcxtPosition]: + def fetch_positions(self, pair: Optional[str] = None) -> list[CcxtPosition]: """ Fetch positions from the exchange. If no pair is given, all positions are returned. @@ -1673,7 +1674,7 @@ class Exchange: symbols = [] if pair: symbols.append(pair) - positions: List[CcxtPosition] = self._api.fetch_positions(symbols) + positions: list[CcxtPosition] = self._api.fetch_positions(symbols) self._log_exchange_response("fetch_positions", positions) return positions except ccxt.DDoSProtection as e: @@ -1685,7 +1686,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def _fetch_orders_emulate(self, pair: str, since_ms: int) -> List[Dict]: + def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[dict]: orders = [] if self.exchange_has("fetchClosedOrders"): orders = self._api.fetch_closed_orders(pair, since=since_ms) @@ -1695,7 +1696,7 @@ class Exchange: return orders @retrier(retries=0) - def fetch_orders(self, pair: str, since: datetime, params: Optional[Dict] = None) -> List[Dict]: + def fetch_orders(self, pair: str, since: datetime, params: Optional[dict] = None) -> list[dict]: """ Fetch all orders for a pair "since" :param pair: Pair for the query @@ -1711,7 +1712,7 @@ class Exchange: if not params: params = {} try: - orders: List[Dict] = self._api.fetch_orders(pair, since=since_ms, params=params) + orders: list[dict] = self._api.fetch_orders(pair, since=since_ms, params=params) except ccxt.NotSupported: # Some exchanges don't support fetchOrders # attempt to fetch open and closed orders separately @@ -1731,7 +1732,7 @@ class Exchange: raise OperationalException(e) from e @retrier - def fetch_trading_fees(self) -> Dict[str, Any]: + def fetch_trading_fees(self) -> dict[str, Any]: """ Fetch user account trading fees Can be cached, should not update often. @@ -1743,7 +1744,7 @@ class Exchange: ): return {} try: - trading_fees: Dict[str, Any] = self._api.fetch_trading_fees() + trading_fees: dict[str, Any] = self._api.fetch_trading_fees() self._log_exchange_response("fetch_trading_fees", trading_fees) return trading_fees except ccxt.DDoSProtection as e: @@ -1756,7 +1757,7 @@ class Exchange: raise OperationalException(e) from e @retrier - def fetch_bids_asks(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Dict: + def fetch_bids_asks(self, symbols: Optional[list[str]] = None, cached: bool = False) -> dict: """ :param symbols: List of symbols to fetch :param cached: Allow cached result @@ -1789,7 +1790,7 @@ class Exchange: raise OperationalException(e) from e @retrier - def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Tickers: + def get_tickers(self, symbols: Optional[list[str]] = None, cached: bool = False) -> Tickers: """ :param cached: Allow cached result :return: fetch_tickers result @@ -1849,7 +1850,7 @@ class Exchange: @staticmethod def get_next_limit_in_list( - limit: int, limit_range: Optional[List[int]], range_required: bool = True + limit: int, limit_range: Optional[list[int]], range_required: bool = True ): """ Get next greater value in the list. @@ -1890,7 +1891,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def _get_price_side(self, side: str, is_short: bool, conf_strategy: Dict) -> BidAsk: + def _get_price_side(self, side: str, is_short: bool, conf_strategy: dict) -> BidAsk: price_side = conf_strategy["price_side"] if price_side in ("same", "other"): @@ -1962,7 +1963,7 @@ class Exchange: return rate def _get_rate_from_ticker( - self, side: EntryExit, ticker: Ticker, conf_strategy: Dict[str, Any], price_side: BidAsk + self, side: EntryExit, ticker: Ticker, conf_strategy: dict[str, Any], price_side: BidAsk ) -> Optional[float]: """ Get rate from ticker. @@ -2008,7 +2009,7 @@ class Exchange: ) return rate - def get_rates(self, pair: str, refresh: bool, is_short: bool) -> Tuple[float, float]: + def get_rates(self, pair: str, refresh: bool, is_short: bool) -> tuple[float, float]: entry_rate = None exit_rate = None if not refresh: @@ -2042,8 +2043,8 @@ class Exchange: @retrier def get_trades_for_order( - self, order_id: str, pair: str, since: datetime, params: Optional[Dict] = None - ) -> List: + self, order_id: str, pair: str, since: datetime, params: Optional[dict] = None + ) -> list: """ Fetch Orders using the "fetch_my_trades" endpoint and filter them by order-id. The "since" argument passed in is coming from the database and is in UTC, @@ -2089,7 +2090,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def get_order_id_conditional(self, order: Dict[str, Any]) -> str: + def get_order_id_conditional(self, order: dict[str, Any]) -> str: return order["id"] @retrier @@ -2138,7 +2139,7 @@ class Exchange: raise OperationalException(e) from e @staticmethod - def order_has_fee(order: Dict) -> bool: + def order_has_fee(order: dict) -> bool: """ Verifies if the passed in order dict has the needed keys to extract fees, and that these keys (currency, cost) are not empty. @@ -2156,7 +2157,7 @@ class Exchange: ) def calculate_fee_rate( - self, fee: Dict, symbol: str, cost: float, amount: float + self, fee: dict, symbol: str, cost: float, amount: float ) -> Optional[float]: """ Calculate fee rate if it's not given by the exchange. @@ -2196,8 +2197,8 @@ class Exchange: return round((fee_cost * fee_to_quote_rate) / cost, 8) def extract_cost_curr_rate( - self, fee: Dict, symbol: str, cost: float, amount: float - ) -> Tuple[float, str, Optional[float]]: + self, fee: dict, symbol: str, cost: float, amount: float + ) -> tuple[float, str, Optional[float]]: """ Extract tuple of cost, currency, rate. Requires order_has_fee to run first! @@ -2277,7 +2278,7 @@ class Exchange: for since in range(since_ms, until_ms or dt_ts(), one_call) ] - data: List = [] + data: list = [] # Chunk requests into batches of 100 to avoid overwhelming ccxt Throttling for input_coro in chunks(input_coroutines, 100): results = await asyncio.gather(*input_coro, return_exceptions=True) @@ -2371,11 +2372,11 @@ class Exchange: def _build_ohlcv_dl_jobs( self, pair_list: ListPairsWithTimeframes, since_ms: Optional[int], cache: bool - ) -> Tuple[List[Coroutine], List[PairWithTimeframe]]: + ) -> tuple[list[Coroutine], list[PairWithTimeframe]]: """ Build Coroutines to execute as part of refresh_latest_ohlcv """ - input_coroutines: List[Coroutine[Any, Any, OHLCVResponse]] = [] + input_coroutines: list[Coroutine[Any, Any, OHLCVResponse]] = [] cached_pairs = [] for pair, timeframe, candle_type in set(pair_list): if timeframe not in self.timeframes and candle_type in ( @@ -2411,7 +2412,7 @@ class Exchange: pair: str, timeframe: str, c_type: CandleType, - ticks: List[List], + ticks: list[list], cache: bool, drop_incomplete: bool, ) -> DataFrame: @@ -2450,7 +2451,7 @@ class Exchange: since_ms: Optional[int] = None, cache: bool = True, drop_incomplete: Optional[bool] = None, - ) -> Dict[PairWithTimeframe, DataFrame]: + ) -> dict[PairWithTimeframe, DataFrame]: """ Refresh in-memory OHLCV asynchronously and set `_klines` with the result Loops asynchronously over pair_list and downloads all pairs async (semi-parallel). @@ -2499,8 +2500,8 @@ class Exchange: return results_df def refresh_ohlcv_with_cache( - self, pairs: List[PairWithTimeframe], since_ms: int - ) -> Dict[PairWithTimeframe, DataFrame]: + self, pairs: list[PairWithTimeframe], since_ms: int + ) -> dict[PairWithTimeframe, DataFrame]: """ Refresh ohlcv data for all pairs in needed_pairs if necessary. Caches data with expiring per timeframe. @@ -2618,7 +2619,7 @@ class Exchange: timeframe: str, limit: int, since_ms: Optional[int] = None, - ) -> List[List]: + ) -> list[list]: """ Fetch funding rate history - used to selectively override this by subclasses. """ @@ -2652,7 +2653,7 @@ class Exchange: pair: str, timeframe: str, c_type: CandleType, - ticks: List[List], + ticks: list[list], cache: bool, first_required_candle_date: int, ) -> DataFrame: @@ -2676,13 +2677,13 @@ class Exchange: async def _build_trades_dl_jobs( self, pairwt: PairWithTimeframe, data_handler, cache: bool - ) -> Tuple[PairWithTimeframe, Optional[DataFrame]]: + ) -> tuple[PairWithTimeframe, Optional[DataFrame]]: """ Build coroutines to refresh trades for (they're then called through async.gather) """ pair, timeframe, candle_type = pairwt since_ms = None - new_ticks: List = [] + new_ticks: list = [] all_stored_ticks_df = DataFrame(columns=DEFAULT_TRADES_COLUMNS + ["date"]) first_candle_ms = self.needed_candle_for_trades_ms(timeframe, candle_type) # refresh, if @@ -2767,7 +2768,7 @@ class Exchange: pair_list: ListPairsWithTimeframes, *, cache: bool = True, - ) -> Dict[PairWithTimeframe, DataFrame]: + ) -> dict[PairWithTimeframe, DataFrame]: """ Refresh in-memory TRADES asynchronously and set `_trades` with the result Loops asynchronously over pair_list and downloads all pairs async (semi-parallel). @@ -2821,7 +2822,7 @@ class Exchange: @retrier_async async def _async_fetch_trades( self, pair: str, since: Optional[int] = None, params: Optional[dict] = None - ) -> Tuple[List[List], Any]: + ) -> tuple[list[list], Any]: """ Asynchronously gets trade history using fetch_trades. Handles exchange errors, does one call to the exchange. @@ -2867,7 +2868,7 @@ class Exchange: """ return True - def _get_trade_pagination_next_value(self, trades: List[Dict]): + def _get_trade_pagination_next_value(self, trades: list[dict]): """ Extract pagination id for the next "from_id" value Applies only to fetch_trade_history by id. @@ -2881,7 +2882,7 @@ class Exchange: async def _async_get_trade_history_id( self, pair: str, until: int, since: Optional[int] = None, from_id: Optional[str] = None - ) -> Tuple[str, List[List]]: + ) -> tuple[str, list[list]]: """ Asynchronously gets trade history using fetch_trades use this when exchange uses id-based iteration (check `self._trades_pagination`) @@ -2892,7 +2893,7 @@ class Exchange: returns tuple: (pair, trades-list) """ - trades: List[List] = [] + trades: list[list] = [] # DEFAULT_TRADES_COLUMNS: 0 -> timestamp # DEFAULT_TRADES_COLUMNS: 1 -> id has_overlap = self._ft_has.get("trades_pagination_overlap", True) @@ -2936,7 +2937,7 @@ class Exchange: async def _async_get_trade_history_time( self, pair: str, until: int, since: Optional[int] = None - ) -> Tuple[str, List[List]]: + ) -> tuple[str, list[list]]: """ Asynchronously gets trade history using fetch_trades, when the exchange uses time-based iteration (check `self._trades_pagination`) @@ -2946,7 +2947,7 @@ class Exchange: returns tuple: (pair, trades-list) """ - trades: List[List] = [] + trades: list[list] = [] # DEFAULT_TRADES_COLUMNS: 0 -> timestamp # DEFAULT_TRADES_COLUMNS: 1 -> id while True: @@ -2979,7 +2980,7 @@ class Exchange: since: Optional[int] = None, until: Optional[int] = None, from_id: Optional[str] = None, - ) -> Tuple[str, List[List]]: + ) -> tuple[str, list[list]]: """ Async wrapper handling downloading trades using either time or id based methods. """ @@ -3010,7 +3011,7 @@ class Exchange: since: Optional[int] = None, until: Optional[int] = None, from_id: Optional[str] = None, - ) -> Tuple[str, List]: + ) -> tuple[str, list]: """ Get trade history data using asyncio. Handles all async work and returns the list of candles. @@ -3070,7 +3071,7 @@ class Exchange: raise OperationalException(e) from e @retrier - def get_leverage_tiers(self) -> Dict[str, List[Dict]]: + def get_leverage_tiers(self) -> dict[str, list[dict]]: try: return self._api.fetch_leverage_tiers() except ccxt.DDoSProtection as e: @@ -3083,7 +3084,7 @@ class Exchange: raise OperationalException(e) from e @retrier_async - async def get_market_leverage_tiers(self, symbol: str) -> Tuple[str, List[Dict]]: + async def get_market_leverage_tiers(self, symbol: str) -> tuple[str, list[dict]]: """Leverage tiers per symbol""" try: tier = await self._api_async.fetch_market_leverage_tiers(symbol) @@ -3098,7 +3099,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - def load_leverage_tiers(self) -> Dict[str, List[Dict]]: + def load_leverage_tiers(self) -> dict[str, list[dict]]: if self.trading_mode == TradingMode.FUTURES: if self.exchange_has("fetchLeverageTiers"): # Fetch all leverage tiers at once @@ -3117,7 +3118,7 @@ class Exchange: ) ] - tiers: Dict[str, List[Dict]] = {} + tiers: dict[str, list[dict]] = {} tiers_cached = self.load_cached_leverage_tiers(self._config["stake_currency"]) if tiers_cached: @@ -3158,7 +3159,7 @@ class Exchange: return tiers return {} - def cache_leverage_tiers(self, tiers: Dict[str, List[Dict]], stake_currency: str) -> None: + def cache_leverage_tiers(self, tiers: dict[str, list[dict]], stake_currency: str) -> None: filename = self._config["datadir"] / "futures" / f"leverage_tiers_{stake_currency}.json" if not filename.parent.is_dir(): filename.parent.mkdir(parents=True) @@ -3170,7 +3171,7 @@ class Exchange: def load_cached_leverage_tiers( self, stake_currency: str, cache_time: Optional[timedelta] = None - ) -> Optional[Dict[str, List[Dict]]]: + ) -> Optional[dict[str, list[dict]]]: """ Load cached leverage tiers from disk :param cache_time: The maximum age of the cache before it is considered outdated @@ -3205,7 +3206,7 @@ class Exchange: pair_tiers.append(self.parse_leverage_tier(tier)) self._leverage_tiers[pair] = pair_tiers - def parse_leverage_tier(self, tier) -> Dict: + def parse_leverage_tier(self, tier) -> dict: info = tier.get("info", {}) return { "minNotional": tier["minNotional"], @@ -3345,7 +3346,7 @@ class Exchange: pair: str, margin_mode: MarginMode, accept_fail: bool = False, - params: Optional[Dict] = None, + params: Optional[dict] = None, ): """ Set's the margin mode on the exchange to cross or isolated for a specific pair @@ -3632,7 +3633,7 @@ class Exchange: self, pair: str, notional_value: float, - ) -> Tuple[float, Optional[float]]: + ) -> tuple[float, Optional[float]]: """ Important: Must be fetching data from cached values as this is used by backtesting! :param pair: Market symbol diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index ef3ed274b..e9c58ec38 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -1,11 +1,11 @@ -from typing import Dict, List, Optional, Tuple, TypedDict +from typing import Optional, TypedDict from freqtrade.enums import CandleType class FtHas(TypedDict, total=False): - order_time_in_force: List[str] - exchange_has_overrides: Dict[str, bool] + order_time_in_force: list[str] + exchange_has_overrides: dict[str, bool] marketOrderRequiresPrice: bool # Stoploss on exchange @@ -13,16 +13,16 @@ class FtHas(TypedDict, total=False): stop_price_param: str stop_price_prop: str stop_price_type_field: str - stop_price_type_value_mapping: Dict - stoploss_order_types: Dict[str, str] + stop_price_type_value_mapping: dict + stoploss_order_types: dict[str, str] # ohlcv - ohlcv_params: Dict + ohlcv_params: dict ohlcv_candle_limit: int ohlcv_has_history: bool ohlcv_partial_candle: bool ohlcv_require_since: bool ohlcv_volume_currency: str - ohlcv_candle_limit_per_timeframe: Dict[str, int] + ohlcv_candle_limit_per_timeframe: dict[str, int] # Tickers tickers_have_quoteVolume: bool tickers_have_percentage: bool @@ -35,7 +35,7 @@ class FtHas(TypedDict, total=False): trades_has_history: bool trades_pagination_overlap: bool # Orderbook - l2_limit_range: Optional[List[int]] + l2_limit_range: Optional[list[int]] l2_limit_range_required: bool # Futures ccxt_futures_name: str # usually swap @@ -44,7 +44,7 @@ class FtHas(TypedDict, total=False): funding_fee_timeframe: str floor_leverage: bool needs_trading_fees: bool - order_props_in_contracts: List[str] + order_props_in_contracts: list[str] # Websocket control ws_enabled: bool @@ -63,13 +63,13 @@ class Ticker(TypedDict): # Several more - only listing required. -Tickers = Dict[str, Ticker] +Tickers = dict[str, Ticker] class OrderBook(TypedDict): symbol: str - bids: List[Tuple[float, float]] - asks: List[Tuple[float, float]] + bids: list[tuple[float, float]] + asks: list[tuple[float, float]] timestamp: Optional[int] datetime: Optional[str] nonce: Optional[int] @@ -81,7 +81,7 @@ class CcxtBalance(TypedDict): total: float -CcxtBalances = Dict[str, CcxtBalance] +CcxtBalances = dict[str, CcxtBalance] class CcxtPosition(TypedDict): @@ -95,4 +95,4 @@ class CcxtPosition(TypedDict): # pair, timeframe, candleType, OHLCV, drop last?, -OHLCVResponse = Tuple[str, str, CandleType, List, bool] +OHLCVResponse = tuple[str, str, CandleType, list, bool] diff --git a/freqtrade/exchange/exchange_utils.py b/freqtrade/exchange/exchange_utils.py index c150d751b..f7b53a836 100644 --- a/freqtrade/exchange/exchange_utils.py +++ b/freqtrade/exchange/exchange_utils.py @@ -5,7 +5,7 @@ Exchange support utils import inspect from datetime import datetime, timedelta, timezone from math import ceil, floor -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Optional import ccxt from ccxt import ( @@ -39,14 +39,14 @@ def is_exchange_known_ccxt( return exchange_name in ccxt_exchanges(ccxt_module) -def ccxt_exchanges(ccxt_module: Optional[CcxtModuleType] = None) -> List[str]: +def ccxt_exchanges(ccxt_module: Optional[CcxtModuleType] = None) -> list[str]: """ Return the list of all exchanges known to ccxt """ return ccxt_module.exchanges if ccxt_module is not None else ccxt.exchanges -def available_exchanges(ccxt_module: Optional[CcxtModuleType] = None) -> List[str]: +def available_exchanges(ccxt_module: Optional[CcxtModuleType] = None) -> list[str]: """ Return exchanges available to the bot, i.e. non-bad exchanges in the ccxt list """ @@ -54,7 +54,7 @@ def available_exchanges(ccxt_module: Optional[CcxtModuleType] = None) -> List[st return [x for x in exchanges if validate_exchange(x)[0]] -def validate_exchange(exchange: str) -> Tuple[bool, str, Optional[ccxt.Exchange]]: +def validate_exchange(exchange: str) -> tuple[bool, str, Optional[ccxt.Exchange]]: """ returns: can_use, reason, exchange_object with Reason including both missing and missing_opt @@ -91,7 +91,7 @@ def validate_exchange(exchange: str) -> Tuple[bool, str, Optional[ccxt.Exchange] def _build_exchange_list_entry( - exchange_name: str, exchangeClasses: Dict[str, Any] + exchange_name: str, exchangeClasses: dict[str, Any] ) -> ValidExchangesType: valid, comment, ex_mod = validate_exchange(exchange_name) result: ValidExchangesType = { @@ -121,7 +121,7 @@ def _build_exchange_list_entry( return result -def list_available_exchanges(all_exchanges: bool) -> List[ValidExchangesType]: +def list_available_exchanges(all_exchanges: bool) -> list[ValidExchangesType]: """ :return: List of tuples with exchangename, valid, reason. """ @@ -130,7 +130,7 @@ def list_available_exchanges(all_exchanges: bool) -> List[ValidExchangesType]: subclassed = {e["name"].lower(): e for e in ExchangeResolver.search_all_objects({}, False)} - exchanges_valid: List[ValidExchangesType] = [ + exchanges_valid: list[ValidExchangesType] = [ _build_exchange_list_entry(e, subclassed) for e in exchanges ] @@ -155,7 +155,7 @@ def date_minus_candles( return new_date -def market_is_active(market: Dict) -> bool: +def market_is_active(market: dict) -> bool: """ Return True if the market is active. """ diff --git a/freqtrade/exchange/exchange_ws.py b/freqtrade/exchange/exchange_ws.py index 5851cdea6..bddd2ca86 100644 --- a/freqtrade/exchange/exchange_ws.py +++ b/freqtrade/exchange/exchange_ws.py @@ -4,7 +4,6 @@ import time from copy import deepcopy from functools import partial from threading import Thread -from typing import Dict, Set import ccxt @@ -22,12 +21,12 @@ class ExchangeWS: def __init__(self, config: Config, ccxt_object: ccxt.Exchange) -> None: self.config = config self.ccxt_object = ccxt_object - self._background_tasks: Set[asyncio.Task] = set() + self._background_tasks: set[asyncio.Task] = set() - self._klines_watching: Set[PairWithTimeframe] = set() - self._klines_scheduled: Set[PairWithTimeframe] = set() - self.klines_last_refresh: Dict[PairWithTimeframe, float] = {} - self.klines_last_request: Dict[PairWithTimeframe, float] = {} + self._klines_watching: set[PairWithTimeframe] = set() + self._klines_scheduled: set[PairWithTimeframe] = set() + self.klines_last_refresh: dict[PairWithTimeframe, float] = {} + self.klines_last_request: dict[PairWithTimeframe, float] = {} self._thread = Thread(name="ccxt_ws", target=self._start_forever) self._thread.start() self.__cleanup_called = False diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 4096a851a..70f877210 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -2,7 +2,7 @@ import logging from datetime import datetime -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Optional from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, PriceType, TradingMode @@ -46,7 +46,7 @@ class Gate(Exchange): }, } - _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ + _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list # (TradingMode.MARGIN, MarginMode.CROSS), # (TradingMode.FUTURES, MarginMode.CROSS), @@ -60,7 +60,7 @@ class Gate(Exchange): leverage: float, reduceOnly: bool, time_in_force: str = "GTC", - ) -> Dict: + ) -> dict: params = super()._get_params( side=side, ordertype=ordertype, @@ -74,8 +74,8 @@ class Gate(Exchange): return params def get_trades_for_order( - self, order_id: str, pair: str, since: datetime, params: Optional[Dict] = None - ) -> List: + self, order_id: str, pair: str, since: datetime, params: Optional[dict] = None + ) -> list: trades = super().get_trades_for_order(order_id, pair, since, params) if self.trading_mode == TradingMode.FUTURES: @@ -99,10 +99,10 @@ class Gate(Exchange): } return trades - def get_order_id_conditional(self, order: Dict[str, Any]) -> str: + def get_order_id_conditional(self, order: dict[str, Any]) -> str: return safe_value_fallback2(order, order, "id_stop", "id") - def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: + def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[dict] = None) -> dict: order = self.fetch_order(order_id=order_id, pair=pair, params={"stop": True}) if order.get("status", "open") == "closed": # Places a real order - which we need to fetch explicitly. @@ -120,6 +120,6 @@ class Gate(Exchange): return order def cancel_stoploss_order( - self, order_id: str, pair: str, params: Optional[Dict] = None - ) -> Dict: + self, order_id: str, pair: str, params: Optional[dict] = None + ) -> dict: return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True}) diff --git a/freqtrade/exchange/htx.py b/freqtrade/exchange/htx.py index 9bd931f51..ba158d4c5 100644 --- a/freqtrade/exchange/htx.py +++ b/freqtrade/exchange/htx.py @@ -1,7 +1,6 @@ """HTX exchange subclass""" import logging -from typing import Dict from freqtrade.constants import BuySell from freqtrade.exchange import Exchange @@ -32,7 +31,7 @@ class Htx(Exchange): "trades_has_history": False, # Endpoint doesn't have a "since" parameter } - def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> Dict: + def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict: params = self._params.copy() params.update( { diff --git a/freqtrade/exchange/hyperliquid.py b/freqtrade/exchange/hyperliquid.py index 25c262e9f..144edbf3a 100644 --- a/freqtrade/exchange/hyperliquid.py +++ b/freqtrade/exchange/hyperliquid.py @@ -1,7 +1,6 @@ """Hyperliquid exchange subclass""" import logging -from typing import Dict from freqtrade.enums import TradingMode from freqtrade.exchange import Exchange @@ -26,7 +25,7 @@ class Hyperliquid(Exchange): } @property - def _ccxt_config(self) -> Dict: + def _ccxt_config(self) -> dict: # Parameters to add directly to ccxt sync/async initialization. # ccxt defaults to swap mode. config = {} diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 9df9836b0..7dea0e435 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -2,7 +2,7 @@ import logging from datetime import datetime -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Optional import ccxt from pandas import DataFrame @@ -19,7 +19,7 @@ logger = logging.getLogger(__name__) class Kraken(Exchange): - _params: Dict = {"trading_agreement": "agree"} + _params: dict = {"trading_agreement": "agree"} _ft_has: FtHas = { "stoploss_on_exchange": True, "stop_price_param": "stopLossPrice", @@ -35,13 +35,13 @@ class Kraken(Exchange): "mark_ohlcv_timeframe": "4h", } - _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ + _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list # (TradingMode.MARGIN, MarginMode.CROSS), # (TradingMode.FUTURES, MarginMode.CROSS) ] - def market_is_tradable(self, market: Dict[str, Any]) -> bool: + 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 darkpool pair. @@ -50,7 +50,7 @@ class Kraken(Exchange): return parent_check and market.get("darkpool", False) is False - def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Tickers: + def get_tickers(self, symbols: Optional[list[str]] = None, cached: bool = False) -> Tickers: # Only fetch tickers for current stake currency # Otherwise the request for kraken becomes too large. symbols = list(self.get_markets(quote_currencies=[self._config["stake_currency"]])) @@ -115,7 +115,7 @@ class Kraken(Exchange): leverage: float, reduceOnly: bool, time_in_force: str = "GTC", - ) -> Dict: + ) -> dict: params = super()._get_params( side=side, ordertype=ordertype, @@ -165,7 +165,7 @@ class Kraken(Exchange): return fees if is_short else -fees - def _get_trade_pagination_next_value(self, trades: List[Dict]): + def _get_trade_pagination_next_value(self, trades: list[dict]): """ Extract pagination id for the next "from_id" value Applies only to fetch_trade_history by id. diff --git a/freqtrade/exchange/kucoin.py b/freqtrade/exchange/kucoin.py index bbf120d40..fc4433f0b 100644 --- a/freqtrade/exchange/kucoin.py +++ b/freqtrade/exchange/kucoin.py @@ -1,7 +1,6 @@ """Kucoin exchange subclass.""" import logging -from typing import Dict from freqtrade.constants import BuySell from freqtrade.exchange import Exchange @@ -32,7 +31,7 @@ class Kucoin(Exchange): "ohlcv_candle_limit": 1500, } - def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> Dict: + def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict: params = self._params.copy() params.update({"stopPrice": stop_price, "stop": "loss"}) return params @@ -48,7 +47,7 @@ class Kucoin(Exchange): leverage: float, reduceOnly: bool = False, time_in_force: str = "GTC", - ) -> Dict: + ) -> dict: res = super().create_order( pair=pair, ordertype=ordertype, diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index a0fbb6729..fbbf21757 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -1,6 +1,6 @@ import logging from datetime import timedelta -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Optional import ccxt @@ -48,7 +48,7 @@ class Okx(Exchange): "ws_enabled": True, } - _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ + _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list # (TradingMode.MARGIN, MarginMode.CROSS), # (TradingMode.FUTURES, MarginMode.CROSS), @@ -57,7 +57,7 @@ class Okx(Exchange): net_only = True - _ccxt_params: Dict = {"options": {"brokerId": "ffb5405ad327SUDE"}} + _ccxt_params: dict = {"options": {"brokerId": "ffb5405ad327SUDE"}} def ohlcv_candle_limit( self, timeframe: str, candle_type: CandleType, since_ms: Optional[int] = None @@ -119,7 +119,7 @@ class Okx(Exchange): leverage: float, reduceOnly: bool, time_in_force: str = "GTC", - ) -> Dict: + ) -> dict: params = super()._get_params( side=side, ordertype=ordertype, @@ -184,14 +184,14 @@ class Okx(Exchange): pair_tiers = self._leverage_tiers[pair] return pair_tiers[-1]["maxNotional"] / leverage - def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> Dict: + def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict: params = super()._get_stop_params(side, ordertype, stop_price) if self.trading_mode == TradingMode.FUTURES and self.margin_mode: params["tdMode"] = self.margin_mode.value params["posSide"] = self._get_posSide(side, True) return params - def _convert_stop_order(self, pair: str, order_id: str, order: Dict) -> Dict: + def _convert_stop_order(self, pair: str, order_id: str, order: dict) -> dict: if ( order.get("status", "open") == "closed" and (real_order_id := order.get("info", {}).get("ordId")) is not None @@ -209,7 +209,7 @@ class Okx(Exchange): return order @retrier(retries=API_RETRY_COUNT) - def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: + def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[dict] = None) -> dict: if self._config["dry_run"]: return self.fetch_dry_run_order(order_id) @@ -231,7 +231,7 @@ class Okx(Exchange): return self._fetch_stop_order_fallback(order_id, pair) - def _fetch_stop_order_fallback(self, order_id: str, pair: str) -> Dict: + def _fetch_stop_order_fallback(self, order_id: str, pair: str) -> dict: params2 = {"stop": True, "ordType": "conditional"} for method in ( self._api.fetch_open_orders, @@ -256,14 +256,14 @@ class Okx(Exchange): raise OperationalException(e) from e raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).") - def get_order_id_conditional(self, order: Dict[str, Any]) -> str: + def get_order_id_conditional(self, order: dict[str, Any]) -> str: if order.get("type", "") == "stop": return safe_value_fallback2(order, order, "id_stop", "id") return order["id"] def cancel_stoploss_order( - self, order_id: str, pair: str, params: Optional[Dict] = None - ) -> Dict: + self, order_id: str, pair: str, params: Optional[dict] = None + ) -> dict: params1 = {"stop": True} # 'ordType': 'conditional' # @@ -273,7 +273,7 @@ class Okx(Exchange): params=params1, ) - def _fetch_orders_emulate(self, pair: str, since_ms: int) -> List[Dict]: + def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[dict]: orders = [] orders = self._api.fetch_closed_orders(pair, since=since_ms)