
13 Investment Errors You Should Avoid
Successful investing is often less about making the right moves and more about avoiding the wrong ones. With our guide, 13 Retirement Investment Blunders to Avoid, you can learn ways to steer clear of common errors to help get the most from your $1M+ portfolio—and enjoy the retirement you deserve.
🚀 Your Investing Journey Just Got Better: Premium Subscriptions Are Here! 🚀
It’s been 4 months since we launched our premium subscription plans at GuruFinance Insights, and the results have been phenomenal! Now, we’re making it even better for you to take your investing game to the next level. Whether you’re just starting out or you’re a seasoned trader, our updated plans are designed to give you the tools, insights, and support you need to succeed.
Here’s what you’ll get as a premium member:
Exclusive Trading Strategies: Unlock proven methods to maximize your returns.
In-Depth Research Analysis: Stay ahead with insights from the latest market trends.
Ad-Free Experience: Focus on what matters most—your investments.
Monthly AMA Sessions: Get your questions answered by top industry experts.
Coding Tutorials: Learn how to automate your trading strategies like a pro.
Masterclasses & One-on-One Consultations: Elevate your skills with personalized guidance.
Our three tailored plans—Starter Investor, Pro Trader, and Elite Investor—are designed to fit your unique needs and goals. Whether you’re looking for foundational tools or advanced strategies, we’ve got you covered.
Don’t wait any longer to transform your investment strategy. The last 4 months have shown just how powerful these tools can be—now it’s your turn to experience the difference.
What if your trend indicator could tune itself with no optimization, no backtesting, or no overfitting?
That’s the idea behind the indicator presented in this analysis. Instead of manually choosing parameters, we built a SuperTrend variant that adapts its sensitivity based on how price behaves.
It uses its own performance as feedback, clusters the results, and selects the best configuration. In real time.
No predictive modeling. No supervised learning. Just price action, a custom scoring function, and unsupervised clustering.
We’ll duscuss the entire architecture, from parameter sweeping and performance metrics to k-means clustering and final signal generation.
End-to-end Implementation Python Notebook provided below.

This article is structured as follows:
The Self-Tuning Trend System
Python Implementation
Applications and Extentions
Kickstart your holiday campaigns
CTV should be central to any growth marketer’s Q4 strategy. And with Roku Ads Manager, launching high-performing holiday campaigns is simple and effective.
With our intuitive interface, you can set up A/B tests to dial in the most effective messages and offers, then drive direct on-screen purchases via the remote with shoppable Action Ads that integrate with your Shopify store for a seamless checkout experience.
Don’t wait to get started. Streaming on Roku picks up sharply in early October. By launching your campaign now, you can capture early shopping demand and be top of mind as the seasonal spirit kicks in.
Get a $500 ad credit when you spend your first $500 today with code: ROKUADS500. Terms apply.
1. A Self-Tuning Trend Signal
Most technical indicators rely on fixed parameters. You pick a multiplier, a moving average length, or a threshold and hope it works across time.
But markets don’t stay constant. Volatility shifts. Trends evolve. What worked last month breaks down the next, etc.
The inidciator, SuperTrend, flips bullish when price closes above a dynamic upper band and flips bearish when it closes below a lower band .
Instead of picking a single setting, we let the system explore a range of ‘SuperTrend’ configurations. Then we score each one based on how well it tracked the price.
Here’s the core idea:
Build multiple SuperTrend signals using different multipliers:
Instead of using one multiplier (say, 3.0), we test a range, from 1.0 to 5.0 in small steps. Each version creates a different set of signals.
The SuperTrend levels are calculated using the formula:

2. Score each version using a custom, price-based performance metric.
We look at each version and track whether the price moves in the expected direction after the signal flips.
We measure this using a custom performance metric. It works like a memory:
If the signal flips and the price moves in that direction, the score goes up.
If the signal flips and price moves the other way, it drops.
The scoring system uses this smoothed formula:

This rewards signals that actually lead price in the right direction.
3. Cluster the results using unsupervised learning (k-means, k=3). This groups all tested multipliers into:
Best-performing signals
Average signals
Poor-performing signals
So instead of picking the highest score directly, we let k-means do the job.
4. Rebuild the indicator using the “Best” group.
Once we know which group performed best, we take the average multiplier from that group, maybe it’s 2.5, maybe it’s 4.2, and use it to compute a final SuperTrend.
This is the implementation architecture diagram:

Figure 1. Architecture Diagram: Trading Signals with Adaptive SuperTrend and K-Means Clustering.
2. Python Implementation
2.1 Define Core Inputs and Signal Settings
The ticker, start date, and end date control which asset is analyzed and over what time period.
ATR length controls how volatility is measured. Lower values react faster to price spikes but add noise. Higher values smooth the signal but respond slower to trend shifts.
SuperTrend multipliers set the sensitivity range. Lower values mean tighter stops, higher values mean wider ones. STEP size controls testing granularity: smaller steps give more options but increase compute time.
PERF_ALPHA affects how the system scores each SuperTrend version. Lower values make the performance metric more responsive to recent changes. Higher values favor long-term stability.
FROM_CLUSTER tells the model which group of signals to trust, Best, Average, or Worst. Most users will select “Best” for trend-following strategies.
Finally, MAX_ITER caps how long the k-means clustering runs. Higher values allow more accurate grouping but increase computation time.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.style as mplstyle
import yfinance as yf
# User parameters
TICKER = "ASML"
# Time range for the analysis
START_DATE = "2022-01-01"
END_DATE = "2025-12-01"
# ATR_LENGTH controls how reactive the ATR (Average True Range) is.
# Shorter = more sensitive to price spikes. Longer = smoother.
ATR_LENGTH = 10
# These define the range of SuperTrend factors to test.
# We'll run the SuperTrend indicator across multiple values between MIN_MULT and MAX_MULT.
MIN_MULT = 1.0 # smallest factor (tightest stop)
MAX_MULT = 5.0 # largest factor (widest stop)
STEP = 0.5 # spacing between factors (e.g., 1.0, 1.5, 2.0, ...)
# PERF_ALPHA controls smoothing of the performance metric.
# Lower = more reactive to recent price behavior.
PERF_ALPHA = 10
# We'll cluster the performance results into 3 groups (Best, Average, Worst)
# and select the factor from the chosen group.
FROM_CLUSTER = "Best" # Options: "Best", "Average", or "Worst"
# Maximum number of iterations for the k-means clustering step
MAX_ITER = 1000
# Note: maxData isn’t needed here, but could be used to limit data length if desired.
200+ AI Side Hustles to Start Right Now
From prompt engineering to AI apps, there are countless ways to profit from AI now. Our guide reveals 200+ actionable AI business models, from no-code solutions to advanced applications. Learn how people are earning $500-$10,000 monthly with tools that didn't exist last year. Sign up for The Hustle to get the guide and daily insights.
2.2 Fetch and Prepare Market Data
Historical price data for the chosen ticker (e.g, ASML) is downloaded and Key columns (High, Low, Close) are retained.
The mid-price is computed as:

# Download data
df = yf.download(TICKER, start=START_DATE, end=END_DATE, interval="1d", auto_adjust=False)
if df.empty:
raise ValueError("No data returned from yfinance")
# Flatten multi-index columns if needed
if isinstance(df.columns, pd.MultiIndex):
df.columns = df.columns.get_level_values(0)
# For safety, rename columns so we can do df["High"], etc.
df.rename(columns={"Open":"Open", "High":"High", "Low":"Low", "Close":"Close", "Volume":"Volume"}, inplace=True)
df.dropna(subset=["High","Low","Close"], inplace=True)
# hl2
df["hl2"] = (df["High"] + df["Low"]) / 2.0
2.3 Measure Volatility with ATR
We use a 10-bar Average True Range to measure volatility. It can capture both intraday movement and overnight gaps.
First, we compute the True Range, TR, for each bar:

Then we smooth it using an Exponential Moving Average EMA:

This measure is dynamic. It expands in volatile markets and contracts in stable ones. This is the foundation for the SuperTrend bands that follow.
# Compute ATR
df["prev_close"] = df["Close"].shift(1)
df["tr1"] = df["High"] - df["Low"]
df["tr2"] = (df["High"] - df["prev_close"]).abs()
df["tr3"] = (df["Low"] - df["prev_close"]).abs()
df["tr"] = df[["tr1","tr2","tr3"]].max(axis=1)
# Use EMA for ATR
df["atr"] = df["tr"].ewm(alpha=2/(ATR_LENGTH+1), adjust=False).mean()
df.dropna(inplace=True)
df.reset_index(drop=False, inplace=True) # We'll keep a normal numeric index plus a 'Date' column
n = len(df)
# Helper: sign --> Returns +1 for up, -1 for down, 0 for no change. used to track price direction
def sign(x):
# x > 0 => 1, x < 0 => -1, x=0 => 0
return np.where(x>0, 1, np.where(x<0, -1, 0))
2.4 Generate SuperTrend Variants Across Multiple Factors
Now that we have volatility and a range of candidate multipliers, we generate multiple SuperTrend signals, one for each value of f between the minimum and maximum range.
For every bar, each SuperTrend variation builds dynamic upper and lower bands:

These bands trail price action. When the close rises above the previous upper band, the signal flips bullish.
When it drops below the lower band, it flips bearish. If price stays between the bands, the trend remains unchanged.
At every step, we also compute a custom performance score. This score increases when the trend direction matches the price move, and decreases when it doesn’t.
It’s smoothed using an EMA so recent behavior has more influence:

This scoring method tracks how well each SuperTrend version aligns with actual price movement.
It gives us a single number that summarizes the “quality” of each factor.
We loop through the full range of factors and compute:
The trend direction over time
The upper and lower stop bands
A performance score for each factor
Once all variations are processed, we collect the final performance values and the corresponding factors.
These are the inputs for clustering in the next step.
# Compute supertrend for each factor
def compute_supertrend(df, factor, perf_alpha):
"""
For each bar:
up = hl2 + atr * factor
dn = hl2 - atr * factor
We track 'trend' (1 = bullish, 0 = bearish), 'upper', 'lower', 'output', 'perf'.
'perf' is updated with an EMA of sign-based price changes.
"""
arr_close = df["Close"].values
arr_hl2 = df["hl2"].values
arr_atr = df["atr"].values
trend = np.zeros(n, dtype=int)
upper = np.zeros(n, dtype=float)
lower = np.zeros(n, dtype=float)
output = np.zeros(n, dtype=float)
perf = np.zeros(n, dtype=float)
# Init
trend[0] = 1 if arr_close[0] > arr_hl2[0] else 0
upper[0] = arr_hl2[0]
lower[0] = arr_hl2[0]
output[0] = arr_hl2[0]
perf[0] = 0.0
for i in range(1, n):
up = arr_hl2[i] + arr_atr[i]*factor
dn = arr_hl2[i] - arr_atr[i]*factor
# Determine new trend
if arr_close[i] > upper[i-1]:
trend[i] = 1
elif arr_close[i] < lower[i-1]:
trend[i] = 0
else:
trend[i] = trend[i-1]
# Update upper & lower
if arr_close[i-1] < upper[i-1]:
upper[i] = min(up, upper[i-1])
else:
upper[i] = up
if arr_close[i-1] > lower[i-1]:
lower[i] = max(dn, lower[i-1])
else:
lower[i] = dn
# Performance metric
diff_sign = sign(arr_close[i-1] - output[i-1])
# Weighted EMA approach
perf[i] = perf[i-1] + 2/(perf_alpha+1)*((arr_close[i] - arr_close[i-1]) * diff_sign - perf[i-1])
# Output
output[i] = lower[i] if trend[i] == 1 else upper[i]
return {
"trend": trend,
"upper": upper,
"lower": lower,
"output": output,
"perf": perf,
"factor": factor
}
factors = np.arange(MIN_MULT, MAX_MULT + 0.0001, STEP)
st_results = []
for f in factors:
st = compute_supertrend(df, f, PERF_ALPHA)
st_results.append(st)
# We'll cluster using the final performance value
perf_vals = np.array([res["perf"][-1] for res in st_results])
fact_vals = np.array([res["factor"] for res in st_results])
2.5 Group Signal Quality Using K-Means Clustering
The final performance metric for each factor is clustered into three groups (Best, Average, Worst) using k-means clustering (with k=3).
The cluster corresponding to the chosen category, e.g. “Best”, determines the target factor for the SuperTrend indicator.
# K-means clustering (k=3)
def k_means(data, factors, k=3, max_iter=1000):
# Initialize centroids from quartiles
c1, c2, c3 = np.percentile(data, [25, 50, 75])
centroids = np.array([c1, c2, c3])
for _ in range(max_iter):
clusters = {0: [], 1: [], 2: []}
cluster_factors = {0: [], 1: [], 2: []}
# Assign each data point
for d, f in zip(data, factors):
dist = np.abs(d - centroids)
idx = dist.argmin()
clusters[idx].append(d)
cluster_factors[idx].append(f)
# Recompute centroids
new_centroids = np.array([np.mean(clusters[i]) if len(clusters[i])>0 else centroids[i]
for i in range(k)])
# Check if converged
if np.allclose(new_centroids, centroids):
break
centroids = new_centroids
return clusters, cluster_factors, centroids
clusters, cluster_factors, centroids = k_means(perf_vals, fact_vals, k=3, max_iter=MAX_ITER)
# Sort clusters by centroid
order = np.argsort(centroids)
sorted_clusters = {i: clusters[j] for i,j in enumerate(order)}
sorted_cluster_factors = {i: cluster_factors[j] for i,j in enumerate(order)}
sorted_centroids = centroids[order]
# 'Best' => index 2, 'Average' => index 1, 'Worst' => index 0
if FROM_CLUSTER == "Best":
chosen_index = 2
elif FROM_CLUSTER == "Average":
chosen_index = 1
else:
chosen_index = 0
if len(sorted_cluster_factors[chosen_index])>0:
target_factor = np.mean(sorted_cluster_factors[chosen_index])
else:
target_factor = factors[-1]
# Compute the average performance for that cluster
if len(sorted_clusters[chosen_index])>0:
target_perf = np.mean(sorted_clusters[chosen_index])
else:
target_perf = 0.0
2.6 Build the Final Adaptive SuperTrend Signal
With the target factor, the final SuperTrend is recalculated. Trend states are updated for each bar (1 for bullish, 0 for bearish).
Additionally, an adaptive moving average is built for the trailing stop to smooth the indicator based on recent price changes.
# Recompute final supertrend with target_factor
st_final = compute_supertrend(df, target_factor, PERF_ALPHA)
ts = st_final["output"]
os = np.zeros(n, dtype=int) # 1 = bullish, 0 = bearish
# The logic: os = close>upper => 1, close<lower => 0, else same as prior
os[0] = 1 if df["Close"].iloc[0] > st_final["upper"][0] else 0
for i in range(1, n):
c = df["Close"].iloc[i]
up = st_final["upper"][i]
dn = st_final["lower"][i]
if c > up:
os[i] = 1
elif c < dn:
os[i] = 0
else:
os[i] = os[i-1]
# Build an adaptive MA for the trailing stop
den_close_diff = (df["Close"] - df["Close"].shift(1)).abs()
den = den_close_diff.ewm(alpha=2/(PERF_ALPHA+1), adjust=False).mean()
den_val = den.iloc[-1] if den.iloc[-1] != 0 else 1e-9
perf_idx = max(target_perf, 0) / den_val
perf_ama = np.zeros(n, dtype=float)
perf_ama[0] = ts[0]
for i in range(1, n):
perf_ama[i] = perf_ama[i-1] + perf_idx*(ts[i] - perf_ama[i-1])
If you want to get a full code snippet, scroll down, become a paid member and grab a full ready to run Google Collab Notebook 👇
2.7 Visualize Signals
Finally, we display the results to show the close price, the bullish and bearish trailing stop lines, and the AMA of the trailing stop.
Trend change signals are also marked on the chart.
# Plotting
mplstyle.use("dark_background")
fig, ax = plt.subplots(figsize=(18,6))
dates = mdates.date2num(df["Date"])
# Plot close price
ax.plot(dates, df["Close"], color="silver", lw=1.2, label="Close Price")
# Build separate lines for bullish/bearish trailing stop
ts_bull = np.where(os==1, ts, np.nan)
ts_bear = np.where(os==0, ts, np.nan)
ax.plot(dates, ts_bull, color="teal", lw=1.2, label="Bullish Stop")
ax.plot(dates, ts_bear, color="red", lw=1.2, label="Bearish Stop")
# Plot AMA line
ax.plot(dates, perf_ama, color="orange", lw=1.0, alpha=0.7, label="Trailing Stop AMA")
# Plot signals where os changes
for i in range(1, n):
if os[i] != os[i-1]:
# If os[i] = 1 => bullish signal
if os[i] == 1:
ax.scatter(dates[i], ts[i], marker='^', s=80, color="teal", edgecolor="white", zorder=5)
else:
ax.scatter(dates[i], ts[i], marker='v', s=80, color="red", edgecolor="white", zorder=5)
ax.set_title(f"SuperTrend (Clustering) - {TICKER} [Factor ~ {target_factor:.2f}]", color="white")
ax.set_xlabel("Date", color="white")
ax.set_ylabel("Price", color="white")
ax.legend(loc="upper left")
# Format date axis
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
fig.autofmt_xdate()
plt.grid(True, alpha=0.2)
plt.tight_layout()
plt.show()

Figure 2. Buy and sell signals generated using the average factor from the best-performing k-means cluster.
The model handled trend shifts well. It stayed responsive during strong moves and avoided overreacting in choppy zones.
The stop levels adjusted naturally, i.e. tight when the market was quiet, wider when things got volatile.
Most signals aligned with clear trend changes and the adaptive stop helped hold onto gains without getting shaken out too early.
4. Applications and Extensions
This system isn’t limited to SuperTrend. The same self-tuning approach can be applied to almost any indicator.
You can swap out SuperTrend for RSI, MACD, Bollinger Bands or even custom logic.
As long as the output produces a directional signal, you can score, cluster, and adapt it the same way.
It’s also easy to plug into a backtest engine. Wrap the output in a position management layer and run full PnL simulations.
You can also tune across timeframes, like running a short-term model on 5-minute bars and a slower version on hourly data.
5. Limitations and Considerations
This isn’t a full trading strategy, but a signal engine. It gives structure and adaptability, but not position sizing, portfolio context, or trade execution.
It also doesn’t guarantee profit. A good signal can still perform poorly in difficult markets.
What it does offer is a way to reduce the guesswork around parameter tuning.
Most importantly, this model is adaptive, not predictive. It reacts to recent conditions but doesn’t try to forecast future price.
Concluding Thoughts
You don’t always need complex machine learning to make your signals smarter.
Sometimes, unsupervised clustering and a simple scoring function are enough.
We didn’t optimize parameters. We built a system that optimizes itself, using only price action and recent performance as feedback.
Adaptation beats optimization when the market keeps changing. This project is just one way to put that idea into action.
There you have it—the complete guide. For paid subscribers only: Scroll down to grab the full, ready-to-run code snippet. Not subscribed yet? Join now and unlock all our premium content, including this one. Your projects deserve the best tools—let's level up together!
Subscribe to our premium content to get the full code snippet.
Become a paying subscriber to get access to this post and other subscriber-only content.
Upgrade