• GuruFinance Insights
  • Posts
  • I Optimized the Ichimoku Cloud Trading Strategy with Bayesian Optimization from 32% to 168% Returns

I Optimized the Ichimoku Cloud Trading Strategy with Bayesian Optimization from 32% to 168% Returns

How optimizing Ichimoku parameters with Bayesian methods can dramatically improve your trading strategy

In partnership with

A free newsletter with the marketing ideas you need

The best marketing ideas come from marketers who live it. That’s what The Marketing Millennials delivers: real insights, fresh takes, and no fluff. Written by Daniel Murray, a marketer who knows what works, this newsletter cuts through the noise so you can stop guessing and start winning. Subscribe and level up your marketing game.

🚀 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.

This article is a continuation of my previous article where I introduced the Ichimoku Cloud trading strategy with hand-picked parameters.

AAPL Ichimoku Cloud with Optimized Parameters and Buy/Sell Signals

Background

In a previous article where I broke down the Ichimoku Cloud Trading Strategy, we explored a simple Ichimoku Cloud strategy applied to AAPL historical data.

By manually selecting parameters for Tenkan, Kijun, and Senkou B, the strategy generated a 31.77% return over four years. While the results were promising, manually selecting parameters is limited.

Bayesian Optimization offers a systematic way to find parameter combinations that maximize returns.

In this article, we will apply Bayesian Optimization to take our strategy to the next level, increasing returns to 167.50%.

It’s go-time for holiday campaigns

Roku Ads Manager makes it easy to extend your Q4 campaign to performance CTV.

You can:

  • Easily launch self-serve CTV ads

  • Repurpose your social content for TV

  • Drive purchases directly on-screen with shoppable ads

  • A/B test to discover your most effective offers

The holidays only come once a year. Get started now with a $500 ad credit when you spend your first $500 today with code: ROKUADS500. Terms apply.

1. Install Required Packages

First, we install the necessary Python packages for data handling, visualization, and optimization.

# Install necessary packages
%pip install yfinance matplotlib tabulate scikit-optimize -q

These libraries allow us to download historical stock data (yfinance), visualize results (matplotlib), format tables (tabulate), and perform Bayesian Optimization (scikit-optimize).

2. Import Libraries

We import the libraries needed for this analysis.

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from tabulate import tabulate
from skopt import gp_minimize
from skopt.space import Integer
import numpy as np

This sets up the environment for data processing, visualization, and optimization.

3. Download Historical Data

We will analyze AAPL stock from 2020 to 2024.

# Download historical stock data
symbol = "AAPL"
data = yf.download(symbol, start="2020-01-01", end="2024-12-31")

# Flatten multi-level columns
data.columns = data.columns.get_level_values(0)
data.head()

This provides the raw Open, High, Low and Cloud data needed to calculate Ichimoku lines and signals.

What Smart Investors Read Before the Bell Rings

Clickbait headlines won’t grow your portfolio. That’s why over 1M investors — including Wall Street insiders — start their day with The Daily Upside. Founded by investment bankers and journalists, it cuts through the noise with clear insights on business, markets, and the economy. Stop guessing and get smarter every morning.

4. Visualize Closing Price

Before we start optimizing, it’s useful to see the stock’s price history.

# Plot raw closing price
plt.figure(figsize=(14,6))
plt.plot(data.index, data['Close'], color='black', linewidth=1.5, label=f"{symbol} Closing Price")
plt.title(f"{symbol} Closing Price (2020-2024)")
plt.xlabel("Date")
plt.ylabel("Price ($)")
plt.legend()
plt.savefig(f"figures/{symbol}_closing_price.png", dpi=300)
plt.show()

AAPL Closing Price (2020–2024)

This chart provides context for our strategy identifying trends, volatility, and potential trading opportunities.

5. Define Ichimoku Strategy Function

We define a function to calculate Ichimoku lines, generate buy signals, and backtest performance.

def ichimoku_signals(data, tenkan_period, kijun_period, senkou_b_period):
    df = data.copy()
    high = df['High']
    low = df['Low']
    close = df['Close']

    # Ichimoku lines
    df['tenkan'] = (high.rolling(tenkan_period).max() + low.rolling(tenkan_period).min()) / 2
    df['kijun'] = (high.rolling(kijun_period).max() + low.rolling(kijun_period).min()) / 2
    df['senkou_a'] = ((df['tenkan'] + df['kijun']) / 2).shift(kijun_period)
    df['senkou_b'] = ((high.rolling(senkou_b_period).max() + low.rolling(senkou_b_period).min()) / 2).shift(kijun_period)

    # Signals
    df['above_cloud'] = df['Close'] > df[['senkou_a', 'senkou_b']].max(axis=1)
    df['tenkan_cross'] = (df['tenkan'] > df['kijun']) & (df['tenkan'].shift(1) <= df['kijun'].shift(1))
    df['signal'] = np.where(df['above_cloud'] & df['tenkan_cross'], 1, 0)

    # Backtest
    balance = 10000
    position = 0
    equity_curve = []

    for i in range(len(df)):
        if df['signal'].iloc[i] == 1 and position == 0:
            position = balance / df['Close'].iloc[i]
            balance = 0
        elif position > 0 and df['Close'].iloc[i] < df[['senkou_a','senkou_b']].min(axis=1).iloc[i]:
            balance = position * df['Close'].iloc[i]
            position = 0
        equity_curve.append(balance + position * df['Close'].iloc[i])

    df['equity'] = equity_curve
    total_return = (df['equity'].iloc[-1] - 10000) / 10000 * 100

    return df, total_return  # return the full df for plotting

This function allows us to calculate signals and returns for any given Ichimoku parameters, necessary for Bayesian Optimization.

6. Define Objective Function for Optimization

Bayesian Optimization requires an objective function that it will minimize. Since we want to maximize return, we return the negative total return.

# Negative return because gp_minimize minimizes the function
def objective(params):
    tenkan, kijun, senkou_b = params
    _, total_return = ichimoku_signals(data, tenkan, kijun, senkou_b)  # discard df, keep return
    return -total_return  # gp_minimize minimizes

This ensures that the optimizer searches for parameters that produce the highest total return.

7. Define Search Space and Run Optimization

We define ranges for Tenkan, Kijun, and Senkou B periods and run Bayesian Optimization.

search_space = [
    Integer(5, 20, name='tenkan_period'),    # Tenkan period
    Integer(20, 50, name='kijun_period'),    # Kijun period
    Integer(40, 100, name='senkou_b_period') # Senkou B period
]

result = gp_minimize(objective, search_space, n_calls=25, random_state=42)

The optimizer explores combinations of parameters and converges to the set that maximizes returns.

8. Run the Strategy with Optimized Parameters

After running the optimization, we run the strategy and visualize the returns alongside the optimal parameters.

best_tenkan, best_kijun, best_senkou_b = result.x
best_return = -result.fun

# Run strategy with optimized parameters to get equity curve
data_optimized, final_return = ichimoku_signals(data.copy(), best_tenkan, best_kijun, best_senkou_b)

# Calculate max drawdown
max_equity = data_optimized['equity'].cummax()
drawdown = (data_optimized['equity'] - max_equity) / max_equity
max_drawdown = drawdown.min() * 100  # convert to percentage

# Prepare stats table including max drawdown
stats = [
    ["Optimized Tenkan Period", best_tenkan],
    ["Optimized Kijun Period", best_kijun],
    ["Optimized Senkou B Period", best_senkou_b],
    ["Expected Total Return", f"{best_return:.0f}%"],
    ["Max Drawdown", f"{max_drawdown:.0f}%"]
]

print(tabulate(stats, headers=["Metric", "Value"], tablefmt="rounded_outline"))
╭───────────────────────────┬─────────╮
 Metric                     Value   
├───────────────────────────┼─────────┤
 Optimized Tenkan Period    19      
 Optimized Kijun Period     20      
 Optimized Senkou B Period  98      
 Expected Total Return      168%    
 Max Drawdown               -21%    
╰───────────────────────────┴─────────╯

9. Interpreting the Optimized Results

The optimization process has clearly had a significant impact on the performance of our Ichimoku Cloud strategy.

With the hand-picked parameters from the previous article, the strategy returned 31.77% with a maximum drawdown of -32%. This meant that while the strategy was profitable, it exposed the portfolio to relatively large temporary losses.

After applying Bayesian Optimization, the strategy’s expected return skyrocketed to 168%, and the maximum drawdown shrank dramatically to -21%.

In short, the optimized strategy is both more profitable and safer than the original approach. The results make a strong case for using data-driven optimization methods when fine-tuning technical strategies.

10. Identify Trades and Visualize Signals

# Identify trades for optimized parameters
buy_points = []
sell_points = []
position = 0  # 0 = flat, 1 = long

for i in range(len(data_optimized)):
    if data_optimized['signal'].iloc[i] == 1 and position == 0:
        buy_points.append((data_optimized.index[i], data_optimized['Close'].iloc[i]))
        position = 1
    elif position == 1 and data_optimized['Close'].iloc[i] < data_optimized[['senkou_a','senkou_b']].min(axis=1).iloc[i]:
        sell_points.append((data_optimized.index[i], data_optimized['Close'].iloc[i]))
        position = 0

# Convert to DataFrames for plotting
buy_df = pd.DataFrame(buy_points, columns=["Date","Price"]).set_index("Date")
sell_df = pd.DataFrame(sell_points, columns=["Date","Price"]).set_index("Date")

# Plot price, Ichimoku lines, cloud, and trade signals
plt.figure(figsize=(14,8))
plt.plot(data_optimized.index, data_optimized['Close'], label="Close", color='black', linewidth=1)
plt.plot(data_optimized.index, data_optimized['tenkan'], label="Tenkan (Conversion)", color='blue', linewidth=1.2)
plt.plot(data_optimized.index, data_optimized['kijun'], label="Kijun (Base)", color='red', linewidth=1.2)

# Cloud area
plt.fill_between(
    data_optimized.index, data_optimized['senkou_a'], data_optimized['senkou_b'],
    where=data_optimized['senkou_a'] >= data_optimized['senkou_b'],
    color='lightgreen', alpha=0.4
)
plt.fill_between(
    data_optimized.index, data_optimized['senkou_a'], data_optimized['senkou_b'],
    where=data_optimized['senkou_a'] < data_optimized['senkou_b'],
    color='lightcoral', alpha=0.4
)

# Buy & Sell signals with higher zorder and lime color
plt.scatter(buy_df.index, buy_df['Price'], marker="^", color="lime", s=100, label="Buy Signal", zorder=5)
plt.scatter(sell_df.index, sell_df['Price'], marker="v", color="red", s=100, label="Sell Signal", zorder=5)

plt.title(f"{symbol} Ichimoku Cloud with Optimized Parameters and Trade Signals")
plt.legend(loc="upper left")
plt.savefig(f"figures/{symbol}_ichimoku_optimized_signals.png", dpi=300)
plt.show()

AAPL Ichimoku Cloud with Optimized Parameters and Trade Signals

This visualization clearly shows the optimized strategy in action. Green arrows mark buys, red arrows mark sells, and the Ichimoku cloud provides trend context.

By applying Bayesian Optimization to the Ichimoku Cloud strategy:

  • Return increased from 31.77% to 167.50% over the same period.

  • Optimization helped identify the best combination of Tenkan, Kijun, and Senkou B periods.

  • Visualizations show how the strategy adapts to price trends and signals.

In future work, we can further refine the strategy using more advanced optimization techniques, multi-asset backtesting, or dynamic risk-adjusted parameters.

Stay updated: Subscribe to catch the next article where I dive deeper into optimizing this strategy using Bayesian methods with live parameter tuning.