Simple dip-buying strategy for SOL, ADA, and XLM. Buys when price drops 2% from recent high, sells when price gains 3%.
Timeframe
15m
Direction
Long Only
Stoploss
-9.0%
Trailing Stop
No
ROI
0m: 3.0%
Interface Version
3
Startup Candles
N/A
Indicators
0
import logging
from datetime import datetime
from typing import Any, Dict, List, Optional
import pandas as pd
from pandas import DataFrame
from freqtrade.strategy import IStrategy, DecimalParameter
logger = logging.getLogger(__name__)
class DipBuyingStrategy(IStrategy):
"""
Simple dip-buying strategy for SOL, ADA, and XLM.
Buys when price drops 2% from recent high, sells when price gains 3%.
"""
INTERFACE_VERSION = 3
# Minimal ROI designed for the strategy - we'll use custom exit logic instead
minimal_roi = {
"0": 0.03, # 3% profit target
}
# Optimal stoploss designed for the strategy
stoploss = -0.09 # 5% stop loss as safety net
# Optimal timeframe for the strategy - 15 minutes is good balance
timeframe = '15m'
# 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
# Number of candles the strategy requires before producing valid signals
startup_candle_count: int = 15
# Strategy parameters
dip_threshold = DecimalParameter(0.015, 0.025, default=0.04, space="buy", decimals=3)
profit_target = DecimalParameter(0.025, 0.035, default=0.03, space="sell", decimals=3)
lookback_period = 10 # Number of candles to look back for high/low
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Adds indicators to the given DataFrame
"""
# Calculate rolling high and low over lookback period
dataframe['rolling_high'] = dataframe['high'].rolling(window=self.lookback_period).max()
dataframe['rolling_low'] = dataframe['low'].rolling(window=self.lookback_period).min()
# Calculate percentage drop from recent high
dataframe['drop_from_high'] = (dataframe['close'] - dataframe['rolling_high']) / dataframe['rolling_high']
# Calculate percentage gain from recent low
dataframe['gain_from_low'] = (dataframe['close'] - dataframe['rolling_low']) / dataframe['rolling_low']
# Volume filter - ensure there's some activity
dataframe['volume_ma'] = dataframe['volume'].rolling(window=10).mean()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Entry signal: Buy when price drops 2% from recent high
"""
dataframe.loc[
(
(dataframe['drop_from_high'] <= -self.dip_threshold.value) & # Price dropped 2% from high
(dataframe['volume'] > dataframe['volume_ma'] * 0.5) & # Some volume activity
(dataframe['volume'] > 0) # Ensure volume exists
),
'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Exit signal: Sell when price gains 3% from recent low
"""
dataframe.loc[
(
(dataframe['gain_from_low'] >= self.profit_target.value) & # Price gained 3% from low
(dataframe['volume'] > 0) # Ensure volume exists
),
'exit_long'] = 1
return dataframe
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
"""
Custom stoploss logic, returning the new distance relative to current_rate
"""
# If we're in profit, tighten the stop loss
if current_profit > 0.01: # 1% profit
return -0.02 # 2% stop loss
elif current_profit > 0.02: # 2% profit
return -0.01 # 1% stop loss
return self.stoploss # Default stop loss