Create strategy_wrapper to call user-defined code with
This commit is contained in:
parent
b5ee4f17cb
commit
2816b96650
@ -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.
|
||||||
|
"""
|
||||||
|
@ -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:
|
||||||
|
29
freqtrade/strategy/strategy_wrapper.py
Normal file
29
freqtrade/strategy/strategy_wrapper.py
Normal 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
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user