Back To Top

August 14, 2025

Improving RSI with Machine Learning

Integrate historical pattern matching and signal smoothing into your RSI with K-Nearest Neighbors and Kalman filtering

Every trader looks for an edge that adapts as fast as the market. However, most traditional indicators don’t keep up.

Here, we’ll aim to upgrade on of the most popular tools: RSI. We’ll transform the classic indicator by fusing unsupervised ML into it.

Specifically, we adapt RSI to implement K-Nearest Neighbors for historical pattern matching and Kalman filtering for noise reduction.

The complete Python notebook for the analysis is provided below.

Improving RSI with Machine Learning

1. Infusing KNN into RSI

RSI measures the speed and magnitude of recent price changes to identify overbought and oversold conditions.

The classic RSI is computed as:

Improving RSI with Machine Learning

where

Improving RSI with Machine Learning

This approach works, but there are some limitations:

  • Treats all past data equally, regardless of volatility regimes
  • Responds slowly in fast-moving markets
  • Prone to whipsaw and false signals during sideways price action

1.1 Improving RSI with KNN and Kalman Filter

Step 1: Multi-Feature Extraction

Instead of relying only on the RSI value, we build a feature set at each time step. Example features:

  • Current RSI value
  • RSI momentum:
Improving RSI with Machine Learning
  • RSI volatility (rolling standard deviation)
  • RSI slope (from linear regression over a window)
  • Price momentum

Each feature is scaled to the [0,1] range to ensure comparability.

Step 2: Each feature is scaled to ensure comparability.

For each bar, a K-Nearest Neighbors model searches the historical lookback window for periods where the feature set is similar to the current one.

The KNN prediction is the weighted average RSI value of the k most similar past states:

Improving RSI with Machine Learning

Step 3: Weighted Combination

The final RSI signal is a weighted sum of the standard RSI and the KNN-adjusted RSI:

Improving RSI with Machine Learning

The parameter w adjusts how much influence the machine learning estimate has.

Step 4: Signal Smoothing with Kalman Filter

The ML_RSI series can still show noise and small spikes. To address this, a Kalman filter is applied:

Improving RSI with Machine Learning

where s (filter strength) controls smoothness.

  • Higher s = smoother, but more lag
  • Lower s = more responsive, but noisier

2. ML-Enhanced RSI in Python

2.1 Setup and Parameters

First, set up your Python environment and key parameters.

This includes importing all required libraries, defining the ticker symbol, date range, and price interval, and settings for the RSI calculation, KNN.

This parameterization makes the workflow more adjustable. Each parameter is explained as comments in the code snippet.

				
					import numpy as np
import pandas as pd
import yfinance as yf
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import MinMaxScaler
from scipy.stats import linregress
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# Parameters
TICKER = 'TSLA'              # Ticker symbol to download data from yfinance
START = '2020-01-01'         # Start date of historical data
END = '2025-06-15'           # End date of historical data
INTERVAL = '1d'              # Data interval (e.g. '1d' = daily, '1h' = hourly)

# RSI settings
rsiLength = 14               # Number of bars to calculate RSI; higher = smoother RSI, slower signals; lower = faster, more noise
useSmoothing = True          # Whether to apply a moving average to RSI; reduces noise but adds lag
smoothingLength = 3          # Number of bars for RSI smoothing; higher = smoother RSI line; lower = faster response
maType = 'EMA'               # Type of smoothing used; 'EMA' reacts faster to recent changes, 'SMA' smooths evenly

# KNN settings
useKnn = True                # Enables ML-enhanced RSI using K-nearest neighbors; finds similar past RSI conditions and adjusts current RSI accordingly
knnNeighbors = 5             # Number of nearest historical matches to include; higher = more general behavior; lower = more specific but sensitive
knnLookback = 100            # How far back in history to search for similar RSI patterns; larger = more reference points but slower and more averaged
knnWeight = 0.5              # Blend between standard RSI and KNN output; 0.0 = pure RSI, 1.0 = full ML override
featureCount = 3             # Number of features used to match patterns; 1 = RSI only, up to 5 = adds momentum, volatility, slope, and price change

# Thresholds
rsiOverbought = 70           # RSI value above which the asset is considered overbought; often used as a sell zone
rsiOversold = 30             # RSI value below which the asset is considered oversold; often used as a buy zone

# Filtering
useFilter = True             # Whether to apply post-processing smoothing to the final RSI line (after KNN)
filterMethod = 'Kalman'      # Smoothing method; 'Kalman' is adaptive and suppresses short-term spikes
filterStrength = 0.3         # Controls strength of the filter; higher = smoother but more lag, lower = more responsive but noisier
				
			

2.2 Download Historical Data

Pull historical price data using yfinance.

This dataset includes open, high, low, close, and volume for the selected ticker and period.

				
					# Download data
df = yf.download(TICKER, START, END, interval=INTERVAL,
                 auto_adjust=True, progress=False, threads=False)
				
			

2.3 Compute Standard RSI

Calculate the standard RSI for each bar. This step uses the classic RSI formula, i.e. based on the rolling averages of gains and losses.

				
					# Compute RSI
delta = df['Close'].diff()
gain = delta.clip(lower=0)
loss = -delta.clip(upper=0)
alpha = 1 / rsiLength
avg_gain = gain.ewm(alpha=alpha, adjust=False).mean()
avg_loss = loss.ewm(alpha=alpha, adjust=False).mean()
rs = avg_gain / avg_loss
df['rsi'] = 100 - (100 / (1 + rs))
				
			

Optionally, apply additional smoothing (EMA or SMA) to the RSI output to reduce noise and create a more stable baseline for further enhancement.

				
					# Optional smoothing
def smooth(series):
    if maType == 'SMA':
        return series.rolling(smoothingLength).mean()
    return series.ewm(span=smoothingLength, adjust=False).mean()

df['standardRsi'] = smooth(df['rsi']) if useSmoothing else df['rsi']
				
			

2.4 Feature Engineering

Now, extract additional features from the price and RSI series.

These features, such as RSI momentum, rolling volatility, slope, and price momentum, will be the context for pattern recognition.

Each feature can be toggled on or off based on the featureCount parameter.

This allows us to balance model complexity and interpretability.

Prev Post

Optimizing Stops via Fair Value Gaps

Next Post

25 Stock Market Facts

post-bars
Mail Icon

Newsletter

Get Every Weekly Update & Insights

[mc4wp_form id=]

Leave a Comment