import logging from copy import deepcopy from functools import wraps from typing import Any, Callable, TypeVar, cast from freqtrade.exceptions import StrategyError logger = logging.getLogger(__name__) 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: # Protect accidental modifications from within the strategy kwargs['trade'] = deepcopy(kwargs['trade']) return f(*args, **kwargs) except ValueError as error: logger.warning( f"{message}" f"Strategy caused the following exception: {error}" f"{f}" ) if default_retval is None and not supress_error: raise StrategyError(str(error)) from error return default_retval except Exception as error: logger.exception( f"{message}" f"Unexpected error {error} calling {f}" ) if default_retval is None and not supress_error: raise StrategyError(str(error)) from error return default_retval return cast(F, wrapper)