Skip to content

Commit b8e19ae

Browse files
committed
refactor: move dataframe assertion to it's own class
1 parent 937bd89 commit b8e19ae

File tree

2 files changed

+45
-27
lines changed

2 files changed

+45
-27
lines changed

freqtrade/strategy/interface.py

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
_create_and_merge_informative_pair,
3939
_format_pair_name,
4040
)
41+
from freqtrade.strategy.strategy_validation import StrategyResultValidator
4142
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
4243
from freqtrade.util import dt_now
4344
from freqtrade.wallets import Wallets
@@ -1213,13 +1214,13 @@ def analyze_pair(self, pair: str) -> None:
12131214
return
12141215

12151216
try:
1216-
df_len, df_close, df_date = self.preserve_df(dataframe)
1217+
validator = StrategyResultValidator(dataframe, self.disable_dataframe_checks)
12171218

12181219
dataframe = strategy_safe_wrapper(self._analyze_ticker_internal, message="")(
12191220
dataframe, {"pair": pair}
12201221
)
12211222

1222-
self.assert_df(dataframe, df_len, df_close, df_date)
1223+
validator.assert_df(dataframe)
12231224
except StrategyError as error:
12241225
logger.warning(f"Unable to analyze candle (OHLCV) data for pair {pair}: {error}")
12251226
return
@@ -1236,31 +1237,6 @@ def analyze(self, pairs: list[str]) -> None:
12361237
for pair in pairs:
12371238
self.analyze_pair(pair)
12381239

1239-
@staticmethod
1240-
def preserve_df(dataframe: DataFrame) -> tuple[int, float, datetime]:
1241-
"""keep some data for dataframes"""
1242-
return len(dataframe), dataframe["close"].iloc[-1], dataframe["date"].iloc[-1]
1243-
1244-
def assert_df(self, dataframe: DataFrame, df_len: int, df_close: float, df_date: datetime):
1245-
"""
1246-
Ensure dataframe (length, last candle) was not modified, and has all elements we need.
1247-
"""
1248-
message_template = "Dataframe returned from strategy has mismatching {}."
1249-
message = ""
1250-
if dataframe is None:
1251-
message = "No dataframe returned (return statement missing?)."
1252-
elif df_len != len(dataframe):
1253-
message = message_template.format("length")
1254-
elif df_close != dataframe["close"].iloc[-1]:
1255-
message = message_template.format("last close price")
1256-
elif df_date != dataframe["date"].iloc[-1]:
1257-
message = message_template.format("last date")
1258-
if message:
1259-
if self.disable_dataframe_checks:
1260-
logger.warning(message)
1261-
else:
1262-
raise StrategyError(message)
1263-
12641240
def get_latest_candle(
12651241
self,
12661242
pair: str,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import logging
2+
from datetime import datetime
3+
4+
from pandas import DataFrame
5+
6+
from freqtrade.exceptions import StrategyError
7+
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
class StrategyResultValidator:
13+
def __init__(self, dataframe: DataFrame, warn_only: bool = False):
14+
self._warn_only = warn_only
15+
self._length: int = len(dataframe)
16+
self._close: float = dataframe["close"].iloc[-1]
17+
self._date: datetime = dataframe["date"].iloc[-1]
18+
19+
def assert_df(self, dataframe: DataFrame):
20+
"""
21+
Ensure dataframe (length, last candle) was not modified, and has all elements we need.
22+
Raises a StrategyError if the dataframe does not match the expected values.
23+
If warn_only is set, it will log a warning instead of raising an error.
24+
:param dataframe: DataFrame to validate
25+
:raises StrategyError: If the dataframe does not match the expected values.
26+
:logs Warning: If warn_only is set and the dataframe does not match the expected values.
27+
"""
28+
message_template = "Dataframe returned from strategy has mismatching {}."
29+
message = ""
30+
if dataframe is None:
31+
message = "No dataframe returned (return statement missing?)."
32+
elif self._length != len(dataframe):
33+
self.message = message_template.format("length")
34+
elif self._close != dataframe["close"].iloc[-1]:
35+
message = message_template.format("last close price")
36+
elif self._date != dataframe["date"].iloc[-1]:
37+
message = message_template.format("last date")
38+
if message:
39+
if self._warn_only:
40+
logger.warning(message)
41+
else:
42+
raise StrategyError(message)

0 commit comments

Comments
 (0)