Trading strategy using a pre-trained XGBoost classifier.
Timeframe
1h
Direction
Long Only
Stoploss
-10.0%
Trailing Stop
No
ROI
0m: 10.0%, 60m: 5.0%, 120m: 2.0%
Interface Version
N/A
Startup Candles
N/A
Indicators
0
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
import pandas as pd
import numpy as np
# import talib # Removed TA-Lib dependency
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, CategoricalParameter
import joblib # For loading the model
import os
# Define thresholds for target variable calculation (used during OFFLINE training)
# These are hyperparameters for the problem definition
TARGET_THRESHOLD_PCT = 0.005 # e.g., 0.5% price movement
LOOKAHEAD_PERIODS = 4 # e.g., predict for 4 candles ahead
class MLXGBoostStrategy(IStrategy):
"""
Trading strategy using a pre-trained XGBoost classifier.
Features are calculated using standard technical indicators.
The target variable (Buy/Sell/Hold based on future price movement)
is calculated OFFLINE during model training.
This strategy LOADS a pre-trained model and uses it for predictions.
"""
# --- Strategy Hyperparameters ---
# Define model path as a parameter for flexibility
model_path = CategoricalParameter(
['user_data/models/xgb_model.joblib', '/path/to/your/model.joblib'],
default='user_data/models/xgb_model.joblib', space='buy'
)
# Add other strategy hyperparameters from EnhancedMAStrategy if needed for reference
# (These are NOT used by the ML model directly but might be useful context or for hybrid approaches)
# --- Removed TA-Lib dependent parameters ---
# fast_ma_period = IntParameter(5, 20, default=9, space='buy')
# medium_ma_period = IntParameter(15, 30, default=21, space='buy')
# slow_ma_period = IntParameter(40, 60, default=50, space='buy')
# adx_period = IntParameter(10, 20, default=14, space='buy')
# adx_threshold = IntParameter(20, 35, default=25, space='buy')
# volume_ma_period = IntParameter(15, 30, default=20, space='buy') # Keeping volume MA - not TA-Lib
# volume_factor = DecimalParameter(1.0, 2.5, default=1.5, space='buy') # Keeping volume factor
# atr_period = IntParameter(10, 20, default=14, space='buy')
# rsi_period = IntParameter(10, 20, default=14, space='buy')
# --- Minimal ROI and Stoploss (Required by Freqtrade) ---
minimal_roi = {
"0": 0.10, # Example: 10% profit after 0 minutes (uses exit signal mainly)
"60": 0.05, # Example: 5% after 1 hour
"120": 0.02 # Example: 2% after 2 hours
}
stoploss = -0.10 # Example: 10% stoploss
# Timeframe needs to be defined
timeframe = '1h' # Example timeframe - should match data used for training
# --- Model Loading ---
_model = None
@property
def model(self):
"""Load the model lazily."""
if self._model is None:
model_file = self.model_path.value
if os.path.exists(model_file):
try:
self._model = joblib.load(model_file)
print(f"Successfully loaded model from {model_file}")
except Exception as e:
print(f"Error loading model from {model_file}: {e}")
# Decide how to handle failure: stop bot, fall back, etc.
# For now, we'll let it fail later during prediction
else:
print(f"Model file not found at {model_file}. Prediction will fail.")
return self._model
# --- Feature Engineering (Indicators) ---
def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
"""
Calculate all technical indicators that will be used as features
for the ML model.
REMOVED TA-LIB INDICATORS.
"""
# --- Basic OHLCV features (Example - Expand this!) ---
dataframe['price_change_pct'] = dataframe['close'].pct_change()
dataframe['high_low_diff'] = dataframe['high'] - dataframe['low']
dataframe['close_open_diff'] = dataframe['close'] - dataframe['open']
# --- Volume features (Not TA-Lib based) ---
dataframe['volume_ma_20'] = dataframe['volume'].rolling(window=20).mean()
dataframe['volume_ratio'] = np.where(
dataframe['volume_ma_20'] > 0, dataframe['volume'] / dataframe['volume_ma_20'], 1
)
# ---- Add more NON-TA-LIB features here! Examples: ----
# Rolling window features
dataframe['rolling_mean_close_5'] = dataframe['close'].rolling(window=5).mean()
dataframe['rolling_std_close_20'] = dataframe['close'].rolling(window=20).std()
# Time-based features
dataframe['hour_of_day'] = dataframe.index.hour
dataframe['day_of_week'] = dataframe.index.dayofweek
return dataframe
# --- Prediction Logic (Buy/Sell Signals) ---
def populate_buy_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
"""
Use the loaded ML model to predict the buy signal.
Target classes (example): 0=Hold, 1=Buy, 2=Sell
"""
dataframe['buy'] = 0 # Default to no buy signal
if self.model is None:
print("Model not loaded, cannot generate buy signals.")
return dataframe
# Prepare features for the model (ensure order and scaling matches training)
# This needs to be robust and match the *exact* preprocessing used during training
# UPDATE THIS LIST TO MATCH populate_indicators (NO TA-LIB)
feature_columns = [
'price_change_pct', 'high_low_diff', 'close_open_diff',
'volume_ma_20', 'volume_ratio',
'rolling_mean_close_5', 'rolling_std_close_20',
'hour_of_day', 'day_of_week'
# Add all features used in training
]
# Check if all required columns exist and handle potential NaNs
if not all(col in dataframe.columns for col in feature_columns):
print(f"Missing required feature columns for prediction: {dataframe.columns.tolist()} vs {feature_columns}")
return dataframe
# Select only the features the model was trained on
features = dataframe[feature_columns]
# Handle any remaining NaNs (e.g., fill with 0 or a mean, MUST match training)
features = features.fillna(0) # Example: Fill NaNs with 0 - ADJUST AS NEEDED
# Predict
try:
predictions = self.model.predict(features) # Use predict_proba for probabilities if needed
# Assuming target 1 means "Buy"
dataframe.loc[predictions == 1, 'buy'] = 1
except Exception as e:
print(f"Error during model prediction (buy): {e}")
# Potentially add error handling or fallback logic
# print(f"Buy signals generated: {dataframe['buy'].sum()}") # Basic logging
return dataframe
def populate_sell_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
"""
Use the loaded ML model to predict the sell signal.
Target classes (example): 0=Hold, 1=Buy, 2=Sell
"""
dataframe['sell'] = 0 # Default to no sell signal
if self.model is None:
print("Model not loaded, cannot generate sell signals.")
return dataframe
# Prepare features (same process as in populate_buy_trend)
# UPDATE THIS LIST TO MATCH populate_indicators (NO TA-LIB)
feature_columns = [
'price_change_pct', 'high_low_diff', 'close_open_diff',
'volume_ma_20', 'volume_ratio',
'rolling_mean_close_5', 'rolling_std_close_20',
'hour_of_day', 'day_of_week'
# Add all features used in training
]
if not all(col in dataframe.columns for col in feature_columns):
print(f"Missing required feature columns for prediction: {dataframe.columns.tolist()} vs {feature_columns}")
return dataframe
features = dataframe[feature_columns].fillna(0) # MUST match training
# Predict
try:
predictions = self.model.predict(features)
# Assuming target 2 means "Sell"
dataframe.loc[predictions == 2, 'sell'] = 1
except Exception as e:
print(f"Error during model prediction (sell): {e}")
# print(f"Sell signals generated: {dataframe['sell'].sum()}") # Basic logging
return dataframe
# --- Optional: Custom Exit Logic (Can be ML-based too) ---
# def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
# current_profit: float, **kwargs):
# """
# Optional: Implement custom exit logic, potentially using another ML model
# or specific conditions based on the entry signal model's prediction confidence.
# """
# # Example: Exit if prediction confidence drops below a threshold
# return None # Return string reason to exit, or None to keep trade