Timeframe
1h
Direction
Long Only
Stoploss
-10.0%
Trailing Stop
Yes
ROI
N/A
Interface Version
N/A
Startup Candles
20
Indicators
1
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
freqtrade/freqtrade-strategies
# ================================================================
# SekkaRvol v1.0 – RVOL + VWAP Breakout Strategy
# ---------------------------------------------------------------
# Entry when price is above VWAP, breaks above previous candle,
# and relative volume (RVOL) exceeds threshold.
# ================================================================
from freqtrade.strategy import IStrategy, CategoricalParameter
from pandas import DataFrame
import pandas as pd
import numpy as np
import logging
from datetime import datetime
from typing import Optional
class SekkaRvol(IStrategy):
timeframe = "1h"
informative_timeframes = []
process_only_new_candles = False
can_short = False
use_exit_signal = True
exit_profit_only = False
ignore_buying_expiry = True
position_adjustment_enable = True
startup_candle_count = 20
DCA_THRESHOLD = CategoricalParameter([0.03, 0.05, 0.06], default=0.03, space="buy", optimize=False)
DCA_STEP = 3
RVOL_THRESHOLD = CategoricalParameter([2.0, 2.5, 3.0, 3.5, 4.0], default=3.0, space="buy", optimize=True)
RVOL_WINDOW = 20
VWAP_WINDOW = 14
LEVERAGE = 1
minimal_roi = {}
stoploss = -0.1
logger = logging.getLogger(__name__)
_last_dca_stage = None
## -- TRAILING SETUP --
trailing_stop = True
trailing_stop_positive_offset = 0.01
trailing_stop_positive = 0.005
trailing_only_offset_is_reached = True
# ------------------ Informative Pairs ------------------
def informative_pairs(self):
return []
# ------------------ Plot Config ------------------
plot_config = {
"main_plot": {
"vwap": {"color": "orange"},
},
"subplots": {
"RVOL": {
"rvol": {"color": "blue"},
},
},
}
# ------------------ Indicators ------------------
def hlc3(self, df: DataFrame) -> pd.Series:
return (df["high"] + df["low"] + df["close"]) / 3.0
def compute_vwap(self, df: DataFrame, window: int) -> pd.Series:
hlc3 = self.hlc3(df)
pv = hlc3 * df["volume"]
pv_sum = pv.rolling(window, min_periods=1).sum()
vol_sum = df["volume"].rolling(window, min_periods=1).sum()
vwap = (pv_sum / vol_sum.replace(0, np.nan)).ffill().fillna(df["close"])
return vwap
def populate_indicators(self, df: DataFrame, metadata: dict) -> DataFrame:
# VWAP
df["vwap"] = self.compute_vwap(df, self.VWAP_WINDOW)
# RVOL: current volume / average volume over window
avg_vol = df["volume"].rolling(self.RVOL_WINDOW, min_periods=1).mean()
df["rvol"] = (df["volume"] / avg_vol.replace(0, np.nan)).fillna(0)
# Previous candle info for breakout check
df["prev_open"] = df["open"].shift(1)
df["prev_close"] = df["close"].shift(1)
df["prev_is_red"] = df["prev_close"] < df["prev_open"]
return df
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
df["enter_long"] = 0
# Price above VWAP
above_vwap = df["close"] > df["vwap"]
# Breakout above previous candle:
# - If prev candle red: close > prev open
# - If prev candle green: close > prev close
breakout_level = df["prev_open"].where(df["prev_is_red"], df["prev_close"])
above_prev = df["close"] > breakout_level
# RVOL above threshold
high_rvol = df["rvol"] > self.RVOL_THRESHOLD.value
df.loc[above_vwap & above_prev & high_rvol, "enter_long"] = 1
return df
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
df["exit_long"] = 0
return df
# ------------------ Custom Stake ------------------
def custom_stake_amount(self, pair: str, current_time: pd.Timestamp, current_rate: float, **kwargs) -> float:
balance = self.wallets.get_total_stake_amount()
free = self.wallets.get_available_stake_amount()
used = max(balance - free, 0)
remaining = max(balance - used, 0)
trade = kwargs.get("trade", None)
stage = trade.nr_of_successful_entries if trade else 0
total_steps = self.DCA_STEP
if stage == 0:
stake = balance / total_steps
else:
remaining_steps = total_steps - stage
if remaining_steps > 0:
stake = remaining / remaining_steps
else:
stake = 0
return float(max(min(stake, remaining), 0.0))
# ------------------ DCA Logic ------------------
def adjust_trade_position(self, trade, current_time, current_rate, current_profit, **kwargs):
if self._last_dca_stage is None:
self._last_dca_stage = {}
trade_id = getattr(trade, "id", None) or f"{trade.pair}_{getattr(trade, 'open_date', None)}"
current_stage = trade.nr_of_successful_entries
recorded = self._last_dca_stage.get(trade_id)
if recorded is not None and recorded == current_stage:
return 0
if current_stage >= (self.DCA_STEP + 1):
return 0
avg_rate = trade.open_rate
drop_ratio = (current_rate / avg_rate) - 1.0
next_dca_trigger = -self.DCA_THRESHOLD.value
free_balance = self.wallets.get_available_stake_amount()
est_stake = self.custom_stake_amount(trade.pair, current_time, current_rate, trade=trade)
if drop_ratio <= next_dca_trigger and free_balance >= est_stake:
next_stage = current_stage + 1
tag = f"DCA_{next_stage}"
self._last_dca_stage[trade_id] = current_stage
trade.enter_tag = tag
return est_stake
return 0
# ------------------ Exit Logic ------------------
def custom_exit(self, pair: str, trade, current_time, current_rate, **kwargs):
return None
def leverage(self, pair: str, current_time: datetime, current_rate: float,
proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str,
**kwargs) -> float:
return self.LEVERAGE