Mean Reversion Strategy V3 - Hyperopt Optimized.
Timeframe
5m
Direction
Long Only
Stoploss
-16.1%
Trailing Stop
No
ROI
0m: 11.0%, 37m: 4.5%, 71m: 1.3%, 141m: 0.0%
Interface Version
3
Startup Candles
N/A
Indicators
4
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
import numpy as np
import pandas as pd
from pandas import DataFrame
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class MeanReversionStrategyV3(IStrategy):
"""
Mean Reversion Strategy V3 - Hyperopt Optimized.
Core: Prices revert to mean after extreme Z-score deviations.
Profitable in bear/sideways markets.
Optimized params: buy_zscore=-1.5, ma_period=92, rsi_buy=24,
volume_threshold=2.9, sell_zscore=0.3, rsi_sell=80, max_hold_candles=43
Results (bear market -28.86%):
- Win Rate: 54.3%, Total Return: +0.18%
- Max Drawdown: 0.54%, Sharpe: 2.28
Best: AVAX/USDT, DOT/USDT, ETH/USDT
Avoid: BTC/USDT (strong trends break mean reversion)
"""
INTERFACE_VERSION = 3
buy_zscore = DecimalParameter(-3.0, -0.5, default=-1.5, space="buy", decimals=1)
sell_zscore = DecimalParameter(0.0, 2.0, default=0.3, space="sell", decimals=1)
ma_period = IntParameter(20, 150, default=92, space="buy")
rsi_buy = IntParameter(15, 40, default=24, space="buy")
rsi_sell = IntParameter(65, 90, default=80, space="sell")
volume_threshold = DecimalParameter(1.0, 5.0, default=2.9, space="buy", decimals=1)
max_hold_candles = IntParameter(10, 100, default=43, space="sell")
minimal_roi = {"141": 0, "71": 0.013, "37": 0.045, "0": 0.11}
stoploss = -0.161
trailing_stop = False
timeframe = "5m"
process_only_new_candles = True
startup_candle_count: int = 150
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe["ma"] = ta.SMA(dataframe, timeperiod=self.ma_period.value)
dataframe["std"] = dataframe["close"].rolling(window=self.ma_period.value).std()
dataframe["zscore"] = (dataframe["close"] - dataframe["ma"]) / dataframe["std"]
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["volume_mean"] = dataframe["volume"].rolling(window=20).mean()
dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe["bb_lower"] = bollinger["lower"]
dataframe["bb_upper"] = bollinger["upper"]
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe["zscore"] < self.buy_zscore.value) &
(dataframe["rsi"] < self.rsi_buy.value) &
(dataframe["volume"] > self.volume_threshold.value * dataframe["volume_mean"]) &
(dataframe["std"] > 0) &
(dataframe["volume"] > 0)
),
"enter_long",
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe["zscore"] > self.sell_zscore.value) &
(dataframe["rsi"] > self.rsi_sell.value) &
(dataframe["volume"] > 0)
),
"exit_long",
] = 1
return dataframe