Timeframe
15m
Direction
Long Only
Stoploss
N/A
Trailing Stop
No
ROI
N/A
Interface Version
N/A
Startup Candles
N/A
Indicators
9
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""
NostalgiaForInfinityX6 — Institutional-Grade Multi-Signal System (Simplified)
Source: https://github.com/iterativv/NostalgiaForInfinity (simplified adaptation)
Timeframe: 5m (informative: 15m, 1h, 1d)
Description: Faithful simplified version of the NostalgiaForInfinityX architecture.
Features 8 distinct buy conditions, 5 sell conditions, derisking system,
and multi-timeframe indicator analysis.
Blacklisted leveraged tokens:
*UP/USDT, *DOWN/USDT, *BULL/USDT, *BEAR/USDT, *2L/USDT, *2S/USDT,
*3L/USDT, *3S/USDT, *4L/USDT, *4S/USDT, *5L/USDT, *5S/USDT
These should be excluded via pair_blacklist in config.
"""
from functools import reduce
from freqtrade.strategy import (
IStrategy,
BooleanParameter,
DecimalParameter,
IntParameter,
merge_informative_pair,
)
from pandas import DataFrame
import numpy as np
import pandas_ta as pta
class NostalgiaForInfinityX6(IStrategy):
INTERFACE_VERSION: int = 3
timeframe: str = "5m"
# Fully signal-driven
minimal_roi: dict = {"0": 100}
stoploss: float = -0.99
use_exit_signal: bool = True
exit_profit_only: bool = False
ignore_roi_if_entry_signal: bool = True
# Informative timeframes
inf_timeframes: list = ["15m", "1h", "1d"]
# -----------------------------------------------------------------------
# Buy condition toggles
# -----------------------------------------------------------------------
buy_cond_1_enabled = BooleanParameter(default=True, space="buy")
buy_cond_2_enabled = BooleanParameter(default=True, space="buy")
buy_cond_3_enabled = BooleanParameter(default=True, space="buy")
buy_cond_4_enabled = BooleanParameter(default=True, space="buy")
buy_cond_5_enabled = BooleanParameter(default=True, space="buy")
buy_cond_6_enabled = BooleanParameter(default=True, space="buy")
buy_cond_7_enabled = BooleanParameter(default=True, space="buy")
buy_cond_8_enabled = BooleanParameter(default=True, space="buy")
# -----------------------------------------------------------------------
# Buy hyperopt parameters — per condition
# -----------------------------------------------------------------------
# Condition 1 — EMA crossover + RSI
buy_1_rsi = IntParameter(25, 50, default=40, space="buy")
# Condition 2 — BB lower band bounce + volume
buy_2_rsi = IntParameter(20, 40, default=30, space="buy")
buy_2_mfi = IntParameter(15, 40, default=30, space="buy")
# Condition 3 — Williams %R oversold
buy_3_willr = IntParameter(-99, -70, default=-85, space="buy")
buy_3_rsi = IntParameter(20, 45, default=35, space="buy")
# Condition 4 — VWAP dip
buy_4_vwap_factor = DecimalParameter(0.96, 1.0, default=0.99, space="buy")
buy_4_rsi = IntParameter(20, 45, default=35, space="buy")
buy_4_rsi_1h_min = IntParameter(30, 55, default=40, space="buy")
# Condition 5 — 1h StochRSI oversold
buy_5_stochrsi_k = IntParameter(10, 35, default=20, space="buy")
buy_5_stochrsi_d = IntParameter(10, 35, default=20, space="buy")
buy_5_rsi = IntParameter(20, 45, default=35, space="buy")
# Condition 6 — EMA 50/200 golden zone
buy_6_rsi = IntParameter(15, 40, default=30, space="buy")
buy_6_willr_1h = IntParameter(-99, -60, default=-75, space="buy")
# Condition 7 — BB squeeze
buy_7_bb_width = DecimalParameter(0.02, 0.08, default=0.04, space="buy")
buy_7_rsi = IntParameter(20, 42, default=32, space="buy")
buy_7_rsi_1h_min = IntParameter(35, 60, default=45, space="buy")
# Condition 8 — Multi-TF RSI alignment
buy_8_rsi = IntParameter(15, 38, default=28, space="buy")
buy_8_rsi_15m = IntParameter(20, 45, default=35, space="buy")
buy_8_rsi_1h = IntParameter(30, 55, default=45, space="buy")
# -----------------------------------------------------------------------
# Sell hyperopt parameters
# -----------------------------------------------------------------------
# Condition 1 — RSI overbought
sell_1_rsi = IntParameter(60, 85, default=70, space="sell")
# Condition 2 — Williams %R overbought
sell_2_willr = IntParameter(-20, -3, default=-10, space="sell")
sell_2_rsi = IntParameter(55, 80, default=65, space="sell")
# Condition 3 — EMA bearish crossover
sell_3_rsi = IntParameter(50, 75, default=60, space="sell")
# Condition 4 — MFI overbought
sell_4_mfi = IntParameter(70, 95, default=80, space="sell")
sell_4_bb_factor = DecimalParameter(0.97, 1.01, default=0.99, space="sell")
# Condition 5 — 1h StochRSI overbought
sell_5_stochrsi = IntParameter(65, 95, default=80, space="sell")
sell_5_rsi = IntParameter(55, 80, default=65, space="sell")
# Derisking threshold
derisk_profit_threshold = DecimalParameter(-0.08, -0.01, default=-0.03, space="sell")
def informative_pairs(self) -> list:
pairs = self.dp.current_whitelist()
informative_pairs = []
for tf in self.inf_timeframes:
informative_pairs.extend([(pair, tf) for pair in pairs])
return informative_pairs
def _stochastic_rsi(
self,
dataframe: DataFrame,
rsi_length: int = 14,
stoch_length: int = 14,
smooth_k: int = 3,
smooth_d: int = 3,
) -> tuple:
"""Stochastic RSI indicator."""
rsi = pta.rsi(dataframe["close"], length=rsi_length)
stoch_rsi_k = (
(rsi - rsi.rolling(window=stoch_length).min())
/ (rsi.rolling(window=stoch_length).max() - rsi.rolling(window=stoch_length).min())
) * 100
stoch_rsi_k = stoch_rsi_k.rolling(window=smooth_k).mean()
stoch_rsi_d = stoch_rsi_k.rolling(window=smooth_d).mean()
return stoch_rsi_k, stoch_rsi_d
def _williams_r(self, dataframe: DataFrame, length: int = 14) -> "Series":
"""Williams %R indicator."""
highest_high = dataframe["high"].rolling(window=length).max()
lowest_low = dataframe["low"].rolling(window=length).min()
wr = ((highest_high - dataframe["close"]) / (highest_high - lowest_low)) * -100
return wr
def _compute_vwap(self, dataframe: DataFrame) -> "Series":
"""Compute VWAP (Volume Weighted Average Price)."""
typical_price = (dataframe["high"] + dataframe["low"] + dataframe["close"]) / 3
cumulative_tp_vol = (typical_price * dataframe["volume"]).cumsum()
cumulative_vol = dataframe["volume"].cumsum()
return cumulative_tp_vol / cumulative_vol
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# =====================================================================
# 5m indicators
# =====================================================================
# EMAs
for length in [5, 8, 13, 21, 50, 200]:
dataframe[f"ema_{length}"] = pta.ema(dataframe["close"], length=length)
# RSI
dataframe["rsi_14"] = pta.rsi(dataframe["close"], length=14)
# Bollinger Bands
bb = pta.bbands(dataframe["close"], length=20, std=2.0)
dataframe["bb_lowerband"] = bb["BBL_20_2.0"]
dataframe["bb_middleband"] = bb["BBM_20_2.0"]
dataframe["bb_upperband"] = bb["BBU_20_2.0"]
dataframe["bb_width"] = (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]
# Williams %R
dataframe["willr_14"] = self._williams_r(dataframe, length=14)
# VWAP
dataframe["vwap"] = self._compute_vwap(dataframe)
# MFI
dataframe["mfi_14"] = pta.mfi(
dataframe["high"], dataframe["low"], dataframe["close"], dataframe["volume"], length=14
)
# SMA 200 for trend
dataframe["sma_200"] = pta.sma(dataframe["close"], length=200)
# =====================================================================
# 15m informative
# =====================================================================
inf_15m = self.dp.get_pair_dataframe(pair=metadata["pair"], timeframe="15m")
inf_15m["rsi_14_15m"] = pta.rsi(inf_15m["close"], length=14)
dataframe = merge_informative_pair(dataframe, inf_15m, self.timeframe, "15m", ffill=True)
# =====================================================================
# 1h informative
# =====================================================================
inf_1h = self.dp.get_pair_dataframe(pair=metadata["pair"], timeframe="1h")
inf_1h["rsi_14_1h"] = pta.rsi(inf_1h["close"], length=14)
for length in [5, 8, 13, 21, 50, 200]:
inf_1h[f"ema_{length}_1h"] = pta.ema(inf_1h["close"], length=length)
inf_1h["willr_14_1h"] = self._williams_r(inf_1h, length=14)
stoch_k, stoch_d = self._stochastic_rsi(inf_1h)
inf_1h["stochrsi_k_1h"] = stoch_k
inf_1h["stochrsi_d_1h"] = stoch_d
dataframe = merge_informative_pair(dataframe, inf_1h, self.timeframe, "1h", ffill=True)
# =====================================================================
# 1d informative
# =====================================================================
inf_1d = self.dp.get_pair_dataframe(pair=metadata["pair"], timeframe="1d")
inf_1d["ema_200_1d"] = pta.ema(inf_1d["close"], length=200)
inf_1d["rsi_14_1d"] = pta.rsi(inf_1d["close"], length=14)
dataframe = merge_informative_pair(dataframe, inf_1d, self.timeframe, "1d", ffill=True)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions: list = []
# -----------------------------------------------------------------
# Buy condition 1 — EMA crossover with RSI confirmation
# -----------------------------------------------------------------
if self.buy_cond_1_enabled.value:
buy_cond_1 = (
(dataframe["ema_5"] > dataframe["ema_13"])
& (dataframe["ema_13"] > dataframe["ema_21"])
& (dataframe["rsi_14"] < self.buy_1_rsi.value)
& (dataframe["close"] < dataframe["bb_lowerband"])
& (dataframe["volume"] > 0)
)
conditions.append(buy_cond_1)
# -----------------------------------------------------------------
# Buy condition 2 — BB lower band bounce with volume
# -----------------------------------------------------------------
if self.buy_cond_2_enabled.value:
buy_cond_2 = (
(dataframe["close"] < dataframe["bb_lowerband"])
& (dataframe["rsi_14"] < self.buy_2_rsi.value)
& (dataframe["mfi_14"] < self.buy_2_mfi.value)
& (dataframe["volume"] > dataframe["volume"].shift(1))
)
conditions.append(buy_cond_2)
# -----------------------------------------------------------------
# Buy condition 3 — Williams %R oversold with EMA support
# -----------------------------------------------------------------
if self.buy_cond_3_enabled.value:
buy_cond_3 = (
(dataframe["willr_14"] < self.buy_3_willr.value)
& (dataframe["close"] > dataframe["ema_200"])
& (dataframe["rsi_14"] < self.buy_3_rsi.value)
& (dataframe["volume"] > 0)
)
conditions.append(buy_cond_3)
# -----------------------------------------------------------------
# Buy condition 4 — VWAP dip buy
# -----------------------------------------------------------------
if self.buy_cond_4_enabled.value:
buy_cond_4 = (
(dataframe["close"] < dataframe["vwap"] * self.buy_4_vwap_factor.value)
& (dataframe["rsi_14"] < self.buy_4_rsi.value)
& (dataframe[f"rsi_14_1h_1h"] > self.buy_4_rsi_1h_min.value)
& (dataframe["volume"] > 0)
)
conditions.append(buy_cond_4)
# -----------------------------------------------------------------
# Buy condition 5 — 1h Stochastic RSI oversold
# -----------------------------------------------------------------
if self.buy_cond_5_enabled.value:
buy_cond_5 = (
(dataframe[f"stochrsi_k_1h_1h"] < self.buy_5_stochrsi_k.value)
& (dataframe[f"stochrsi_d_1h_1h"] < self.buy_5_stochrsi_d.value)
& (dataframe["rsi_14"] < self.buy_5_rsi.value)
& (dataframe["close"] < dataframe["ema_50"])
& (dataframe["volume"] > 0)
)
conditions.append(buy_cond_5)
# -----------------------------------------------------------------
# Buy condition 6 — EMA 50/200 golden zone with RSI
# -----------------------------------------------------------------
if self.buy_cond_6_enabled.value:
buy_cond_6 = (
(dataframe["close"] < dataframe["ema_50"])
& (dataframe["ema_50"] > dataframe["ema_200"])
& (dataframe["rsi_14"] < self.buy_6_rsi.value)
& (dataframe[f"willr_14_1h_1h"] < self.buy_6_willr_1h.value)
& (dataframe["volume"] > 0)
)
conditions.append(buy_cond_6)
# -----------------------------------------------------------------
# Buy condition 7 — BB squeeze with 1h RSI divergence
# -----------------------------------------------------------------
if self.buy_cond_7_enabled.value:
buy_cond_7 = (
(dataframe["bb_width"] < self.buy_7_bb_width.value)
& (dataframe["close"] < dataframe["bb_lowerband"])
& (dataframe[f"rsi_14_1h_1h"] > self.buy_7_rsi_1h_min.value)
& (dataframe["rsi_14"] < self.buy_7_rsi.value)
& (dataframe["volume"] > 0)
)
conditions.append(buy_cond_7)
# -----------------------------------------------------------------
# Buy condition 8 — Multi-timeframe RSI alignment
# -----------------------------------------------------------------
if self.buy_cond_8_enabled.value:
buy_cond_8 = (
(dataframe["rsi_14"] < self.buy_8_rsi.value)
& (dataframe[f"rsi_14_15m_15m"] < self.buy_8_rsi_15m.value)
& (dataframe[f"rsi_14_1h_1h"] < self.buy_8_rsi_1h.value)
& (dataframe["close"] < dataframe["sma_200"])
& (dataframe["close"] < dataframe["bb_middleband"])
& (dataframe["volume"] > 0)
)
conditions.append(buy_cond_8)
# OR all conditions together
if conditions:
dataframe.loc[reduce(lambda x, y: x | y, conditions), "enter_long"] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions: list = []
# -----------------------------------------------------------------
# Sell condition 1 — RSI overbought
# -----------------------------------------------------------------
sell_cond_1 = (
(dataframe["rsi_14"] > self.sell_1_rsi.value)
& (dataframe["close"] > dataframe["bb_upperband"])
& (dataframe["volume"] > 0)
)
conditions.append(sell_cond_1)
# -----------------------------------------------------------------
# Sell condition 2 — Williams %R overbought
# -----------------------------------------------------------------
sell_cond_2 = (
(dataframe["willr_14"] > self.sell_2_willr.value)
& (dataframe["rsi_14"] > self.sell_2_rsi.value)
& (dataframe["volume"] > 0)
)
conditions.append(sell_cond_2)
# -----------------------------------------------------------------
# Sell condition 3 — EMA bearish crossover
# -----------------------------------------------------------------
sell_cond_3 = (
(dataframe["ema_5"] < dataframe["ema_13"])
& (dataframe["ema_13"] < dataframe["ema_21"])
& (dataframe["rsi_14"] > self.sell_3_rsi.value)
& (dataframe["volume"] > 0)
)
conditions.append(sell_cond_3)
# -----------------------------------------------------------------
# Sell condition 4 — MFI overbought with BB upper band
# -----------------------------------------------------------------
sell_cond_4 = (
(dataframe["mfi_14"] > self.sell_4_mfi.value)
& (dataframe["close"] > dataframe["bb_upperband"] * self.sell_4_bb_factor.value)
& (dataframe["volume"] > 0)
)
conditions.append(sell_cond_4)
# -----------------------------------------------------------------
# Sell condition 5 — 1h Stochastic RSI overbought
# -----------------------------------------------------------------
sell_cond_5 = (
(dataframe[f"stochrsi_k_1h_1h"] > self.sell_5_stochrsi.value)
& (dataframe[f"stochrsi_d_1h_1h"] > self.sell_5_stochrsi.value)
& (dataframe["rsi_14"] > self.sell_5_rsi.value)
& (dataframe["volume"] > 0)
)
conditions.append(sell_cond_5)
if conditions:
dataframe.loc[reduce(lambda x, y: x | y, conditions), "exit_long"] = 1
return dataframe
def custom_exit(
self,
pair: str,
trade: "Trade",
current_time: "datetime",
current_rate: float,
current_profit: float,
**kwargs,
) -> str | bool:
"""
Derisking system: if open profit drops below -3% AND the 1h EMA-21 < EMA-50
(downtrend), trigger a sell.
"""
if current_profit > self.derisk_profit_threshold.value:
return False
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) < 1:
return False
last_candle = dataframe.iloc[-1]
ema_21_1h = last_candle.get(f"ema_21_1h_1h", 0)
ema_50_1h = last_candle.get(f"ema_50_1h_1h", 0)
if ema_21_1h > 0 and ema_50_1h > 0 and ema_21_1h < ema_50_1h:
return "derisk_1h_downtrend"
return False
def custom_stoploss(
self,
pair: str,
trade: "Trade",
current_time: "datetime",
current_rate: float,
current_profit: float,
**kwargs,
) -> float:
"""Exchange stoploss safety net at -0.12."""
return -0.12