Repository: freqtrade/freqtrade

Issue:
allow disabling strategy validation
**For building accurate renko or other candle types its best to have main dataframe updated with the given parameters.**

### Enhancement Proposal
introduce a parameter, like internals.check_dataframe = False (True by default) to switch off dataframe checks and specify in the docs that it shall only be used when the user understands what he is doing.



Discussion:
Please update the above post with a problem description or enhancement proposal including more information.
From the above it's not clear what you'd like, so unless we get more information we'll have to close it.

Thanks for your understanding.
as I understood it from Slack:

he uses renko dataframes with renko bricks and indicators built on this renko brick, and he substitutes the original dataframe with the renko dataframe; previously it was okay, but with the current freqtrade version he obtains an error that the number of rows in the dataframe was altered (our checks for the dataframe we introduced are in action).

Here is the code as uploaded into Slack:

![screen_shot_2020-04-25_at_6 30 24_am](https://user-images.githubusercontent.com/47309513/80274341-4b2a0f80-86e2-11ea-914a-a5cafb45ac0d.png)

In principle, it's okay for dry/live since only last row is used as the signal.

Possible extension: introduce a parameter, like `internals.check_dataframe = False` (True by default) to switch off dataframe checks and specify in the docs that it shall only be used when the user understands what he is doing.

thanks for reiterating in a better way @hroff-1902.

Resolving patch:
diff --git a/freqtrade/constants.py b/freqtrade/constants.py
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -227,6 +227,7 @@
         'db_url': {'type': 'string'},
         'initial_state': {'type': 'string', 'enum': ['running', 'stopped']},
         'forcebuy_enable': {'type': 'boolean'},
+        'disable_dataframe_checks': {'type': 'boolean'},
         'internals': {
             'type': 'object',
             'default': {},
diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py
--- a/freqtrade/resolvers/strategy_resolver.py
+++ b/freqtrade/resolvers/strategy_resolver.py
@@ -52,37 +52,38 @@ def load_strategy(config: Dict[str, Any] = None) -> IStrategy:
 
         # Set attributes
         # Check if we need to override configuration
-        #             (Attribute name,                    default,     ask_strategy)
-        attributes = [("minimal_roi",                     {"0": 10.0}, False),
-                      ("ticker_interval",                 None,        False),
-                      ("stoploss",                        None,        False),
-                      ("trailing_stop",                   None,        False),
-                      ("trailing_stop_positive",          None,        False),
-                      ("trailing_stop_positive_offset",   0.0,         False),
-                      ("trailing_only_offset_is_reached", None,        False),
-                      ("process_only_new_candles",        None,        False),
-                      ("order_types",                     None,        False),
-                      ("order_time_in_force",             None,        False),
-                      ("stake_currency",                  None,        False),
-                      ("stake_amount",                    None,        False),
-                      ("startup_candle_count",            None,        False),
-                      ("unfilledtimeout",                 None,        False),
-                      ("use_sell_signal",                 True,        True),
-                      ("sell_profit_only",                False,       True),
-                      ("ignore_roi_if_buy_signal",        False,       True),
+        #             (Attribute name,                    default,     subkey)
+        attributes = [("minimal_roi",                     {"0": 10.0}, None),
+                      ("ticker_interval",                 None,        None),
+                      ("stoploss",                        None,        None),
+                      ("trailing_stop",                   None,        None),
+                      ("trailing_stop_positive",          None,        None),
+                      ("trailing_stop_positive_offset",   0.0,         None),
+                      ("trailing_only_offset_is_reached", None,        None),
+                      ("process_only_new_candles",        None,        None),
+                      ("order_types",                     None,        None),
+                      ("order_time_in_force",             None,        None),
+                      ("stake_currency",                  None,        None),
+                      ("stake_amount",                    None,        None),
+                      ("startup_candle_count",            None,        None),
+                      ("unfilledtimeout",                 None,        None),
+                      ("use_sell_signal",                 True,        'ask_strategy'),
+                      ("sell_profit_only",                False,       'ask_strategy'),
+                      ("ignore_roi_if_buy_signal",        False,       'ask_strategy'),
+                      ("disable_dataframe_checks",        False,       'internals')
                       ]
-        for attribute, default, ask_strategy in attributes:
-            if ask_strategy:
-                StrategyResolver._override_attribute_helper(strategy, config['ask_strategy'],
+        for attribute, default, subkey in attributes:
+            if subkey:
+                StrategyResolver._override_attribute_helper(strategy, config.get(subkey, {}),
                                                             attribute, default)
             else:
                 StrategyResolver._override_attribute_helper(strategy, config,
                                                             attribute, default)
 
         # Loop this list again to have output combined
-        for attribute, _, exp in attributes:
-            if exp and attribute in config['ask_strategy']:
-                logger.info("Strategy using %s: %s", attribute, config['ask_strategy'][attribute])
+        for attribute, _, subkey in attributes:
+            if subkey and attribute in config[subkey]:
+                logger.info("Strategy using %s: %s", attribute, config[subkey][attribute])
             elif attribute in config:
                 logger.info("Strategy using %s: %s", attribute, config[attribute])
 
diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py
--- a/freqtrade/strategy/interface.py
+++ b/freqtrade/strategy/interface.py
@@ -106,6 +106,9 @@ class IStrategy(ABC):
     # run "populate_indicators" only for new candle
     process_only_new_candles: bool = False
 
+    # Disable checking the dataframe (converts the error into a warning message)
+    disable_dataframe_checks: bool = False
+
     # Count of candles the strategy requires before producing valid signals
     startup_candle_count: int = 0
 
@@ -285,8 +288,7 @@ def preserve_df(dataframe: DataFrame) -> Tuple[int, float, datetime]:
         """ keep some data for dataframes """
         return len(dataframe), dataframe["close"].iloc[-1], dataframe["date"].iloc[-1]
 
-    @staticmethod
-    def assert_df(dataframe: DataFrame, df_len: int, df_close: float, df_date: datetime):
+    def assert_df(self, dataframe: DataFrame, df_len: int, df_close: float, df_date: datetime):
         """ make sure data is unmodified """
         message = ""
         if df_len != len(dataframe):
@@ -296,7 +298,10 @@ def assert_df(dataframe: DataFrame, df_len: int, df_close: float, df_date: datet
         elif df_date != dataframe["date"].iloc[-1]:
             message = "last date"
         if message:
-            raise StrategyError(f"Dataframe returned from strategy has mismatching {message}.")
+            if self.disable_dataframe_checks:
+                logger.warning(f"Dataframe returned from strategy has mismatching {message}.")
+            else:
+                raise StrategyError(f"Dataframe returned from strategy has mismatching {message}.")
 
     def get_signal(self, pair: str, interval: str, dataframe: DataFrame) -> Tuple[bool, bool]:
         """


Test patch:
diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py
--- a/tests/strategy/test_interface.py
+++ b/tests/strategy/test_interface.py
@@ -130,7 +130,7 @@ def test_assert_df_raise(default_conf, mocker, caplog, ohlcv_history):
                    caplog)
 
 
-def test_assert_df(default_conf, mocker, ohlcv_history):
+def test_assert_df(default_conf, mocker, ohlcv_history, caplog):
     # Ensure it's running when passed correctly
     _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
                         ohlcv_history.loc[1, 'close'], ohlcv_history.loc[1, 'date'])
@@ -148,6 +148,14 @@ def test_assert_df(default_conf, mocker, ohlcv_history):
         _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
                             ohlcv_history.loc[1, 'close'], ohlcv_history.loc[0, 'date'])
 
+    _STRATEGY.disable_dataframe_checks = True
+    caplog.clear()
+    _STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
+                        ohlcv_history.loc[1, 'close'], ohlcv_history.loc[0, 'date'])
+    assert log_has_re(r"Dataframe returned from strategy.*last date\.", caplog)
+    # reset to avoid problems in other tests due to test leakage
+    _STRATEGY.disable_dataframe_checks = False
+
 
 def test_get_signal_handles_exceptions(mocker, default_conf):
     exchange = get_patched_exchange(mocker, default_conf)