Timeframe
5m
Direction
Long Only
Stoploss
-2.0%
Trailing Stop
Yes
ROI
0m: 2.0%, 60m: 1.5%, 180m: 1.0%, 360m: 0.5%
Interface Version
N/A
Startup Candles
500
Indicators
6
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import numpy as np
import pandas as pd
import talib.abstract as ta
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
import os
class FutureMLV1(IStrategy):
timeframe = '5m'
max_open_trades = 5
stake_amount = 0.20
startup_candle_count = 500
minimal_roi = {
"0": 0.02,
"60": 0.015,
"180": 0.01,
"360": 0.005
}
stoploss = -0.02
trailing_stop = True
trailing_stop_positive = 0.01
trailing_stop_positive_offset = 0.015
trailing_only_offset_is_reached = True
order_types = {
'entry': 'market',
'exit': 'market',
'stoploss': 'market',
'stoploss_on_exchange': False
}
unfilledtimeout = {
'entry': 10,
'exit': 10,
'unit': 'seconds'
}
model_path = '/freqtrade/user_data/ml_models'
confidence_threshold = 0.55
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.model = None
self.scaler = None
self.feature_names = None
os.makedirs(self.model_path, exist_ok=True)
def informative_pairs(self) -> list:
return []
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
df = dataframe.copy()
numeric_cols = ['open', 'high', 'low', 'close', 'volume']
for col in numeric_cols:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
df['rsi'] = ta.RSI(df['close'].values, timeperiod=14)
df['rsi_6'] = ta.RSI(df['close'].values, timeperiod=6)
df['rsi_24'] = ta.RSI(df['close'].values, timeperiod=24)
df['ema_9'] = ta.EMA(df['close'].values, timeperiod=9)
df['ema_21'] = ta.EMA(df['close'].values, timeperiod=21)
df['ema_50'] = ta.EMA(df['close'].values, timeperiod=50)
df['ema_200'] = ta.EMA(df['close'].values, timeperiod=200)
macd = ta.MACD(df['close'].values)
df['macd'] = macd[0]
df['macd_signal'] = macd[1]
df['macd_hist'] = macd[2]
df['bb_upper'] = ta.BBANDS(df['close'].values)[0]
df['bb_middle'] = ta.BBANDS(df['close'].values)[1]
df['bb_lower'] = ta.BBANDS(df['close'].values)[2]
df['atr'] = ta.ATR(df['high'].values, df['low'].values, df['close'].values, timeperiod=14)
df['volume_sma'] = ta.SMA(df['volume'].values, timeperiod=20)
close_arr = df['close'].values
df['momentum'] = close_arr / np.roll(close_arr, 12) - 1
df['momentum_6'] = close_arr / np.roll(close_arr, 6) - 1
df['momentum_3'] = close_arr / np.roll(close_arr, 3) - 1
df['ema_trend'] = (df['ema_9'] - df['ema_50']) / close_arr
df['ema_trend_2'] = (df['ema_21'] - df['ema_50']) / close_arr
df['rsi_trend'] = df['rsi'] - np.roll(df['rsi'], 6)
df['volatility'] = pd.Series(close_arr).pct_change().rolling(12).std().values
df['price_position'] = close_arr / df['ema_200'] - 1
df['candle_range'] = (df['high'] - df['low']) / close_arr
df['return_1'] = pd.Series(close_arr).pct_change(1).values
df['return_3'] = pd.Series(close_arr).pct_change(3).values
df['return_6'] = pd.Series(close_arr).pct_change(6).values
df['volume_ratio'] = df['volume'] / df['volume_sma']
df['bb_width'] = (df['bb_upper'] - df['bb_lower']) / df['bb_middle']
df['bb_position'] = (close_arr - df['bb_lower']) / (df['bb_upper'] - df['bb_lower'])
df['atr_percent'] = df['atr'] / close_arr
return df
def create_features(self, df: DataFrame) -> tuple:
feature_columns = [
'rsi', 'rsi_6', 'rsi_24',
'ema_trend', 'ema_trend_2',
'macd', 'macd_signal', 'macd_hist',
'bb_position', 'bb_width',
'atr_percent',
'volume_ratio',
'momentum', 'momentum_6', 'momentum_3',
'rsi_trend',
'volatility',
'price_position',
'candle_range',
'return_1', 'return_3', 'return_6'
]
available_cols = [c for c in feature_columns if c in df.columns]
df_features = df[available_cols].copy()
df_features = df_features.replace([np.inf, -np.inf], np.nan)
df_features = df_features.fillna(0)
return df_features.values, available_cols
def create_labels(self, df: DataFrame, lookahead: int = 3) -> np.ndarray:
close = df['close'].values
future = close[lookahead:]
current = close[:-lookahead]
returns = (future - current) / current
labels = np.zeros(len(returns))
labels[returns > 0.003] = 1
labels[returns < -0.003] = -1
return labels
def train_model(self, df: DataFrame):
features, feature_names = self.create_features(df)
labels = self.create_labels(df)
valid_idx = np.isfinite(features).all(axis=1) & np.isfinite(labels)
features = features[valid_idx]
labels = labels[valid_idx]
if len(features) < 100:
return False
self.scaler = StandardScaler()
features_scaled = self.scaler.fit_transform(features)
self.model = RandomForestClassifier(
n_estimators=100,
max_depth=10,
min_samples_split=20,
min_samples_leaf=10,
random_state=42,
n_jobs=-1
)
self.model.fit(features_scaled, labels)
self.feature_names = feature_names
return True
def predict(self, df: DataFrame) -> tuple:
if self.model is None:
return 0.5, 0
features, _ = self.create_features(df)
features = features[-1:]
if not np.isfinite(features).all():
return 0.5, 0
try:
features_scaled = self.scaler.transform(features)
except:
return 0.5, 0
try:
proba = self.model.predict_proba(features_scaled)[0]
except:
return 0.5, 0
if len(proba) < 3:
return 0.5, 0
up_prob = proba[2] if len(proba) == 3 else proba[1]
down_prob = proba[0] if len(proba) == 3 else proba[0]
confidence = abs(up_prob - down_prob)
signal = 1 if up_prob > down_prob else -1
return confidence, signal
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['enter_long'] = 0
if len(dataframe) < self.startup_candle_count:
return dataframe
try:
if self.model is None:
df_train = dataframe.iloc[:-10].copy()
self.train_model(df_train)
confidence, signal = self.predict(dataframe)
strong_buy = (confidence > self.confidence_threshold) & (signal == 1)
dataframe.loc[strong_buy, 'enter_long'] = 1
except Exception:
pass
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['exit'] = 0
if len(dataframe) < self.startup_candle_count:
return dataframe
try:
if self.model is None:
return dataframe
confidence, signal = self.predict(dataframe)
strong_sell = (confidence > self.confidence_threshold) & (signal == -1)
dataframe.loc[strong_sell, 'exit'] = 1
if 'rsi' in dataframe.columns:
overbought = dataframe['rsi'] > 80
dataframe.loc[overbought, 'exit'] = 1
except Exception:
pass
return dataframe