Short-Selling Mean Reversion Strategy.
Timeframe
5m
Direction
Long & Short
Stoploss
-15.0%
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 ShortStrategy(IStrategy):
"""
Short-Selling Mean Reversion Strategy.
Mirrors MeanReversionStrategyV3 logic for SHORT entries.
Enters SHORT when price is significantly ABOVE mean (overbought).
Designed for bearish/downtrend market conditions.
REQUIRES: Futures/margin trading (set trading_mode: "futures" in config)
NOT for spot trading.
Entry (short): Z-score > +1.5 AND RSI > 75 AND volume spike
Exit (cover): Z-score < +0.3 AND RSI < 45
Parameters match V3 long strategy defaults (mirrored):
- sell_zscore (short entry): +1.8
- buy_zscore (short exit): +0.3
- RSI thresholds: enter > 76, exit < 45
CAUTION: Short selling carries higher risk than long-only.
Always paper trade extensively before going live.
"""
INTERFACE_VERSION = 3
can_short = True
# Short entry params (overbought thresholds)
short_zscore_entry = DecimalParameter(1.0, 3.0, default=1.8, space="sell", decimals=1)
short_zscore_exit = DecimalParameter(-1.0, 1.0, default=0.3, space="buy", decimals=1)
short_rsi_entry = IntParameter(65, 85, default=76, space="sell")
short_rsi_exit = IntParameter(30, 55, default=45, space="buy")
short_ma_period = IntParameter(20, 150, default=92, space="sell")
short_volume_spike = DecimalParameter(1.5, 4.0, default=2.5, space="sell", decimals=1)
minimal_roi = {"141": 0, "71": 0.013, "37": 0.045, "0": 0.11}
stoploss = -0.15 # Wider stop for shorts (gap risk)
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.short_ma_period.value)
dataframe["std"] = dataframe["close"].rolling(window=self.short_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)
# Additional overbought confirmation
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe["bb_upper"] = bollinger["upper"]
dataframe["bb_lower"] = bollinger["lower"]
dataframe["bb_mid"] = bollinger["mid"]
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Long entries disabled (short-only strategy)
dataframe.loc[:, "enter_long"] = 0
# Short entry: price significantly above mean
dataframe.loc[
(
(dataframe["zscore"] > self.short_zscore_entry.value) &
(dataframe["rsi"] > self.short_rsi_entry.value) &
(dataframe["volume"] > self.short_volume_spike.value * dataframe["volume_mean"]) &
(dataframe["std"] > 0) &
(dataframe["volume"] > 0)
),
"enter_short",
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[:, "exit_long"] = 0
# Exit short: price reverted back to mean
dataframe.loc[
(
(dataframe["zscore"] < self.short_zscore_exit.value) &
(dataframe["rsi"] < self.short_rsi_exit.value) &
(dataframe["volume"] > 0)
),
"exit_short",
] = 1
return dataframe