MetaRegimeV2.1 (Bulletproof Edition) - MTF Simulated natively on 1H chart to prevent NaN dropping - Pure TA-Lib (No pandas-ta dependencies) - Vectorized Scoring Engine
Timeframe
1h
Direction
Long Only
Stoploss
-99.0%
Trailing Stop
No
ROI
N/A
Interface Version
3
Startup Candles
N/A
Indicators
7
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
import pandas as pd
import talib.abstract as ta
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
from datetime import datetime
class MetaRegimeV2(IStrategy):
"""
MetaRegimeV2.1 (Bulletproof Edition)
- MTF Simulated natively on 1H chart to prevent NaN dropping
- Pure TA-Lib (No pandas-ta dependencies)
- Vectorized Scoring Engine
"""
INTERFACE_VERSION = 3
timeframe = '1h'
# برای محاسبه EMA 800 (معادل EMA200 چهار ساعته) به حداقل 900 کندل نیاز داریم
startup_candle_count: int = 900
# --- Hyperopt Parameters ---
adx_threshold = IntParameter(15, 35, default=20, space="buy")
score_threshold = IntParameter(40, 90, default=60, space="buy")
rsi_long = IntParameter(50, 65, default=55, space="buy")
rsi_mr = IntParameter(20, 35, default=30, space="buy")
atr_sl_multiplier = DecimalParameter(1.5, 3.5, default=2.5, space="sell")
risk_per_trade = 0.02
stoploss = -0.99
trailing_stop = False
roi = {"0": 100}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 1. شبیهسازی رژیم 4 ساعته روی چارت 1 ساعته
dataframe['ema800'] = ta.EMA(dataframe, timeperiod=800) # معادل EMA 200 در 4H
dataframe['adx_long'] = ta.ADX(dataframe, timeperiod=56) # معادل ADX 14 در 4H
# 2. اندیکاتورهای سیگنال 1 ساعته
dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
# 3. محاسبه حجم و نوسان
dataframe['vol_sma'] = ta.SMA(dataframe['volume'], timeperiod=20)
dataframe['volume_spike'] = dataframe['volume'] > (dataframe['vol_sma'] * 1.2)
# Bollinger & Donchian
bollinger = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
dataframe['bb_lower'] = bollinger['lowerband']
dataframe['donchian_high'] = dataframe['high'].rolling(20).max().shift(1)
# برای حد ضرر متحرک
dataframe['highest_high'] = dataframe['high'].rolling(20).max()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# --- تشخیص رژیم بازار ---
# فقط وقتی وارد میشویم که EMA800 مقدار گرفته باشد (جلوگیری از باگ NaN)
valid_regime = dataframe['ema800'].notnull()
regime_bull = valid_regime & (dataframe['close'] > dataframe['ema800']) & (dataframe['adx_long'] > self.adx_threshold.value)
regime_range = valid_regime & (dataframe['adx_long'] < 20)
# --- سیستم امتیازدهی (Scoring Engine) ---
dataframe['score'] = 0
# Trend (معادل سوپرترند): 40 امتیاز
dataframe.loc[(dataframe['ema20'] > dataframe['ema50']) & (dataframe['close'] > dataframe['ema20']), 'score'] += 40
# Volume: 20 امتیاز
dataframe.loc[dataframe['volume_spike'] == True, 'score'] += 20
# RSI: 15 امتیاز
dataframe.loc[dataframe['rsi'] > self.rsi_long.value, 'score'] += 15
# Breakout: 25 امتیاز
dataframe.loc[dataframe['close'] > dataframe['donchian_high'], 'score'] += 25
# --- تریگرهای ورود ---
bull_signal = regime_bull & (dataframe['score'] >= self.score_threshold.value)
range_signal = regime_range & (dataframe['close'] < dataframe['bb_lower']) & (dataframe['rsi'] < self.rsi_mr.value)
dataframe.loc[bull_signal | range_signal, 'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# خروج استراکچرال (وقتی روند کاملا برمیگردد)
dataframe.loc[(dataframe['ema20'] < dataframe['ema50']) & (dataframe['close'] < dataframe['ema50']), 'exit_long'] = 1
return dataframe
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
""" ATR-Based Dynamic Stoploss """
try:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
# محاسبه حد ضرر اولیه و متحرک
initial_sl = trade.open_rate - (last_candle['atr'] * self.atr_sl_multiplier.value)
trailing_sl = last_candle['highest_high'] - (last_candle['atr'] * 1.5)
sl_price = max(initial_sl, trailing_sl)
# مطمئن میشویم استاپ لاس بالاتر از قیمت فعلی نباشد تا ربات الکی خارج نشود
if 0 < sl_price < current_rate:
return (current_rate - sl_price) / current_rate
return self.stoploss
except Exception:
return self.stoploss
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, leverage: float, entry_tag: str, side: str, **kwargs) -> float:
""" Risk Engine: Position Size = Risk / Stop Distance """
try:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
stop_distance = last_candle['atr'] * self.atr_sl_multiplier.value
if stop_distance <= 0:
return proposed_stake
risk_amount = max_stake * self.risk_per_trade
target_size_crypto = risk_amount / stop_distance
target_size_usdt = target_size_crypto * current_rate
final_stake = min(target_size_usdt, max_stake)
if min_stake is not None:
final_stake = max(final_stake, min_stake)
return final_stake
except Exception:
return proposed_stake