Timeframe
1h
Direction
Long Only
Stoploss
-3.0%
Trailing Stop
Yes
ROI
0m: 1.5%, 15m: 1.2%, 30m: 5.0%, 60m: 2.5%
Interface Version
N/A
Startup Candles
N/A
Indicators
1
freqtrade/freqtrade-strategies
Strategy 001 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
freqtrade/freqtrade-strategies
freqtrade/freqtrade-strategies
this is an example class, implementing a PSAR based trailing stop loss you are supposed to take the `custom_stoploss()` and `populate_indicators()` parts and adapt it to your own strategy
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import pandas_ta as ta
from technical import qtpylib
class TwoCandle(IStrategy):
# Strategy parameters
timeframe = "1h" # 5-minute timeframe as per the video example
minimal_roi = {"0": 0.015, "15": 0.012, "30": 0.05, "60": 0.025} # 1.5% ROI (1:1.5 risk-reward ratio)
stoploss = -0.03 # 1% stop loss (adjustable)
# Trailing stop
trailing_stop = True
trailing_stop_positive = 0.013
trailing_stop_positive_offset = 0.035
trailing_only_offset_is_reached = True
plot_config = {
"main_plot": {
"support": {
"color": "green",
"type": "line"
},
"resistance": {
"color": "red",
"type": "line"
}
},
"subplots": {
"close": {
"close_position": {
"color": "#76f639",
"type": "bar"
},
"close_comparison": {
"color": "#dedcf9",
"type": "line"
}
},
"pattern": {
"pattern": {
"color": "#165e70",
"type": "bar"
},
"pattern diff": {
"color": "#744bce",
"type": "bar"
}
}
}
}
# Define custom variables
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
heikinashi = qtpylib.heikinashi(dataframe)
dataframe['ha_open'] = heikinashi['open']
dataframe['ha_close'] = heikinashi['close']
dataframe['ha_high'] = heikinashi['high']
dataframe['ha_low'] = heikinashi['low']
# Calculate candle range (high - low)
dataframe['range'] = dataframe['high'] - dataframe['low']
dataframe['range_third'] = dataframe['range'] / 3
# Calculate candle body (close - open)
dataframe['body'] = abs(dataframe['close'] - dataframe['open'])
dataframe['body_third'] = dataframe['body'] / 3
# Simple support/resistance levels using rolling min/max
dataframe['support'] = dataframe['low'].rolling(window=80).min().shift(80)
dataframe['resistance'] = dataframe['high'].rolling(window=80).max().shift(80)
dataframe['midpoint'] = ((dataframe['resistance']-dataframe['support'])/2) + dataframe['support']
dataframe['trading_range'] = abs(dataframe['support'] - dataframe['resistance']) / 8
# Close position: Determine if close is in upper, mid, or lower third
dataframe['close_position'] = 0 # Default: mid
dataframe.loc[dataframe['close'] > (dataframe['high'] - dataframe['range_third']), 'close_position'] = 1 # High close
dataframe.loc[dataframe['close'] < (dataframe['low'] + dataframe['range_third']), 'close_position'] = -1 # Low close
# Close comparison: Compare current close to previous candle's range
dataframe['prev_high'] = dataframe['high'].shift(1)
dataframe['prev_low'] = dataframe['low'].shift(1)
dataframe['hl_close_comparison'] = 0 # Default: range
dataframe.loc[dataframe['close'] > dataframe['prev_high'], 'hl_close_comparison'] = 1 # Bull candle
dataframe.loc[dataframe['close'] < dataframe['prev_low'], 'hl_close_comparison'] = -1 # Bear candle
# Close position: Determine if close is in upper, mid, or lower third
dataframe['close_body_position'] = 0 # Default: mid
dataframe.loc[dataframe['close'] > (dataframe['high'] - dataframe['range_third']), 'close_body_position'] = 1 # High close
dataframe.loc[dataframe['close'] < (dataframe['low'] + dataframe['range_third']), 'close_body_position'] = -1 # Low close
# Close comparison: Compare current close to previous candle's range
dataframe['prev_close'] = dataframe['close'].shift(1)
dataframe['close_comparison'] = 0 # Default: range
dataframe.loc[dataframe['close'] > dataframe['prev_high'], 'close_comparison'] = 1 # Bull candle
dataframe.loc[dataframe['close'] < dataframe['prev_low'], 'close_comparison'] = -1 # Bear candle
# Combine close position and close comparison into 9 patterns
dataframe['pattern'] = dataframe['close_position'] * 3 + dataframe['hl_close_comparison'] + 4 # Maps to 0-8 (9 patterns)
dataframe['body_pattern'] = dataframe['close_body_position'] * 3 + dataframe['close_comparison'] + 4 # Maps to 0-8 (9 patterns)
dataframe['pattern_mean'] = dataframe['pattern'].rolling(2).mean()
dataframe['pattern_trend'] = dataframe['pattern'].rolling(9).mean()
dataframe['pattern_trend_ma'] = dataframe['support'] + (dataframe['pattern_trend'] * dataframe['trading_range'])
dataframe['pattern diff'] = dataframe['pattern'].diff()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Buy conditions based on Two Candle Theory
# dataframe.loc[
# (
# # High close bull candle (pattern 4: most bullish)
# (dataframe['pattern'] == 4) &
# # Breakout above resistance
# (dataframe['close'] > dataframe['resistance'].shift(1)) &
# (dataframe['pattern_mean'] >= 4) &
# # Previous candle was not bearish (avoid false breakouts)
# (dataframe['pattern'].shift(1) != 0) # Not low close bear
# ),
# ['enter_long', 'enter_tag']] = (1, 'long 1')
# dataframe.loc[
# (
# # High close bull candle (pattern 4: most bullish)
# (dataframe['pattern'] == 8) &
# # Breakout above resistance
# (dataframe['pattern_mean'] >= 4) &
# # Previous candle was not bearish (avoid false breakouts)
# (dataframe['pattern'].shift(1) == 0) # Not low close bear
# ),
# ['enter_long', 'enter_tag']] = (1, 'long 3')
# dataframe.loc[
# (
# # High close bull candle (pattern 4: most bullish)
# (dataframe['pattern'] <= 7) &
# # Breakout above resistance
# (dataframe['pattern_mean'] >= 4) &
# # Previous candle was not bearish (avoid false breakouts)
# (dataframe['pattern'].shift(1) <= 0) # Not low close bear
# ),
# ['enter_long', 'enter_tag']] = (1, 'long 4')
# dataframe.loc[
# (
# # High close bull candle (pattern 4: most bullish)
# (dataframe['pattern'] <= 6) &
# # Breakout above resistance
# (dataframe['pattern_mean'] >= 4) &
# # Previous candle was not bearish (avoid false breakouts)
# (dataframe['pattern'].shift(1) <= 0) # Not low close bear
# ),
# ['enter_long', 'enter_tag']] = (1, 'long 5')
# Alternative buy: High close bull after support bounce
dataframe.loc[
(
(dataframe['pattern_mean'].shift() < 4) &
(dataframe['pattern_mean'].shift() < 4) &
(dataframe['pattern_mean'] > 4)
# (dataframe['close'] > dataframe['support']) &
# (dataframe['pattern'].shift(1) == 0) # Previous was low close bear
),
['enter_long', 'enter_tag']] = (1, 'long 2')
# Track bias (simplified as a rolling score)
dataframe['bias'] = 0 # Neutral
dataframe.loc[dataframe['pattern'] == 4, 'bias'] = 1 # Bullish on high close bull
dataframe.loc[dataframe['pattern'] == 0, 'bias'] = -1 # Bearish on low close bear
dataframe['bias'] = dataframe['bias'].rolling(window=3).mean().fillna(0) # 3-candle bias
# # Bullish entries
# dataframe.loc[
# # High close bull at breakout
# ((dataframe['pattern'] == 4) & (dataframe['close'] > dataframe['resistance'].shift(1))) |
# # High close bull after support bounce
# ((dataframe['pattern'] == 4) & (dataframe['pattern'].shift(1) == 0) & (dataframe['close'] > dataframe['support'])) |
# # Two consecutive high close bull candles
# ((dataframe['pattern'] == 4) & (dataframe['pattern'].shift(1) == 4) & (dataframe['bias'] > 0)) |
# # High close range after pullback in uptrend
# ((dataframe['pattern'] == 1) & (dataframe['bias'] > 0) & (dataframe['close'] > dataframe['support'])),
# 'enter_long'] = 1
# # Bearish entries
# dataframe.loc[
# # Low close bear at breakdown
# ((dataframe['pattern'] == 0) & (dataframe['close'] < dataframe['support'].shift(1))) |
# # Two consecutive low close bear candles
# ((dataframe['pattern'] == 0) & (dataframe['pattern'].shift(1) == 0) & (dataframe['bias'] < 0)) |
# # Low close bear after resistance rejection
# ((dataframe['pattern'] == 0) & (dataframe['close'] < dataframe['resistance']) & (dataframe['bias'] < 0)),
# 'enter_short'] = 1
# # Sell conditions (short entry)
# dataframe.loc[
# (
# # Low close bear candle (pattern 0: most bearish)
# (dataframe['pattern'] == 0) &
# # Breakdown below support
# (dataframe['close'] < dataframe['support'].shift(1)) &
# # Previous candle was not bullish (avoid false breakdowns)
# (dataframe['pattern'].shift(1) != 4) # Not high close bull
# ),
# 'enter_short'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Exit long on strong bearish signal
dataframe.loc[
(
# High close bull candle (pattern 4: most bullish)
(dataframe['pattern'] >= 0) &
# Breakout above resistance
(dataframe['pattern_mean'] < 3.5) &
# Previous candle was not bearish (avoid false breakouts)
(dataframe['pattern'].shift(1) == 8) # Not low close bear
),
['exit_long', 'exit_tag']] = (1, 'short1')
dataframe.loc[
(
# High close bull candle (pattern 4: most bullish)
(dataframe['pattern'] >= 0) &
# Breakout above resistance3.5
(dataframe['pattern_mean'] < 3.5) &
# Previous candle was not bearish (avoid false breakouts)
(dataframe['pattern'].shift(1) == 7) # Not low close bear
),
['exit_long', 'exit_tag']] = (1, 'short2')
dataframe.loc[
(
# High close bull candle (pattern 4: most bullish)
(dataframe['pattern'] >= 0) &
# Breakout above resistance
(dataframe['pattern_mean'] < 3.5) &
# Previous candle was not bearish (avoid false breakouts)
(dataframe['pattern'].shift(1) == 6) # Not low close bear
),
['exit_long', 'exit_tag']] = (1, 'short1')
dataframe.loc[
(
(dataframe['pattern_mean'].shift() > 4) &
(dataframe['pattern_mean'] < 4)
),
['exit_long', 'exit_tag']] = (1, 'exit 2')
# # Bearish entries
# dataframe.loc[
# # Low close bear at breakdown
# ((dataframe['pattern'] == 0) & (dataframe['close'] < dataframe['support'].shift(1))) |
# # Two consecutive low close bear candles
# ((dataframe['pattern'] == 0) & (dataframe['pattern'].shift(1) == 0) & (dataframe['bias'] < 0)) |
# # Low close bear after resistance rejection
# ((dataframe['pattern'] == 0) & (dataframe['close'] < dataframe['resistance']) & (dataframe['bias'] < 0)),
# 'exit_long'] = 1
# Exit short on strong bullish signal
# dataframe.loc[
# (dataframe['enter_short'].shift(1) == 1) &
# (dataframe['pattern'] == 4), # High close bull candle
# 'exit_short'] = 1
return dataframe