Estratégia WaveHyperNW Melhorada
Timeframe
5m
Direction
Long Only
Stoploss
-8.0%
Trailing Stop
Yes
ROI
0m: 5.0%, 5m: 3.5%, 10m: 2.5%, 15m: 1.5%
Interface Version
3
Startup Candles
N/A
Indicators
4
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
# -*- coding: utf-8 -*-
"""
WaveHyperNWEnhanced - Versão melhorada da WaveHyperNW Strategy
Implementações nativas + melhorias de performance e sinais
"""
import logging
from datetime import datetime
import numpy as np
import pandas as pd
from freqtrade.strategy import (
CategoricalParameter,
DecimalParameter,
IntParameter,
IStrategy,
)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class WaveHyperNWEnhanced(IStrategy):
"""
Estratégia WaveHyperNW Melhorada
Melhorias implementadas:
- Implementações nativas (sem TA-Lib)
- WaveTrend otimizado
- Nadaraya-Watson melhorado
- Filtros de volatilidade
- Stop loss dinâmico
- Gestão de risco aprimorada
"""
INTERFACE_VERSION = 3
# Configurações básicas
timeframe = "5m"
stoploss = -0.08 # Melhorado: stop mais conservador
minimal_roi = {
"0": 0.05, # Melhorado: ROI mais realista
"5": 0.035,
"10": 0.025,
"15": 0.015,
"30": 0.008,
"60": 0.003,
}
# Trailing stop melhorado
trailing_stop = True
trailing_stop_positive = 0.02
trailing_stop_positive_offset = 0.035
trailing_only_offset_is_reached = True
# Parâmetros WaveTrend otimizados
wt_channel_len = IntParameter(6, 12, default=8, space="buy")
wt_average_len = IntParameter(14, 22, default=18, space="buy")
wt_overbought = DecimalParameter(45, 65, default=55, space="sell")
wt_oversold = DecimalParameter(-65, -45, default=-55, space="buy")
# Parâmetros Nadaraya-Watson
nw_bandwidth = DecimalParameter(2.0, 5.0, default=3.5, space="buy")
nw_lookback = IntParameter(15, 35, default=25, space="buy")
nw_std_multiplier = DecimalParameter(0.8, 2.0, default=1.2, space="buy")
# Filtros de qualidade
min_volume_ratio = DecimalParameter(0.3, 1.0, default=0.5, space="buy")
max_volatility = DecimalParameter(0.02, 0.08, default=0.05, space="buy")
trend_strength_min = DecimalParameter(0.4, 0.8, default=0.6, space="buy")
# Proteções melhoradas
cooldown_lookback = IntParameter(5, 30, default=12, space="protection")
stop_duration = IntParameter(20, 120, default=45, space="protection")
max_drawdown_protection = DecimalParameter(
0.05, 0.15, default=0.10, space="protection"
)
use_stop_protection = CategoricalParameter(
[True, False], default=True, space="protection"
)
def get_int_value(self, param):
"""Helper para converter parâmetros para int"""
return int(param.value)
@property
def protections(self):
if not self.use_stop_protection.value:
return []
return [
{
"method": "CooldownPeriod",
"stop_duration_candles": self.get_int_value(self.stop_duration),
},
{
"method": "StoplossGuard",
"lookback_period_candles": self.get_int_value(self.cooldown_lookback),
"trade_limit": 3, # Mais conservador
"stop_duration_candles": self.get_int_value(self.stop_duration),
"only_per_pair": False,
},
{
"method": "MaxDrawdown",
"lookback_period_candles": 200,
"trade_limit": 5,
"stop_duration_candles": self.get_int_value(self.stop_duration) * 2,
"max_allowed_drawdown": self.max_drawdown_protection.value,
},
]
def ema(self, series: pd.Series, period: int) -> pd.Series:
"""Implementação nativa da EMA"""
return series.ewm(span=period, adjust=False).mean()
def sma(self, series: pd.Series, period: int) -> pd.Series:
"""Implementação nativa da SMA"""
return series.rolling(window=period).mean()
def rsi(self, series: pd.Series, period: int = 14) -> pd.Series:
"""Implementação nativa do RSI"""
delta = series.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
return 100 - (100 / (1 + rs))
def atr(self, high: pd.Series, low: pd.Series, close: pd.Series, period: int = 14):
"""Implementação nativa do ATR"""
tr1 = high - low
tr2 = abs(high - close.shift())
tr3 = abs(low - close.shift())
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
return tr.rolling(window=period).mean()
def wavetrend(self, dataframe: pd.DataFrame) -> tuple:
"""
Implementação melhorada do WaveTrend
"""
# Average Price
ap = (dataframe["high"] + dataframe["low"] + dataframe["close"]) / 3
# Parâmetros
channel_len = self.get_int_value(self.wt_channel_len)
average_len = self.get_int_value(self.wt_average_len)
# ESA (Exponential Smoothed Average)
esa = self.ema(ap, channel_len)
# Deviation
d = self.ema(abs(ap - esa), channel_len)
# Channel Index
ci = (ap - esa) / (0.015 * d)
# WaveTrend lines
wt1 = self.ema(ci, average_len)
wt2 = self.sma(wt1, 4)
return wt1, wt2
def nadaraya_watson(self, dataframe: pd.DataFrame) -> tuple:
"""
Implementação melhorada do Nadaraya-Watson Estimator
"""
close = dataframe["close"].values
bandwidth = self.nw_bandwidth.value
lookback = self.get_int_value(self.nw_lookback)
# Kernel Gaussiano
weights = np.array(
[
np.exp(-(i**2) / (2 * bandwidth**2))
for i in range(-lookback, lookback + 1)
]
)
weights = weights / weights.sum()
# Aplicar convolução
nw_estimate = np.convolve(close, weights, mode="same")
nw_series = pd.Series(nw_estimate, index=dataframe.index)
# Bandas baseadas em desvio padrão
std = dataframe["close"].rolling(lookback).std()
multiplier = self.nw_std_multiplier.value
nw_upper = nw_series + multiplier * std
nw_lower = nw_series - multiplier * std
return nw_series, nw_upper, nw_lower
def calculate_trend_strength(self, dataframe: pd.DataFrame) -> pd.Series:
"""
Calcular força da tendência (0 = bearish, 1 = bullish)
"""
# EMA alignment
ema_8 = dataframe["ema_8"]
ema_21 = dataframe["ema_21"]
ema_50 = dataframe["ema_50"]
ema_score = (
(ema_8 > ema_21).astype(int) * 0.4
+ (ema_21 > ema_50).astype(int) * 0.3
+ (dataframe["close"] > ema_8).astype(int) * 0.3
)
# RSI momentum
rsi_normalized = (dataframe["rsi"] - 50) / 50
rsi_score = (rsi_normalized.clip(-1, 1) + 1) / 2
# WaveTrend momentum
wt_score = ((dataframe["wt1"] + 100) / 200).clip(0, 1)
# Combine scores
trend_strength = ema_score * 0.5 + rsi_score * 0.3 + wt_score * 0.2
return trend_strength.clip(0, 1)
def calculate_volatility_filter(self, dataframe: pd.DataFrame) -> pd.Series:
"""
Filtro de volatilidade para evitar mercados muito voláteis
"""
# ATR normalizado
atr_normalized = dataframe["atr"] / dataframe["close"]
# Volatilidade baseada em desvio padrão
price_volatility = dataframe["close"].rolling(20).std() / dataframe["close"]
# Combinar métricas
volatility = (atr_normalized + price_volatility) / 2
return volatility < self.max_volatility.value
def populate_indicators(self, dataframe, metadata):
"""
Calcular todos os indicadores
"""
# Indicadores básicos
dataframe["rsi"] = self.rsi(dataframe["close"])
dataframe["ema_8"] = self.ema(dataframe["close"], 8)
dataframe["ema_21"] = self.ema(dataframe["close"], 21)
dataframe["ema_50"] = self.ema(dataframe["close"], 50)
dataframe["atr"] = self.atr(
dataframe["high"], dataframe["low"], dataframe["close"]
)
# Volume
dataframe["volume_sma"] = self.sma(dataframe["volume"], 24)
dataframe["volume_ratio"] = dataframe["volume"] / dataframe["volume_sma"]
# WaveTrend melhorado
dataframe["wt1"], dataframe["wt2"] = self.wavetrend(dataframe)
# Nadaraya-Watson melhorado
dataframe["nw_estimate"], dataframe["nw_upper"], dataframe["nw_lower"] = (
self.nadaraya_watson(dataframe)
)
# Trend strength
dataframe["trend_strength"] = self.calculate_trend_strength(dataframe)
# Filtros de qualidade
dataframe["volatility_ok"] = self.calculate_volatility_filter(dataframe)
# Momentum adicional
dataframe["price_momentum"] = dataframe["close"].pct_change(5)
dataframe["volume_momentum"] = dataframe["volume"].pct_change(3)
# Support/Resistance dinâmico
dataframe["resistance"] = dataframe["high"].rolling(20).max()
dataframe["support"] = dataframe["low"].rolling(20).min()
dataframe["price_position"] = (dataframe["close"] - dataframe["support"]) / (
dataframe["resistance"] - dataframe["support"]
)
return dataframe
def populate_entry_trend(self, dataframe, metadata):
"""
Sinais de entrada melhorados
"""
# Condições WaveTrend melhoradas
wt_bullish = (
(dataframe["wt1"] > dataframe["wt2"])
& (dataframe["wt1"] < self.wt_oversold.value)
& (dataframe["wt1"].shift(1) <= dataframe["wt1"]) # Momentum crescente
)
# Condições Nadaraya-Watson
nw_support = (dataframe["close"] <= dataframe["nw_lower"]) | (
(dataframe["close"] < dataframe["nw_estimate"]) & (dataframe["rsi"] < 40)
)
# Condições de tendência
trend_favorable = (
dataframe["trend_strength"] > self.trend_strength_min.value
) & (dataframe["ema_8"] > dataframe["ema_21"])
# Condições de volume
volume_ok = (dataframe["volume_ratio"] > self.min_volume_ratio.value) & (
dataframe["volume_momentum"] > -0.2
) # Volume não em queda livre
# Filtros de qualidade
quality_filters = (
dataframe["volatility_ok"]
& (dataframe["price_momentum"] > -0.01) # Não em queda livre
& (dataframe["price_position"] < 0.8) # Não muito próximo da resistência
)
# Confirmação adicional: RSI não overbought
rsi_ok = dataframe["rsi"] < 65
# Sinal final
entry_signal = (
wt_bullish
& nw_support
& trend_favorable
& volume_ok
& quality_filters
& rsi_ok
)
dataframe.loc[entry_signal, "enter_long"] = 1
dataframe.loc[entry_signal, "enter_tag"] = "WT_NW_enhanced"
return dataframe
def populate_exit_trend(self, dataframe, metadata):
"""
Sinais de saída melhorados
"""
# Saída por WaveTrend overbought
wt_exit = (
(dataframe["wt1"] < dataframe["wt2"])
& (dataframe["wt1"] > self.wt_overbought.value)
& (dataframe["wt1"].shift(1) >= dataframe["wt1"]) # Momentum decrescente
)
# Saída por RSI overbought
rsi_exit = dataframe["rsi"] > 75
# Saída por deterioração da tendência
trend_deterioration = (dataframe["trend_strength"] < 0.3) | (
dataframe["ema_8"] < dataframe["ema_21"]
)
# Saída por profit taking
profit_taking = (
dataframe["close"] > dataframe["ema_8"] * 1.04
) | ( # 4% acima da EMA
(dataframe["close"] > dataframe["ema_8"] * 1.025)
& (dataframe["rsi"] > 70)
& (dataframe["volume_ratio"] > 1.5)
)
# Saída por Nadaraya-Watson resistance
nw_resistance = (dataframe["close"] >= dataframe["nw_upper"]) & (
dataframe["rsi"] > 60
)
# Saída por volume anômalo
volume_spike = dataframe["volume_ratio"] > 4.0
# Saída por posição próxima à resistência
resistance_exit = (dataframe["price_position"] > 0.9) & (dataframe["rsi"] > 65)
# Sinal de saída
exit_signal = (
wt_exit
| rsi_exit
| trend_deterioration
| profit_taking
| nw_resistance
| volume_spike
| resistance_exit
)
# Confirmar com volume mínimo
volume_confirm = dataframe["volume_ratio"] > 0.3
dataframe.loc[exit_signal & volume_confirm, "exit_long"] = 1
return dataframe
def custom_stoploss(
self,
pair: str,
trade: "Trade",
current_time: datetime,
current_rate: float,
current_profit: float,
**kwargs
) -> float:
"""
Stop loss dinâmico baseado no ATR e volatilidade
"""
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
if "atr" in last_candle and "trend_strength" in last_candle:
# ATR normalizado
atr_normalized = last_candle["atr"] / current_rate
# Ajustar stop baseado na força da tendência
trend_multiplier = 1.0 + (1.0 - last_candle["trend_strength"])
# Stop loss dinâmico
dynamic_stop = atr_normalized * 2.5 * trend_multiplier
# Limites: mínimo 4%, máximo 12%
dynamic_stop = max(0.04, min(0.12, dynamic_stop))
return -dynamic_stop
# Fallback
return self.stoploss