Revized version of the strategy with improved risk management, dynamic stop loss, multi-timeframe analysis, and additional filters for better signal quality.
Timeframe
15m
Direction
Long Only
Stoploss
-5.0%
Trailing Stop
Yes
ROI
0m: 10.0%
Interface Version
N/A
Startup Candles
N/A
Indicators
5
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
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these libs ---
import numpy as np # noqa
import pandas as pd # noqa
from pandas import DataFrame
from typing import Optional, Union
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import DecimalParameter, CategoricalParameter
from freqtrade.persistence import Trade
from datetime import datetime, timedelta
import logging
class Vedat22OcatRevized(IStrategy):
"""
Revized version of the strategy with improved risk management, dynamic stop loss,
multi-timeframe analysis, and additional filters for better signal quality.
"""
# Strategy configuration
INTERFACE_VERSION: int = 3
timeframe = '15m'
can_short: bool = True
process_only_new_candles = True
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
# Risk management
stoploss = -0.05 # Initial stop loss (5%)
minimal_roi = {"0": 0.10} # Take profit at 10%
trailing_stop = True
trailing_stop_positive = 0.01
trailing_stop_positive_offset = 0.15
max_entry_position_adjustment = 3
max_dca_multiplier = 4.6
# Order types
order_types = {
'entry': 'limit',
'exit': 'limit',
'stoploss': 'market',
'stoploss_on_exchange': False
}
# Hyperparameters (optional, for optimization)
buy_rsi = DecimalParameter(30, 70, default=50, space='buy', optimize=True)
sell_rsi = DecimalParameter(70, 90, default=70, space='sell', optimize=True)
atr_multiplier = DecimalParameter(1.5, 3.0, default=2.0, space='sell', optimize=True)
def informative_pairs(self):
# Add higher timeframe for trend confirmation
return [(self.pair, "1h")]
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Add indicators
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['mfi'] = ta.MFI(dataframe, timeperiod=14)
dataframe['macd'] = ta.MACD(dataframe)['macd']
dataframe['macdsignal'] = ta.MACD(dataframe)['macdsignal']
dataframe['macdhist'] = ta.MACD(dataframe)['macdhist']
dataframe['ema9'] = ta.EMA(dataframe, timeperiod=9)
dataframe['ema26'] = ta.EMA(dataframe, timeperiod=26)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema200'] = ta.EMA(dataframe, timeperiod=200)
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
# Multi-timeframe indicators (1h)
informative = self.dp.get_pair_dataframe(pair=self.pair, timeframe="1h")
dataframe['ema200_1h'] = ta.EMA(informative, timeperiod=200)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Long entry conditions
dataframe.loc[
(
(dataframe['rsi'] < self.buy_rsi.value) &
(dataframe['mfi'] < 30) &
(dataframe['ema9'] > dataframe['ema26']) &
(dataframe['ema26'] > dataframe['ema50']) &
(dataframe['close'] > dataframe['ema200_1h']) # Trend confirmation
),
'enter_long'] = 1
# Short entry conditions
dataframe.loc[
(
(dataframe['rsi'] > self.sell_rsi.value) &
(dataframe['mfi'] > 70) &
(dataframe['ema9'] < dataframe['ema26']) &
(dataframe['ema26'] < dataframe['ema50']) &
(dataframe['close'] < dataframe['ema200_1h']) # Trend confirmation
),
'enter_short'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Long exit conditions
dataframe.loc[
(
(dataframe['rsi'] > 70) |
(dataframe['ema9'] < dataframe['ema26']) # Trend reversal
),
'exit_long'] = 1
# Short exit conditions
dataframe.loc[
(
(dataframe['rsi'] < 30) |
(dataframe['ema9'] > dataframe['ema26']) # Trend reversal
),
'exit_short'] = 1
return dataframe
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
current_profit: float, **kwargs) -> float:
# Dynamic stop loss based on ATR
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
atr_stoploss = last_candle['atr'] * self.atr_multiplier.value
return -atr_stoploss / current_rate
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: Optional[float], max_stake: float,
leverage: float, entry_tag: Optional[str], side: str,
**kwargs) -> float:
# Adjust stake amount for DCA
return proposed_stake / self.max_dca_multiplier
def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float,
min_stake: Optional[float], max_stake: float,
current_entry_rate: float, current_exit_rate: float,
current_entry_profit: float, current_exit_profit: float,
**kwargs) -> Union[Optional[float], Optional[str]]:
# DCA logic
if current_profit < -0.10: # Add to position if loss exceeds 10%
filled_entries = trade.select_filled_orders(trade.entry_side)
stake_amount = filled_entries[0].stake_amount * 1.5 # Increase stake by 50%
return stake_amount, 'dca_adjustment'
return None