From 07ec3b27fe03bdb5587ffdfaddf8fc605cbe77fe Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 15 Apr 2022 15:48:37 +0200 Subject: [PATCH 1/8] Add typing information to retrier decorator --- freqtrade/exchange/common.py | 21 ++++++++++++++++++--- freqtrade/exchange/exchange.py | 4 ++-- freqtrade/exchange/ftx.py | 4 ++-- freqtrade/exchange/gateio.py | 4 ++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index 4355662a8..a9f03ba1a 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -2,6 +2,7 @@ import asyncio import logging import time from functools import wraps +from typing import Any, Callable, Optional, TypeVar, cast, overload from freqtrade.exceptions import DDosProtection, RetryableOrderError, TemporaryError from freqtrade.mixins import LoggingMixin @@ -133,8 +134,22 @@ def retrier_async(f): return wrapper -def retrier(_func=None, retries=API_RETRY_COUNT): - def decorator(f): +F = TypeVar('F', bound=Callable[..., Any]) + + +# Type shenanigans +@overload +def retrier(_func: F) -> F: + ... + + +@overload +def retrier(*, retries=API_RETRY_COUNT) -> Callable[[F], F]: + ... + + +def retrier(_func: Optional[F] = None, *, retries=API_RETRY_COUNT): + def decorator(f: F) -> F: @wraps(f) def wrapper(*args, **kwargs): count = kwargs.pop('count', retries) @@ -155,7 +170,7 @@ def retrier(_func=None, retries=API_RETRY_COUNT): else: logger.warning(msg + 'Giving up.') raise ex - return wrapper + return cast(F, wrapper) # Support both @retrier and @retrier(retries=2) syntax if _func is None: return decorator diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 2fa397300..b25886868 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1164,7 +1164,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={}) -> Dict: + def fetch_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: return self.fetch_dry_run_order(order_id) try: @@ -1212,7 +1212,7 @@ class Exchange: and order.get('filled') == 0.0) @retrier - def cancel_order(self, order_id: str, pair: str, params={}) -> Dict: + def cancel_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: try: order = self.fetch_dry_run_order(order_id) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 65c2a53ca..9ee6894f1 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -104,7 +104,7 @@ class Ftx(Exchange): raise OperationalException(e) from e @retrier(retries=API_FETCH_ORDER_RETRY_COUNT) - def fetch_stoploss_order(self, order_id: str, pair: str) -> Dict: + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: return self.fetch_dry_run_order(order_id) @@ -145,7 +145,7 @@ class Ftx(Exchange): raise OperationalException(e) from e @retrier - def cancel_stoploss_order(self, order_id: str, pair: str) -> Dict: + def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: return {} try: diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 609cf4901..4147e8290 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -71,14 +71,14 @@ class Gateio(Exchange): } return trades - def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: return self.fetch_order( order_id=order_id, pair=pair, params={'stop': True} ) - def cancel_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: + def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: return self.cancel_order( order_id=order_id, pair=pair, From 7f4161ff782857f648f5a55efe8bf5be30b15688 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 16 Apr 2022 06:53:38 +0200 Subject: [PATCH 2/8] Add typehints to strategy wrapper --- freqtrade/strategy/strategy_wrapper.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/freqtrade/strategy/strategy_wrapper.py b/freqtrade/strategy/strategy_wrapper.py index 9aead8395..8cb0bde15 100644 --- a/freqtrade/strategy/strategy_wrapper.py +++ b/freqtrade/strategy/strategy_wrapper.py @@ -1,5 +1,7 @@ import logging from copy import deepcopy +from functools import wraps +from typing import Any, Callable, TypeVar, cast from freqtrade.exceptions import StrategyError @@ -7,12 +9,16 @@ from freqtrade.exceptions import StrategyError logger = logging.getLogger(__name__) -def strategy_safe_wrapper(f, message: str = "", default_retval=None, supress_error=False): +F = TypeVar('F', bound=Callable[..., Any]) + + +def strategy_safe_wrapper(f: F, message: str = "", default_retval=None, supress_error=False) -> F: """ Wrapper around user-provided methods and functions. Caches all exceptions and returns either the default_retval (if it's not None) or raises a StrategyError exception, which then needs to be handled by the calling method. """ + @wraps(f) def wrapper(*args, **kwargs): try: if 'trade' in kwargs: @@ -37,4 +43,4 @@ def strategy_safe_wrapper(f, message: str = "", default_retval=None, supress_err raise StrategyError(str(error)) from error return default_retval - return wrapper + return cast(F, wrapper) From 502404c0cc3c36faafb0039b9e19aaed9fd3c5c8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 May 2022 11:30:45 +0200 Subject: [PATCH 3/8] Use pyproject.toml instead of setup.cfg --- pyproject.toml | 11 +++++++++++ setup.cfg | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e8d5ed47e..935874ab8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,17 @@ skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*", "**/user_data/*" [tool.pytest.ini_options] asyncio_mode = "auto" +[tool.mypy] +ignore_missing_imports = true +warn_unused_ignores = true +exclude = [ + '^build_helpers\.py$' +] + +[[tool.mypy.overrides]] +module = "tests.*" +ignore_errors = true + [build-system] requires = ["setuptools >= 46.4.0", "wheel"] build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 042517ec9..d711534d9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -50,13 +50,3 @@ exclude = .eggs, user_data, -[mypy] -ignore_missing_imports = True -warn_unused_ignores = True -exclude = (?x)( - ^build_helpers\.py$ - ) - - -[mypy-tests.*] -ignore_errors = True From 3f68c3b68e8d23d1f5d7c42264c73af0670b9d65 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 May 2022 11:41:57 +0200 Subject: [PATCH 4/8] Update some types --- freqtrade/configuration/check_exchange.py | 2 +- freqtrade/exchange/exchange.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index fa1f47f9b..2be13ce4f 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -27,7 +27,7 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: return True logger.info("Checking exchange...") - exchange = config.get('exchange', {}).get('name').lower() + exchange = config.get('exchange', {}).get('name', '').lower() if not exchange: raise OperationalException( f'This command requires a configured exchange. You should either use ' diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index b25886868..1044ad652 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -92,8 +92,8 @@ class Exchange: it does basic validation whether the specified exchange and pairs are valid. :return: None """ - self._api: ccxt.Exchange = None - self._api_async: ccxt_async.Exchange = None + self._api: ccxt.Exchange + self._api_async: ccxt_async.Exchange self._markets: Dict = {} self._trading_fees: Dict[str, Any] = {} self._leverage_tiers: Dict[str, List[Dict]] = {} @@ -291,7 +291,7 @@ class Exchange: return self._markets @property - def precisionMode(self) -> str: + def precisionMode(self) -> int: """exchange ccxt precisionMode""" return self._api.precisionMode @@ -322,7 +322,7 @@ class Exchange: return int(self._ft_has.get('ohlcv_candle_limit_per_timeframe', {}).get( timeframe, self._ft_has.get('ohlcv_candle_limit'))) - def get_markets(self, base_currencies: List[str] = None, quote_currencies: List[str] = None, + def get_markets(self, base_currencies: List[str] = [], quote_currencies: List[str] = [], spot_only: bool = False, margin_only: bool = False, futures_only: bool = False, tradable_only: bool = True, active_only: bool = False) -> Dict[str, Any]: @@ -1718,7 +1718,7 @@ class Exchange: async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, since_ms: int, candle_type: CandleType, is_new_pair: bool = False, raise_: bool = False, - until_ms: int = None + until_ms: Optional[int] = None ) -> Tuple[str, str, str, List]: """ Download historic ohlcv @@ -1779,7 +1779,7 @@ class Exchange: def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *, since_ms: Optional[int] = None, cache: bool = True, - drop_incomplete: bool = None + drop_incomplete: Optional[bool] = None ) -> Dict[PairWithTimeframe, DataFrame]: """ Refresh in-memory OHLCV asynchronously and set `_klines` with the result From f1a72e448a49c0774a84202942575815ea948189 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 May 2022 11:55:49 +0200 Subject: [PATCH 5/8] Align interfaces and strategy templates --- docs/strategy-callbacks.md | 4 ++-- docs/strategy_migration.md | 4 ++-- freqtrade/strategy/interface.py | 7 ++++--- freqtrade/templates/base_strategy.py.j2 | 2 +- .../subtemplates/strategy_methods_advanced.j2 | 10 +++++----- tests/strategy/strats/strategy_test_v2.py | 4 +++- tests/strategy/strats/strategy_test_v3.py | 4 +++- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index ab67a3c26..63bd4f958 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -656,7 +656,7 @@ class DigDeeperStrategy(IStrategy): # This is called when placing the initial order (opening trade) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], side: str, **kwargs) -> float: # We need to leave most of the funds for possible further DCA orders @@ -664,7 +664,7 @@ class DigDeeperStrategy(IStrategy): return proposed_stake / self.max_dca_multiplier def adjust_trade_position(self, trade: Trade, current_time: datetime, - current_rate: float, current_profit: float, min_stake: float, + current_rate: float, current_profit: float, min_stake: Optional[float], max_stake: float, **kwargs): """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 458e80d0e..471ffa601 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -199,7 +199,7 @@ New string argument `side` - which can be either `"long"` or `"short"`. ``` python hl_lines="4" class AwesomeStrategy(IStrategy): def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], **kwargs) -> float: # ... return proposed_stake @@ -208,7 +208,7 @@ class AwesomeStrategy(IStrategy): ``` python hl_lines="4" class AwesomeStrategy(IStrategy): def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], side: str, **kwargs) -> float: # ... return proposed_stake diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 57afbf32a..44f7466ec 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -429,7 +429,7 @@ class IStrategy(ABC, HyperStrategyMixin): return self.custom_sell(pair, trade, current_time, current_rate, current_profit, **kwargs) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], side: str, **kwargs) -> float: """ Customize stake size for each new trade. @@ -447,8 +447,9 @@ class IStrategy(ABC, HyperStrategyMixin): return proposed_stake def adjust_trade_position(self, trade: Trade, current_time: datetime, - current_rate: float, current_profit: float, min_stake: float, - max_stake: float, **kwargs) -> Optional[float]: + current_rate: float, current_profit: float, + min_stake: Optional[float], max_stake: float, + **kwargs) -> Optional[float]: """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. This means extra buy orders with additional fees. diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index 9e7e1fe50..8c7594322 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -6,7 +6,7 @@ import numpy as np # noqa import pandas as pd # noqa from pandas import DataFrame # noqa from datetime import datetime # noqa -from typing import Optional # noqa +from typing import Optional, Union # noqa from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, IStrategy, IntParameter) diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index 317602da9..3854efd85 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -13,7 +13,7 @@ def bot_loop_start(self, **kwargs) -> None: pass def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: float, - entry_tag: Optional[str], **kwargs) -> float: + entry_tag: 'Optional[str]', side: str, **kwargs) -> float: """ Custom entry price logic, returning the new entry price. @@ -80,8 +80,8 @@ def custom_exit_price(self, pair: str, trade: 'Trade', return proposed_rate def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, - side: str, entry_tag: Optional[str], **kwargs) -> float: + proposed_stake: float, min_stake: Optional[float], max_stake: float, + entry_tag: 'Optional[str]', side: str, **kwargs) -> float: """ Customize stake size for each new trade. @@ -244,8 +244,8 @@ def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order', return False def adjust_trade_position(self, trade: 'Trade', current_time: 'datetime', - current_rate: float, current_profit: float, min_stake: float, - max_stake: float, **kwargs) -> Optional[float]: + current_rate: float, current_profit: float, min_stake: Optional[float], + max_stake: float, **kwargs) -> 'Optional[float]': """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. This means extra buy orders with additional fees. diff --git a/tests/strategy/strats/strategy_test_v2.py b/tests/strategy/strats/strategy_test_v2.py index 85ff856e1..46181ac7e 100644 --- a/tests/strategy/strats/strategy_test_v2.py +++ b/tests/strategy/strats/strategy_test_v2.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement from datetime import datetime +from typing import Optional import talib.abstract as ta from pandas import DataFrame @@ -151,7 +152,8 @@ class StrategyTestV2(IStrategy): return dataframe def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, min_stake: float, max_stake: float, **kwargs): + current_profit: float, + min_stake: Optional[float], max_stake: float, **kwargs): if current_profit < -0.0075: orders = trade.select_filled_orders('buy') diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py index df83d3663..340001ef2 100644 --- a/tests/strategy/strats/strategy_test_v3.py +++ b/tests/strategy/strats/strategy_test_v3.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement from datetime import datetime +from typing import Optional import talib.abstract as ta from pandas import DataFrame @@ -185,7 +186,8 @@ class StrategyTestV3(IStrategy): return 3.0 def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, min_stake: float, max_stake: float, **kwargs): + current_profit: float, + min_stake: Optional[float], max_stake: float, **kwargs): if current_profit < -0.0075: orders = trade.select_filled_orders(trade.entry_side) From 0a713faca84c5039b9245ab6d25ba22c1b4d112e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 May 2022 14:53:51 +0200 Subject: [PATCH 6/8] Fix some type errors --- freqtrade/optimize/backtesting.py | 22 ++++++++++++++++------ freqtrade/strategy/interface.py | 17 +++++++++-------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 3a3660c39..19922ee57 100755 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -500,7 +500,8 @@ class Backtesting: stake_available = self.wallets.get_available_stake_amount() stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)( - trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], + trade=trade, # type: ignore[arg-type] + current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], current_profit=current_profit, min_stake=min_stake, max_stake=min(max_stake, stake_available)) @@ -566,7 +567,8 @@ class Backtesting: if order_type == 'limit': close_rate = strategy_safe_wrapper(self.strategy.custom_exit_price, default_retval=close_rate)( - pair=trade.pair, trade=trade, + pair=trade.pair, + trade=trade, # type: ignore[arg-type] current_time=exit_candle_time, proposed_rate=close_rate, current_profit=current_profit, exit_tag=exit_reason) @@ -580,7 +582,10 @@ class Backtesting: time_in_force = self.strategy.order_time_in_force['exit'] if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( - pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount, + pair=trade.pair, + trade=trade, # type: ignore[arg-type] + order_type='limit', + amount=trade.amount, rate=close_rate, time_in_force=time_in_force, sell_reason=exit_reason, # deprecated @@ -656,7 +661,7 @@ class Backtesting: return self._get_exit_trade_entry_for_candle(trade, row) def get_valid_price_and_stake( - self, pair: str, row: Tuple, propose_rate: float, stake_amount: Optional[float], + self, pair: str, row: Tuple, propose_rate: float, stake_amount_inp: Optional[float], direction: LongShort, current_time: datetime, entry_tag: Optional[str], trade: Optional[LocalTrade], order_type: str ) -> Tuple[float, float, float, float]: @@ -694,6 +699,8 @@ class Backtesting: ) if self._can_short else 1.0 # Cap leverage between 1.0 and max_leverage. leverage = min(max(leverage, 1.0), max_leverage) + elif stake_amount_inp is not None: + stake_amount = stake_amount_inp min_stake_amount = self.exchange.get_min_pair_stake_amount( pair, propose_rate, -0.05, leverage=leverage) or 0 @@ -901,7 +908,9 @@ class Backtesting: Check if current analyzed order has to be canceled. Returns True if the trade should be Deleted (initial order was canceled). """ - timedout = self.strategy.ft_check_timed_out(trade, order, current_time) + timedout = self.strategy.ft_check_timed_out( + trade, # type: ignore[arg-type] + order, current_time) if timedout: if order.side == trade.entry_side: self.timedout_entry_orders += 1 @@ -930,7 +939,8 @@ class Backtesting: if order.side == trade.entry_side and current_time > order.order_date_utc: requested_rate = strategy_safe_wrapper(self.strategy.adjust_entry_price, default_retval=order.price)( - trade=trade, order=order, pair=trade.pair, current_time=current_time, + trade=trade, # type: ignore[arg-type] + order=order, pair=trade.pair, current_time=current_time, proposed_rate=row[OPEN_IDX], current_order_rate=order.price, entry_tag=trade.enter_tag, side=trade.trade_direction ) # default value is current order price diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 44f7466ec..002a7aca5 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -16,7 +16,7 @@ from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirecti SignalType, TradingMode) from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds -from freqtrade.persistence import LocalTrade, Order, PairLocks, Trade +from freqtrade.persistence import Order, PairLocks, Trade from freqtrade.strategy.hyper import HyperStrategyMixin from freqtrade.strategy.informative_decorator import (InformativeData, PopulateIndicators, _create_and_merge_informative_pair, @@ -918,19 +918,20 @@ class IStrategy(ABC, HyperStrategyMixin): if exit_ and not enter: exit_signal = ExitType.EXIT_SIGNAL else: - custom_reason = strategy_safe_wrapper(self.custom_exit, default_retval=False)( + reason_cust = strategy_safe_wrapper(self.custom_exit, default_retval=False)( pair=trade.pair, trade=trade, current_time=current_time, current_rate=current_rate, current_profit=current_profit) - if custom_reason: + if reason_cust: exit_signal = ExitType.CUSTOM_EXIT - if isinstance(custom_reason, str): - if len(custom_reason) > CUSTOM_EXIT_MAX_LENGTH: + if isinstance(reason_cust, str): + custom_reason = reason_cust + if len(reason_cust) > CUSTOM_EXIT_MAX_LENGTH: logger.warning(f'Custom exit reason returned from ' f'custom_exit is too long and was trimmed' f'to {CUSTOM_EXIT_MAX_LENGTH} characters.') - custom_reason = custom_reason[:CUSTOM_EXIT_MAX_LENGTH] + custom_reason = reason_cust[:CUSTOM_EXIT_MAX_LENGTH] else: - custom_reason = None + custom_reason = '' if ( exit_signal == ExitType.CUSTOM_EXIT or (exit_signal == ExitType.EXIT_SIGNAL @@ -1071,7 +1072,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: return current_profit > roi - def ft_check_timed_out(self, trade: LocalTrade, order: Order, + def ft_check_timed_out(self, trade: Trade, order: Order, current_time: datetime) -> bool: """ FT Internal method. From 904f094b806d9a7ee68034ca86c531f7fa83b876 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 May 2022 06:45:56 +0200 Subject: [PATCH 7/8] Don't reassign method, but implement it properly --- freqtrade/data/history/history_utils.py | 1 + freqtrade/exchange/exchange.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index 4600d6ab4..bead59814 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -282,6 +282,7 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes pairs_not_available = [] data_handler = get_datahandler(datadir, data_format) candle_type = CandleType.get_default(trading_mode) + process = '' for idx, pair in enumerate(pairs, start=1): if pair not in exchange.markets: pairs_not_available.append(pair) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 1044ad652..6ef61f227 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1186,8 +1186,8 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - # Assign method to fetch_stoploss_order to allow easy overriding in other classes - fetch_stoploss_order = fetch_order + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> 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: @@ -1238,8 +1238,8 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - # Assign method to cancel_stoploss_order to allow easy overriding in other classes - cancel_stoploss_order = cancel_order + def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: + return self.cancel_order(order_id, pair, params) def is_cancel_order_result_suitable(self, corder) -> bool: if not isinstance(corder, dict): From a8ee77cd5e39b002d629f649147a96264394ad45 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 May 2022 19:13:35 +0200 Subject: [PATCH 8/8] Simplify backtesting typechecking --- freqtrade/optimize/backtesting.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 19922ee57..445de69f0 100755 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -661,7 +661,7 @@ class Backtesting: return self._get_exit_trade_entry_for_candle(trade, row) def get_valid_price_and_stake( - self, pair: str, row: Tuple, propose_rate: float, stake_amount_inp: Optional[float], + self, pair: str, row: Tuple, propose_rate: float, stake_amount: float, direction: LongShort, current_time: datetime, entry_tag: Optional[str], trade: Optional[LocalTrade], order_type: str ) -> Tuple[float, float, float, float]: @@ -699,8 +699,6 @@ class Backtesting: ) if self._can_short else 1.0 # Cap leverage between 1.0 and max_leverage. leverage = min(max(leverage, 1.0), max_leverage) - elif stake_amount_inp is not None: - stake_amount = stake_amount_inp min_stake_amount = self.exchange.get_min_pair_stake_amount( pair, propose_rate, -0.05, leverage=leverage) or 0 @@ -737,8 +735,9 @@ class Backtesting: order_type = self.strategy.order_types['entry'] pos_adjust = trade is not None and requested_rate is None + stake_amount_ = stake_amount or (trade.stake_amount if trade else 0.0) propose_rate, stake_amount, leverage, min_stake_amount = self.get_valid_price_and_stake( - pair, row, row[OPEN_IDX], stake_amount, direction, current_time, entry_tag, trade, + pair, row, row[OPEN_IDX], stake_amount_, direction, current_time, entry_tag, trade, order_type )