critical: Fix v11 missing use_quality_filters parameter + RSI index bug
TWO CRITICAL BUGS FIXED: 1. Missing use_quality_filters parameter (Pine Script parity): - Added use_quality_filters: bool = True to MoneyLineV11Inputs - Implemented bypass logic in signal generation for both long/short - When False: only trend flips generate signals (no filtering) - When True: all filters must pass (original v11 behavior) - Matches Pine Script: finalSignal = buyReady and (not useQualityFilters or (...filters...)) 2. RSI index misalignment causing 100% NaN values: - np.where() returns numpy arrays without indices - pd.Series(gain/loss) created NEW integer indices (0,1,2...) - Result: RSI values misaligned with original datetime index - Fix: pd.Series(gain/loss, index=series.index) preserves alignment - Impact: RSI NaN count 100 → 0, all filters now work correctly VERIFICATION: - Test 1 (no filters): 1,424 signals ✓ - Test 2 (permissive RSI): 1,308 signals ✓ - Test 3 (moderate RSI 25-70/30-80): 1,157 signals ✓ Progressive sweep can now proceed with corrected signal generation.
This commit is contained in:
@@ -49,6 +49,9 @@ class MoneyLineV11Inputs:
|
||||
confirm_bars: int = 0 # Immediate signals
|
||||
cooldown_bars: int = 3 # Prevent overtrading
|
||||
|
||||
# Master filter toggle (matches Pine Script useQualityFilters)
|
||||
use_quality_filters: bool = True # When False, only trend flips generate signals (no filtering)
|
||||
|
||||
# ATR profile (fixed for test - 5-minute chart defaults)
|
||||
atr_period: int = 12 # ATR calculation length
|
||||
multiplier: float = 3.8 # ATR band multiplier
|
||||
@@ -107,8 +110,10 @@ def rsi(series: pd.Series, length: int) -> pd.Series:
|
||||
delta = series.diff()
|
||||
gain = np.where(delta > 0, delta, 0.0)
|
||||
loss = np.where(delta < 0, -delta, 0.0)
|
||||
avg_gain = rma(pd.Series(gain), length)
|
||||
avg_loss = rma(pd.Series(loss), length)
|
||||
# CRITICAL FIX (Dec 6, 2025): Preserve index from input series
|
||||
# np.where returns array, pd.Series without index=series.index creates misaligned data
|
||||
avg_gain = rma(pd.Series(gain, index=series.index), length)
|
||||
avg_loss = rma(pd.Series(loss, index=series.index), length)
|
||||
rs = avg_gain / avg_loss.replace(0, np.nan)
|
||||
rsi_series = 100 - (100 / (1 + rs))
|
||||
return rsi_series.fillna(50.0)
|
||||
@@ -279,6 +284,25 @@ def money_line_v11_signals(df: pd.DataFrame, inputs: Optional[MoneyLineV11Inputs
|
||||
volume_ok = row.volume_ratio <= inputs.vol_max # Only check upper bound
|
||||
|
||||
if flip_long:
|
||||
# CRITICAL FIX (Dec 6, 2025): Add use_quality_filters bypass to match Pine Script
|
||||
# Pine Script: finalLongSignal = buyReady and (not useQualityFilters or (...filters...))
|
||||
if not inputs.use_quality_filters:
|
||||
# No filters - just the trend flip (like v9 behavior)
|
||||
signals.append(
|
||||
MoneyLineV11Signal(
|
||||
timestamp=row.name,
|
||||
direction="long",
|
||||
entry_price=float(row.close),
|
||||
adx=float(row.adx),
|
||||
atr=float(row.atr),
|
||||
rsi=float(row.rsi),
|
||||
volume_ratio=float(row.volume_ratio),
|
||||
price_position=float(row.price_position),
|
||||
)
|
||||
)
|
||||
cooldown_remaining = inputs.cooldown_bars
|
||||
else:
|
||||
# Original v11 filter logic - ALL filters must pass
|
||||
# Entry buffer check (longBufferOk) - disabled if entry_buffer_atr == 0
|
||||
if inputs.entry_buffer_atr > 0:
|
||||
entry_buffer_ok = row.close > (row.supertrend + inputs.entry_buffer_atr * row.atr)
|
||||
@@ -306,6 +330,25 @@ def money_line_v11_signals(df: pd.DataFrame, inputs: Optional[MoneyLineV11Inputs
|
||||
cooldown_remaining = inputs.cooldown_bars
|
||||
|
||||
elif flip_short:
|
||||
# CRITICAL FIX (Dec 6, 2025): Add use_quality_filters bypass to match Pine Script
|
||||
# Pine Script: finalShortSignal = sellReady and (not useQualityFilters or (...filters...))
|
||||
if not inputs.use_quality_filters:
|
||||
# No filters - just the trend flip (like v9 behavior)
|
||||
signals.append(
|
||||
MoneyLineV11Signal(
|
||||
timestamp=row.name,
|
||||
direction="short",
|
||||
entry_price=float(row.close),
|
||||
adx=float(row.adx),
|
||||
atr=float(row.atr),
|
||||
rsi=float(row.rsi),
|
||||
volume_ratio=float(row.volume_ratio),
|
||||
price_position=float(row.price_position),
|
||||
)
|
||||
)
|
||||
cooldown_remaining = inputs.cooldown_bars
|
||||
else:
|
||||
# Original v11 filter logic - ALL filters must pass
|
||||
# Entry buffer check (shortBufferOk) - disabled if entry_buffer_atr == 0
|
||||
if inputs.entry_buffer_atr > 0:
|
||||
entry_buffer_ok = row.close < (row.supertrend - inputs.entry_buffer_atr * row.atr)
|
||||
|
||||
Reference in New Issue
Block a user