Here is a roadmap for evolving the KNN Momentum Indicator into a professional-grade trading system, structured across three additive levels of enhancement.
Level 1: Parameter Optimization & Dynamic Adaptability¶
The current script, while conceptually advanced, relies on static, “hard-coded” parameters for its core logic and risk management (or lack thereof). This makes it brittle and prone to curve-fitting. Level 1 upgrades focus on introducing dynamic adaptability, allowing the system to breathe with the market’s current volatility and behavior.
Technical Logic & Suggested Upgrades¶
Implement an ATR-Based Risk Management Module: A signal is meaningless without a predefined exit strategy. The most critical first step is to integrate a non-discretionary risk framework.
Logic: Upon a signal, calculate the Average True Range (ATR) over a lookback period (e.g., 14 periods). The stop-loss is placed at a multiple of this ATR below the entry price for a long (or above for a short). The take-profit is set at a different multiple, establishing a fixed Risk/Reward ratio.
Pine Script Implementation:
// --- INPUTS FOR RISK MANAGEMENT --- atr_len = input.int(14, "ATR Period", group="🛡️ Risk Management") stop_loss_mult = input.float(1.5, "Stop Loss ATR Multiple", group="🛡️ Risk Management") take_profit_mult = input.float(3.0, "Take Profit ATR Multiple", group="🛡️ Risk Management") // --- STRATEGY CONVERSION --- // Convert the indicator to a strategy // strategy("KNN Professional System", overlay=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=10) // --- DYNAMIC EXIT LOGIC --- float atr_val = ta.atr(atr_len) if (long_signal) strategy.entry("KNN Long", strategy.long) strategy.exit("Exit Long", "KNN Long", stop=close - atr_val * stop_loss_mult, limit=close + atr_val * take_profit_mult) if (short_signal) strategy.entry("KNN Short", strategy.short) strategy.exit("Exit Short", "KNN Short", stop=close + atr_val * stop_loss_mult, limit=close - atr_val * take_profit_mult)
Introduce an Adaptive Prediction Threshold: The static
prob_thresholdof0.9is arbitrary. In a low-volatility environment, the model may never reach this confidence level, leading to zero trades. In a highly directional market, it might be too low. The threshold should adapt to the model’s own recent output.Logic: Instead of a fixed value, calculate the Nth percentile (e.g., 90th) of the
prob_upandprob_downvalues over thewindow_size. A signal is triggered when the current probability crosses this dynamic, self-adjusting threshold.Pine Script Implementation:
// --- ADAPTIVE THRESHOLD LOGIC --- adaptive_percentile = input.float(90.0, "Adaptive Threshold Percentile", group=group_ml) // Calculate historical distribution of probabilities float prob_up_history = prob_up[1] float prob_down_history = prob_down[1] // Calculate the percentile over the learning window float dynamic_threshold_up = ta.percentile_linear_interpolation(prob_up_history, window_size, adaptive_percentile) float dynamic_threshold_down = ta.percentile_linear_interpolation(prob_down_history, window_size, adaptive_percentile) // Update signal generation bool raw_long_signal = ta.crossover(prob_up, dynamic_threshold_up) bool raw_short_signal = ta.crossover(prob_down, dynamic_threshold_down)
Quantitative Benefit¶
Reduction in Maximum Drawdown & Improved Calmar Ratio: The ATR-based risk module is the single most important upgrade for capital preservation. By defining a maximum acceptable loss per trade based on current volatility, it prevents catastrophic losses during unexpected reversals. This directly contains drawdowns and, by extension, improves risk-adjusted returns as measured by the Calmar Ratio.
Increased Robustness & Reduced Curve-Fitting: Adaptive thresholds allow the strategy to self-calibrate to different assets and market conditions. This reduces the need for manual re-optimization, making the strategy more robust and less likely to be a product of curve-fitting to a specific historical dataset. It improves the Signal-to-Noise Ratio by adjusting its sensitivity based on recent model performance.
Level 2: Secondary Confluence & Noise Filtration¶
With a dynamic foundation in place, Level 2 focuses on improving the quality of signals by eliminating low-probability setups. The goal is to increase precision by adding secondary filters that confirm the conviction behind a KNN-identified pattern.
Technical Logic & Suggested Upgrades¶
Implement a Volume-Weighted Confirmation Filter: A price move without volume is a low-conviction move. The KNN model, being price-based, is blind to market participation. This filter ensures that a signal is backed by institutional interest or broad consensus.
Logic: A signal is only considered valid if the volume on the signal bar is greater than a moving average of volume (e.g.,
volume > ta.sma(volume, 20)). This simple check filters out signals occurring in illiquid, “choppy” environments where follow-through is unlikely.Pine Script Implementation:
// --- INPUTS FOR FILTERS --- use_volume_filter = input.bool(true, "Enable Volume Filter", group="🔍 Filters") volume_filter_len = input.int(20, "Volume MA Period", group="🔍 Filters") // --- FILTER LOGIC --- bool volume_confirmed = volume > ta.sma(volume, volume_filter_len) // --- APPLY TO SIGNAL --- if (raw_long_signal and last_dir <= 0 and (volume_confirmed or not use_volume_filter)) long_signal := true last_dir := 1 if (raw_short_signal and last_dir >= 0 and (volume_confirmed or not use_volume_filter)) short_signal := true last_dir := -1
Integrate a Higher-Timeframe (HTF) Directional Bias: A momentum signal on a 15-minute chart is significantly more likely to succeed if it aligns with the prevailing trend on the 4-hour or Daily chart. This filter prevents fighting the “primary tide.”
Logic: Use Pine Script’s
request.security()function to fetch the state of a long-term moving average (e.g., 200 EMA) from a higher timeframe. A long signal is only permitted if the price on the HTF is above its 200 EMA. A short signal is only permitted if the price is below it.Pine Script Implementation:
// --- INPUTS FOR FILTERS --- use_htf_filter = input.bool(true, "Enable HTF Trend Filter", group="🔍 Filters") htf_timeframe = input.timeframe("240", "Higher Timeframe", group="🔍 Filters") htf_ema_len = input.int(50, "HTF EMA Period", group="🔍 Filters") // --- HTF LOGIC --- float htf_ema = request.security(syminfo.tickerid, htf_timeframe, ta.ema(close, htf_ema_len)) bool htf_is_bullish = close > htf_ema bool htf_is_bearish = close < htf_ema // --- APPLY TO SIGNAL (EXAMPLE FOR LONG) --- if (raw_long_signal and last_dir <= 0 and (volume_confirmed or not use_volume_filter) and (htf_is_bullish or not use_htf_filter)) long_signal := true last_dir := 1 // ... similar logic for short signal
Quantitative Benefit¶
Increased Profit Factor & Win Rate: By filtering out trades that lack volume confirmation or are counter to the primary trend, we systematically eliminate a cohort of low-probability setups. While this reduces the total number of trades, the trades that are taken have a higher statistical edge. This directly translates to a higher Win Rate and, consequently, a significantly improved Profit Factor (Gross Profit / Gross Loss).
Level 3: Structural Architecture & Regime Detection¶
Level 3 moves beyond simple filters to fundamentally alter the strategy’s engine. The goal is to make the system “market-aware,” enabling it to adapt its core behavior based on the prevailing market cycle or “regime.” This is the hallmark of a truly professional, all-weather system.
Technical Logic & Suggested Upgrades¶
Implement a Market Regime Filter (e.g., Gaussian Filter or Hurst Exponent): The KNN momentum model is designed for trending or transitional markets. It will perform poorly in sideways, mean-reverting chop. A regime filter acts as a master switch, activating the strategy only when conditions are favorable.
Logic: A low-lag filter like a 2-pole Gaussian Filter can be used to model the dominant market cycle. The first derivative (slope) of this filter indicates the market’s mode. A steep, positive/negative slope signifies a strong trend (enable the KNN strategy). A near-zero slope signifies a ranging market (disable the KNN strategy).
Pine Script Implementation (Conceptual):
// --- REGIME FILTER LOGIC (GAUSSIAN) --- // [Function to calculate a 2-pole Gaussian Filter would be defined here] // alpha = 1 - math.cos(2 * math.pi / period) // g_filt = alpha*src + (1-alpha)*g_filt[1] ... (simplified concept) float regime_filter = calc_gaussian_filter(close, 50) // Example float filter_slope = regime_filter - regime_filter[1] float slope_threshold = ta.atr(100) * 0.05 // Dynamic threshold for "flat" bool is_trending_regime = math.abs(filter_slope) > slope_threshold // --- MASTER SWITCH --- // The final signal is now gated by the regime bool final_long_signal = long_signal and is_trending_regime bool final_short_signal = short_signal and is_trending_regime
Evolve to a Multi-Timeframe (MTF) Signal Confirmation Engine: This is a significant architectural upgrade from the HTF bias in Level 2. Instead of just checking a trend, we require the KNN model itself to generate a congruent signal on multiple timeframes simultaneously. This confirms the signal has structural depth.
Logic: Encapsulate the entire KNN feature calculation and probability scoring logic into a reusable function. Call this function for the chart’s current timeframe. Then, use
request.security()to call the same function on a higher timeframe (e.g., 4x the chart’s timeframe). A trade is only triggered if both the local timeframe and the higher timeframe produce a high-probability signal in the same direction.Pine Script Implementation (Conceptual):
// --- REUSABLE KNN FUNCTION --- f_getKnnSignal(k, window, threshold) => // ... all feature calculation and KNN logic from the original script ... // Returns a tuple: [prob_up, prob_down] [prob_up, prob_down] // --- MTF CONFIRMATION --- [local_prob_up, local_prob_down] = f_getKnnSignal(k_neighbors, window_size, prob_threshold) [htf_prob_up, htf_prob_down] = request.security(syminfo.tickerid, htf_timeframe, f_getKnnSignal(k_neighbors, window_size, prob_threshold)) // A much more robust signal bool mtf_confirmed_long = ta.crossover(local_prob_up, prob_threshold) and htf_prob_up > prob_threshold bool mtf_confirmed_short = ta.crossover(local_prob_down, prob_threshold) and htf_prob_down > prob_threshold
Quantitative Benefit¶
Enhanced Robustness & Survival: A regime filter is the ultimate defense against prolonged, unfavorable market conditions. By forcing the strategy to “sit on its hands” during choppy, range-bound periods, it dramatically cuts down on the sequence of losing trades that typically destroy momentum systems. This significantly improves the strategy’s Robustness, its ability to survive “Black Swan” events, and its performance on out-of-sample data. The MTF confirmation engine ensures that signals are not just local noise but are part of a larger, structurally significant market move, further enhancing the Expected Value (EV) of each trade taken.