freqtrade / freqtrade

Free, open source crypto trading bot
https://www.freqtrade.io
GNU General Public License v3.0
28.32k stars 6.07k forks source link

TypeError: cannot do positional indexing on RangeIndex with these indexers [nan] of type float - Backtesting #9831

Closed Corfucinas closed 7 months ago

Corfucinas commented 7 months ago

Describe your environment

Describe the problem:

Trying to run a backtest with this command

 freqtrade backtesting  -c config.json --timerange 20170101-20240206 --strategy Test  --freqaimodel XG
BoostRegressor --timeframe 8h

Explain the problem you have encountered

2024-02-18 18:56:05,122 - freqtrade.optimize.backtesting - INFO - Backtesting with data from 2019-09-16 00:00:00 up to 2024-02-06 00:00:00 (1604 days).
/home/the-box/Github/freqtrade/freqtrade/data/metrics.py:147: FutureWarning: The behavior of Series.idxmin with all-NA values, or any-NA and skipna=False, is deprecated. In a future version this will raise ValueError
  else max_drawdown_df['drawdown'].idxmin()
2024-02-18 18:56:08,230 - freqtrade - ERROR - Fatal exception!
Traceback (most recent call last):
  File "/home/the-box/Github/freqtrade/freqtrade/main.py", line 42, in main
    return_code = args['func'](args)
                  ^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/freqtrade/commands/optimize_commands.py", line 58, in start_backtesting
    backtesting.start()
  File "/home/the-box/Github/freqtrade/freqtrade/optimize/backtesting.py", line 1397, in start
    results = generate_backtest_stats(
              ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/freqtrade/optimize/optimize_reports/optimize_reports.py", line 559, in generate_backtest_stats
    strat_stats = generate_strategy_stats(pairlist, strategy, content,
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/freqtrade/optimize/optimize_reports/optimize_reports.py", line 438, in generate_strategy_stats
    'calmar': calculate_calmar(results, min_date, max_date, start_balance),
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/freqtrade/data/metrics.py", line 300, in calculate_calmar
    _, _, _, _, _, max_drawdown = calculate_max_drawdown(
                                  ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/freqtrade/data/metrics.py", line 150, in calculate_max_drawdown
    high_date = profit_results.loc[max_drawdown_df.iloc[:idxmin]['high_value'].idxmax(), date_col]
                                   ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexing.py", line 1153, in __getitem__
    return self._getitem_axis(maybe_callable, axis=axis)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexing.py", line 1691, in _getitem_axis
    return self._get_slice_axis(key, axis=axis)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexing.py", line 1726, in _get_slice_axis
    labels._validate_positional_slice(slice_obj)
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 4197, in _validate_positional_slice
    self._validate_indexer("positional", key.stop, "iloc")
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 6637, in _validate_indexer
    self._raise_invalid_indexer(form, key)
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 4306, in _raise_invalid_indexer
    raise TypeError(msg)
TypeError: cannot do positional indexing on RangeIndex with these indexers [nan] of type float

Steps to reproduce:

Run the commands above with my strategy The weird thing is that this happens when I run on 8h, running on 12h and 1d works perfectly fine.

Observed Results:

I also ran the freqtrade lookahead-analysis to see if I was doing something weird, but it passed the test

I make my indicators, I don't think I'm doing anything wrong with this (below snippet). I use it to calculate an average based on a rolling period of previous candles, so for each new candle, look back to start_at_bar and compute something

            for _index, (_, _) in enumerate(dataframe.iterrows()):
                for i in range(start_at_bar + 1):
                    y = dataframe['ohlc4'].shift(i).iloc[_index]
                    # Do some arithmetic, no more .shift() nor .iloc[i]

Update: I tried running it on dry mode to see if the logs showed something

2024-02-18 20:03:14,199 - freqtrade.strategy.interface - WARNING - Unable to analyze candle (OHLCV) data for pair BTC/USDT:USDT: Dataframe returned from strategy has mismatching last close price.

I'm not sure if this is the reason

    heikinashi = qtpylib.heikinashi(dataframe)
    dataframe["open"] = heikinashi["open"]
    dataframe["close"] = heikinashi["close"]
    dataframe["high"] = heikinashi["high"]
    dataframe["low"] = heikinashi["low"]

Is this allowed?

Reverting to the normal candles (removing the snippet above) causes the same error to appear on backtesting on 1d

However, this happens if I run the historical data, if I run the last few months, the backtest is successful .

Maybe something with this?

FutureWarning: The behavior of Series.idxmin with all-NA values, or any-NA and skipna=False, is deprecated. In a future version this will raise ValueError
  else max_drawdown_df['drawdown'].idxmin()
2024-02-18 20:19:19,329 - freqtrade - ERROR - Fatal exception!
xmatthias commented 7 months ago

you can easily use heikinashi candles - but don't overwrite the regular "ohlcv" columns (use other column names instead). Doing so will mess with backtesting (which will now use these columns for prices). It's not explicitly forbidden / checked for in backtesting (mainly for performance reasons) - but doing so invalidates all backtests made with this - which runs under the assumption of having regular candles - and takes certain assumptions based on that.


The error itself suggests something odd with your result (which would obviously change on different timeframes) - but to look at this, i'll need a full (ideally minimal) reproduction - which includes strategy, config, timerange used (obviously one that doesn't overwrite the original ohlc columns). (Feel free to share that in private via discord DM's or so).

You seem to reach a point where .idxmin() returns NaN (we do explicitly handle 0). This is however not a dataframe your strategy produces directly - which makes it pretty odd - and not something i've seen before. The pandas warning would suggest that you have a drawdown of "NaN" - which would suggest empty (or at least non-numeric) profits in the profit column ...


A quick way (without full reproduction) could be to print / save the dataframe in the offending call (metrics, before the .idxmin() call mentioned above) (potentially both the max_drawdown_df dataframe, as well as trades).

Corfucinas commented 7 months ago

There is something funky happening here

running

freqtrade lookahead-analysis -c config.json --timerange 20170101-20240 206 --strategy Test --freqaimodel XGBoostRegressor --timeframe 1d --pairs BTC/USDT:USDT

results in

text Empty DataFrame Columns: [] Index: [] cumulative high_value drawdown date drawdown_relative 0 NaN NaN NaN 2019-11-10 00:00:00+00:00 NaN

but running

freqtrade lookahead-analysis -c config.json --timerange 20170101-20240 206 --strategy Test --freqaimodel XGBoostRegressor --timeframe 1d --pairs ETH/USDT:USDT

(just changed the pair) works perfectly fine

Also removing ---pairs works fine as well (including BTC/USDT:USDT)

And no, not doing anything weird with BTC....nor any other pairs

removing BTC/USDT:USDT from the pair_whitelist makes all of these errors go away...still testing it, but it seems like it (I don't get it...I guess debugging with breakpoints...)

xmatthias commented 7 months ago

but what's the output of the initial dataframe? (trades) ?

Corfucinas commented 7 months ago

but what's the output of the initial dataframe? (trades) ?

I'll try to reach out soon via Discord.

I've been trying to make sense of this, but it keeps getting weirder and weirder.

I refactored my indicator (since .iterrows() is slow), it returns the same dataframe as the previous one (I ran the backtest and compared the dataframe from the old function and the refactored one and they both return the same values), but the backtests results are completely different.

I don't want to clutter this issue with nonsense, so I'll send you a reproducible example over DM and we can reach the conclusion there and put it here.

Either there's a bug, or I'm about to learn something new

Corfucinas commented 7 months ago

@xmatthias I've traveling at the moment, sorry I've not reached out, been tinkering with the code to see what is actually "causing it" since I'm not even sure at this point.

Is there's something wrong with the config.json?


{
  "max_open_trades": 33,
  "stake_currency": "USDT",
  "timeframe": "1d",
  "stake_amount": "unlimited",
  "tradable_balance_ratio": 0.99,
  "fiat_display_currency": "USD",
  "amend_last_stake_amount": true,
  "last_stake_amount_min_ratio": 0.5,
  "amount_reserve_percent": 0.05,
  "dry_run": true,
  "dry_run_wallet": 10000,
  "cancel_open_orders_on_exit": true,
  "trading_mode": "futures",
  "margin_mode": "isolated",
  "futures_funding_rate": 0,
  "liquidation_buffer": 0.05,
  "unfilledtimeout": {
    "entry": 10,
    "exit": 10,
    "exit_timeout_count": 0,
    "unit": "minutes"
  },
  "entry_pricing": {
    "price_side": "other",
    "use_order_book": true,
    "order_book_top": 2,
    "price_last_balance": 0.0,
    "check_depth_of_market": { "enabled": false, "bids_to_ask_delta": 1 }
  },
  "exit_pricing": {
    "price_side": "other",
    "use_order_book": true,
    "order_book_top": 2
  },
  "exchange": {
    "name": "binance",
    "key": "key",
    "secret": "anotherkey",
    "log_responses": true,
    "ccxt_config": { "enableRateLimit": true },
    "ccxt_async_config": { "enableRateLimit": true, "rateLimit": 500 },
    "pair_whitelist": [
      "BTC/USDT:USDT",
      "ETH/USDT:USDT",
      "XRP/USDT:USDT",
      "ADA/USDT:USDT",
      "MATIC/USDT:USDT",
      "DOGE/USDT:USDT",
      "SOL/USDT:USDT",
      "DOT/USDT:USDT",
      "LTC/USDT:USDT",
      "TRX/USDT:USDT",
      "AVAX/USDT:USDT",
      "LINK/USDT:USDT",
      "ATOM/USDT:USDT",
      "UNI/USDT:USDT",
      "XMR/USDT:USDT",
      "ETC/USDT:USDT",
      "SUSHI/USDT:USDT",
      "NEO/USDT:USDT",
      "XLM/USDT:USDT",
      "FIL/USDT:USDT",
      "APT/USDT:USDT",
      "MKR/USDT:USDT",
      "ZEC/USDT:USDT",
      "APE/USDT:USDT",
      "ALGO/USDT:USDT",
      "DASH/USDT:USDT",
      "FTM/USDT:USDT",
      "EOS/USDT:USDT",
      "OMG/USDT:USDT",
      "XTZ/USDT:USDT",
      "AXS/USDT:USDT",
      "CRV/USDT:USDT",
      "SAND/USDT:USDT",
      "SUI/USDT:USDT"
    ],
    "pair_blacklist": [
      "AUCTION/USDT:USDT",
      "1000RATS/USDT:USDT",
      "ACE/USDT:USDT",
      "MOVR/USDT:USDT",
      "NFP/USDT:USDT",
      "AI/USDT:USDT",
      "XAI/USDT:USDT",
      "WIF/USDT:USDT",
      "MANTA/USDT:USDT",
      "ONDO/USDT:USDT",
      "LSK/USDT:USDT",
      "ALT/USDT:USDT",
      "JUP/USDT:USDT",
      "ZETA/USDT:USDT",
      "RONIN/USDT:USDT",
      "DYM/USDT:USDT",
      "OM/USDT:USDT",
      "PIXEL/USDT:USDT",
      "STRK/USDT:USDT",
      "MAVIA/USDT:USDT",
      "GLM/USDT:USDT"
    ]
  },
  "pairlists": [{ "method": "StaticPairList" }],
  "telegram": {
    "enabled": true,
    "token": "token",
    "chat_id": "chatid",
    "allow_custom_messages": true,
    "notification_settings": {
      "status": "on",
      "warning": "on",
      "startup": "on",
      "entry": "on",
      "entry_fill": "on",
      "entry_cancel": "on",
      "exit": {
        "roi": "on",
        "emergency_exit": "on",
        "force_exit": "on",
        "exit_signal": "on",
        "trailing_stop_loss": "on",
        "stop_loss": "on",
        "stoploss_on_exchange": "on",
        "custom_exit": "on",
        "partial_exit": "on"
      },
      "exit_cancel": "on",
      "exit_fill": "on",
      "protection_trigger": "on",
      "protection_trigger_global": "on",
      "strategy_msg": "on",
      "show_candle": "ohlc"
    },
    "reload": true,
    "balance_dust_level": 0.01
  },
  "api_server": {
    "enabled": true,
    "listen_ip_address": "127.0.0.1",
    "listen_port": 48081,
    "verbosity": "info",
    "enable_openapi": false,
    "jwt_secret_key": "token",
    "ws_token": "token",
    "CORS_origins": [],
    "username": "freqtrader-bot",
    "password": "ThisIsNotMyPasswordReally!"
  },
  "bot_name": "freqtrade-margin",
  "initial_state": "running",
  "force_entry_enable": false,
  "internals": {
    "process_throttle_secs": 5,
    "heartbeat_interval": 60,
    "sd_notify": true
  },
  "db_url": "sqlite:///user_data/db/futures.sqlite",
  "logfile": "./user_data/logs/freqtrade-margin.log",
  "dataformat_ohlcv": "feather",
  "dataformat_trades": "feather",
  "edge": {
    "enabled": false,
    "process_throttle_secs": 3600,
    "calculate_since_number_of_days": 7,
    "allowed_risk": 0.01,
    "stoploss_range_min": -0.01,
    "stoploss_range_max": -0.1,
    "stoploss_range_step": -0.01,
    "minimum_winrate": 0.6,
    "minimum_expectancy": 0.2,
    "min_trade_number": 10,
    "max_trade_duration_minute": 1440,
    "remove_pumps": false
  },
  "freqai": {
    "enabled": false,
    "reduce_df_footprint": true,
    "identifier": "strategy_zero",
    "purge_old_models": true,
    "train_period_days": 30,
    "backtest_period_days": 7,
    "live_retrain_hours": 0,
    "expiration_hours": 0,
    "save_backtest_models": true,
    "fit_live_predictions_candles": 300,
    "continual_learning": false,
    "write_metrics_to_disk": true,
    "feature_parameters": {
      "include_timeframes": ["1d"],
      "include_corr_pairlist": ["BTC/USDT:USDT", "ETH/USDT:USDT"],
      "label_period_candles": 24,
      "include_shifted_candles": 2,
      "weight_factor": 0.9,
      "indicator_periods_candles": [10, 20],
      "principal_component_analysis": true,
      "DI_threshold": 0.9,
      "use_SVM_to_remove_outliers": true,
      "svm_params": {},
      "use_DBSCAN_to_remove_outliers": true,
      "inlier_metric_window": 5,
      "noise_standard_deviation": 0.05,
      "outlier_protection_percentage": 30,
      "reverse_train_test_order": false,
      "shuffle_after_split": true,
      "plot_feature_importances": 1,
      "buffer_train_data_candles": 0
    },
    "data_split_parameters": {
      "test_size": 0.33,
      "shuffle": false,
      "random_state": 1
    },
    "model_training_parameters": {
      "n_estimators": 1000,
      "model_kwargs": { "max_iters": 100 }
    }
  },
  "rl_config": {
    "train_cycles": 25,
    "add_state_info": true,
    "net_arch": [256, 256, 256, 256, 256],
    "drop_ohlc_from_features": false,
    "progress_bar": true,
    "max_trade_duration_candles": 300,
    "max_training_drawdown_pct": 0.02,
    "model_type": "PPO",
    "policy_type": "MlpPolicy",
    "model_reward_parameters": { "rr": 1, "profit_aim": 0.025 }
  }
}
Corfucinas commented 7 months ago

but what's the output of the initial dataframe? (trades) ?

This is the dataframe that gets passed to calculate_expectancy

    pair    stake_amount    max_stake_amount    amount  open_date   close_date  open_rate   close_rate  fee_open    fee_close   trade_duration  profit_ratio    profit_abs  exit_reason initial_stop_loss_abs   initial_stop_loss_ratio stop_loss_abs   stop_loss_ratio min_rate    max_rate    is_open enter_tag   leverage    is_short    open_timestamp  close_timestamp orders
0   BTC/USDT:USDT   301.28064   301.28064   0.032   2019-10-30 00:00:00+00:00   2019-11-21 00:00:00+00:00   9415.02 8094.57 0.0002  0.0002  31680           exit_signal 94.2    -0.99   94.2    -0.99   7432.0  9550.0  False       1.0 False   1572393600000   1574294400000   [{'amount': 0.032, 'safe_price': 9415.02, 'ft_order_side': 'buy', 'order_filled_timestamp': 1572393600000, 'ft_is_entry': True}, {'amount': 0.032, 'safe_price': 8094.57, 'ft_order_side': 'sell', 'order_filled_timestamp': 1574294400000, 'ft_is_entry': False}]

This is the profit_results value after trades.sort_values(date_col).reset_index(drop=True)

    pair    stake_amount    max_stake_amount    amount  open_date   close_date  open_rate   close_rate  fee_open    fee_close   trade_duration  profit_ratio    profit_abs  exit_reason initial_stop_loss_abs   initial_stop_loss_ratio stop_loss_abs   stop_loss_ratio min_rate    max_rate    is_open enter_tag   leverage    is_short    open_timestamp  close_timestamp orders
0   BTC/USDT:USDT   301.28064   301.28064   0.032   2019-10-30 00:00:00+00:00   2019-11-21 00:00:00+00:00   9415.02 8094.57 0.0002  0.0002  31680           exit_signal 94.2    -0.99   94.2    -0.99   7432.0  9550.0  False       1.0 False   1572393600000   1574294400000   [{'amount': 0.032, 'safe_price': 9415.02, 'ft_order_side': 'buy', 'order_filled_timestamp': 1572393600000, 'ft_is_entry': True}, {'amount': 0.032, 'safe_price': 8094.57, 'ft_order_side': 'sell', 'order_filled_timestamp': 1574294400000, 'ft_is_entry': False}]

This is max_drawdown_df after calling _calc_drawdown_series(

    cumulative  high_value  drawdown    date    drawdown_relative
0               2019-11-21 00:00:00+00:00   
    idxmin = max_drawdown_df['drawdown_relative'].idxmax() if relative \
        else max_drawdown_df['drawdown'].idxmin()

This equal to nan, and prints the following warning FutureWarning: The behavior of Series.idxmin with all-NA values, or any-NA and skipna=False, is deprecated. In a future version this will raise ValueError

The error is located on line 153,

high_date = profit_results.loc[max_drawdown_df.iloc[:idxmin]['high_value'].idxmax(), date_col]

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexing.py", line 1153, in __getitem__
    return self._getitem_axis(maybe_callable, axis=axis)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexing.py", line 1691, in _getitem_axis
    return self._get_slice_axis(key, axis=axis)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexing.py", line 1726, in _get_slice_axis
    labels._validate_positional_slice(slice_obj)
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 4197, in _validate_positional_slice
    self._validate_indexer("positional", key.stop, "iloc")
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 6637, in _validate_indexer
    self._raise_invalid_indexer(form, key)
  File "/home/the-box/Github/freqtrade/.venv/lib/python3.11/site-packages/pandas/core/indexes/base.py", line 4306, in _raise_invalid_indexer
    raise TypeError(msg)
TypeError: cannot do positional indexing on RangeIndex with these indexers [nan] of type float

(original error of the issue).

Corfucinas commented 7 months ago

This is the strategy, let me know if you need something else

# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these libs ---

import numpy as np

from pandas import DataFrame
from datetime import datetime
from typing import Optional, Union
from freqtrade.strategy import (
    BooleanParameter,
    CategoricalParameter,
    DecimalParameter,
    IntParameter,
    IStrategy,
    merge_informative_pair,
)

# --------------------------------
# Add your lib to import here
import talib.abstract as ta
import pandas_ta as pta
from technical import qtpylib

from typing import Literal, Union

import numpy as np
from pandas import DataFrame

class MagicGoo:
    __slots__ = ()

    @classmethod
    def convert_source(cls, dataframe: DataFrame) -> DataFrame:
        dataframe["hl2"] = (dataframe["high_ha"] + dataframe["low_ha"]) / 2
        dataframe["hlc3"] = (
            dataframe["high_ha"] + dataframe["low_ha"] + +dataframe["close_ha"]
        ) / 3
        dataframe["ohlc4"] = (
            dataframe["open_ha"]
            + dataframe["high_ha"]
            + dataframe["low_ha"]
            + dataframe["close_ha"]
        ) / 4
        dataframe["hlcc4"] = (
            dataframe["high_ha"]
            + dataframe["low_ha"]
            + dataframe["close_ha"]
            + dataframe["close_ha"]
        ) / 4

        # Double weighted HA

        dataframe["close_ha_dw"] = (
            dataframe["open_ha"]
            + dataframe["high_ha"]
            + dataframe["low_ha"]
            + dataframe["close_ha"]
        ) / 4
        dataframe["open_ha_dw"] = (
            dataframe["open_ha"].shift(1) + dataframe["close_ha"].shift(1)
        ) / 2
        dataframe["low_ha_dw"] = dataframe[["low_ha", "open_ha_dw", "close_ha_dw"]].min(axis=1)
        dataframe["high_ha_dw"] = dataframe[["high_ha", "open_ha_dw", "close_ha_dw"]].max(axis=1)

        return dataframe

    @classmethod
    def gaussian(
        cls,
        dataframe: DataFrame,
        lookback: int,
        start_at_bar: int,
        candle_type: Literal[
            "open",
            "high",
            "low",
            "close",
            "open_ha",
            "high_ha",
            "low_ha",
            "close_ha",
            "ohlc4",
            "hl2",
            "hlc3",
            "ohlc4",
            "hlcc4",
            "open_ha_dw",
            "high_ha_dw",
            "low_ha_dw",
            "close_ha_dw",
        ],
    ) -> DataFrame:

        ohlc4_values = dataframe[candle_type].values  # Precompute ohlc4 values

        no_filter_values = np.zeros_like(ohlc4_values, dtype=float)
        yhatdelt2_values = np.zeros_like(ohlc4_values, dtype=float)

        lookback_squared = np.power(lookback, 2)

        for index in range(len(dataframe)):

            current_weight = 0.0
            cumulative_weight = 0.0

            for i in range(start_at_bar + 1):
                y = np.nan if (index - i) < 0 else ohlc4_values[index - i]  # Use precomputed values
                w = np.exp(-np.power(i, 2) / (2 * lookback_squared))
                current_weight += y * w
                cumulative_weight += w

            no_filter_values[index] = current_weight / cumulative_weight

            current_weight2 = 0.0
            cumulative_weight2 = 0.0

            for i in range(start_at_bar + 1):
                y = (
                    np.nan if (i - index) > 0 else no_filter_values[i - index]
                )  # Use precomputed values
                w = np.exp(np.power(i, 2) / (2 * lookback_squared))
                current_weight2 += y * w
                cumulative_weight2 += w

            yhatdelt2_values[index] = current_weight2 / cumulative_weight2

        dataframe["no_filter"] = no_filter_values

        return dataframe

class GooMagic(IStrategy):
    """
    This is a strategy template to get you started.
    More information in https://www.freqtrade.io/en/latest/strategy-customization/

    You can:
        :return: a Dataframe with all mandatory indicators for the strategies
    - Rename the class name (Do not forget to update class_name)
    - Add any methods you want to build your strategy
    - Add any lib you need to build your strategy

    You must keep:
    - the lib in the section "Do not remove these libs"
    - the methods: populate_indicators, populate_entry_trend, populate_exit_trend
    You should keep:
    - timeframe, minimal_roi, stoploss, trailing_*
    """

    # Strategy interface version - allow new iterations of the strategy interface.
    # Check the documentation or the Sample strategy to get the latest version.
    INTERFACE_VERSION = 3

    # Optimal timeframe for the strategy.
    timeframe = "1d"

    # Can this strategy go short?
    can_short: bool = True

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi".
    # minimal_roi = {"60": 0.01, "30": 0.02, "0": 0.04}
    minimal_roi = {}  # Disabled
    # minimal_roi = {"60": 9999999999999999999}

    # Optimal stoploss designed for the strategy.
    # This attribute will be overridden if the config file contains "stoploss".
    # stoploss = -0.10
    stoploss = -0.99

    # Trailing stoploss
    trailing_stop = False
    # trailing_only_offset_is_reached = False
    # trailing_stop_positive = 0.01
    # trailing_stop_positive_offset = 0.0  # Disabled / not configured

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = True

    # These values can be overridden in the config.
    use_exit_signal = True
    exit_profit_only = False
    # ignore_roi_if_entry_signal = False
    ignore_roi_if_entry_signal = True

    # Number of candles the strategy requires before producing valid signals
    # startup_candle_count: int = 30
    startup_candle_count: int = 30

    # Optional order type mapping.
    order_types = {
        "entry": "limit",
        "exit": "limit",
        "stoploss": "market",
        "stoploss_on_exchange": False,
    }

    # Optional order time in force.
    order_time_in_force = {"entry": "GTC", "exit": "GTC"}

    # Rational Quadratic
    lookback_period = IntParameter(1, 100, default=8, space="buy")
    relative_weight = DecimalParameter(0.01, 100, default=0.69, space="buy")
    start_at_bar = IntParameter(1, 100, default=19, space="buy")
    lag_for_crossover = IntParameter(1, 100, default=1, space="buy")

    type_of_filter = CategoricalParameter(
        ["smooth", "zero_lag", "no_filter"], default="smooth", space="buy"
    )
    candle_type = CategoricalParameter(
        [
            "open",
            "high",
            "low",
            "close",
            "open_ha",
            "high_ha",
            "low_ha",
            "close_ha",
            "hl2",
            "hlc3",
            "ohlc4",
            "hlcc4",
            "open_ha_dw",
            "high_ha_dw",
            "low_ha_dw",
            "close_ha_dw",
        ],
        default="low",
        space="buy",
    )

    @property
    def plot_config(self):
        return {
            # Main plot indicators (Moving averages, ...)
            "main_plot": {
                "smooth": {},
                "zero_lag": {},
                "no_filter": {},
            },
            "subplots": {
                # Subplots - each dict defines one additional plot
            },
        }

    def informative_pairs(self):
        """
        Define additional, informative pair/interval combinations to be cached from the exchange.
        These pair/interval combinations are non-tradeable, unless they are part
        of the whitelist as well.
        For more information, please consult the documentation
        :return: List of tuples in the format (pair, interval)
            Sample: return [("ETH/USDT", "5m"),
                            ("BTC/USDT", "15m"),
                            ]
        """
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Adds several different TA indicators to the given DataFrame

        Performance Note: For the best performance be frugal on the number of indicators
        you are using. Let uncomment only the indicator you are using in your strategies
        or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
        :param dataframe: Dataframe with data from the exchange
        :param metadata: Additional information, like the currently traded pair
        :return: a Dataframe with all mandatory indicators for the strategies
        """

        self.convert_to_heikinashi(dataframe)
        MagicGoo.convert_source(dataframe)

        MagicGoo.gaussian(
            dataframe=dataframe,
            lookback=self.lookback_period.value,
            start_at_bar=self.start_at_bar.value,
            candle_type=self.candle_type.value,
        )

        return dataframe

    def convert_to_heikinashi(self, dataframe: DataFrame) -> DataFrame:
        heikinashi = qtpylib.heikinashi(dataframe)
        dataframe["open_ha"] = heikinashi["open"]
        dataframe["close_ha"] = heikinashi["close"]
        dataframe["high_ha"] = heikinashi["high"]
        dataframe["low_ha"] = heikinashi["low"]
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the entry signal for the given dataframe
        :param dataframe: DataFrame
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with entry columns populated
        """
        dataframe.loc[
            (
                qtpylib.crossed_above(
                    dataframe[self.type_of_filter.value],
                    dataframe[self.type_of_filter.value].shift(self.lag_for_crossover.value),
                )
            ),
            "enter_long",
        ] = 1

        # Uncomment to use shorts (Only used in futures/margin mode. Check the documentation for more info)
        dataframe.loc[
            (
                qtpylib.crossed_below(
                    dataframe[self.type_of_filter.value],
                    dataframe[self.type_of_filter.value].shift(self.lag_for_crossover.value),
                )
            ),
            "enter_short",
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the exit signal for the given dataframe
        :param dataframe: DataFrame
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with exit columns populated
        """

        dataframe.loc[
            (
                qtpylib.crossed_below(
                    dataframe[self.type_of_filter.value],
                    dataframe[self.type_of_filter.value].shift(self.lag_for_crossover.value),
                )
            ),
            "exit_long",
        ] = 1

        # Uncomment to use shorts (Only used in futures/margin mode. Check the documentation for more info)
        dataframe.loc[
            (
                qtpylib.crossed_above(
                    dataframe[self.type_of_filter.value],
                    dataframe[self.type_of_filter.value].shift(self.lag_for_crossover.value),
                )
            ),
            "exit_short",
        ] = 1

        return dataframe

    def bot_loop_start(self, current_time: datetime, **kwargs) -> None:
        """
        Called at the start of the bot iteration (one loop).
        Might be used to perform pair-independent tasks
        (e.g. gather some remote ressource for comparison)

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, this simply does nothing.
        :param current_time: datetime object, containing the current datetime
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        """
        pass

    def custom_entry_price(
        self,
        pair: str,
        trade: Optional["Trade"],
        current_time: "datetime",
        proposed_rate: float,
        entry_tag: "Optional[str]",
        side: str,
        **kwargs,
    ) -> float:
        """
        Custom entry price logic, returning the new entry price.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns None, orderbook is used to set entry price

        :param pair: Pair that's currently analyzed
        :param trade: trade object (None for initial entries).
        :param current_time: datetime object, containing the current datetime
        :param proposed_rate: Rate, calculated based on pricing settings in exit_pricing.
        :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return float: New entry price value if provided
        """
        return proposed_rate

    def adjust_entry_price(
        self,
        trade: "Trade",
        order: "Optional[Order]",
        pair: str,
        current_time: datetime,
        proposed_rate: float,
        current_order_rate: float,
        entry_tag: Optional[str],
        side: str,
        **kwargs,
    ) -> float:
        """
        Entry price re-adjustment logic, returning the user desired limit price.
        This only executes when a order was already placed, still open (unfilled fully or partially)
        and not timed out on subsequent candles after entry trigger.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-callbacks/

        When not implemented by a strategy, returns current_order_rate as default.
        If current_order_rate is returned then the existing order is maintained.
        If None is returned then order gets canceled but not replaced by a new one.

        :param pair: Pair that's currently analyzed
        :param trade: Trade object.
        :param order: Order object
        :param current_time: datetime object, containing the current datetime
        :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing.
        :param current_order_rate: Rate of the existing order in place.
        :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
        :param side: 'long' or 'short' - indicating the direction of the proposed trade
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return float: New entry price value if provided

        """
        return current_order_rate

    def custom_exit_price(
        self,
        pair: str,
        trade: "Trade",
        current_time: "datetime",
        proposed_rate: float,
        current_profit: float,
        exit_tag: Optional[str],
        **kwargs,
    ) -> float:
        """
        Custom exit price logic, returning the new exit price.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns None, orderbook is used to set exit price

        :param pair: Pair that's currently analyzed
        :param trade: trade object.
        :param current_time: datetime object, containing the current datetime
        :param proposed_rate: Rate, calculated based on pricing settings in exit_pricing.
        :param current_profit: Current profit (as ratio), calculated based on current_rate.
        :param exit_tag: Exit reason.
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return float: New exit price value if provided
        """
        return proposed_rate

    def custom_stake_amount(
        self,
        pair: str,
        current_time: datetime,
        current_rate: float,
        proposed_stake: float,
        min_stake: Optional[float],
        max_stake: float,
        leverage: float,
        entry_tag: Optional[str],
        side: str,
        **kwargs,
    ) -> float:
        """
        Customize stake size for each new trade.

        :param pair: Pair that's currently analyzed
        :param current_time: datetime object, containing the current datetime
        :param current_rate: Rate, calculated based on pricing settings in exit_pricing.
        :param proposed_stake: A stake amount proposed by the bot.
        :param min_stake: Minimal stake size allowed by exchange.
        :param max_stake: Balance available for trading.
        :param leverage: Leverage selected for this trade.
        :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
        :param side: 'long' or 'short' - indicating the direction of the proposed trade
        :return: A stake size, which is between min_stake and max_stake.
        """
        return proposed_stake

    use_custom_stoploss = True

    def custom_stoploss(
        self,
        pair: str,
        trade: "Trade",
        current_time: "datetime",
        current_rate: float,
        current_profit: float,
        after_fill: bool,
        **kwargs,
    ) -> float:
        """
        Custom stoploss logic, returning the new distance relative to current_rate (as ratio).
        e.g. returning -0.05 would create a stoploss 5% below current_rate.
        The custom stoploss can never be below self.stoploss, which serves as a hard maximum loss.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns the initial stoploss value.
        Only called when use_custom_stoploss is set to True.

        :param pair: Pair that's currently analyzed
        :param trade: trade object.
        :param current_time: datetime object, containing the current datetime
        :param current_rate: Rate, calculated based on pricing settings in exit_pricing.
        :param current_profit: Current profit (as ratio), calculated based on current_rate.
        :param after_fill: True if the stoploss is called after the order was filled.
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return float: New stoploss value, relative to the current_rate
        """

    def custom_exit(
        self,
        pair: str,
        trade: "Trade",
        current_time: "datetime",
        current_rate: float,
        current_profit: float,
        **kwargs,
    ) -> "Optional[Union[str, bool]]":
        """
        Custom exit signal logic indicating that specified position should be sold. Returning a
        string or True from this method is equal to setting sell signal on a candle at specified
        time. This method is not called when sell signal is set.

        This method should be overridden to create sell signals that depend on trade parameters. For
        example you could implement a sell relative to the candle when the trade was opened,
        or a custom 1:2 risk-reward ROI.

        Custom exit reason max length is 64. Exceeding characters will be removed.

        :param pair: Pair that's currently analyzed
        :param trade: trade object.
        :param current_time: datetime object, containing the current datetime
        :param current_rate: Rate, calculated based on pricing settings in exit_pricing.
        :param current_profit: Current profit (as ratio), calculated based on current_rate.
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return: To execute sell, return a string with custom exit reason or True. Otherwise return
        None or False.
        """
        return None

    def confirm_trade_entry(
        self,
        pair: str,
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        current_time: datetime,
        entry_tag: Optional[str],
        side: str,
        **kwargs,
    ) -> bool:
        """
        Called right before placing a entry order.
        Timing for this function is critical, so avoid doing heavy computations or
        network requests in this method.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns True (always confirming).

        :param pair: Pair that's about to be bought/shorted.
        :param order_type: Order type (as configured in order_types). usually limit or market.
        :param amount: Amount in target (base) currency that's going to be traded.
        :param rate: Rate that's going to be used when using limit orders
                     or current rate for market orders.
        :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
        :param current_time: datetime object, containing the current datetime
        :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
        :param side: 'long' or 'short' - indicating the direction of the proposed trade
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return bool: When True is returned, then the buy-order is placed on the exchange.
            False aborts the process
        """
        return True

    def confirm_trade_exit(
        self,
        pair: str,
        trade: "Trade",
        order_type: str,
        amount: float,
        rate: float,
        time_in_force: str,
        exit_reason: str,
        current_time: "datetime",
        **kwargs,
    ) -> bool:
        """
        Called right before placing a regular exit order.
        Timing for this function is critical, so avoid doing heavy computations or
        network requests in this method.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns True (always confirming).

        :param pair: Pair for trade that's about to be exited.
        :param trade: trade object.
        :param order_type: Order type (as configured in order_types). usually limit or market.
        :param amount: Amount in base currency.
        :param rate: Rate that's going to be used when using limit orders
                     or current rate for market orders.
        :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
        :param exit_reason: Exit reason.
            Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss',
                            'exit_signal', 'force_exit', 'emergency_exit']
        :param current_time: datetime object, containing the current datetime
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return bool: When True, then the exit-order is placed on the exchange.
            False aborts the process
        """
        return True

    def check_entry_timeout(
        self, pair: str, trade: "Trade", order: "Order", current_time: datetime, **kwargs
    ) -> bool:
        """
        Check entry timeout function callback.
        This method can be used to override the entry-timeout.
        It is called whenever a limit entry order has been created,
        and is not yet fully filled.
        Configuration options in `unfilledtimeout` will be verified before this,
        so ensure to set these timeouts high enough.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, this simply returns False.
        :param pair: Pair the trade is for
        :param trade: Trade object.
        :param order: Order object.
        :param current_time: datetime object, containing the current datetime
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return bool: When True is returned, then the entry order is cancelled.
        """
        return False

    def check_exit_timeout(
        self, pair: str, trade: "Trade", order: "Order", current_time: datetime, **kwargs
    ) -> bool:
        """
        Check exit timeout function callback.
        This method can be used to override the exit-timeout.
        It is called whenever a limit exit order has been created,
        and is not yet fully filled.
        Configuration options in `unfilledtimeout` will be verified before this,
        so ensure to set these timeouts high enough.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, this simply returns False.
        :param pair: Pair the trade is for
        :param trade: Trade object.
        :param order: Order object.
        :param current_time: datetime object, containing the current datetime
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return bool: When True is returned, then the exit-order is cancelled.
        """
        return False

    def adjust_trade_position(
        self,
        trade: "Trade",
        current_time: datetime,
        current_rate: float,
        current_profit: float,
        min_stake: Optional[float],
        max_stake: float,
        current_entry_rate: float,
        current_exit_rate: float,
        current_entry_profit: float,
        current_exit_profit: float,
        **kwargs,
    ) -> Optional[float]:
        """
        Custom trade adjustment logic, returning the stake amount that a trade should be
        increased or decreased.
        This means extra entry or exit orders with additional fees.
        Only called when `position_adjustment_enable` is set to True.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns None

        :param trade: trade object.
        :param current_time: datetime object, containing the current datetime
        :param current_rate: Current entry rate (same as current_entry_profit)
        :param current_profit: Current profit (as ratio), calculated based on current_rate
                                (same as current_entry_profit).
        :param min_stake: Minimal stake size allowed by exchange (for both entries and exits)
        :param max_stake: Maximum stake allowed (either through balance, or by exchange limits).
        :param current_entry_rate: Current rate using entry pricing.
        :param current_exit_rate: Current rate using exit pricing.
        :param current_entry_profit: Current profit using entry pricing.
        :param current_exit_profit: Current profit using exit pricing.
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return float: Stake amount to adjust your trade,
                       Positive values to increase position, Negative values to decrease position.
                       Return None for no action.
        """
        return None

    def leverage(
        self,
        pair: str,
        current_time: datetime,
        current_rate: float,
        proposed_leverage: float,
        max_leverage: float,
        entry_tag: Optional[str],
        side: str,
        **kwargs,
    ) -> float:
        """
        Customize leverage for each new trade. This method is only called in futures mode.

        :param pair: Pair that's currently analyzed
        :param current_time: datetime object, containing the current datetime
        :param current_rate: Rate, calculated based on pricing settings in exit_pricing.
        :param proposed_leverage: A leverage proposed by the bot.
        :param max_leverage: Max leverage allowed on this pair
        :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
        :param side: 'long' or 'short' - indicating the direction of the proposed trade
        :return: A leverage amount, which is between 1.0 and max_leverage.
        """
        return 1.0
xmatthias commented 7 months ago

well for your config - i'd remove what is disabled (edge, freqAI) - but that's a clarity point more than anything - allowing you to easily glance what's used and what isn't.

I'd also suggest to remove explicit rateLimit settings - ccxt does dynamic ratelimit now (ratelimits are different by endpoint) - by providing this key however, you're forcing a static (and in your case for binance, too high) ratelimit.


well after adjusting the whitespace in our profit_results column ... it seems like both profit columns are empty

    pair             stake_amount    max_stake_amount    amount    open_date                    close_date                    open_rate    close_rate    fee_open    fee_close    trade_duration    profit_ratio    profit_abs    exit_reason    initial_stop_loss_abs    initial_stop_loss_ratio    stop_loss_abs    stop_loss_ratio    min_rate    max_rate    is_open    enter_tag    leverage    is_short    open_timestamp    close_timestamp    orders
0   BTC/USDT:USDT    301.28064       301.28064           0.032     2019-10-30 00:00:00+00:00    2019-11-21 00:00:00+00:00     9415.02      8094.57       0.0002      0.0002       31680                                           exit_signal    94.2                     -0.99                     94.2              -0.99              7432.0      9550.0      False                   1.0         False       1572393600000     1574294400000     [{'amount': 0.032, 'safe_price': 9415.02, 'ft_order_side': 'buy', 'order_filled_timestamp': 1572393600000, 'ft_is_entry': True}, {'amount': 0.032, 'safe_price': 8094.57, 'ft_order_side': 'sell', 'order_filled_timestamp': 1574294400000, 'ft_is_entry': False}]

Now what's causing it - i cannot say. the strategy is non-runnable (AttributeError: type object 'MagicGoo' has no attribute 'convert_source') ...

Removing that part (and renaming the class so it's not overwriting the MagicGoo class) - gaussian is no longer callable due to wrong arguments.

This is clearly not the strategy causing any problem - as it's non functional.

Corfucinas commented 7 months ago

well for your config - i'd remove what is disabled (edge, freqAI) - but that's a clarity point more than anything - allowing you to easily glance what's used and what isn't.

I'd also suggest to remove explicit rateLimit settings - ccxt does dynamic ratelimit now (ratelimits are different by endpoint) - by providing this key however, you're forcing a static (and in your case for binance, too high) ratelimit.

well after adjusting the whitespace in our profit_results column ... it seems like both profit columns are empty

    pair             stake_amount    max_stake_amount    amount    open_date                    close_date                    open_rate    close_rate    fee_open    fee_close    trade_duration    profit_ratio    profit_abs    exit_reason    initial_stop_loss_abs    initial_stop_loss_ratio    stop_loss_abs    stop_loss_ratio    min_rate    max_rate    is_open    enter_tag    leverage    is_short    open_timestamp    close_timestamp    orders
0   BTC/USDT:USDT    301.28064       301.28064           0.032     2019-10-30 00:00:00+00:00    2019-11-21 00:00:00+00:00     9415.02      8094.57       0.0002      0.0002       31680                                           exit_signal    94.2                     -0.99                     94.2              -0.99              7432.0      9550.0      False                   1.0         False       1572393600000     1574294400000     [{'amount': 0.032, 'safe_price': 9415.02, 'ft_order_side': 'buy', 'order_filled_timestamp': 1572393600000, 'ft_is_entry': True}, {'amount': 0.032, 'safe_price': 8094.57, 'ft_order_side': 'sell', 'order_filled_timestamp': 1574294400000, 'ft_is_entry': False}]

Now what's causing it - i cannot say. the strategy is non-runnable (AttributeError: type object 'MagicGoo' has no attribute 'convert_source') ...

Removing that part (and renaming the class so it's not overwriting the MagicGoo class) - gaussian is no longer callable due to wrong arguments.

This is clearly not the strategy causing any problem - as it's non functional.

sorry @xmatthias ! I tried just giving you the min reproducible example, but forgot to add that in, I have now edited the strategy.py to include the missing function, I don't think it changes anything, try it and let me know :pray:

xmatthias commented 7 months ago

Well it's now runnable - but i don't see any problem here. sure, the single trade is a loser, but that's besides the point.

i've moved timerange to be around your trade ... but didn't get the same exact entry date.

freqtrade backtesting --userdir ../xmatt -s GooMagic -i 1d  --pairs BTC/USDT:USDT --timerange 20191001-20191201

image

The single trade i get does work correctly and so does backtesting (though i get a slightly different trade, starting 1 day early) (shown in freqUI as i'm too lazy to print the individual trade - as it clearly didn't trigger what you saw).

Initially, i thought that's caused by the starting date - as moving the start-date to 20191029 does change the trade date to start mid-november (but again, backtest just fine) - but checking the chart shows that there is no signal on the 30th - so this strategy will probably not cause what you've seen. image

Corfucinas commented 7 months ago

Well it's now runnable - but i don't see any problem here. sure, the single trade is a loser, but that's besides the point.

i've moved timerange to be around your trade ... but didn't get the same exact entry date.

freqtrade backtesting --userdir ../xmatt -s GooMagic -i 1d  --pairs BTC/USDT:USDT --timerange 20191001-20191201

image

The single trade i get does work correctly and so does backtesting (though i get a slightly different trade, starting 1 day early) (shown in freqUI as i'm too lazy to print the individual trade - as it clearly didn't trigger what you saw).

Initially, i thought that's caused by the starting date - as moving the start-date to 20191029 does change the trade date to start mid-november (but again, backtest just fine) - but checking the chart shows that there is no signal on the 30th - so this strategy will probably not cause what you've seen. image

Can you try running it with the following timerange 20170101-20240225

If the error doest not show up, just remove the -p and let the pairs from the staticlist in the config be

Thank you for taking the time doing this

Ps. I think the indicators won't have data to populate that trade you're referring too, the first few new candles are always NA depending on the "lookback" parameter

xmatthias commented 7 months ago

well you've had the error on BTC/USDT:USDT. running against all pairs won't change that - neither will it change where signals are.

I'm happy to look at this further if you can confirm the precise timerange and pair (one) - it's a single pair problem. I'll however not "try until i find reproduction for your problem" - if i need to search for the reproduction - it's no problem - but something you or your environment are causing.

"run against all" does not - neither am i convinced that your reproduction strategy actually causes any problem.

Corfucinas commented 7 months ago

well you've had the error on BTC/USDT:USDT. running against all pairs won't change that - neither will it change where signals are.

I'm happy to look at this further if you can confirm the precise timerange and pair (one) - it's a single pair problem. I'll however not "try until i find reproduction for your problem" - if i need to search for the reproduction - it's no problem - but something you or your environment are causing.

"run against all" does not - neither am i convinced that your reproduction strategy actually causes any problem.

Sorry if I wasn't that clear, yes, the error is on BTC/USDT:USDT as you pointed out, however this is triggered by running also

freqtrade backtesting -c config.json --timerange 20170101-20240206 --strategy GooMagic --timeframe 1d (I also had problems nailing down what's triggering it).

I just ran the above command and got the same stack error (with the same config and strategy as I shared here).

xmatthias commented 7 months ago

An interesting problem indeed :laughing:

it's caused by "futures_funding_rate": 0, - which slightly changes behavior in one merge statement (outer instead of inner). Now per se - that's not a problem - however, mark-rates on binance seem to start only on "2019-12-23 08:00" - which causes the funding-rate calculation to be empty (NaN) - which then populates through the trade, causing profit to be NaN (something * NaN is always NaN).

the place it triggers doesn't really suggest this to be a possibility or cause - as it's FAR from where point becomes problematic.

Corfucinas commented 7 months ago

An interesting problem indeed 😆

it's caused by "futures_funding_rate": 0, - which slightly changes behavior in one merge statement (outer instead of inner). Now per se - that's not a problem - however, mark-rates on binance seem to start only on "2019-12-23 08:00" - which causes the funding-rate calculation to be empty (NaN) - which then populates through the trade, causing profit to be NaN (something * NaN is always NaN).

the place it triggers doesn't really suggest this to be a possibility or cause - as it's FAR from where point becomes problematic.

thanks for the patch! I just copied it until release is out with the commit

xmatthias commented 7 months ago

just one cherry-picked commit (which is what a individual pick of one commit is) is not a supported version - if you're keen on using that version - please use the develop branch.

i often do multiple commits related to one issue - with the last closing the issue. That'll however implicitly mean that just that commit is NOT sufficient to fix the problem you're encountering.

Corfucinas commented 7 months ago

just one cherry-picked commit (which is what a individual pick of one commit is) is not a supported version - if you're keen on using that version - please use the develop branch.

i often do multiple commits related to one issue - with the last closing the issue. That'll however implicitly mean that just that commit is NOT sufficient to fix the problem you're encountering.

Fair point, thank you, doing that