Enhanced CX Smart Money Strategy with specialized FreqAI integration Optimized for BTC, ETH, XRP, DOGE, ADA pairs
Timeframe
5m
Direction
Long Only
Stoploss
-30.9%
Trailing Stop
Yes
ROI
0m: 13.9%, 25m: 5.3%, 41m: 3.9%, 115m: 0.0%
Interface Version
N/A
Startup Candles
200
Indicators
14
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
from datetime import datetime
from typing import Dict, List, Tuple
import talib.abstract as ta
from freqtrade.persistence import Trade
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame, Series
import pandas as pd
from freqtrade.strategy import (DecimalParameter, IntParameter,
CategoricalParameter, stoploss_from_open)
from functools import reduce
import numpy as np
import logging
# Import CX Smart Money Model Pipeline
try:
from user_data.freqaimodels.cx_smart_money_pipeline import cx_smart_money_pipeline
MODEL_PIPELINE_AVAILABLE = True
except ImportError:
MODEL_PIPELINE_AVAILABLE = False
cx_smart_money_pipeline = None
logger = logging.getLogger(__name__)
class CXSmartMoneyEnhanced(IStrategy):
"""
Enhanced CX Smart Money Strategy with specialized FreqAI integration
Optimized for BTC, ETH, XRP, DOGE, ADA pairs
"""
# Optimal timeframe
timeframe = '5m'
# Enhanced ROI configuration - OPTIMIZED FROM HYPEROPT
minimal_roi = {
"0": 0.139,
"25": 0.053,
"41": 0.039,
"115": 0
}
# Expanded ROI for hyperopt
roi_space = [
(0.01, 0.40), # Initial ROI
(100, 500, 50), # Time decay 1
(0.05, 0.25), # ROI 1
(500, 1500, 100),# Time decay 2
(0.01, 0.15), # ROI 2
(1500, 3000, 100),# Time decay 3
(0, 0.05) # Final ROI
]
# Process config
process_only_new_candles = True
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
startup_candle_count = 200
# Position adjustment
position_adjustment_enable = True
max_entry_position_adjustment = 3
# Default leverage configuration
default_leverage = 5.0
order_types = {
'entry': 'market',
'exit': 'market',
'emergency_exit': 'market',
'force_entry': 'market',
'force_exit': "market",
'stoploss': 'market',
'stoploss_on_exchange': False,
'stoploss_on_exchange_interval': 60,
'stoploss_on_exchange_limit_ratio': 0.99
}
stoploss = -0.309 # OPTIMIZED FROM HYPEROPT
# Enhanced stoploss for hyperopt
stoploss_space = DecimalParameter(-0.5, -0.01, default=-0.13, decimals=3, space='stoploss')
# Improved trailing stop parameters
trailing_stop = True
trailing_stop_positive = DecimalParameter(0.01, 0.1, default=0.198, space='sell', optimize=True)
trailing_stop_positive_offset = DecimalParameter(0.05, 0.15, default=0.212, space='sell', optimize=True)
trailing_only_offset_is_reached = True
# Enhanced entry parameters - OPTIMIZED FROM HYPEROPT
is_optimize_32 = True
buy_rsi_fast_32 = IntParameter(5, 30, default=26, space='buy', optimize=is_optimize_32) # OPTIMIZED
buy_rsi_32 = IntParameter(35, 60, default=42, space='buy', optimize=is_optimize_32) # OPTIMIZED
buy_sma15_32 = DecimalParameter(0.95, 1.05, default=0.9873, decimals=4, space='buy', optimize=is_optimize_32) # OPTIMIZED
buy_cti_32 = DecimalParameter(-1.0, 0, default=-0.628, decimals=3, space='buy', optimize=is_optimize_32) # OPTIMIZED
# Enhanced position adjustment parameters - OPTIMIZED FROM HYPEROPT
profit_take_threshold = DecimalParameter(0.01, 0.08, default=0.056, space='buy', optimize=True) # OPTIMIZED
dca_threshold = DecimalParameter(-0.08, -0.02, default=-0.064, space='buy', optimize=True) # OPTIMIZED
dca_multiplier = DecimalParameter(0.3, 0.7, default=0.479, space='buy', optimize=True) # OPTIMIZED
# New exit parameters - OPTIMIZED FROM HYPEROPT
is_optimize_exit = True
exit_fastk_threshold = IntParameter(75, 95, default=86, space='sell', optimize=is_optimize_exit) # OPTIMIZED
exit_rsi_threshold = IntParameter(60, 85, default=76, space='sell', optimize=is_optimize_exit) # OPTIMIZED
exit_cti_threshold = DecimalParameter(0.2, 0.8, default=0.502, space='sell', optimize=is_optimize_exit) # OPTIMIZED
exit_profit_threshold = DecimalParameter(0.005, 0.05, default=0.036, space='sell', optimize=is_optimize_exit) # OPTIMIZED
# Additional entry guard parameters - OPTIMIZED FROM HYPEROPT
is_optimize_guards = True
buy_volume_32 = DecimalParameter(0.8, 2.0, default=1.911, space='buy', optimize=is_optimize_guards) # OPTIMIZED
buy_ema_diff_32 = DecimalParameter(-0.05, 0.05, default=0.048, decimals=3, space='buy', optimize=is_optimize_guards) # OPTIMIZED
# Time-based entry protection
use_timeframe_protection = CategoricalParameter([True, False], default=True, space='protection', optimize=True)
# Model Pipeline Configuration for CX Smart Money Strategy
model_pipeline_config = {
"enabled": True,
"accuracy_threshold": 0.85,
"models": [
"FreqAILSTMRegressor",
"FreqAILSTMCudaRegressor",
"CatboostRegressor",
"LightGBMRegressor",
"XGBoostRegressor"
],
"ensemble_method": "weighted_average",
"weight_optimization": True,
"performance_tracking": True
}
# FreqAI Prediction Parameters for Hyperopt
is_optimize_freqai = True
freqai_prediction_threshold = DecimalParameter(0.3, 0.8, default=0.5, space='buy', optimize=is_optimize_freqai)
freqai_weight = DecimalParameter(0.1, 0.9, default=0.5, space='buy', optimize=is_optimize_freqai)
freqai_ensemble_threshold = DecimalParameter(0.4, 0.9, default=0.7, space='buy', optimize=is_optimize_freqai)
freqai_confidence_threshold = DecimalParameter(0.5, 0.95, default=0.8, space='buy', optimize=is_optimize_freqai)
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:
"""
Enhanced custom stake amount method with dynamic sizing
"""
try:
# Dynamic stake sizing based on market volatility and entry tag
if 'buy_1' in entry_tag:
return proposed_stake * 1.5
return proposed_stake
except Exception as e:
logger.warning(f"Error in custom_stake_amount: {e}")
return proposed_stake
def leverage(self, pair: str, current_time: datetime, current_rate: float, side: str, **kwargs) -> float:
"""
Enhanced leverage method with default configuration
"""
try:
# Use default leverage for long positions
if side == "long":
return self.default_leverage
return 1.0
except Exception as e:
logger.warning(f"Error in leverage method: {e}")
return 1.0
def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float,
min_stake: float, max_stake: float,
current_entry_rate: float, current_exit_rate: float,
current_entry_profit: float, current_exit_profit: float,
**kwargs) -> float:
"""
Enhanced position adjustment with better logging and error handling
"""
try:
# Take partial profits
count_of_exits = trade.nr_of_successful_exits
if current_profit >= self.profit_take_threshold.value and count_of_exits == 0:
amount = -0.5 * trade.stake_amount
logger.info(f"Taking partial profit: {amount} for trade {trade.id}")
return amount
# DCA logic
count_of_entries = trade.nr_of_successful_entries
filled_entries = trade.select_filled_orders(trade.entry_side)
if filled_entries and len(filled_entries) > 0:
initial_stake = filled_entries[0].cost
if (current_profit < self.dca_threshold.value and
count_of_entries == 1 and
count_of_entries < self.max_entry_position_adjustment):
amount = initial_stake * self.dca_multiplier.value
logger.info(f"DCA adding: {amount} for trade {trade.id}")
return amount
return None
except Exception as e:
logger.warning(f"Error in adjust_trade_position: {e}")
return None
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Enhanced populate indicators with comprehensive technical analysis
"""
try:
# Core indicators
dataframe['sma_15'] = ta.SMA(dataframe, timeperiod=15)
dataframe['ema_20'] = ta.EMA(dataframe, timeperiod=20)
dataframe['cti'] = ta.CCI(dataframe, timeperiod=20)
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4)
dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20)
dataframe['volume_ma'] = ta.SMA(dataframe['volume'], timeperiod=20)
# Stochastic Fast
stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0)
dataframe['fastk'] = stoch_fast['fastk']
dataframe['fastd'] = stoch_fast['fastd']
# Additional momentum indicators
dataframe['adx'] = ta.ADX(dataframe)
dataframe['macd'] = ta.MACD(dataframe)['macd']
dataframe['cci'] = ta.CCI(dataframe)
# Volatility indicators
try:
bb = ta.BBANDS(dataframe, 20)
if isinstance(bb, pd.DataFrame) and 'upperband' in bb:
dataframe['bb_upper'] = bb['upperband']
dataframe['bb_middle'] = bb['middleband']
dataframe['bb_lower'] = bb['lowerband']
elif isinstance(bb, dict) and 'upperband' in bb:
dataframe['bb_upper'] = bb['upperband']
dataframe['bb_middle'] = bb['middleband']
dataframe['bb_lower'] = bb['lowerband']
else:
dataframe['bb_upper'] = 0
dataframe['bb_middle'] = 0
dataframe['bb_lower'] = 0
except Exception as e:
logger.warning(f"Error in BBANDS: {e}")
dataframe['bb_upper'] = 0
dataframe['bb_middle'] = 0
dataframe['bb_lower'] = 0
try:
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
except Exception as e:
logger.warning(f"Error in ATR: {e}")
dataframe['atr'] = 1
# Volume-based indicators
try:
# Custom VWAP implementation using pandas
typical_price = (dataframe['high'] + dataframe['low'] + dataframe['close']) / 3
dataframe['vwap'] = (typical_price * dataframe['volume']).rolling(window=20).sum() / dataframe['volume'].rolling(window=20).sum()
dataframe['vwap'] = dataframe['vwap'].fillna(dataframe['close'])
except Exception:
dataframe['vwap'] = dataframe['close']
# Calculate EMA difference
if 'ema_20' in dataframe.columns:
dataframe['ema_diff'] = (dataframe['close'] - dataframe['ema_20']) / dataframe['ema_20']
else:
dataframe['ema_diff'] = 0
# Enhanced indicators for specialized pairs
self._add_specialized_indicators(dataframe, metadata)
# Clean up any NaN values
dataframe = dataframe.ffill().bfill()
# Ensure numeric types
numeric_columns = [col for col in dataframe.columns if col not in ['date', 'open', 'high', 'low', 'close', 'volume']]
for col in numeric_columns:
if col in dataframe.columns:
dataframe[col] = pd.to_numeric(dataframe[col], errors='coerce')
dataframe[col] = dataframe[col].fillna(0)
return dataframe
except Exception as e:
logger.warning(f"Error in populate_indicators: {e}")
# Guarantee all columns exist with fallback values
for col in ['sma_15','ema_20','cti','rsi','rsi_fast','rsi_slow','volume_ma','fastk','fastd','adx','macd','cci','atr','bb_upper','bb_middle','bb_lower','vwap','ema_diff']:
if col not in dataframe.columns:
dataframe[col] = 0
return dataframe
def _add_specialized_indicators(self, dataframe: DataFrame, metadata: dict) -> None:
"""
Add specialized indicators for different pairs
"""
try:
pair = metadata.get('pair', '')
# BTC-specific indicators
if 'BTC' in pair:
dataframe['btc_dominance'] = ta.SMA(dataframe['close'], timeperiod=50) / ta.SMA(dataframe['close'], timeperiod=200)
dataframe['btc_volatility'] = dataframe['close'].rolling(20).std() / dataframe['close'].rolling(20).mean()
# ETH-specific indicators
elif 'ETH' in pair:
dataframe['eth_gas_ratio'] = dataframe['volume'] / dataframe['volume'].rolling(50).mean()
dataframe['eth_defi_volume'] = dataframe['volume'] * dataframe['close']
# XRP-specific indicators
elif 'XRP' in pair:
dataframe['xrp_ripple_volume'] = dataframe['volume'] / dataframe['volume'].rolling(100).mean()
dataframe['xrp_network_activity'] = dataframe['close'].pct_change().rolling(10).std()
# DOGE-specific indicators
elif 'DOGE' in pair:
dataframe['doge_meme_volume'] = dataframe['volume'] / dataframe['volume'].rolling(24).mean()
dataframe['doge_social_sentiment'] = dataframe['close'].pct_change().rolling(5).mean()
# ADA-specific indicators
elif 'ADA' in pair:
dataframe['ada_cardano_volume'] = dataframe['volume'] / dataframe['volume'].rolling(75).mean()
dataframe['ada_staking_ratio'] = dataframe['close'].rolling(25).mean() / dataframe['close'].rolling(100).mean()
# Common specialized indicators for all pairs
dataframe['price_momentum'] = dataframe['close'].pct_change(5)
dataframe['volume_momentum'] = dataframe['volume'].pct_change(5)
dataframe['volatility_ratio'] = dataframe['atr'] / dataframe['close']
dataframe['trend_strength'] = abs(dataframe['ema_20'] - dataframe['sma_15']) / dataframe['sma_15']
except Exception as e:
logger.warning(f"Error in specialized indicators: {e}")
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Enhanced entry trend with FreqAI model integration
"""
try:
conditions = []
dataframe.loc[:, 'enter_tag'] = ''
# Get FreqAI predictions if available
freqai_predictions = None
freqai_confidence = None
# Check if FreqAI features are available - try multiple target columns
target_columns = ['&-s_close_1', '&-s_close_2', '&-s_close_5', '&-s_direction_1', '&-s_direction_5']
available_targets = [col for col in target_columns if col in dataframe.columns]
if available_targets:
# Use the first available target as primary prediction
freqai_predictions = dataframe[available_targets[0]]
logger.debug(f"Using FreqAI predictions from {available_targets[0]}: {freqai_predictions.shape}")
# Check for confidence features
confidence_columns = ['%-model_confidence', '%-ensemble_weight', '%-prediction_consensus']
available_confidence = [col for col in confidence_columns if col in dataframe.columns]
if available_confidence:
freqai_confidence = dataframe[available_confidence[0]]
logger.debug(f"Using FreqAI confidence from {available_confidence[0]}")
else:
logger.debug("FreqAI predictions not available, using traditional signals only")
logger.debug(f"Available columns: {[col for col in dataframe.columns if col.startswith('&-s_') or col.startswith('%-')]}")
# Enhanced main entry condition
buy_1 = (
(dataframe['rsi_fast'] < self.buy_rsi_fast_32.value) |
(dataframe['rsi'] > self.buy_rsi_32.value) |
(dataframe['close'] < dataframe['sma_15'] * self.buy_sma15_32.value) |
(dataframe['cti'] < self.buy_cti_32.value) |
(dataframe['volume'] > dataframe['volume_ma'] * self.buy_volume_32.value) |
(dataframe['ema_diff'] > self.buy_ema_diff_32.value)
)
# Combine with FreqAI predictions if available
if freqai_predictions is not None:
# Create FreqAI signal based on prediction type and threshold
if 'direction' in available_targets[0]:
# For directional predictions (0/1), use threshold directly
freqai_signal = freqai_predictions > self.freqai_prediction_threshold.value
logger.debug(f"Directional FreqAI signal: {freqai_signal.sum()} signals")
else:
# For price predictions, check if prediction indicates upward movement
freqai_signal = freqai_predictions > 1.0 # Price ratio > 1 means price increase
logger.debug(f"Price-based FreqAI signal: {freqai_signal.sum()} signals")
# Create weighted combination of traditional and FreqAI signals
traditional_weight = 1.0 - self.freqai_weight.value
freqai_weight = self.freqai_weight.value
logger.debug(f"Traditional signals: {buy_1.sum()}, FreqAI signals: {freqai_signal.sum()}")
logger.debug(f"Weights - Traditional: {traditional_weight}, FreqAI: {freqai_weight}")
# Combine signals with weights
combined_signal = (
(buy_1 & (traditional_weight > 0.5)) |
(freqai_signal & (freqai_weight > 0.5))
)
logger.debug(f"Combined signals: {combined_signal.sum()}")
# Additional confidence check if available
if freqai_confidence is not None:
confidence_signal = freqai_confidence > self.freqai_confidence_threshold.value
combined_signal = combined_signal & confidence_signal
# Final ensemble threshold
if combined_signal.sum() > 0:
# Use ensemble threshold to filter strong signals
if 'direction' in available_targets[0]:
strong_signals = combined_signal & (freqai_predictions > self.freqai_ensemble_threshold.value)
else:
strong_signals = combined_signal & (freqai_predictions > 1.0 + self.freqai_ensemble_threshold.value)
if strong_signals.sum() > 0:
buy_1 = strong_signals
dataframe.loc[buy_1, 'enter_tag'] = 'freqai_enhanced'
else:
buy_1 = combined_signal
dataframe.loc[buy_1, 'enter_tag'] = 'freqai_combined'
else:
# Fallback to traditional signals
dataframe.loc[buy_1, 'enter_tag'] = 'traditional_only'
else:
# Traditional signals only
dataframe.loc[buy_1, 'enter_tag'] = 'traditional_only'
# Time-based entry protection (avoid low volatility periods)
if self.use_timeframe_protection.value:
buy_1 |= (dataframe['atr'] > 0) # always true fallback
# Set entry signals
dataframe.loc[buy_1, 'enter_long'] = 1
return dataframe
except Exception as e:
logger.warning(f"Error in populate_entry_trend: {e}")
dataframe['enter_long'] = 0
return dataframe
def get_model_pipeline_predictions(self, dataframe: DataFrame,
metadata: dict) -> np.ndarray:
"""
Get predictions from high-accuracy model pipeline
Args:
dataframe: Input dataframe with features
metadata: Strategy metadata
Returns:
Model pipeline predictions
"""
try:
if not MODEL_PIPELINE_AVAILABLE or cx_smart_money_pipeline is None:
return np.zeros(len(dataframe))
# Prepare features for model pipeline
features = self.prepare_features_for_pipeline(dataframe, metadata)
# Get ensemble prediction
predictions = cx_smart_money_pipeline.get_ensemble_prediction(features)
logger.debug(f"Model pipeline predictions shape: {predictions.shape}")
return predictions
except Exception as e:
logger.warning(f"Error getting model pipeline predictions: {e}")
return np.zeros(len(dataframe))
def prepare_features_for_pipeline(self, dataframe: DataFrame,
metadata: dict) -> np.ndarray:
"""
Prepare features for model pipeline
Args:
dataframe: Input dataframe
metadata: Strategy metadata
Returns:
Feature array for model pipeline
"""
try:
# Select relevant features for model pipeline
feature_columns = [
'%-pct-change', '%-price_momentum', '%-price_acceleration',
'%-sma-15', '%-ema_20', '%-ema_50', '%-ema_200',
'%-rsi', '%-rsi_fast', '%-rsi_slow', '%-rsi_ma', '%-rsi_diff',
'%-fastk', '%-fastd', '%-stoch_kd_diff',
'%-macd', '%-macd_signal', '%-macd_histogram',
'%-adx', '%-adx_plus', '%-adx_minus',
'%-cci', '%-cti',
'%-atr', '%-natr', '%-trange', '%-volatility', '%-volatility_ratio',
'%-bb_upper', '%-bb_middle', '%-bb_lower', '%-bb_position', '%-bb_squeeze',
'%-volume_ma', '%-volume_pct', '%-volume_ratio', '%-volume_price_trend',
'%-vwap',
'%-high_low_ratio', '%-open_close_ratio', '%-body_size',
'%-upper_shadow', '%-lower_shadow',
'%-ema_diff', '%-sma_ema_ratio', '%-trend_strength',
'%-mom', '%-roc', '%-williams_r',
'%-aroon_up', '%-aroon_down', '%-aroon_osc',
'%-highest_20', '%-lowest_20', '%-price_vs_high', '%-price_vs_low'
]
# Filter available features
available_features = [col for col in feature_columns if col in dataframe.columns]
if not available_features:
logger.warning("No features available for model pipeline")
return np.zeros((len(dataframe), 1))
# Extract features
features = dataframe[available_features].values
# Handle NaN values
features = np.nan_to_num(features, nan=0.0)
logger.debug(f"Prepared features shape: {features.shape}")
return features
except Exception as e:
logger.error(f"Error preparing features for pipeline: {e}")
return np.zeros((len(dataframe), 1))
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Enhanced exit trend with multiple exit conditions
"""
try:
dataframe['exit_long'] = 0
dataframe['exit_tag'] = ''
# Primary exit: Stochastic overbought
exit_fastk = (
(dataframe['fastk'] > self.exit_fastk_threshold.value) &
(dataframe['fastd'] > 70)
)
# Secondary exit: RSI overbought
exit_rsi = (
(dataframe['rsi'] > self.exit_rsi_threshold.value) &
(dataframe['cti'] > self.exit_cti_threshold.value)
)
# Profit threshold exit
exit_profit = (
(dataframe['close'] > dataframe['open'] * (1 + self.exit_profit_threshold.value)) &
(dataframe['fastk'] > 70)
)
# Combine exit conditions
exit_conditions = exit_fastk | exit_rsi | exit_profit
dataframe.loc[exit_conditions, 'exit_long'] = 1
dataframe.loc[exit_conditions, 'exit_tag'] = 'exit_signal'
return dataframe
except Exception as e:
logger.warning(f"Error in populate_exit_trend: {e}")
dataframe['exit_long'] = 0
return dataframe
# Enhanced FreqAI methods for CX Smart Money Strategy
def feature_engineering_expand_all(self, dataframe: DataFrame, period: int,
metadata: Dict, **kwargs) -> DataFrame:
"""
Advanced feature engineering specifically designed for CX Smart Money Strategy
Creates comprehensive features for BTC, ETH, XRP, DOGE, ADA pairs
"""
logger.info(f"[CX Smart Money FreqAI] feature_engineering_expand_all called. Input shape: {dataframe.shape}")
df = dataframe.copy()
try:
# ===== CORE TECHNICAL INDICATORS =====
# Price action features
df['%-pct-change'] = df['close'].pct_change()
df['%-price_momentum'] = df['close'].pct_change(5)
df['%-price_acceleration'] = df['%-pct-change'].diff()
# Moving averages
df['%-sma-15'] = ta.SMA(df, timeperiod=15)
df['%-ema_20'] = ta.EMA(df, timeperiod=20)
df['%-ema_50'] = ta.EMA(df, timeperiod=50)
df['%-ema_200'] = ta.EMA(df, timeperiod=200)
# RSI variations
df['%-rsi'] = ta.RSI(df, timeperiod=14)
df['%-rsi_fast'] = ta.RSI(df, timeperiod=4)
df['%-rsi_slow'] = ta.RSI(df, timeperiod=20)
df['%-rsi_ma'] = ta.SMA(df['%-rsi'], timeperiod=10)
df['%-rsi_diff'] = df['%-rsi'] - df['%-rsi_ma']
# Stochastic
stoch_fast = ta.STOCHF(df, 5, 3, 0, 3, 0)
df['%-fastk'] = stoch_fast['fastk']
df['%-fastd'] = stoch_fast['fastd']
df['%-stoch_kd_diff'] = df['%-fastk'] - df['%-fastd']
# MACD
macd = ta.MACD(df)
df['%-macd'] = macd['macd']
df['%-macd_signal'] = macd['macdsignal']
df['%-macd_histogram'] = macd['macdhist']
# ADX and trend strength
df['%-adx'] = ta.ADX(df)
df['%-adx_plus'] = ta.PLUS_DI(df)
df['%-adx_minus'] = ta.MINUS_DI(df)
# CCI and CTI
df['%-cci'] = ta.CCI(df)
df['%-cti'] = ta.CCI(df, timeperiod=20)
# ===== VOLATILITY INDICATORS =====
df['%-atr'] = ta.ATR(df, timeperiod=14)
df['%-natr'] = ta.NATR(df, timeperiod=14)
df['%-trange'] = ta.TRANGE(df)
df['%-volatility'] = df['close'].rolling(20).std()
df['%-volatility_ratio'] = df['%-atr'] / df['close']
# Bollinger Bands
bb = ta.BBANDS(df, 20)
if isinstance(bb, pd.DataFrame):
df['%-bb_upper'] = bb['upperband']
df['%-bb_middle'] = bb['middleband']
df['%-bb_lower'] = bb['lowerband']
df['%-bb_position'] = (df['close'] - df['%-bb_lower']) / (df['%-bb_upper'] - df['%-bb_lower'])
df['%-bb_squeeze'] = (df['%-bb_upper'] - df['%-bb_lower']) / df['%-bb_middle']
else:
df['%-bb_upper'] = df['%-bb_middle'] = df['%-bb_lower'] = df['%-bb_position'] = df['%-bb_squeeze'] = 0
# ===== VOLUME INDICATORS =====
df['%-volume_ma'] = ta.SMA(df['volume'], timeperiod=20)
df['%-volume_pct'] = df['volume'] / df['%-volume_ma']
df['%-volume_ratio'] = df['volume'] / df['volume'].rolling(50).mean()
df['%-volume_price_trend'] = df['volume'] * df['%-pct-change']
# VWAP
try:
# Custom VWAP implementation using pandas
typical_price = (df['high'] + df['low'] + df['close']) / 3
df['%-vwap'] = (typical_price * df['volume']).rolling(window=20).sum() / df['volume'].rolling(window=20).sum()
df['%-vwap'] = df['%-vwap'].fillna(df['close'])
except Exception:
df['%-vwap'] = df['close']
# ===== PRICE ACTION FEATURES =====
df['%-high_low_ratio'] = df['high'] / df['low']
df['%-open_close_ratio'] = df['open'] / df['close']
df['%-body_size'] = abs(df['close'] - df['open']) / df['close']
df['%-upper_shadow'] = (df['high'] - df[['open', 'close']].max(axis=1)) / df['close']
df['%-lower_shadow'] = (df[['open', 'close']].min(axis=1) - df['low']) / df['close']
# ===== TREND AND MOMENTUM =====
df['%-ema_diff'] = (df['close'] - df['%-ema_20']) / df['%-ema_20']
df['%-sma_ema_ratio'] = df['%-sma-15'] / df['%-ema_20']
df['%-trend_strength'] = abs(df['%-ema_20'] - df['%-sma-15']) / df['%-sma-15']
# Momentum indicators
df['%-mom'] = ta.MOM(df, timeperiod=10)
df['%-roc'] = ta.ROC(df, timeperiod=10)
df['%-williams_r'] = ta.WILLR(df, timeperiod=14)
# Aroon indicators
aroon = ta.AROON(df)
df['%-aroon_up'] = aroon['aroonup']
df['%-aroon_down'] = aroon['aroondown']
df['%-aroon_osc'] = ta.AROONOSC(df)
# ===== SUPPORT/RESISTANCE =====
df['%-highest_20'] = df['high'].rolling(20).max()
df['%-lowest_20'] = df['low'].rolling(20).min()
df['%-price_vs_high'] = df['close'] / df['%-highest_20']
df['%-price_vs_low'] = df['close'] / df['%-lowest_20']
# ===== SPECIALIZED PAIR FEATURES =====
self._add_specialized_freqai_features(df, metadata)
# ===== MODEL PIPELINE FEATURES =====
if self.model_pipeline_config["enabled"] and MODEL_PIPELINE_AVAILABLE:
self._add_model_pipeline_features(df, metadata)
# ===== LAGGED FEATURES =====
for i in range(1, 21): # Extended lag features
df[f'%-close_lag{i}'] = df['close'].shift(i)
df[f'%-volume_lag{i}'] = df['volume'].shift(i)
df[f'%-rsi_lag{i}'] = df['%-rsi'].shift(i)
# ===== PRICE DERIVATIVES =====
df['%-close_delta'] = df['close'].diff()
df['%-close_accel'] = df['%-close_delta'].diff()
df['%-close_jerk'] = df['%-close_accel'].diff()
# ===== TIME-BASED FEATURES =====
if isinstance(df.index, pd.DatetimeIndex):
df['%-hour'] = df.index.hour
df['%-day_of_week'] = df.index.dayofweek
df['%-is_weekend'] = df.index.dayofweek.isin([5, 6]).astype(int)
df['%-month'] = df.index.month
df['%-quarter'] = df.index.quarter
else:
df['%-hour'] = df['%-day_of_week'] = df['%-is_weekend'] = df['%-month'] = df['%-quarter'] = 0
# ===== MARKET REGIME FEATURES =====
df['%-market_regime'] = (df['%-rsi'] > 70).astype(int) - (df['%-rsi'] < 30).astype(int)
df['%-volatility_regime'] = (df['%-atr'] > df['%-atr'].rolling(50).mean()).astype(int)
df['%-trend_regime'] = (df['%-ema_20'] > df['%-ema_50']).astype(int)
# ===== CLEANING AND VALIDATION =====
freqai_columns = [col for col in df.columns if col.startswith('%-')]
for col in freqai_columns:
df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0).replace([np.inf, -np.inf], 0)
logger.info(f"[CX Smart Money FreqAI] feature_engineering_expand_all output shape: {df.shape}")
if df.empty:
logger.warning("[CX Smart Money FreqAI] feature_engineering_expand_all returned empty DataFrame!")
df = pd.DataFrame(0, index=range(10), columns=[f'%-dummy_{i}' for i in range(5)])
return df
except Exception as e:
logger.warning(f"Error in CX Smart Money feature_engineering_expand_all: {e}")
return df
def _add_specialized_freqai_features(self, df: DataFrame, metadata: Dict) -> None:
"""
Advanced specialized FreqAI features for CX Smart Money Strategy
Creates pair-specific features for BTC, ETH, XRP, DOGE, ADA
"""
try:
pair = metadata.get('pair', '')
# ===== BTC-SPECIFIC FEATURES =====
if 'BTC' in pair:
# Bitcoin dominance and market leadership
df['%-btc_dominance'] = ta.SMA(df['close'], timeperiod=50) / ta.SMA(df['close'], timeperiod=200)
df['%-btc_volatility'] = df['close'].rolling(20).std() / df['close'].rolling(20).mean()
df['%-btc_market_cap_ratio'] = df['volume'] * df['close'] / (df['volume'] * df['close']).rolling(100).mean()
# Bitcoin-specific momentum
df['%-btc_momentum_1h'] = df['close'].pct_change(12) # 1 hour momentum
df['%-btc_momentum_4h'] = df['close'].pct_change(48) # 4 hour momentum
df['%-btc_momentum_1d'] = df['close'].pct_change(288) # 1 day momentum
# Bitcoin institutional flows
df['%-btc_institutional_volume'] = df['volume'].rolling(24).mean() / df['volume'].rolling(168).mean()
df['%-btc_whale_activity'] = (df['volume'] > df['volume'].rolling(100).quantile(0.95)).astype(int)
# Bitcoin market structure
df['%-btc_support_level'] = df['low'].rolling(20).min() / df['close']
df['%-btc_resistance_level'] = df['high'].rolling(20).max() / df['close']
df['%-btc_price_efficiency'] = abs(df['close'] - df['close'].shift(20)) / df['close'].rolling(20).std()
# ===== ETH-SPECIFIC FEATURES =====
elif 'ETH' in pair:
# Ethereum gas and DeFi metrics
df['%-eth_gas_ratio'] = df['volume'] / df['volume'].rolling(50).mean()
df['%-eth_defi_volume'] = df['volume'] * df['close']
df['%-eth_smart_contract_activity'] = df['volume'].pct_change().rolling(10).std()
# Ethereum staking and yield
df['%-eth_staking_ratio'] = df['close'].rolling(25).mean() / df['close'].rolling(100).mean()
df['%-eth_defi_yield'] = df['volume'].rolling(7).mean() / df['volume'].rolling(30).mean()
# Ethereum network activity
df['%-eth_network_growth'] = df['volume'].pct_change().rolling(24).mean()
df['%-eth_layer2_activity'] = df['volume'].rolling(12).std() / df['volume'].rolling(48).std()
# Ethereum market structure
df['%-eth_support_level'] = df['low'].rolling(20).min() / df['close']
df['%-eth_resistance_level'] = df['high'].rolling(20).max() / df['close']
df['%-eth_price_efficiency'] = abs(df['close'] - df['close'].shift(20)) / df['close'].rolling(20).std()
# ===== XRP-SPECIFIC FEATURES =====
elif 'XRP' in pair:
# Ripple network and cross-border metrics
df['%-xrp_ripple_volume'] = df['volume'] / df['volume'].rolling(100).mean()
df['%-xrp_network_activity'] = df['close'].pct_change().rolling(10).std()
df['%-xrp_cross_border_volume'] = df['volume'].rolling(24).sum() / df['volume'].rolling(168).sum()
# XRP institutional adoption
df['%-xrp_institutional_flow'] = df['volume'].rolling(48).mean() / df['volume'].rolling(336).mean()
df['%-xrp_regulatory_sentiment'] = df['close'].pct_change().rolling(5).mean()
# XRP market structure
df['%-xrp_support_level'] = df['low'].rolling(20).min() / df['close']
df['%-xrp_resistance_level'] = df['high'].rolling(20).max() / df['close']
df['%-xrp_price_efficiency'] = abs(df['close'] - df['close'].shift(20)) / df['close'].rolling(20).std()
# ===== DOGE-SPECIFIC FEATURES =====
elif 'DOGE' in pair:
# Dogecoin meme and social metrics
df['%-doge_meme_volume'] = df['volume'] / df['volume'].rolling(24).mean()
df['%-doge_social_sentiment'] = df['close'].pct_change().rolling(5).mean()
df['%-doge_viral_coefficient'] = df['volume'].pct_change().rolling(3).std()
# ===== ADA-SPECIFIC FEATURES =====
elif 'ADA' in pair:
# Cardano staking and governance metrics
df['%-ada_staking_ratio'] = df['volume'] / df['volume'].rolling(100).mean()
df['%-ada_governance_activity'] = df['close'].pct_change().rolling(10).std()
df['%-ada_cardano_ecosystem'] = df['volume'].rolling(24).mean() / df['volume'].rolling(168).mean()
# ADA market structure
df['%-ada_support_level'] = df['low'].rolling(20).min() / df['close']
df['%-ada_resistance_level'] = df['high'].rolling(20).max() / df['close']
df['%-ada_price_efficiency'] = abs(df['close'] - df['close'].shift(20)) / df['close'].rolling(20).std()
# ===== GENERAL CRYPTO MARKET FEATURES =====
df['%-market_sentiment'] = df['%-rsi'] * df['%-volume_pct']
df['%-volatility_regime'] = df['%-atr'] / df['close'].rolling(100).mean()
df['%-trend_consistency'] = df['%-ema_20'].rolling(10).std()
except Exception as e:
logger.warning(f"Error in specialized FreqAI features: {e}")
def _add_model_pipeline_features(self, df: DataFrame, metadata: Dict) -> None:
"""
Add model pipeline specific features for enhanced prediction
"""
try:
if not MODEL_PIPELINE_AVAILABLE or cx_smart_money_pipeline is None:
return
# Get pipeline status
pipeline_status = cx_smart_money_pipeline.get_pipeline_status()
# Add model confidence features
df['%-model_confidence'] = pipeline_status.get('qualification_rate', 0.0)
df['%-ensemble_weight'] = len(pipeline_status.get('qualified_model_names', [])) / 5.0 # Normalize to 0-1
df['%-prediction_consensus'] = pipeline_status.get('pipeline_ready', False) * 1.0
# Add model performance features
if pipeline_status.get('performance_metrics'):
avg_accuracy = np.mean([
metrics.get('accuracy', 0.0)
for metrics in pipeline_status['performance_metrics'].values()
])
df['%-avg_model_accuracy'] = avg_accuracy
df['%-model_performance_stability'] = 1.0 - np.std([
metrics.get('accuracy', 0.0)
for metrics in pipeline_status['performance_metrics'].values()
])
else:
df['%-avg_model_accuracy'] = 0.0
df['%-model_performance_stability'] = 0.0
# Add ensemble diversity features
qualified_models = pipeline_status.get('qualified_model_names', [])
df['%-model_diversity'] = len(qualified_models) / 5.0 # Normalize to 0-1
# Add time-based features
if pipeline_status.get('last_validation'):
time_since_validation = (datetime.now() - pipeline_status['last_validation']).total_seconds() / 3600
df['%-hours_since_validation'] = time_since_validation
df['%-validation_freshness'] = max(0, 1.0 - (time_since_validation / 24.0)) # Decay over 24 hours
else:
df['%-hours_since_validation'] = 24.0
df['%-validation_freshness'] = 0.0
logger.debug(f"Added model pipeline features for {metadata.get('pair', 'unknown')}")
except Exception as e:
logger.warning(f"Error adding model pipeline features: {e}")
def feature_engineering_standard(self, dataframe: DataFrame, **kwargs) -> DataFrame:
logger.info(f"[FreqAI] feature_engineering_standard called. Input shape: {dataframe.shape}")
df = dataframe.copy()
try:
# Core features (existing)
df['%-pct-change'] = df['close'].pct_change()
df['%-rsi'] = ta.RSI(df, timeperiod=14)
df['%-sma-15'] = ta.SMA(df, timeperiod=15)
df['%-cti'] = ta.CCI(df, timeperiod=20)
df['%-rsi_fast'] = ta.RSI(df, timeperiod=4)
df['%-rsi_slow'] = ta.RSI(df, timeperiod=20)
# Stochastic (existing)
stoch_fast = ta.STOCHF(df, 5, 3, 0, 3, 0)
df['%-fastk'] = stoch_fast['fastk']
df['%-fastd'] = stoch_fast['fastd']
# NEW: Missing indicators from populate_indicators
df['%-ema_20'] = ta.EMA(df, timeperiod=20)
df['%-volume_ma'] = ta.SMA(df['volume'], timeperiod=20)
df['%-atr'] = ta.ATR(df, timeperiod=14)
# NEW: Basic Bollinger Bands
bb = ta.BBANDS(df, 20)
if isinstance(bb, pd.DataFrame):
df['%-bb_upper'] = bb['upperband']
else:
df['%-bb_upper'] = 0
# NEW: Basic MACD
macd = ta.MACD(df)
df['%-macd'] = macd['macd']
# NEW: Basic ADX
df['%-adx'] = ta.ADX(df)
# NEW: Basic CCI
df['%-cci'] = ta.CCI(df)
# NEW: EMA difference (used in strategy)
df['%-ema_diff'] = (df['close'] - df['%-ema_20']) / df['%-ema_20']
# NEW: VWAP
try:
# Custom VWAP implementation using pandas
typical_price = (df['high'] + df['low'] + df['close']) / 3
df['%-vwap'] = (typical_price * df['volume']).rolling(window=20).sum() / df['volume'].rolling(window=20).sum()
df['%-vwap'] = df['%-vwap'].fillna(df['close'])
except Exception:
df['%-vwap'] = df['close']
# Clean FreqAI features
freqai_columns = [col for col in df.columns if col.startswith('%-')]
for col in freqai_columns:
df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
logger.info(f"[FreqAI] feature_engineering_standard output shape: {df.shape}")
if df.empty:
logger.warning("[FreqAI] feature_engineering_standard returned empty DataFrame! Filling with zeros.")
df = pd.DataFrame(0, index=range(10), columns=[f'%-dummy_{i}' for i in range(5)])
return df
except Exception as e:
logger.warning(f"Error in feature_engineering_standard: {e}")
return df
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
"""
Advanced target engineering for CX Smart Money Strategy
Creates comprehensive targets for multi-timeframe prediction
"""
logger.info(f"[CX Smart Money FreqAI] set_freqai_targets called. Input shape: {dataframe.shape}")
df = dataframe.copy()
try:
# ===== PRICE TARGETS =====
# Short-term price targets (1-5 periods)
df['&-s_close_1'] = df['close'].shift(-1) / df['close']
df['&-s_close_2'] = df['close'].shift(-2) / df['close']
df['&-s_close_5'] = df['close'].shift(-5) / df['close']
# Medium-term price targets (10-20 periods)
df['&-s_close_10'] = df['close'].shift(-10) / df['close']
df['&-s_close_15'] = df['close'].shift(-15) / df['close']
df['&-s_close_20'] = df['close'].shift(-20) / df['close']
# Long-term price targets (30-50 periods)
df['&-s_close_30'] = df['close'].shift(-30) / df['close']
df['&-s_close_50'] = df['close'].shift(-50) / df['close']
# ===== DIRECTIONAL TARGETS =====
# Binary directional targets
df['&-s_direction_1'] = (df['close'].shift(-1) > df['close']).astype(int)
df['&-s_direction_5'] = (df['close'].shift(-5) > df['close']).astype(int)
df['&-s_direction_10'] = (df['close'].shift(-10) > df['close']).astype(int)
df['&-s_direction_20'] = (df['close'].shift(-20) > df['close']).astype(int)
# Multi-class directional targets (Strong Buy, Buy, Hold, Sell, Strong Sell)
price_change_1 = df['close'].shift(-1) / df['close'] - 1
direction_class = pd.cut(price_change_1,
bins=[-np.inf, -0.02, -0.005, 0.005, 0.02, np.inf],
labels=[0, 1, 2, 3, 4])
# Fix: handle NaN by assigning -1, then convert to int
df['&-s_direction_class_1'] = direction_class.cat.add_categories([-1]).fillna(-1).astype(int)
# ===== VOLATILITY TARGETS =====
# Rolling volatility targets
df['&-s_volatility_1'] = df['close'].rolling(5).std().shift(-1) / df['close']
df['&-s_volatility_5'] = df['close'].rolling(5).std().shift(-5) / df['close']
df['&-s_volatility_10'] = df['close'].rolling(10).std().shift(-10) / df['close']
df['&-s_volatility_20'] = df['close'].rolling(20).std().shift(-20) / df['close']
# ===== HIGH/LOW TARGETS =====
# Support and resistance targets
df['&-s_high_5'] = df['high'].rolling(5).max().shift(-5) / df['close']
df['&-s_low_5'] = df['low'].rolling(5).min().shift(-5) / df['close']
df['&-s_high_10'] = df['high'].rolling(10).max().shift(-10) / df['close']
df['&-s_low_10'] = df['low'].rolling(10).min().shift(-10) / df['close']
# ===== VOLUME TARGETS =====
# Volume change targets
df['&-s_volume_1'] = df['volume'].shift(-1) / df['volume']
df['&-s_volume_5'] = df['volume'].shift(-5) / df['volume']
df['&-s_volume_10'] = df['volume'].shift(-10) / df['volume']
# ===== TECHNICAL INDICATOR TARGETS =====
# RSI targets
rsi = ta.RSI(df, timeperiod=14)
df['&-s_rsi_1'] = rsi.shift(-1)
df['&-s_rsi_5'] = rsi.shift(-5)
df['&-s_rsi_10'] = rsi.shift(-10)
# MACD targets
macd = ta.MACD(df)
df['&-s_macd_1'] = macd['macd'].shift(-1)
df['&-s_macd_5'] = macd['macd'].shift(-5)
# ===== MOMENTUM TARGETS =====
# Price momentum targets
df['&-s_momentum_1'] = df['close'].pct_change().shift(-1)
df['&-s_momentum_5'] = df['close'].pct_change(5).shift(-5)
df['&-s_momentum_10'] = df['close'].pct_change(10).shift(-10)
# ===== TREND TARGETS =====
# Trend strength targets
df['&-s_trend_5'] = (df['close'].shift(-5) - df['close']) / df['close']
df['&-s_trend_10'] = (df['close'].shift(-10) - df['close']) / df['close']
df['&-s_trend_20'] = (df['close'].shift(-20) - df['close']) / df['close']
# ===== SUPPORT/RESISTANCE TARGETS =====
# Dynamic support/resistance levels
df['&-s_resistance_5'] = df['high'].rolling(20).max().shift(-5) / df['close']
df['&-s_support_5'] = df['low'].rolling(20).min().shift(-5) / df['close']
df['&-s_resistance_10'] = df['high'].rolling(20).max().shift(-10) / df['close']
df['&-s_support_10'] = df['low'].rolling(20).min().shift(-10) / df['close']
# ===== ADVANCED TARGETS =====
# Price efficiency targets
df['&-s_price_efficiency_5'] = abs(df['close'].shift(-5) - df['close']) / df['close'].rolling(5).std()
df['&-s_price_efficiency_10'] = abs(df['close'].shift(-10) - df['close']) / df['close'].rolling(10).std()
# Market regime targets
df['&-s_bull_market_5'] = (df['close'].shift(-5) > df['close'] * 1.02).astype(int)
df['&-s_bear_market_5'] = (df['close'].shift(-5) < df['close'] * 0.98).astype(int)
# Volatility regime targets
df['&-s_high_volatility_5'] = (df['close'].rolling(5).std().shift(-5) > df['close'].rolling(20).std()).astype(int)
df['&-s_low_volatility_5'] = (df['close'].rolling(5).std().shift(-5) < df['close'].rolling(20).std() * 0.5).astype(int)
# ===== SPECIALIZED PAIR TARGETS =====
self._add_specialized_freqai_targets(df)
# ===== CLEANING AND VALIDATION =====
# Clean target columns
target_columns = [col for col in df.columns if col.startswith('&-s_')]
for col in target_columns:
df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0).replace([np.inf, -np.inf], 0)
logger.info(f"[CX Smart Money FreqAI] set_freqai_targets output shape: {df.shape}")
if df.empty:
logger.warning("[CX Smart Money FreqAI] set_freqai_targets returned empty DataFrame!")
df = pd.DataFrame(0, index=range(10), columns=['&-s_dummy_0'])
return df
except Exception as e:
logger.warning(f"Error in CX Smart Money set_freqai_targets: {e}")
return df
def _add_specialized_freqai_targets(self, df: DataFrame) -> None:
"""
Advanced specialized FreqAI targets for CX Smart Money Strategy
Creates pair-specific targets for BTC, ETH, XRP, DOGE, ADA
"""
try:
# ===== BTC-SPECIFIC TARGETS =====
# Bitcoin dominance and market leadership targets
df['&-s_btc_dominance_5'] = (ta.SMA(df['close'], timeperiod=50) / ta.SMA(df['close'], timeperiod=200)).shift(-5)
df['&-s_btc_dominance_10'] = (ta.SMA(df['close'], timeperiod=50) / ta.SMA(df['close'], timeperiod=200)).shift(-10)
df['&-s_btc_volatility_5'] = (df['close'].rolling(20).std() / df['close'].rolling(20).mean()).shift(-5)
df['&-s_btc_volatility_10'] = (df['close'].rolling(20).std() / df['close'].rolling(20).mean()).shift(-10)
# Bitcoin institutional flow targets
df['&-s_btc_institutional_volume_5'] = (df['volume'].rolling(24).mean() / df['volume'].rolling(168).mean()).shift(-5)
df['&-s_btc_whale_activity_5'] = ((df['volume'] > df['volume'].rolling(100).quantile(0.95)).astype(int)).shift(-5)
# Bitcoin market structure targets
df['&-s_btc_support_level_5'] = (df['low'].rolling(20).min() / df['close']).shift(-5)
df['&-s_btc_resistance_level_5'] = (df['high'].rolling(20).max() / df['close']).shift(-5)
df['&-s_btc_price_efficiency_5'] = (abs(df['close'] - df['close'].shift(20)) / df['close'].rolling(20).std()).shift(-5)
# ===== ETH-SPECIFIC TARGETS =====
# Ethereum DeFi and gas targets
df['&-s_eth_gas_ratio_5'] = (df['volume'] / df['volume'].rolling(50).mean()).shift(-5)
df['&-s_eth_gas_ratio_10'] = (df['volume'] / df['volume'].rolling(50).mean()).shift(-10)
df['&-s_eth_defi_volume_5'] = (df['volume'] * df['close']).shift(-5)
df['&-s_eth_defi_volume_10'] = (df['volume'] * df['close']).shift(-10)
# Ethereum staking and yield targets
df['&-s_eth_staking_ratio_5'] = (df['close'].rolling(25).mean() / df['close'].rolling(100).mean()).shift(-5)
df['&-s_eth_defi_yield_5'] = (df['volume'].rolling(7).mean() / df['volume'].rolling(30).mean()).shift(-5)
# Ethereum network activity targets
df['&-s_eth_network_growth_5'] = (df['volume'].pct_change().rolling(24).mean()).shift(-5)
df['&-s_eth_layer2_activity_5'] = (df['volume'].rolling(12).std() / df['volume'].rolling(48).std()).shift(-5)
# ===== XRP-SPECIFIC TARGETS =====
# Ripple network and cross-border targets
df['&-s_xrp_ripple_volume_5'] = (df['volume'] / df['volume'].rolling(100).mean()).shift(-5)
df['&-s_xrp_ripple_volume_10'] = (df['volume'] / df['volume'].rolling(100).mean()).shift(-10)
df['&-s_xrp_network_activity_5'] = df['close'].pct_change().rolling(10).std().shift(-5)
df['&-s_xrp_cross_border_volume_5'] = (df['volume'].rolling(24).sum() / df['volume'].rolling(168).sum()).shift(-5)
# XRP institutional adoption targets
df['&-s_xrp_institutional_flow_5'] = (df['volume'].rolling(48).mean() / df['volume'].rolling(336).mean()).shift(-5)
df['&-s_xrp_regulatory_sentiment_5'] = (df['close'].pct_change().rolling(5).mean()).shift(-5)
# ===== DOGE-SPECIFIC TARGETS =====
# Dogecoin meme and social targets
df['&-s_doge_meme_volume_5'] = (df['volume'] / df['volume'].rolling(24).mean()).shift(-5)
df['&-s_doge_social_sentiment_5'] = (df['close'].pct_change().rolling(5).mean()).shift(-5)
df['&-s_doge_viral_coefficient_5'] = (df['volume'].pct_change().rolling(3).std()).shift(-5)
# Dogecoin community activity targets
df['&-s_doge_community_engagement_5'] = (df['volume'].rolling(6).mean() / df['volume'].rolling(24).mean()).shift(-5)
df['&-s_doge_celebrity_impact_5'] = (df['volume'].pct_change().rolling(2).std()).shift(-5)
# ===== ADA-SPECIFIC TARGETS =====
# Cardano staking and governance targets
df['&-s_ada_cardano_volume_5'] = (df['volume'] / df['volume'].rolling(75).mean()).shift(-5)
df['&-s_ada_cardano_volume_10'] = (df['volume'] / df['volume'].rolling(75).mean()).shift(-10)
df['&-s_ada_staking_ratio_5'] = (df['close'].rolling(25).mean() / df['close'].rolling(100).mean()).shift(-5)
df['&-s_ada_governance_activity_5'] = (df['volume'].rolling(7).mean() / df['volume'].rolling(30).mean()).shift(-5)
# Cardano development activity targets
df['&-s_ada_development_momentum_5'] = (df['volume'].pct_change().rolling(14).mean()).shift(-5)
df['&-s_ada_ecosystem_growth_5'] = (df['volume'].rolling(21).mean() / df['volume'].rolling(84).mean()).shift(-5)
# ===== COMMON SPECIALIZED TARGETS FOR ALL PAIRS =====
# Advanced momentum targets
df['&-s_momentum_acceleration_5'] = (df['close'].pct_change().diff()).shift(-5)
df['&-s_momentum_strength_5'] = (df['close'].pct_change().rolling(10).std()).shift(-5)
# Advanced volatility targets
df['&-s_volatility_skew_5'] = (df['close'].rolling(20).std().rolling(20).skew()).shift(-5)
df['&-s_volatility_regime_5'] = ((df['close'].rolling(5).std() > df['close'].rolling(20).std()).astype(int)).shift(-5)
# Advanced trend targets
df['&-s_trend_consistency_5'] = ((df['close'] > df['close'].shift(10)).rolling(10).mean()).shift(-5)
df['&-s_trend_strength_5'] = (abs(df['close'] - df['close'].shift(10)) / df['close'].rolling(10).std()).shift(-5)
# Advanced market efficiency targets
df['&-s_market_efficiency_5'] = (abs(df['close'] - df['close'].shift(10)) / df['close'].rolling(10).std()).shift(-5)
df['&-s_market_microstructure_5'] = (df['volume'].rolling(5).std() / df['volume'].rolling(20).std()).shift(-5)
# Advanced support/resistance targets
df['&-s_support_strength_5'] = ((df['close'] - df['low'].rolling(20).min()) / df['close']).shift(-5)
df['&-s_resistance_strength_5'] = ((df['high'].rolling(20).max() - df['close']) / df['close']).shift(-5)
df['&-s_price_position_5'] = ((df['close'] - df['low'].rolling(20).min()) / (df['high'].rolling(20).max() - df['low'].rolling(20).min())).shift(-5)
# Advanced volume analysis targets
df['&-s_volume_trend_5'] = (df['volume'].rolling(10).mean() / df['volume'].rolling(50).mean()).shift(-5)
df['&-s_volume_volatility_5'] = (df['volume'].rolling(10).std() / df['volume'].rolling(10).mean()).shift(-5)
df['&-s_volume_price_correlation_5'] = (df['volume'].rolling(20).corr(df['close'])).shift(-5)
# Advanced momentum analysis targets
df['&-s_momentum_divergence_5'] = ((df['close'].pct_change() - df['close'].pct_change().rolling(10).mean())).shift(-5)
df['&-s_momentum_regime_5'] = (((df['close'].pct_change() > 0).astype(int) - (df['close'].pct_change() < 0).astype(int)).rolling(5).mean()).shift(-5)
except Exception as e:
logger.warning(f"Error in CX Smart Money specialized FreqAI targets: {e}")
def populate_any_indicators(self, pair: str, df: DataFrame, tf: str) -> DataFrame:
"""
Enhanced populate any indicators for FreqAI
"""
try:
# Core features (existing)
df['%-pct-change'] = df['close'].pct_change()
df['%-rsi'] = ta.RSI(df, timeperiod=14)
df['%-sma-15'] = ta.SMA(df, timeperiod=15)
df['%-cti'] = ta.CCI(df, timeperiod=20)
df['%-rsi_fast'] = ta.RSI(df, timeperiod=4)
df['%-rsi_slow'] = ta.RSI(df, timeperiod=20)
# Stochastic (existing)
stoch_fast = ta.STOCHF(df, 5, 3, 0, 3, 0)
df['%-fastk'] = stoch_fast['fastk']
df['%-fastd'] = stoch_fast['fastd']
# NEW: Missing indicators from populate_indicators
df['%-ema_20'] = ta.EMA(df, timeperiod=20)
df['%-volume_ma'] = ta.SMA(df['volume'], timeperiod=20)
df['%-atr'] = ta.ATR(df, timeperiod=14)
# NEW: Basic indicators
df['%-macd'] = ta.MACD(df)['macd']
df['%-adx'] = ta.ADX(df)
df['%-cci'] = ta.CCI(df)
# NEW: EMA difference (used in strategy)
df['%-ema_diff'] = (df['close'] - df['%-ema_20']) / df['%-ema_20']
# NEW: VWAP
try:
# Custom VWAP implementation using pandas
typical_price = (df['high'] + df['low'] + df['close']) / 3
df['%-vwap'] = (typical_price * df['volume']).rolling(window=20).sum() / df['volume'].rolling(window=20).sum()
df['%-vwap'] = df['%-vwap'].fillna(df['close'])
except Exception:
df['%-vwap'] = df['close']
return df
except Exception as e:
logger.warning(f"Error in populate_any_indicators: {e}")
return df
# Enhanced protections
@property
def protections(self):
return [
{
"method": "CooldownPeriod",
"stop_duration_candles": 5
},
{
"method": "MaxDrawdown",
"lookback_period_candles": 48,
"trade_limit": 20,
"stop_duration_candles": 4,
"max_allowed_drawdown": 0.2
},
{
"method": "StoplossGuard",
"lookback_period_candles": 24,
"trade_limit": 4,
"stop_duration_candles": 2,
"only_per_pair": False
}
]
# Optimal hyperopt ranges (from historical optimization)
@property
def buy_params(self):
return {
"buy_rsi_fast_32": 26,
"buy_rsi_32": 46,
"buy_sma15_32": 1.0016,
"buy_cti_32": -0.615,
"buy_volume_32": 0.881,
"buy_ema_diff_32": -0.004,
"dca_multiplier": 0.587,
"dca_threshold": -0.023,
"profit_take_threshold": 0.047,
}
@property
def sell_params(self):
return {
"exit_fastk_threshold": 89,
"exit_rsi_threshold": 77,
"exit_cti_threshold": 0.407,
"exit_profit_threshold": 0.012,
"trailing_stop_positive": 0.198,
"trailing_stop_positive_offset": 0.212
}