From f3d500085c0fec96a8ae59bc164e06a55a0beacb Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 2 Feb 2020 07:00:40 +0300 Subject: [PATCH] Add some type hints --- freqtrade/commands/data_commands.py | 10 +++-- freqtrade/commands/deploy_commands.py | 4 +- freqtrade/commands/plot_commands.py | 2 +- freqtrade/configuration/check_exchange.py | 2 +- .../configuration/deprecated_settings.py | 4 +- .../configuration/directory_operations.py | 2 +- freqtrade/configuration/timerange.py | 5 ++- freqtrade/data/btanalysis.py | 7 +-- freqtrade/data/history.py | 12 ++--- freqtrade/edge/edge_positioning.py | 4 +- freqtrade/exchange/exchange.py | 45 +++++++++++-------- freqtrade/freqtradebot.py | 16 +++---- freqtrade/misc.py | 9 ++-- freqtrade/optimize/backtesting.py | 10 +++-- freqtrade/optimize/hyperopt.py | 18 ++++---- freqtrade/pairlist/IPairList.py | 5 ++- freqtrade/pairlist/PrecisionFilter.py | 6 +-- freqtrade/pairlist/PriceFilter.py | 5 ++- freqtrade/pairlist/VolumePairList.py | 7 +-- freqtrade/persistence.py | 9 ++-- freqtrade/plot/plotting.py | 2 +- freqtrade/resolvers/iresolver.py | 2 +- freqtrade/resolvers/strategy_resolver.py | 7 +-- freqtrade/rpc/rpc.py | 5 ++- freqtrade/rpc/rpc_manager.py | 2 +- freqtrade/strategy/interface.py | 2 +- freqtrade/wallets.py | 10 ++--- freqtrade/worker.py | 2 +- 28 files changed, 114 insertions(+), 100 deletions(-) diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index c01772023..aeb598009 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -1,6 +1,6 @@ import logging import sys -from typing import Any, Dict, List +from typing import Any, Dict, List, cast import arrow @@ -43,16 +43,18 @@ def start_download_data(args: Dict[str, Any]) -> None: if config.get('download_trades'): pairs_not_available = refresh_backtest_trades_data( exchange, pairs=config["pairs"], datadir=config['datadir'], - timerange=timerange, erase=config.get("erase")) + timerange=timerange, erase=cast(bool, config.get("erase"))) # Convert downloaded trade data to different timeframes convert_trades_to_ohlcv( pairs=config["pairs"], timeframes=config["timeframes"], - datadir=config['datadir'], timerange=timerange, erase=config.get("erase")) + datadir=config['datadir'], timerange=timerange, + erase=cast(bool, config.get("erase"))) else: pairs_not_available = refresh_backtest_ohlcv_data( exchange, pairs=config["pairs"], timeframes=config["timeframes"], - datadir=config['datadir'], timerange=timerange, erase=config.get("erase")) + datadir=config['datadir'], timerange=timerange, + erase=cast(bool, config.get("erase"))) except KeyboardInterrupt: sys.exit("SIGINT received, aborting ...") diff --git a/freqtrade/commands/deploy_commands.py b/freqtrade/commands/deploy_commands.py index 99ae63244..809740661 100644 --- a/freqtrade/commands/deploy_commands.py +++ b/freqtrade/commands/deploy_commands.py @@ -28,7 +28,7 @@ def start_create_userdir(args: Dict[str, Any]) -> None: sys.exit(1) -def deploy_new_strategy(strategy_name, strategy_path: Path, subtemplate: str): +def deploy_new_strategy(strategy_name: str, strategy_path: Path, subtemplate: str) -> None: """ Deploy new strategy from template to strategy_path """ @@ -69,7 +69,7 @@ def start_new_strategy(args: Dict[str, Any]) -> None: raise OperationalException("`new-strategy` requires --strategy to be set.") -def deploy_new_hyperopt(hyperopt_name, hyperopt_path: Path, subtemplate: str): +def deploy_new_hyperopt(hyperopt_name: str, hyperopt_path: Path, subtemplate: str) -> None: """ Deploys a new hyperopt template to hyperopt_path """ diff --git a/freqtrade/commands/plot_commands.py b/freqtrade/commands/plot_commands.py index 028933ba7..5e547acb0 100644 --- a/freqtrade/commands/plot_commands.py +++ b/freqtrade/commands/plot_commands.py @@ -5,7 +5,7 @@ from freqtrade.exceptions import OperationalException from freqtrade.state import RunMode -def validate_plot_args(args: Dict[str, Any]): +def validate_plot_args(args: Dict[str, Any]) -> None: if not args.get('datadir') and not args.get('config'): raise OperationalException( "You need to specify either `--datadir` or `--config` " diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index 0076b1c5d..92daaf251 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -10,7 +10,7 @@ from freqtrade.state import RunMode logger = logging.getLogger(__name__) -def remove_credentials(config: Dict[str, Any]): +def remove_credentials(config: Dict[str, Any]) -> None: """ Removes exchange keys from the configuration and specifies dry-run Used for backtesting / hyperopt / edge and utils. diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 78d8218d4..55497d4f5 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) def check_conflicting_settings(config: Dict[str, Any], section1: str, name1: str, - section2: str, name2: str): + section2: str, name2: str) -> None: section1_config = config.get(section1, {}) section2_config = config.get(section2, {}) if name1 in section1_config and name2 in section2_config: @@ -28,7 +28,7 @@ def check_conflicting_settings(config: Dict[str, Any], def process_deprecated_setting(config: Dict[str, Any], section1: str, name1: str, - section2: str, name2: str): + section2: str, name2: str) -> None: section2_config = config.get(section2, {}) if name2 in section2_config: diff --git a/freqtrade/configuration/directory_operations.py b/freqtrade/configuration/directory_operations.py index 43a209483..5f8eb76b0 100644 --- a/freqtrade/configuration/directory_operations.py +++ b/freqtrade/configuration/directory_operations.py @@ -23,7 +23,7 @@ def create_datadir(config: Dict[str, Any], datadir: Optional[str] = None) -> Pat return folder -def create_userdata_dir(directory: str, create_dir=False) -> Path: +def create_userdata_dir(directory: str, create_dir: bool = False) -> Path: """ Create userdata directory structure. if create_dir is True, then the parent-directory will be created if it does not exist. diff --git a/freqtrade/configuration/timerange.py b/freqtrade/configuration/timerange.py index a8be873df..3db5f6217 100644 --- a/freqtrade/configuration/timerange.py +++ b/freqtrade/configuration/timerange.py @@ -7,6 +7,7 @@ from typing import Optional import arrow + logger = logging.getLogger(__name__) @@ -30,7 +31,7 @@ class TimeRange: return (self.starttype == other.starttype and self.stoptype == other.stoptype and self.startts == other.startts and self.stopts == other.stopts) - def subtract_start(self, seconds) -> None: + def subtract_start(self, seconds: int) -> None: """ Subtracts from startts if startts is set. :param seconds: Seconds to subtract from starttime @@ -59,7 +60,7 @@ class TimeRange: self.starttype = 'date' @staticmethod - def parse_timerange(text: Optional[str]): + def parse_timerange(text: Optional[str]) -> 'TimeRange': """ Parse the value of the argument --timerange to determine what is the range desired :param text: value from --timerange diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 04b2ca980..c28e462ba 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -3,7 +3,7 @@ Helpers when analyzing backtest data """ import logging from pathlib import Path -from typing import Dict +from typing import Dict, Union import numpy as np import pandas as pd @@ -20,7 +20,7 @@ BT_DATA_COLUMNS = ["pair", "profitperc", "open_time", "close_time", "index", "du "open_rate", "close_rate", "open_at_end", "sell_reason"] -def load_backtest_data(filename) -> pd.DataFrame: +def load_backtest_data(filename: Union[Path, str]) -> pd.DataFrame: """ Load backtest data file. :param filename: pathlib.Path object, or string pointing to the file. @@ -151,7 +151,8 @@ def extract_trades_of_period(dataframe: pd.DataFrame, trades: pd.DataFrame) -> p return trades -def combine_tickers_with_mean(tickers: Dict[str, pd.DataFrame], column: str = "close"): +def combine_tickers_with_mean(tickers: Dict[str, pd.DataFrame], + column: str = "close") -> pd.DataFrame: """ Combine multiple dataframes "column" :param tickers: Dict of Dataframes, dict key should be pair. diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 30d168f78..d891aa5b0 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -86,7 +86,7 @@ def load_tickerdata_file(datadir: Path, pair: str, timeframe: str, def store_tickerdata_file(datadir: Path, pair: str, - timeframe: str, data: list, is_zip: bool = False): + timeframe: str, data: list, is_zip: bool = False) -> None: """ Stores tickerdata to file """ @@ -109,7 +109,7 @@ def load_trades_file(datadir: Path, pair: str, def store_trades_file(datadir: Path, pair: str, - data: list, is_zip: bool = True): + data: list, is_zip: bool = True) -> None: """ Stores tickerdata to file """ @@ -117,7 +117,7 @@ def store_trades_file(datadir: Path, pair: str, misc.file_dump_json(filename, data, is_zip=is_zip) -def _validate_pairdata(pair, pairdata, timerange: TimeRange): +def _validate_pairdata(pair: str, pairdata: List[Dict], timerange: TimeRange) -> None: if timerange.starttype == 'date' and pairdata[0][0] > timerange.startts * 1000: logger.warning('Missing data at start for pair %s, data starts at %s', pair, arrow.get(pairdata[0][0] // 1000).strftime('%Y-%m-%d %H:%M:%S')) @@ -331,7 +331,7 @@ def _download_pair_history(datadir: Path, def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes: List[str], datadir: Path, timerange: Optional[TimeRange] = None, - erase=False) -> List[str]: + erase: bool = False) -> List[str]: """ Refresh stored ohlcv data for backtesting and hyperopt operations. Used by freqtrade download-data subcommand. @@ -401,7 +401,7 @@ def _download_trades_history(datadir: Path, def refresh_backtest_trades_data(exchange: Exchange, pairs: List[str], datadir: Path, - timerange: TimeRange, erase=False) -> List[str]: + timerange: TimeRange, erase: bool = False) -> List[str]: """ Refresh stored trades data for backtesting and hyperopt operations. Used by freqtrade download-data subcommand. @@ -428,7 +428,7 @@ def refresh_backtest_trades_data(exchange: Exchange, pairs: List[str], datadir: def convert_trades_to_ohlcv(pairs: List[str], timeframes: List[str], - datadir: Path, timerange: TimeRange, erase=False) -> None: + datadir: Path, timerange: TimeRange, erase: bool = False) -> None: """ Convert stored trades data to ohlcv data """ diff --git a/freqtrade/edge/edge_positioning.py b/freqtrade/edge/edge_positioning.py index 15883357b..1506b4ed5 100644 --- a/freqtrade/edge/edge_positioning.py +++ b/freqtrade/edge/edge_positioning.py @@ -1,7 +1,7 @@ # pragma pylint: disable=W0603 """ Edge positioning package """ import logging -from typing import Any, Dict, NamedTuple +from typing import Any, Dict, List, NamedTuple import arrow import numpy as np @@ -181,7 +181,7 @@ class Edge: 'strategy stoploss is returned instead.') return self.strategy.stoploss - def adjust(self, pairs) -> list: + def adjust(self, pairs: List[str]) -> list: """ Filters out and sorts "pairs" according to Edge calculated pairs """ diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 87c189457..f7bfb0ee1 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -24,6 +24,12 @@ from freqtrade.exceptions import (DependencyException, InvalidOrderException, from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async from freqtrade.misc import deep_merge_dicts + +# Should probably use typing.Literal when we switch to python 3.8+ +# CcxtModuleType = Literal[ccxt, ccxt_async] +CcxtModuleType = Any + + logger = logging.getLogger(__name__) @@ -51,7 +57,7 @@ class Exchange: } _ft_has: Dict = {} - def __init__(self, config: dict, validate: bool = True) -> None: + def __init__(self, config: Dict[str, Any], validate: bool = True) -> None: """ Initializes this module with the given config, it does basic validation whether the specified exchange and pairs are valid. @@ -135,7 +141,7 @@ class Exchange: if self._api_async and inspect.iscoroutinefunction(self._api_async.close): asyncio.get_event_loop().run_until_complete(self._api_async.close()) - def _init_ccxt(self, exchange_config: dict, ccxt_module=ccxt, + def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt, ccxt_kwargs: dict = None) -> ccxt.Exchange: """ Initialize ccxt with given config and return valid @@ -224,13 +230,13 @@ class Exchange: markets = self.markets return sorted(set([x['quote'] for _, x in markets.items()])) - def klines(self, pair_interval: Tuple[str, str], copy=True) -> DataFrame: + def klines(self, pair_interval: Tuple[str, str], copy: bool = True) -> DataFrame: if pair_interval in self._klines: return self._klines[pair_interval].copy() if copy else self._klines[pair_interval] else: return DataFrame() - def set_sandbox(self, api, exchange_config: dict, name: str): + def set_sandbox(self, api: ccxt.Exchange, exchange_config: dict, name: str) -> None: if exchange_config.get('sandbox'): if api.urls.get('test'): api.urls['api'] = api.urls['test'] @@ -240,7 +246,7 @@ class Exchange: "Please check your config.json") raise OperationalException(f'Exchange {name} does not provide a sandbox api') - def _load_async_markets(self, reload=False) -> None: + def _load_async_markets(self, reload: bool = False) -> None: try: if self._api_async: asyncio.get_event_loop().run_until_complete( @@ -273,7 +279,7 @@ class Exchange: except ccxt.BaseError: logger.exception("Could not reload markets.") - def validate_stakecurrency(self, stake_currency) -> None: + def validate_stakecurrency(self, stake_currency: str) -> None: """ Checks stake-currency against available currencies on the exchange. :param stake_currency: Stake-currency to validate @@ -319,7 +325,7 @@ class Exchange: f"Please check if you are impacted by this restriction " f"on the exchange and eventually remove {pair} from your whitelist.") - def get_valid_pair_combination(self, curr_1, curr_2) -> str: + def get_valid_pair_combination(self, curr_1: str, curr_2: str) -> str: """ Get valid pair combination of curr_1 and curr_2 by trying both combinations. """ @@ -373,7 +379,7 @@ class Exchange: raise OperationalException( f'Time in force policies are not supported for {self.name} yet.') - def validate_required_startup_candles(self, startup_candles) -> None: + def validate_required_startup_candles(self, startup_candles: int) -> None: """ Checks if required startup_candles is more than ohlcv_candle_limit. Requires a grace-period of 5 candles - so a startup-period up to 494 is allowed by default. @@ -392,7 +398,7 @@ class Exchange: """ return endpoint in self._api.has and self._api.has[endpoint] - def amount_to_precision(self, pair, amount: float) -> float: + def amount_to_precision(self, pair: str, amount: float) -> float: ''' Returns the amount to buy or sell to a precision the Exchange accepts Reimplementation of ccxt internal methods - ensuring we can test the result is correct @@ -406,7 +412,7 @@ class Exchange: return amount - def price_to_precision(self, pair, price: float) -> float: + def price_to_precision(self, pair: str, price: float) -> float: ''' Returns the price rounded up to the precision the Exchange accepts. Partial Reimplementation of ccxt internal method decimal_to_precision(), @@ -494,7 +500,7 @@ class Exchange: raise OperationalException(e) from e def buy(self, pair: str, ordertype: str, amount: float, - rate: float, time_in_force) -> Dict: + rate: float, time_in_force: str) -> Dict: if self._config['dry_run']: dry_order = self.dry_run_order(pair, ordertype, "buy", amount, rate) @@ -507,7 +513,7 @@ class Exchange: return self.create_order(pair, ordertype, 'buy', amount, rate, params) def sell(self, pair: str, ordertype: str, amount: float, - rate: float, time_in_force='gtc') -> Dict: + rate: float, time_in_force: str = 'gtc') -> Dict: if self._config['dry_run']: dry_order = self.dry_run_order(pair, ordertype, "sell", amount, rate) @@ -976,8 +982,8 @@ class Exchange: raise OperationalException(e) from e @retrier - def get_fee(self, symbol, type='', side='', amount=1, - price=1, taker_or_maker='maker') -> float: + def get_fee(self, symbol: str, type: str = '', side: str = '', amount: float = 1, + price: float = 1, taker_or_maker: str = 'maker') -> float: try: # validate that markets are loaded before trying to get fee if self._api.markets is None or len(self._api.markets) == 0: @@ -1000,7 +1006,7 @@ def get_exchange_bad_reason(exchange_name: str) -> str: return BAD_EXCHANGES.get(exchange_name, "") -def is_exchange_known_ccxt(exchange_name: str, ccxt_module=None) -> bool: +def is_exchange_known_ccxt(exchange_name: str, ccxt_module: CcxtModuleType = None) -> bool: return exchange_name in ccxt_exchanges(ccxt_module) @@ -1008,14 +1014,14 @@ def is_exchange_officially_supported(exchange_name: str) -> bool: return exchange_name in ['bittrex', 'binance'] -def ccxt_exchanges(ccxt_module=None) -> List[str]: +def ccxt_exchanges(ccxt_module: 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=None) -> List[str]: +def available_exchanges(ccxt_module: CcxtModuleType = None) -> List[str]: """ Return exchanges available to the bot, i.e. non-bad exchanges in the ccxt list """ @@ -1075,7 +1081,8 @@ def timeframe_to_next_date(timeframe: str, date: datetime = None) -> datetime: return datetime.fromtimestamp(new_timestamp, tz=timezone.utc) -def symbol_is_pair(market_symbol: str, base_currency: str = None, quote_currency: str = None): +def symbol_is_pair(market_symbol: str, base_currency: str = None, + quote_currency: str = None) -> bool: """ Check if the market symbol is a pair, i.e. that its symbol consists of the base currency and the quote currency separated by '/' character. If base_currency and/or quote_currency is passed, @@ -1088,7 +1095,7 @@ def symbol_is_pair(market_symbol: str, base_currency: str = None, quote_currency (symbol_parts[1] == quote_currency if quote_currency else len(symbol_parts[1]) > 0)) -def market_is_active(market): +def market_is_active(market: Dict) -> bool: """ Return True if the market is active. """ diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index aac501054..34dbca38e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -265,7 +265,7 @@ class FreqtradeBot: return used_rate - def get_trade_stake_amount(self, pair) -> float: + def get_trade_stake_amount(self, pair: str) -> float: """ Calculate stake amount for the trade :return: float: Stake amount @@ -539,7 +539,7 @@ class FreqtradeBot: return True - def _notify_buy(self, trade: Trade, order_type: str): + def _notify_buy(self, trade: Trade, order_type: str) -> None: """ Sends rpc notification when a buy occured. """ @@ -735,7 +735,7 @@ class FreqtradeBot: return False - def handle_trailing_stoploss_on_exchange(self, trade: Trade, order): + def handle_trailing_stoploss_on_exchange(self, trade: Trade, order: dict) -> None: """ Check to see if stoploss on exchange should be updated in case of trailing stoploss on exchange @@ -758,10 +758,8 @@ class FreqtradeBot: f"for pair {trade.pair}") # Create new stoploss order - if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, - rate=trade.stop_loss): - return False - else: + if not self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, + rate=trade.stop_loss): logger.warning(f"Could not create trailing stoploss order " f"for pair {trade.pair}.") @@ -990,7 +988,7 @@ class FreqtradeBot: self._notify_sell(trade, order_type) - def _notify_sell(self, trade: Trade, order_type: str): + def _notify_sell(self, trade: Trade, order_type: str) -> None: """ Sends rpc notification when a sell occured. """ @@ -1031,7 +1029,7 @@ class FreqtradeBot: # Common update trade state methods # - def update_trade_state(self, trade, action_order: dict = None): + def update_trade_state(self, trade: Trade, action_order: dict = None) -> None: """ Checks trades with open orders and updates the amount if necessary """ diff --git a/freqtrade/misc.py b/freqtrade/misc.py index bcba78cf0..2a981c249 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -6,6 +6,7 @@ import logging import re from datetime import datetime from pathlib import Path +from typing import Any from typing.io import IO import numpy as np @@ -40,7 +41,7 @@ def datesarray_to_datetimearray(dates: np.ndarray) -> np.ndarray: return dates.dt.to_pydatetime() -def file_dump_json(filename: Path, data, is_zip=False) -> None: +def file_dump_json(filename: Path, data: Any, is_zip: bool = False) -> None: """ Dump JSON data into a file :param filename: file to create @@ -61,7 +62,7 @@ def file_dump_json(filename: Path, data, is_zip=False) -> None: logger.debug(f'done json to "{filename}"') -def json_load(datafile: IO): +def json_load(datafile: IO) -> Any: """ load data with rapidjson Use this to have a consistent experience, @@ -125,11 +126,11 @@ def round_dict(d, n): return {k: (round(v, n) if isinstance(v, float) else v) for k, v in d.items()} -def plural(num, singular: str, plural: str = None) -> str: +def plural(num: float, singular: str, plural: str = None) -> str: return singular if (num == 1 or num == -1) else plural or singular + 's' -def render_template(templatefile: str, arguments: dict = {}): +def render_template(templatefile: str, arguments: dict = {}) -> str: from jinja2 import Environment, PackageLoader, select_autoescape diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index cdf74f65f..ef493e240 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -9,6 +9,7 @@ from datetime import datetime, timedelta from pathlib import Path from typing import Any, Dict, List, NamedTuple, Optional +import arrow from pandas import DataFrame from freqtrade.configuration import (TimeRange, remove_credentials, @@ -24,7 +25,7 @@ from freqtrade.optimize.optimize_reports import ( from freqtrade.persistence import Trade from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.state import RunMode -from freqtrade.strategy.interface import IStrategy, SellType +from freqtrade.strategy.interface import IStrategy, SellCheckTuple, SellType logger = logging.getLogger(__name__) @@ -148,7 +149,7 @@ class Backtesting: logger.info(f'Dumping backtest results to {recordfilename}') file_dump_json(recordfilename, records) - def _get_ticker_list(self, processed) -> Dict[str, DataFrame]: + def _get_ticker_list(self, processed: Dict) -> Dict[str, DataFrame]: """ Helper function to convert a processed tickerlist into a list for performance reasons. @@ -175,7 +176,8 @@ class Backtesting: ticker[pair] = [x for x in ticker_data.itertuples()] return ticker - def _get_close_rate(self, sell_row, trade: Trade, sell, trade_dur) -> float: + def _get_close_rate(self, sell_row, trade: Trade, sell: SellCheckTuple, + trade_dur: int) -> float: """ Get close rate for backtesting result """ @@ -280,7 +282,7 @@ class Backtesting: return None def backtest(self, processed: Dict, stake_amount: float, - start_date, end_date, + start_date: arrow.Arrow, end_date: arrow.Arrow, max_open_trades: int = 0, position_stacking: bool = False) -> DataFrame: """ Implement backtesting functionality diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 525f491f3..841f8b6db 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -117,11 +117,11 @@ class Hyperopt: self.print_json = self.config.get('print_json', False) @staticmethod - def get_lock_filename(config) -> str: + def get_lock_filename(config: Dict[str, Any]) -> str: return str(config['user_data_dir'] / 'hyperopt.lock') - def clean_hyperopt(self): + def clean_hyperopt(self) -> None: """ Remove hyperopt pickle files to restart hyperopt. """ @@ -158,7 +158,7 @@ class Hyperopt: f"saved to '{self.trials_file}'.") @staticmethod - def _read_trials(trials_file) -> List: + def _read_trials(trials_file: Path) -> List: """ Read hyperopt trials file """ @@ -189,7 +189,7 @@ class Hyperopt: return result @staticmethod - def print_epoch_details(results, total_epochs, print_json: bool, + def print_epoch_details(results, total_epochs: int, print_json: bool, no_header: bool = False, header_str: str = None) -> None: """ Display details of the hyperopt result @@ -218,7 +218,7 @@ class Hyperopt: Hyperopt._params_pretty_print(params, 'trailing', "Trailing stop:") @staticmethod - def _params_update_for_json(result_dict, params, space: str): + def _params_update_for_json(result_dict, params, space: str) -> None: if space in params: space_params = Hyperopt._space_params(params, space) if space in ['buy', 'sell']: @@ -235,7 +235,7 @@ class Hyperopt: result_dict.update(space_params) @staticmethod - def _params_pretty_print(params, space: str, header: str): + def _params_pretty_print(params, space: str, header: str) -> None: if space in params: space_params = Hyperopt._space_params(params, space, 5) if space == 'stoploss': @@ -251,7 +251,7 @@ class Hyperopt: return round_dict(d, r) if r else d @staticmethod - def is_best_loss(results, current_best_loss) -> bool: + def is_best_loss(results, current_best_loss: float) -> bool: return results['loss'] < current_best_loss def print_results(self, results) -> None: @@ -438,7 +438,7 @@ class Hyperopt: random_state=self.random_state, ) - def fix_optimizer_models_list(self): + def fix_optimizer_models_list(self) -> None: """ WORKAROUND: Since skopt is not actively supported, this resolves problems with skopt memory usage, see also: https://github.com/scikit-optimize/scikit-optimize/pull/746 @@ -460,7 +460,7 @@ class Hyperopt: wrap_non_picklable_objects(self.generate_optimizer))(v, i) for v in asked) @staticmethod - def load_previous_results(trials_file) -> List: + def load_previous_results(trials_file: Path) -> List: """ Load data for epochs from the file if we have one """ diff --git a/freqtrade/pairlist/IPairList.py b/freqtrade/pairlist/IPairList.py index d722e70f5..1ad4da523 100644 --- a/freqtrade/pairlist/IPairList.py +++ b/freqtrade/pairlist/IPairList.py @@ -7,7 +7,7 @@ Provides lists as configured in config.json import logging from abc import ABC, abstractmethod, abstractproperty from copy import deepcopy -from typing import Dict, List +from typing import Any, Dict, List from freqtrade.exchange import market_is_active @@ -16,7 +16,8 @@ logger = logging.getLogger(__name__) class IPairList(ABC): - def __init__(self, exchange, pairlistmanager, config, pairlistconfig: dict, + def __init__(self, exchange, pairlistmanager, + config: Dict[str, Any], pairlistconfig: Dict[str, Any], pairlist_pos: int) -> None: """ :param exchange: Exchange instance diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index 5d364795d..f16458ca5 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -48,10 +48,10 @@ class PrecisionFilter(IPairList): """ Filters and sorts pairlists and assigns and returns them again. """ - stoploss = None - if self._config.get('stoploss') is not None: + stoploss = self._config.get('stoploss') + if stoploss is not None: # Precalculate sanitized stoploss value to avoid recalculation for every pair - stoploss = 1 - abs(self._config.get('stoploss')) + stoploss = 1 - abs(stoploss) # Copy list since we're modifying this list for p in deepcopy(pairlist): ticker = tickers.get(p) diff --git a/freqtrade/pairlist/PriceFilter.py b/freqtrade/pairlist/PriceFilter.py index b3546ebd9..dc02ae251 100644 --- a/freqtrade/pairlist/PriceFilter.py +++ b/freqtrade/pairlist/PriceFilter.py @@ -1,6 +1,6 @@ import logging from copy import deepcopy -from typing import Dict, List +from typing import Any, Dict, List from freqtrade.pairlist.IPairList import IPairList @@ -9,7 +9,8 @@ logger = logging.getLogger(__name__) class PriceFilter(IPairList): - def __init__(self, exchange, pairlistmanager, config, pairlistconfig: dict, + def __init__(self, exchange, pairlistmanager, + config: Dict[str, Any], pairlistconfig: Dict[str, Any], pairlist_pos: int) -> None: super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index 4ac9935ba..3b28cb7d1 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -6,7 +6,7 @@ Provides lists as configured in config.json """ import logging from datetime import datetime -from typing import Dict, List +from typing import Any, Dict, List from freqtrade.exceptions import OperationalException from freqtrade.pairlist.IPairList import IPairList @@ -18,7 +18,7 @@ SORT_VALUES = ['askVolume', 'bidVolume', 'quoteVolume'] class VolumePairList(IPairList): - def __init__(self, exchange, pairlistmanager, config, pairlistconfig: dict, + def __init__(self, exchange, pairlistmanager, config: Dict[str, Any], pairlistconfig: dict, pairlist_pos: int) -> None: super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) @@ -77,7 +77,8 @@ class VolumePairList(IPairList): else: return pairlist - def _gen_pair_whitelist(self, pairlist, tickers, base_currency: str, key: str) -> List[str]: + def _gen_pair_whitelist(self, pairlist: List[str], tickers: Dict, + base_currency: str, key: str) -> List[str]: """ Updates the whitelist with with a dynamically generated list :param base_currency: base currency as str diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 75116f1e3..5b0046091 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -64,11 +64,11 @@ def init(db_url: str, clean_open_orders: bool = False) -> None: clean_dry_run_db() -def has_column(columns, searchname: str) -> bool: +def has_column(columns: List, searchname: str) -> bool: return len(list(filter(lambda x: x["name"] == searchname, columns))) == 1 -def get_column_def(columns, column: str, default: str) -> str: +def get_column_def(columns: List, column: str, default: str) -> str: return default if not has_column(columns, column) else column @@ -246,14 +246,15 @@ class Trade(_DECL_BASE): if self.initial_stop_loss_pct else None), } - def adjust_min_max_rates(self, current_price: float): + def adjust_min_max_rates(self, current_price: float) -> None: """ Adjust the max_rate and min_rate. """ self.max_rate = max(current_price, self.max_rate or self.open_rate) self.min_rate = min(current_price, self.min_rate or self.open_rate) - def adjust_stop_loss(self, current_price: float, stoploss: float, initial: bool = False): + def adjust_stop_loss(self, current_price: float, stoploss: float, + initial: bool = False) -> None: """ This adjusts the stop loss to it's most recently observed setting :param current_price: Current rate the asset is traded diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 5301d762d..943133ed0 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -370,7 +370,7 @@ def generate_profit_graph(pairs: str, tickers: Dict[str, pd.DataFrame], return fig -def generate_plot_filename(pair, timeframe) -> str: +def generate_plot_filename(pair: str, timeframe: str) -> str: """ Generate filenames per pair/timeframe to be used for storing plots """ diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index 5a844097c..3aec5f9e9 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -25,7 +25,7 @@ class IResolver: initial_search_path: Path @classmethod - def build_search_paths(cls, config, user_subdir: Optional[str] = None, + def build_search_paths(cls, config: Dict[str, Any], user_subdir: Optional[str] = None, extra_dir: Optional[str] = None) -> List[Path]: abs_paths: List[Path] = [cls.initial_search_path] diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 9e64f38df..015ba24d9 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -9,7 +9,7 @@ from base64 import urlsafe_b64decode from collections import OrderedDict from inspect import getfullargspec from pathlib import Path -from typing import Dict, Optional +from typing import Any, Dict, Optional from freqtrade.constants import (REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGY) @@ -30,7 +30,7 @@ class StrategyResolver(IResolver): initial_search_path = Path(__file__).parent.parent.joinpath('strategy').resolve() @staticmethod - def load_strategy(config: Optional[Dict] = None) -> IStrategy: + def load_strategy(config: Dict[str, Any] = None) -> IStrategy: """ Load the custom class from config parameter :param config: configuration dictionary or None @@ -96,7 +96,8 @@ class StrategyResolver(IResolver): return strategy @staticmethod - def _override_attribute_helper(strategy, config, attribute: str, default): + def _override_attribute_helper(strategy, config: Dict[str, Any], + attribute: str, default: Any): """ Override attributes in the strategy. Prevalence: diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 41097c211..7f5cfc101 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -139,7 +139,8 @@ class RPC: results.append(trade_dict) return results - def _rpc_status_table(self, stake_currency, fiat_display_currency: str) -> Tuple[List, List]: + def _rpc_status_table(self, stake_currency: str, + fiat_display_currency: str) -> Tuple[List, List]: trades = Trade.get_open_trades() if not trades: raise RPCException('no active trade') @@ -385,7 +386,7 @@ class RPC: return {'status': 'No more buy will occur from now. Run /reload_conf to reset.'} - def _rpc_forcesell(self, trade_id) -> Dict[str, str]: + def _rpc_forcesell(self, trade_id: str) -> Dict[str, str]: """ Handler for forcesell . Sells the given trade at current price diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index f687fe4d1..670275991 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -61,7 +61,7 @@ class RPCManager: except NotImplementedError: logger.error(f"Message type {msg['type']} not implemented by handler {mod.name}.") - def startup_messages(self, config, pairlist) -> None: + def startup_messages(self, config: Dict[str, Any], pairlist) -> None: if config['dry_run']: self.send_msg({ 'type': RPCMessageType.WARNING_NOTIFICATION, diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 27bc8280e..6e15c5183 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -180,7 +180,7 @@ class IStrategy(ABC): if pair not in self._pair_locked_until or self._pair_locked_until[pair] < until: self._pair_locked_until[pair] = until - def unlock_pair(self, pair) -> None: + def unlock_pair(self, pair: str) -> None: """ Unlocks a pair previously locked using lock_pair. Not used by freqtrade itself, but intended to be used if users lock pairs diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index c52767162..dd5e34fe6 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -30,24 +30,21 @@ class Wallets: self._last_wallet_refresh = 0 self.update() - def get_free(self, currency) -> float: - + def get_free(self, currency: str) -> float: balance = self._wallets.get(currency) if balance and balance.free: return balance.free else: return 0 - def get_used(self, currency) -> float: - + def get_used(self, currency: str) -> float: balance = self._wallets.get(currency) if balance and balance.used: return balance.used else: return 0 - def get_total(self, currency) -> float: - + def get_total(self, currency: str) -> float: balance = self._wallets.get(currency) if balance and balance.total: return balance.total @@ -87,7 +84,6 @@ class Wallets: self._wallets = _wallets def _update_live(self) -> None: - balances = self._exchange.get_balances() for currency in balances: diff --git a/freqtrade/worker.py b/freqtrade/worker.py index 6da04b4a2..64cc97026 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -22,7 +22,7 @@ class Worker: Freqtradebot worker class """ - def __init__(self, args: Dict[str, Any], config=None) -> None: + def __init__(self, args: Dict[str, Any], config: Dict[str, Any] = None) -> None: """ Init all variables and objects the bot needs to work """