Volatility Breakout Strategy
Timeframe
4h
Direction
Long Only
Stoploss
-99.0%
Trailing Stop
Yes
ROI
0m: 10000.0%
Interface Version
N/A
Startup Candles
N/A
Indicators
1
# --- Do not remove these libs ---
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
# Technical analysis library
import talib.abstract as ta
# Quantstart technical library
import freqtrade.vendor.qtpylib.indicators as qtpylib
class VolatilityBreakout(IStrategy):
"""
Volatility Breakout Strategy
This strategy is designed to identify periods of low volatility (a "squeeze")
and enter a trade when the price breaks out of this consolidation period.
How it works:
1. It uses Bollinger Bandwidth to identify a "squeeze" where the bands are
unusually tight. This is a sign of low volatility.
2. When a squeeze is detected, it waits for the price to close above the
upper Bollinger Band, signaling a potential upward breakout.
3. The exit signal is a close below the middle Bollinger Band, which acts as
a dynamic, trailing stop-loss to let profits run and cut losses.
"""
# --- Strategy Hyperparameters ---
# These are the parameters that can be optimized by Freqtrade's hyperopt
# The numbers below are the "default" values we specified in Phase 2.
# Bollinger Bands
bb_period = IntParameter(20, 40, default=23, space='buy', optimize=True)
bb_stddev = DecimalParameter(1.5, 2.5, default=2.12, space='buy', optimize=True)
squeeze_period = IntParameter(20, 100, default=20, space='buy', optimize=True)
squeeze_ratio = DecimalParameter(0.20, 0.60, default=0.372, space='buy', optimize=True)
# --- Strategy Configuration ---
# The timeframe for the analysis (we chose 4-hour)
timeframe = '4h'
# This strategy uses a custom exit signal, so we disable ROI and standard stop-loss.
minimal_roi = {"0": 100} # Set to a high value to effectively disable it
stoploss = -0.99 # Set to a high value to effectively disable it
# We don't need freqtrade's native trailing stop-loss because we use a custom one
trailing_stop = True
trailing_stop_positive = 0.01
trailing_stop_positive_offset = 0.02
trailing_only_offset_is_reached = True
# Process only new candles
process_only_new_candles = True
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Adds all the necessary indicators to the Freqtrade dataframe.
"""
# --- Bollinger Bands ---
# Calculate Bollinger Bands using the qtpylib library
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
window=self.bb_period.value,
stds=self.bb_stddev.value) # <--- THIS LINE IS NOW FIXED
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_middleband'] = bollinger['mid']
dataframe['bb_upperband'] = bollinger['upper']
# --- Bollinger Bandwidth (for Squeeze Detection) ---
# This measures the distance between the upper and lower bands.
dataframe['bb_width'] = (dataframe['bb_upperband'] - dataframe['bb_lowerband']) / dataframe['bb_middleband']
# --- Squeeze Threshold ---
# This calculates the value below which we consider the market to be "squeezed".
# For example, a squeeze_ratio of 0.25 means we are looking for a bb_width
# in the bottom 25% of its values over the last `squeeze_period` candles.
dataframe['bb_width_quantile'] = dataframe['bb_width'].rolling(self.squeeze_period.value).quantile(self.squeeze_ratio.value)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Defines the conditions for entering a LONG (buy) trade.
"""
# --- Conditions for Entry ---
# Condition 1: The market is in a "squeeze".
squeeze_condition = (
dataframe['bb_width'] < dataframe['bb_width_quantile']
)
# Condition 2: The price has broken out above the upper band.
breakout_condition = (
qtpylib.crossed_above(dataframe['close'], dataframe['bb_upperband'])
)
# The final condition to enter a trade is when BOTH of the above are true.
dataframe.loc[
(
squeeze_condition &
breakout_condition
),
'enter_long'] = 1 # Set the 'enter_long' column to 1 (meaning "BUY")
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Defines the conditions for exiting a trade.
"""
# --- Condition for Exit ---
# We exit the trade if the price closes below the middle Bollinger Band.
# This acts as our dynamic stop-loss and take-profit mechanism.
exit_condition = (
qtpylib.crossed_below(dataframe['close'], dataframe['bb_middleband'])
)
# The final condition to exit a trade.
dataframe.loc[
(
exit_condition
),
'exit_long'] = 1 # Set the 'exit_long' column to 1 (meaning "SELL")
return dataframe