In partnership with

Is Your PPC Strategy Leaving Money on the Table?

When’s the last time you updated your digital marketing strategy?

If you’re relying on old-school PPC tactics you might be missing out on a major revenue opportunity.

Levanta’s Affiliate Ad Shift Calculator shows how shifting budget from PPC to creator-led partnerships can significantly improve conversion rates, ROI, and efficiency.

Discover how optimizing your affiliate strategy can unlock new profit potential:

  • Commission structure: Find the ideal balance between cost and performance

  • Traffic mix: See how creator-driven traffic impacts conversions

  • Creator engagement: Measure how authentic partnerships scale ROI

Built for brands ready to modernize how they grow.

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

Optimized DMAC Buy/Sell Signals on Ford Stock

I used Bayesian Optimization to fine-tune a simple moving average strategy with Sharpe Ratio as the performance metric.

The strategy went head-to-head with buy-and-hold, and the results were more interesting than I expected.

Why I Tried This

Most strategies you see online either rely on brute force grid searches or overfitted machine learning models. I wanted something practical. Something I could control.

So I decided to optimize a basic trading strategy using a smarter search method and measure its performance not just by return, but by risk-adjusted return.

The idea was simple.

  1. Take a basic moving average crossover strategy.

  2. Use Bayesian Optimization to search for the best parameters.

  3. Use the Sharpe Ratio to evaluate how good each version of the strategy is.

  4. Then compare the final result to just buying and holding the stock.

Before jumping into the code, it’s important to understand what I actually optimized and how the pieces fit together.

The Sharpe Ratio

This is our main evaluation metric. It measures how much return a strategy produces per unit of risk taken. It’s more meaningful than just return because it penalizes volatility.

We annualize it by multiplying by the square root of 252 since we’re working with daily returns.

The Strategy: Double Moving Average Crossover (DMAC)

The strategy is simple. If the short-term moving average is above the long-term moving average, we hold a long position. If not, we stay in cash. It’s a rule-based system that can be implemented in a few lines of code.

The only challenge is picking the right short and long window lengths. That’s where optimization comes in.

Bayesian Optimization

Instead of blindly testing every parameter combination, Bayesian Optimization tries to model the function we’re optimizing.

It starts with a few trials, learns which areas of the parameter space are promising, and then explores those areas more thoroughly.

I’m using Optuna with a TPE sampler, which works well for this kind of search.

Don’t Buy Bitcoin at $120k+

Smart investors generate Bitcoin through professional mining operations at production cost instead of buying at peak prices.

Get daily Bitcoin payouts, massive tax benefits, and zero operational hassles. We handle everything in green energy facilities.

Project Setup

If you’d like to explore or run the experiment yourself, the full code is available on my GitHub.

First, the packages. This entire experiment runs in a Jupyter notebook.

%pip install yfinance optuna matplotlib pandas numpy tabulate --quiet

Import Libraries

import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from tabulate import tabulate
import optuna
from optuna.samplers import TPESampler

plt.style.use('dark_background')

Getting the Data

I chose Ford (ticker: F) because it’s a well-known, liquid stock. The data ranges from 2015 to 2024.

def get_data(ticker="F", start="2015-01-01", end="2024-12-31"):
    df = yf.download(ticker, start=start, end=end)
    df.columns = df.columns.get_level_values(0)
    df = df[['Close']]
    return df

data = get_data()
data.head()

Plotting the price gives us a sense of what we’re working with.

plt.figure(figsize=(14, 7))
plt.plot(data.index, data['Close'], color='blue', linewidth=1)
plt.title('Closing Price of Ford (Ticker: F)')
plt.xlabel('Date')
plt.ylabel('Close Price ($)')
plt.grid(True)
plt.tight_layout()
plt.savefig('closing_price_plot.png', dpi=300)
plt.show()

Ford Closing Price

Train-Test Split

We’ll use 80% of the data to optimize the parameters and the remaining 20% to test them.

# 80/20 train-test split
split_date = data.index[int(len(data) * 0.8)]
train_data = data[:split_date]
test_data = data[split_date:]
print(f"Train: {train_data.index[0].date()} to {train_data.index[-1].date()}")
print(f"Test:  {test_data.index[0].date()} to {test_data.index[-1].date()}")
Train: 2015-01-02 to 2022-12-29
Test:  2022-12-29 to 2024-12-30

Strategy Implementation

The strategy goes long when the short moving average crosses above the long one.

When it falls below, we move to cash. This implementation uses long-flat logic. No shorting, just in or out.

def apply_dmac(df, short_window, long_window):
    if short_window >= long_window:
        return pd.DataFrame()

    data = df.copy()
    data['short_ma'] = data['Close'].rolling(window=short_window).mean()
    data['long_ma'] = data['Close'].rolling(window=long_window).mean()

    # Long/flat signals: 1 = long, 0 = flat (cash)
    data['signal'] = 0
    data.loc[data['short_ma'] > data['long_ma'], 'signal'] = 1

    data['position'] = data['signal'].shift(1).fillna(0)

    # Buy and sell flags (optional for plotting)
    data['buy'] = (data['position'] == 1) & (data['position'].shift(1) == 0)
    data['sell'] = (data['position'] == 0) & (data['position'].shift(1) == 1)

    # Calculate returns
    data['returns'] = data['Close'].pct_change()
    data['strategy_returns'] = data['position'] * data['returns']

    # Zero returns before first trade
    first_pos_idx = data['position'].first_valid_index()
    if first_pos_idx is not None:
        data.loc[:first_pos_idx, 'strategy_returns'] = 0

    data.dropna(inplace=True)
    return data

Sharpe Ratio Function

This is how we calculate the metric we’re trying to maximize.

def calculate_sharpe(data, risk_free_rate=0.01):
    excess_returns = data['strategy_returns'] - risk_free_rate / 252
    sharpe = np.sqrt(252) * excess_returns.mean() / excess_returns.std()
    return sharpe

Optimization Function

We define the parameter search space and return the Sharpe ratio for each trial.

def objective(trial):
    short_window = trial.suggest_int("short_window", 5, 50)
    long_window = trial.suggest_int("long_window", short_window + 5, 200)

    df = apply_dmac(train_data, short_window, long_window)
    if df.empty or df['strategy_returns'].std() == 0:
        return -np.inf

    return calculate_sharpe(df)

Run the optimization.

sampler = TPESampler(seed=42)
study = optuna.create_study(direction="maximize", sampler=sampler)
study.optimize(objective, n_trials=50)

print("Best Sharpe Ratio:", round(study.best_value, 4))
print("Best Parameters:", study.best_params)
Best Sharpe Ratio: 0.478
Best Parameters: {'short_window': 10, 'long_window': 40}

Business news as it should be.

Join 4M+ professionals who start their day with Morning Brew—the free newsletter that makes business news quick, clear, and actually enjoyable.

Each morning, it breaks down the biggest stories in business, tech, and finance with a touch of wit to keep things smart and interesting.

Visualizing the Signals

With the best parameters found, we apply the strategy to the test data and visualize the buy and sell points.

best_short = study.best_params['short_window']
best_long = study.best_params['long_window']
result_df = apply_dmac(test_data, best_short, best_long)

plt.figure(figsize=(14, 7))
plt.plot(result_df['Close'], label='Price', color='blue', linewidth=1)
plt.plot(result_df['short_ma'], label=f'Short MA ({best_short})', color='green')
plt.plot(result_df['long_ma'], label=f'Long MA ({best_long})', color='orange')

# Buy ▲ and Sell ▼ markers
plt.plot(result_df[result_df['buy']].index, result_df[result_df['buy']]['Close'],
         '^', markersize=14, color='lime', label='Buy Signal')
plt.plot(result_df[result_df['sell']].index, result_df[result_df['sell']]['Close'],
         'v', markersize=14, color='red', label='Sell Signal')

plt.title(f'DMAC Buy/Sell Signals on Test Set (Short={best_short}, Long={best_long})')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.savefig('dmac_signals_plot.png', dpi=300)
plt.show()

DMAC Buy/Sell Signals on Test Set

Cumulative Returns

This is where we evaluate performance.

# Calculate cumulative returns for DMAC strategy (on test_data with best params)
cumulative_strategy = (1 + result_df['strategy_returns']).cumprod()

# Calculate buy & hold cumulative returns on full test_data (fixed, independent of params)
cumulative_bh = (1 + test_data['Close'].pct_change()).cumprod()

plt.figure(figsize=(12, 6))
plt.plot(result_df.index, cumulative_strategy, label='DMAC Strategy', color='orange')
plt.plot(test_data.index, cumulative_bh, label='Buy & Hold', color='green')
plt.title(f'Cumulative Returns on Test Set (Sharpe: {study.best_value:.2f})')
plt.xlabel('Date')
plt.ylabel('Growth of $1')
plt.legend()
plt.grid(True)
plt.savefig('cumulative_returns_plot.png', dpi=300)
plt.show()

Cumulative Returns on Test Set

Final Results

Let’s look at how the strategies performed.

start_capital = 1  # dollars

final_strategy_value = start_capital * cumulative_strategy.iloc[-1]
final_bh_value = start_capital * cumulative_bh.iloc[-1]

strategy_return_pct = (final_strategy_value - start_capital) / start_capital * 100
bh_return_pct = (final_bh_value - start_capital) / start_capital * 100

num_trades = int(result_df['buy'].sum() + result_df['sell'].sum())

# Prepare table data
table = [
    ["Strategy", "Final Value ($)", "Return (%)", "Total Trades"],
    ["DMAC Strategy", f"{final_strategy_value:.2f}", f"{strategy_return_pct:.2f}%", num_trades],
    ["Buy & Hold", f"{final_bh_value:.2f}", f"{bh_return_pct:.2f}%", ""],
]

print(tabulate(table, headers="firstrow", tablefmt="rounded_grid"))
╭───────────────┬───────────────────┬──────────────┬────────────────╮
 Strategy         Final Value ($)  Return (%)    Total Trades   
├───────────────┼───────────────────┼──────────────┼────────────────┤
 DMAC Strategy               0.79  -20.51%       22             
├───────────────┼───────────────────┼──────────────┼────────────────┤
 Buy & Hold                  1.01  1.23%                       
╰───────────────┴───────────────────┴──────────────┴────────────────╯

Performance on Other Tickers

Verizon Communications Inc. (VZ)

╭───────────────┬───────────────────┬──────────────┬────────────────╮
 Strategy         Final Value ($)  Return (%)    Total Trades   
├───────────────┼───────────────────┼──────────────┼────────────────┤
 DMAC Strategy               1.12  11.86%        1              
├───────────────┼───────────────────┼──────────────┼────────────────┤
 Buy & Hold                  1.15  15.49%                      
╰───────────────┴───────────────────┴──────────────┴────────────────╯

Tesla, Inc. (TSLA)

╭───────────────┬───────────────────┬──────────────┬────────────────╮
 Strategy         Final Value ($)  Return (%)    Total Trades   
├───────────────┼───────────────────┼──────────────┼────────────────┤
 DMAC Strategy               2.21  121.00%       5              
├───────────────┼───────────────────┼──────────────┼────────────────┤
 Buy & Hold                  3.43  242.64%                     
╰───────────────┴───────────────────┴──────────────┴────────────────╯

MP Materials Corp. (MP)

╭───────────────┬───────────────────┬──────────────┬────────────────╮
 Strategy         Final Value ($)  Return (%)    Total Trades   
├───────────────┼───────────────────┼──────────────┼────────────────┤
 DMAC Strategy               1.09  8.58%         10             
├───────────────┼───────────────────┼──────────────┼────────────────┤
 Buy & Hold                  0.98  -1.66%                      
╰───────────────┴───────────────────┴──────────────┴────────────────╯

What I Learned

Running this experiment taught me a few things I couldn’t have predicted just by reading about trading strategies online.

First, optimizing for the Sharpe Ratio doesn’t always mean you’ll beat buy-and-hold. In fact, on Ford, the DMAC strategy underperformed by a wide margin. It made 22 trades and still ended up with a loss, while buy-and-hold stayed relatively flat.

On Verizon, the strategy was barely active. It made one trade and trailed buy-and-hold slightly. That was surprising because I expected more trading activity to lead to overfitting, not underfitting.

Tesla was the clearest example of a missed opportunity. The DMAC strategy captured a good chunk of the trend, but not all of it. It returned 121 percent while buy-and-hold more than doubled that. The strategy worked — but not nearly as well as simply holding the stock.

On MP Materials, the DMAC strategy actually beat buy-and-hold, which was slightly negative over the period. That gave me the first glimpse of what a strategy like this might be useful for: limiting downside more than maximizing upside.

This wasn’t about proving a strategy works everywhere. It was about seeing what kind of behavior emerges when you optimize for risk-adjusted return using Bayesian search.

What I ended up with was a process I can use across assets and timeframes. A repeatable structure that helps isolate what works and what doesn’t.

Keep Reading

No posts found