Create strategy_wrapper to call user-defined code with

This commit is contained in:
Matthias 2020-02-06 20:26:04 +01:00
parent b5ee4f17cb
commit 2816b96650
4 changed files with 47 additions and 18 deletions

View File

@ -35,3 +35,10 @@ class TemporaryError(FreqtradeException):
This could happen when an exchange is congested, unavailable, or the user This could happen when an exchange is congested, unavailable, or the user
has networking problems. Usually resolves itself after a time. has networking problems. Usually resolves itself after a time.
""" """
class StrategyError(FreqtradeException):
"""
Errors with custom user-code deteced.
Usually caused by errors in the strategy.
"""

View File

@ -3,21 +3,22 @@ IStrategy interface
This module defines the interface to apply for strategies This module defines the interface to apply for strategies
""" """
import logging import logging
import warnings
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from datetime import datetime, timezone from datetime import datetime, timezone
from enum import Enum from enum import Enum
from typing import Dict, List, NamedTuple, Optional, Tuple from typing import Dict, List, NamedTuple, Optional, Tuple
import warnings
import arrow import arrow
from pandas import DataFrame from pandas import DataFrame
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.exceptions import StrategyError
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.wallets import Wallets from freqtrade.wallets import Wallets
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -255,20 +256,12 @@ class IStrategy(ABC):
return False, False return False, False
try: try:
dataframe = self._analyze_ticker_internal(dataframe, {'pair': pair}) dataframe = strategy_safe_wrapper(
except ValueError as error: self._analyze_ticker_internal, message=""
logger.warning( )(dataframe, {'pair': pair})
'Unable to analyze ticker for pair %s: %s', except StrategyError as error:
pair, logger.warning(f"Unable to analyze ticker for pair {pair}: {error}")
str(error)
)
return False, False
except Exception as error:
logger.exception(
'Unexpected error when analyzing ticker for pair %s: %s',
pair,
str(error)
)
return False, False return False, False
if dataframe.empty: if dataframe.empty:

View File

@ -0,0 +1,29 @@
import logging
from freqtrade.exceptions import StrategyError
logger = logging.getLogger(__name__)
def strategy_safe_wrapper(f, message: str, default_retval=None):
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except ValueError as error:
logger.warning(
f"{message}"
f"Strategy caused the following exception: {error}"
f"{f}"
)
if not default_retval:
raise StrategyError(str(error)) from error
return default_retval
except Exception as error:
logger.exception(
f"Unexpected error {error} calling {f}"
)
if not default_retval:
raise StrategyError(str(error)) from error
return default_retval
return wrapper

View File

@ -10,8 +10,8 @@ from freqtrade.configuration import TimeRange
from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.history import load_tickerdata_file from freqtrade.data.history import load_tickerdata_file
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from tests.conftest import get_patched_exchange, log_has
from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.default_strategy import DefaultStrategy
from tests.conftest import get_patched_exchange, log_has, log_has_re
# Avoid to reinit the same object again and again # Avoid to reinit the same object again and again
_STRATEGY = DefaultStrategy(config={}) _STRATEGY = DefaultStrategy(config={})
@ -65,7 +65,7 @@ def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ticker_hi
) )
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'], assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'],
ticker_history) ticker_history)
assert log_has('Unable to analyze ticker for pair foo: xyz', caplog) assert log_has_re(r'Strategy caused the following exception: xyz.*', caplog)
def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history): def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ticker_history):