- GuruFinance Insights
- Posts
- I Used Bayesian Optimization to Optimize a Trading Strategy on the Top 10 Most Active Stocks
I Used Bayesian Optimization to Optimize a Trading Strategy on the Top 10 Most Active Stocks
How randomness becomes structure through density functions, stochastic processes, and PDEs
Inventory Software Made Easy—Now $499 Off
Looking for inventory software that’s actually easy to use?
inFlow helps you manage inventory, orders, and shipping—without the hassle.
It includes built-in barcode scanning to facilitate picking, packing, and stock counts. inFlow also integrates seamlessly with Shopify, Amazon, QuickBooks, UPS, and over 90 other apps you already use
93% of users say inFlow is easy to use—and now you can see for yourself.
Try it free and for a limited time, save $499 with code EASY499 when you upgrade.
Free up hours each week—so you can focus more on growing your business.
✅ Hear from real users in our case studies
🚀 Compare plans on our pricing page
🚀 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.

Strategy vs Buy & Hold Performance
I’ve been building and backtesting trading strategies for a while, but I wanted to answer a simple question for myself:
Can something as basic as a moving average crossover strategy — when properly tuned — outperform Buy & Hold?
Most investors, especially beginners, are told to just hold. And to be honest, it works well — over time, most stocks do go up. But I was curious.
What if we gave an active strategy the benefit of optimization? Could we beat the baseline?
So I decided to run an experiment. I used Bayesian Optimization to fine-tune a moving average crossover strategy across the 10 most actively traded stocks on the market.
This article walks through what I did, what I found, and what it means.
The Future of AI in Marketing. Your Shortcut to Smarter, Faster Marketing.
This guide distills 10 AI strategies from industry leaders that are transforming marketing.
Learn how HubSpot's engineering team achieved 15-20% productivity gains with AI
Learn how AI-driven emails achieved 94% higher conversion rates
Discover 7 ways to enhance your marketing strategy with AI.
The Strategy
I kept the strategy deliberately simple by using the classic Moving Average Crossover approach:
Buy when the short-term moving average crosses above the long-term moving average
Sell (or exit) when the short-term moving average crosses back below the long-term moving average
This Moving Average Crossover strategy is one of the oldest and most widely known techniques in technical analysis.
Instead of guessing the best short and long moving average windows, I used Bayesian Optimization to find the combination that maximizes return on the training data.
Why Bayesian Optimization?
It’s faster and smarter than grid search, and it helps navigate noisy objective functions like financial returns, which can be highly non-linear and unstable.
The Stocks
Here’s the list I used — all high-volume names across different industries:
tickers = ["LCID", "NVDA", "INTC", "F", "TSLA", "HOOD", "WULF", "CIFR", "SOFI", "IREN"]
Each stock’s historical data was pulled from Yahoo Finance from January 2020 to June 2025. I split the data into:
Training set: Jan 2020 — Dec 2023 (used for optimization)
Test set: Jan 2024 — Jun 2025 (used to evaluate results)
I kept the initial capital fixed at $10 for every run to compare percentage returns across tickers.
The Process
Setup and Parameters
For each stock in the list of top 10 most active tickers, I executed the following procedure:
First, I imported the necessary Python libraries and set up the parameters and tickers I wanted to analyze.
This includes fetching stock data, handling data with pandas, plotting results, and using Bayesian Optimization to find the best moving average windows.
I set the date range, initial capital, and prepared an empty list to store results.
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from bayes_opt import BayesianOptimization
import time
import numpy as np
import calendar
plt.style.use("dark_background")
tickers = ["LCID", "NVDA", "INTC", "F", "TSLA", "HOOD", "WULF", "CIFR", "SOFI", "IREN"]
start_date = "2020-01-01"
end_date = "2025-06-30"
train_cutoff_date = "2023-12-31"
initial_capital = 10
results = []
Defining the Moving Average Crossover Strategy
Next, I defined the core trading logic implementing the moving average crossover strategy.
📈 Algorithmic Trading Course — Join the Waitlist
I’m launching a premium online course on algorithmic trading starting July 14. The price is $3000, and spots will be limited.
🔥 What to Expect:
Build trading bots with Python
Backtest strategies with real data
Learn trend-following, mean-reversion, and more
Live execution, portfolio risk management
Bonus: machine learning & crypto trading modules
🎁 Free Bonus:
Everyone who enrolls will get a free e-book:
“100 Trading Strategies in Python” — full of ready-to-use code.
Returns are calculated daily and compounded to form an equity curve. The function returns the final portfolio value, total trades, and the detailed dataframe for further analysis.
def backtest_strategy_double_ma(data, short_window, long_window, initial_capital):
df = data.copy()
df['SMA_Short'] = df['Close'].rolling(window=short_window).mean()
df['SMA_Long'] = df['Close'].rolling(window=long_window).mean()
df['Signal'] = 0
df.loc[df['SMA_Short'] > df['SMA_Long'], 'Signal'] = 1
df['Position'] = df['Signal'].shift(1)
df['Return'] = df['Close'].pct_change()
df['Strategy Return'] = df['Position'] * df['Return']
df['Equity Curve'] = (1 + df['Strategy Return']).cumprod() * initial_capital
final_value = df['Equity Curve'].iloc[-1]
num_trades = df['Position'].diff().abs().sum()
return final_value, num_trades, df
Running Bayesian Optimization and Backtesting for Each Ticker
Then, for each ticker, I downloaded the historical closing prices, split the data into training and testing periods, and ran Bayesian Optimization to identify the best short and long moving average window lengths that maximize return on training data.
I applied the optimized strategy to the test data, compared it with a Buy & Hold approach, recorded key metrics, and plotted equity curves for visual comparison.
for symbol in tickers:
print(f"Running for {symbol}...")
df = yf.download(symbol, start=start_date, end=end_date)[['Close']]
df_train = df.loc[start_date:train_cutoff_date].copy()
df_test = df.loc[train_cutoff_date:end_date].copy()
def backtest_bo(short_window, long_window):
short_window = int(round(short_window))
long_window = int(round(long_window))
if short_window >= long_window:
return -1e10
final_value, _, _ = backtest_strategy_double_ma(df_train, short_window, long_window, initial_capital)
return final_value
pbounds = {'short_window': (5, 50), 'long_window': (55, 200)}
optimizer = BayesianOptimization(f=backtest_bo, pbounds=pbounds, random_state=42, verbose=0)
optimizer.maximize(init_points=5, n_iter=45)
best_params = optimizer.max['params']
best_short_window = int(round(best_params['short_window']))
best_long_window = int(round(best_params['long_window']))
_, _, df_test_result = backtest_strategy_double_ma(df_test, best_short_window, best_long_window, initial_capital)
df_test_result['Buy & Hold'] = (1 + df_test_result['Return']).cumprod() * initial_capital
final_strategy_value = df_test_result['Equity Curve'].iloc[-1]
final_bh_value = df_test_result['Buy & Hold'].iloc[-1]
strategy_return_pct = ((final_strategy_value / initial_capital) - 1) * 100
bh_return_pct = ((final_bh_value / initial_capital) - 1) * 100
num_trades = df_test_result['Position'].diff().abs().sum()
results.append({
"Symbol": symbol,
"Short Window": best_short_window,
"Long Window": best_long_window,
"Strategy Final Value": final_strategy_value,
"Buy & Hold Final Value": final_bh_value,
"Strategy Return (%)": strategy_return_pct,
"Buy & Hold Return (%)": bh_return_pct,
"Trades": int(num_trades)
})
# Print the results for the current symbol
print(f"{symbol} - Best Short Window: {best_short_window}, Best Long Window: {best_long_window}")
print(f"{symbol} - Strategy Final Value: ${final_strategy_value:.2f}, Buy & Hold Final Value: ${final_bh_value:.2f}")
print(f"{symbol} - Strategy Return: {strategy_return_pct:.2f}%, Buy & Hold Return: {bh_return_pct:.2f}%")
print(f"{symbol} - Number of Trades: {int(num_trades)}\n")
# Plot equity curve comparison
plt.figure(figsize=(12, 6))
plt.plot(df_test_result['Equity Curve'], label='Strategy', color='green')
plt.plot(df_test_result['Buy & Hold'], label='Buy & Hold', linestyle='--', color='orange')
plt.title(f"{symbol} - Out-of-Sample Equity Curve")
plt.xlabel("Date")
plt.ylabel("Portfolio Value (USD)")
plt.legend()
plt.grid(True, color='gray', linestyle='--', linewidth=0.5)
plt.tight_layout()
plt.savefig(f"{symbol}_equity_curve.png", dpi=300)
plt.show()
A Few Equity Curve Outputs
Here are a few equity curves for the first 3 tickers.
You can generate the rest of the equity curves by running the full notebook, available in this GitHub Repository.

LCID Equity Curve on Testing Data

NVDA Equity Curve on Testing Data

INTC Equity Curve on Testing Data
Compiling and Saving Results for Comparison
After all tickers were processed, I compiled the results into a DataFrame for easy comparison, sorted it by strategy return, saved the summary as a CSV file, and displayed the table for review.
results_df = pd.DataFrame(results)
results_df = results_df.set_index("Symbol")
results_df = results_df.sort_values("Strategy Return (%)", ascending=False)
results_df.to_csv("multi_ticker_strategy_comparison.csv")
results_df

Results of Strategy vs Buy & Hold
Visualizing Comparative Performance
Finally, I visualized the comparative performance by plotting a grouped bar chart showing the return percentages of the optimized strategy versus Buy & Hold for each stock.
This helped identify which tickers benefited most from the strategy.
plt.figure(figsize=(12, 6))
x = np.arange(len(results_df))
width = 0.35
bars1 = plt.bar(x - width/2, results_df["Strategy Return (%)"], width, label="Strategy", color='red')
bars2 = plt.bar(x + width/2, results_df["Buy & Hold Return (%)"], width, label="Buy & Hold", color='green')
for bars in [bars1, bars2]:
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2, height + np.sign(height)*2, f"{height:.1f}%",
ha='center', va='bottom' if height >= 0 else 'top', fontsize=12)
plt.xticks(x, results_df.index, rotation=45)
plt.ylabel("Return (%)")
plt.title("Strategy vs Buy & Hold Performance (Test Set)")
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.savefig("comparison_bar_chart.png", dpi=300)
plt.show()

Strategy vs Buy & Hold Performance
Performance Breakdown

Results of Strategy vs Buy & Hold
I compiled the final returns and number of trades for each stock during the test period. The results revealed some interesting patterns.
For example, the strategy delivered impressive gains on HOOD and SOFI, generating returns of over 130%, while Buy & Hold returns were mixed — extremely high for HOOD but lower for SOFI.
TSLA and WULF also benefited from the strategy, showing double-digit positive returns that in some cases outperformed Buy & Hold.
On the other hand, NVDA had a strong Buy & Hold return of over 227%, while the strategy’s performance was more modest at around 20%.
Some stocks, like INTC, LCID, and CIFR, struggled under the strategy, showing negative returns, though Buy & Hold results varied — INTC and LCID also showed losses with Buy & Hold, while CIFR had a small positive return.
The number of trades varied across stocks, with the strategy typically making fewer than 10 trades during the test period, indicating it was not overly active.
Overall, this comparison helped me understand which tickers responded well to a simple moving average crossover strategy, and which were better suited to passive holding or required more nuanced approaches.
What I Learned
A simple strategy, in the right context, can be surprisingly effective
On fast-moving and highly volatile stocks like HOOD and SOFI, the double moving average crossover strategy managed to capture momentum and ride strong trends.
But on less directional or more erratic stocks, its signals became noisy and less reliable.
Choosing parameters randomly isn’t enough
Using arbitrary moving average windows like 50/200 may work occasionally, but there’s no reason to stick to tradition when we have tools like Bayesian Optimization.
Letting the data guide the parameters gave this simple strategy a real edge during the training phase — and it often carried into the test period.
It’s not about winning everywhere
I didn’t expect this strategy to outperform across the board, and it didn’t. But that wasn’t the point.
The real insight was learning which kinds of stocks respond well to trend-following signals, and which don’t. Buy & Hold is still a solid baseline — but active strategies can have their place, especially when applied with care and context.
All the code in this guide can be found in this GitHub Repository..