
Bitcoin at $120k+? This Changes Everything.
While most people pay $120k+ for Bitcoin, there's a smarter way to acquire it at production cost through professional mining operations.
The math is simple: Why buy Bitcoin at peak prices when you can generate it for a fraction of the cost? Abundant Mines handles everything - from equipment selection to daily operations in green energy facilities.
You receive daily Bitcoin payouts, claim massive tax write-offs through equipment depreciation, and build real wealth through Bitcoin generation rather than speculation. No technical knowledge required. No equipment headaches. No management responsibilities.
This approach works because you're accumulating Bitcoin below market rates while traditional investors pay premium prices. Our professional-grade facilities ensure maximum uptime and profitability.
Limited spots available due to facility capacity. Smart entrepreneurs are already positioning themselves while others hesitate.
Get started with a free month of professional Bitcoin hosting before the next price surge.
🚀 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 SMA Window Sizes and Trades using A Walk-forward Approach
In most trading systems, moving averages are applied with fixed window sizes. The assumption is that a single setting can capture market structure over long periods.
In practice, markets don’t behave that neatly. Trends change pace, volatility rises and falls, and what worked last month can quickly become ineffective.
This project focuses on building a more adaptive approach using the Simple Moving Average (SMA) as the foundation. By combining Simulated Annealing for parameter optimization and a Walk-Forward Testing procedure, the model adjusts its SMA window dynamically across time segments.
The goal is not to predict prices, but to evaluate how adaptive tuning can improve consistency and reduce overfitting.
The full process includes three main parts:
Using the Simple Moving Average as a baseline trading signal.
Applying Simulated Annealing to find better SMA window sizes over training data.
Running a Walk-Forward loop to simulate real-world retraining and testing over multiple periods.
By the end, we’ll have a complete pipeline that downloads live market data, optimizes parameters in segments, evaluates trades, and visualizes how performance evolves over time.
Where the smartest investors start their day
The Alternative Investing Report (A.I.R.) helps you get smarter on alternative assets from crypto treasury companies to Pre-IPO venture secondaries to private credit and more.
Join 100,000+ investors to get the latest insights and trends driving private markets, a weekly investment pick from a notable investor, and bonus offers to join top private market platforms and managers. And it’s totally free.
Methods and Concepts Behind the Model
Simple Moving Average (SMA)
The Simple Moving Average is the foundation of this strategy. It smooths price fluctuations over a given window size, creating a line that filters out noise and reveals underlying trends.
But the key question is: how long should that window be? Too short, and the strategy reacts to noise. Too long, and it becomes sluggish. That’s where optimization steps in.
Simulated Annealing Optimization
Simulated Annealing is an optimization method inspired by the physical process of annealing metals. Rather than greedily accepting only better solutions, it occasionally accepts worse ones early on, allowing the algorithm to explore a wider range of possibilities.
As the process continues, it becomes more selective. This helps us escape local optima and discover more robust SMA window sizes.
Walk-Forward Testing
Markets evolve. A model that performs well during one period may fail miserably in another. Walk-forward testing simulates how a trading strategy would behave in real time by training on one period and testing on the next.
The model “walks forward” through the data, constantly updating its parameters. This approach gives a far more realistic measure of performance than traditional backtests.
Implementation
Below we walk through the full Python implementation, step by step.
The full Jupyter Notebook can be accessed by following this guide or in my GitHub Repository, but only for paid subs
1. Installing Required Packages
Before anything else, we install the necessary dependencies that power our analysis.
These include libraries for data manipulation, visualization, optimization, and environment management.
# Install required packages
%pip install requests pandas matplotlib tabulate python-dotenv -q
2. Importing Libraries
We now bring in the essential Python libraries for data handling, visualization, and mathematical operations.
# Import necessary libraries
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import requests
import random
import math
from tabulate import tabulate
import os
from dotenv import load_dotenv
3. Loading Environment Variables
We’ll be using market data from EODHD, which offers a clean and accessible API for both free and paid users. It’s a reliable data source that provides both free and paid access tiers for equities, ETFs, and other instruments.
If you’d like to explore their data offering or get a Free API key for your own experiments, you can sign up here.
After creating an account, you’ll receive an API key that lets you access their endpoints directly.
Create a .env
file and add your API key like this:
EODHD_API_KEY=your_eodhd_api_key_here
Once that’s done, load the environment variable in your jupyter notebook:
# Load environment variables from .env file
load_dotenv()
# Access the API key
api_key = os.getenv("EODHD_API_KEY")
This keeps your sensitive API key out of the script and GitHub repository, aligning with good development practices.
An espresso shot for your brain
The problem with most business news? It’s too long, too boring, and way too complicated.
Morning Brew fixes all three. In five minutes or less, you’ll catch up on the business, finance, and tech stories that actually matter—written with clarity and just enough humor to keep things interesting.
It’s quick. It’s free. And it’s how over 4 million professionals start their day. Signing up takes less than 15 seconds—and if you’d rather stick with dense, jargon-packed business news, you can always unsubscribe.
4. Downloading Historical Data
We’ll use the EODHD’s API to fetch daily historical prices for Apple (AAPL) from 2020 to 2024.
symbol = 'AAPL'
from_date = "2020-01-01"
to_date = "2024-12-31"
url = (
f"https://eodhd.com/api/eod/{symbol}"
f"?api_token={api_key}"
"&fmt=json"
"&period=d"
f"&from={from_date}"
f"&to={to_date}"
)
data = requests.get(url).json()
data
This gives us raw JSON data containing daily prices, which we’ll soon convert into a more manageable pandas DataFrame.
5. Converting Data to a DataFrame
We now clean and structure the data for analysis.
# Convert JSON to DataFrame
df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values('date')
df.set_index('date', inplace=True)
# Keep only adjusted close
df = df[['adjusted_close']]
df.head()
This produces a well-structured DataFrame with date indices and a single column for adjusted closing prices, ideal for calculating moving averages and running backtests.
6. Visualizing Price History
Before proceeding to trading logic, let’s visualize the closing prices to get a sense of the trend.
plt.figure(figsize=(14,6))
plt.plot(df.index, df['adjusted_close'], color='blue', label='Closing Price')
plt.title(f'{symbol} Closing Price Over Time')
plt.xlabel('Date')
plt.ylabel('Price ($)')
plt.legend()
plt.grid()
plt.xticks(rotation=45)
plt.savefig('closing_price.png')
plt.show()

Apple Stock Closing Price Over Time Chart
This plot helps us confirm data integrity and understand the general price movements before diving into strategy design.
7. Generating Trading Signals
Here, we define a basic trading strategy that issues buy and sell signals based on SMA crossovers.
def generate_signals(prices, window):
sma = prices.rolling(window=window).mean()
signals = pd.Series(0, index=prices.index)
position = 0 # 0 = no position, 1 = holding
for i in range(1, len(prices)):
if prices.iloc[i] > sma.iloc[i] and position == 0:
signals.iloc[i] = 1 # Buy
position = 1
elif prices.iloc[i] < sma.iloc[i] and position == 1:
signals.iloc[i] = -1 # Sell
position = 0
return signals, sma
This simple logic buys when prices move above the SMA and sells when they drop below it. It’s simple but effective when properly tuned.
8. Backtesting Framework
To evaluate performance, we simulate a portfolio that reacts to our signals.
def backtest(prices, signals, initial_cash=10000, transaction_cost=0.001):
cash = initial_cash
position = 0
portfolio_values = []
for i in range(len(prices)):
price = prices.iloc[i]
signal = signals.iloc[i]
if signal == 1 and cash > 0: # Buy
position = (cash * (1 - transaction_cost)) / price
cash = 0
elif signal == -1 and position > 0: # Sell
cash = position * price * (1 - transaction_cost)
position = 0
portfolio_values.append(cash + position * price)
return pd.Series(portfolio_values, index=prices.index)
This backtest assumes we start with $10,000 and switch between cash and equity positions depending on signals, including a small transaction cost for realism.
9. Defining the Optimization Objective
The objective function quantifies how good a specific SMA window length is.
def objective_sma(trial, prices, trade_penalty=0.01):
window = trial.suggest_int("window", 5, 100)
signals, _ = generate_signals(prices, window)
portfolio = backtest(prices, signals)
total_return = portfolio.iloc[-1] - portfolio.iloc[0]
num_trades = (signals != 0).sum()
score = total_return - trade_penalty * num_trades
return score
The function rewards higher portfolio returns while penalizing excessive trading, balancing profitability and stability.
10. Setting Up the Walk-Forward Parameters
We split our data into training and testing segments to simulate real trading progression.
window_train = 126 # 6 months ≈ 126 trading days
window_test = 63 # 3 months ≈ 63 trading days
walk_forward_portfolio = pd.Series(dtype=float)
walk_forward_signals = pd.Series(dtype=int)
walk_forward_sma = pd.Series(dtype=float)
best_windows_list = []
This ensures each model only uses past data for optimization, never future data, which prevents data leakage.
11. Simulated Annealing Function
This is the core of our adaptive optimization.
def simulated_annealing_optimize(prices, window_range=(5, 100), n_iterations=50, initial_temp=10, cooling_rate=0.95, lambda_trade=0.01):
current_window = random.randint(*window_range)
signals, _ = generate_signals(prices, current_window)
portfolio = backtest(prices, signals)
current_score = portfolio.iloc[-1] - lambda_trade * (signals != 0).sum()
best_window = current_window
best_score = current_score
temp = initial_temp
for _ in range(n_iterations):
neighbor_window = max(window_range[0], min(window_range[1], current_window + random.choice([-1,1])))
signals, _ = generate_signals(prices, neighbor_window)
portfolio = backtest(prices, signals)
neighbor_score = portfolio.iloc[-1] - lambda_trade * (signals != 0).sum()
if neighbor_score > current_score or random.random() < math.exp((neighbor_score - current_score)/temp):
current_window = neighbor_window
current_score = neighbor_score
if neighbor_score > best_score:
best_score = neighbor_score
best_window = neighbor_window
temp *= cooling_rate
return best_window
The algorithm explores different SMA windows, gradually cooling its randomness while converging toward the most promising parameter.
12. Walk-Forward Implementation
Now that we have a simulated annealing optimizer for the SMA window, we apply it in a walk-forward framework.
This simulates real-world trading: we optimize on a past training period, then test on a future period, repeatedly moving forward through the dataset.
start = 0
initial_cash = 10000
while start + window_train < len(df):
train_end = start + window_train
test_end = min(train_end + window_test, len(df))
train_data = df['adjusted_close'].iloc[start:train_end]
test_data = df['adjusted_close'].iloc[train_end:test_end]
if len(train_data) < 10:
break
# Optimize SMA window
best_window = simulated_annealing_optimize(train_data)
best_windows_list.append(best_window)
print(f"Training period {len(best_windows_list)}: Optimal SMA window = {best_window}")
combined_data = pd.concat([train_data.tail(best_window), test_data])
test_signals, test_sma = generate_signals(combined_data, best_window)
test_signals = test_signals.loc[test_data.index]
test_sma = test_sma.loc[test_data.index]
current_cash = walk_forward_portfolio.iloc[-1] if not walk_forward_portfolio.empty else initial_cash
test_portfolio = backtest(test_data, test_signals, initial_cash=current_cash)
walk_forward_portfolio = pd.concat([walk_forward_portfolio, test_portfolio])
walk_forward_signals = pd.concat([walk_forward_signals, test_signals])
walk_forward_sma = pd.concat([walk_forward_sma, test_sma])
start += window_test
Here, we iterate through the dataset in segments. For each training window, we find the optimal SMA using simulated annealing, then apply it to the subsequent test window.
We also update our portfolio and trading signals over time. This method prevents data leakage and gives a realistic picture of performance in live-like conditions.
13. Updating Piecewise SMA Series
To visualize how our SMA window changes over time, we build piecewise SMA series, reflecting the best SMA for each segment.
sma_windows_series = pd.Series(index=df.index, dtype=float)
piecewise_sma = pd.Series(index=df.index, dtype=float)
start = 0
for i, window in enumerate(best_windows_list):
train_end = min(start + window_train, len(df))
test_end = min(train_end + window_test, len(df))
smooth_start = max(0, train_end - window)
extended_data = df['adjusted_close'].iloc[smooth_start:test_end]
sma_slice = extended_data.rolling(window=window).mean()
piecewise_sma.iloc[train_end:test_end] = sma_slice.iloc[-(test_end - train_end):].values
sma_windows_series.iloc[train_end:test_end] = window
start += window_test
This ensures the SMA line dynamically adapts to the changing market regime.
The sma_windows_series
keeps track of the window length, while piecewise_sma
provides a smooth moving average curve for plotting.
14. Visualizing Optimized SMA and Signals
We can now plot both the adaptive SMA and the buy/sell signals generated from our walk-forward approach.
fig, (ax_top, ax_bottom) = plt.subplots(2,1, figsize=(14,8), sharex=True, gridspec_kw={'height_ratios':[1,3]})
ax_top.step(sma_windows_series.index, sma_windows_series, where='mid', color='orange', linewidth=2)
ax_top.set_ylabel('Optimized SMA Window')
ax_top.set_title('Optimized SMA Window Size Over Time')
ax_top.grid(alpha=0.3)
ax_bottom.plot(df.index, df['adjusted_close'], color='black', label='Adjusted Close')
ax_bottom.plot(piecewise_sma.index, piecewise_sma, color='blue', linestyle='--', linewidth=1.8, label='Walk-Forward SMA')
buy_signals = walk_forward_signals[walk_forward_signals==1]
sell_signals = walk_forward_signals[walk_forward_signals==-1]
ax_bottom.scatter(buy_signals.index, df['adjusted_close'].loc[buy_signals.index],
color='green', marker='^', s=100, label='Buy Signal')
ax_bottom.scatter(sell_signals.index, df['adjusted_close'].loc[sell_signals.index],
color='red', marker='v', s=100, label='Sell Signal')
ax_bottom.set_xlabel('Date')
ax_bottom.set_ylabel('Price ($)')
ax_bottom.legend()
ax_bottom.grid(alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('sma_walk_forward.png')
plt.show()

Optimized SMA Window Size Over Time
The top panel shows the evolution of the optimized SMA window, while the bottom panel overlays the SMA on the price. This visual makes it clear how the SMA adapts and how the trading strategy reacts over time.
15. Aligning Data
To ensure all plotting and backtesting data aligns perfectly, we forward-fill missing values and confirm the final portfolio point is included.
# Ensure all test periods, including the final partial window, are included properly
buy_signals = walk_forward_signals[walk_forward_signals == 1]
sell_signals = walk_forward_signals[walk_forward_signals == -1]
# Extend portfolio to cover the last data point if missing
last_index = df.index[-1]
if last_index not in walk_forward_portfolio.index:
walk_forward_portfolio.loc[last_index] = walk_forward_portfolio.iloc[-1]
# Align indexes to df for clean plotting
walk_forward_portfolio = walk_forward_portfolio.reindex(df.index, method='ffill')
walk_forward_sma = walk_forward_sma.reindex(df.index, method='ffill')
walk_forward_signals = walk_forward_signals.reindex(df.index, fill_value=0)
This ensures consistency in the final visualizations, preventing gaps caused by partial windows or differing indices between series.
16. Full Portfolio Visualization
Finally, we can generate a clear view of portfolio performance over time, including training/test period shading.
# Two-panel plot: top = optimized SMA window, bottom = prices and signals
fig, (ax_top, ax_bottom) = plt.subplots(2, 1, figsize=(14,8), sharex=True, gridspec_kw={'height_ratios':[1,3]})
# Top panel: Optimized SMA window
ax_top.step(sma_windows_series.index, sma_windows_series, where='mid', color='orange', linewidth=2)
ax_top.set_ylabel("Optimized SMA Window")
ax_top.set_title("Optimized SMA Window Size Over Time")
ax_top.grid(alpha=0.3)
# Bottom panel: Adjusted Close, Walk-Forward SMA
ax_bottom.plot(df.index, df['adjusted_close'], color='black', label='Adjusted Close')
ax_bottom.plot(walk_forward_sma.index, walk_forward_sma, color='blue', linestyle='--', linewidth=1.8, label='Walk-Forward SMA')
# Buy/Sell markers
ax_bottom.scatter(buy_signals.index, df['adjusted_close'].loc[buy_signals.index], color='green', marker='^', s=100, label='Buy Signal')
ax_bottom.scatter(sell_signals.index, df['adjusted_close'].loc[sell_signals.index], color='red', marker='v', s=100, label='Sell Signal')
# Highlight training/testing periods
n_segments = len(best_windows_list)
for i in range(n_segments):
train_start = df.index[i * window_test]
train_end = df.index[min(i * window_test + window_train, len(df)-1)]
test_start = train_end
test_end = df.index[min(i * window_test + window_train + window_test, len(df)-1)]
ax_bottom.axvspan(train_start, train_end, color='orange', alpha=0.2)
ax_bottom.axvspan(test_start, test_end, color='green', alpha=0.2)
# Legend
orange_patch = mpatches.Patch(color='orange', alpha=0.2, label='Training Period')
green_patch = mpatches.Patch(color='green', alpha=0.2, label='Test Period')
ax_bottom.legend(handles=[
orange_patch,
green_patch,
plt.Line2D([], [], color='black', label='Adjusted Close'),
plt.Line2D([], [], color='blue', linestyle='--', label='Walk-Forward SMA'),
plt.Line2D([], [], marker='^', color='green', linestyle='None', label='Buy Signal'),
plt.Line2D([], [], marker='v', color='red', linestyle='None', label='Sell Signal')
])
ax_bottom.set_xlabel("Date")
ax_bottom.set_ylabel("Price ($)")
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
plt.figure(figsize=(14,6))
plt.plot(df.index, walk_forward_portfolio, label='Portfolio Value', color='purple')
plt.title("Portfolio Value Over Time (Virtual Money, Walk-Forward)")
plt.xlabel("Date")
plt.ylabel("Portfolio Value ($)")
plt.legend()
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('portfolio_value.png')
plt.show()

Optimized SMA Window Over Time with Trades

Portfolio Value Over Time
We can now see the full journey of our adaptive SMA strategy, from evolving window sizes to buy/sell signals and virtual portfolio growth.
Training and testing periods are shaded to highlight how the model is optimized sequentially.
17. Performance Metrics
Finally, we summarize the portfolio with simple performance metrics.
initial_cash = 10000
final_value = walk_forward_portfolio.iloc[-1]
total_return = (final_value - initial_cash) / initial_cash * 100
num_trades = (walk_forward_signals != 0).sum()
buy_trades = (walk_forward_signals == 1).sum()
sell_trades = (walk_forward_signals == -1).sum()
metrics = [
["Initial Cash", f"${initial_cash:,.2f}"],
["Final Portfolio Value", f"${final_value:,.2f}"],
["Total Return (%)", f"{total_return:.2f}%"],
["Total Trades", int(num_trades)],
["Buy Trades", int(buy_trades)],
["Sell Trades", int(sell_trades)]
]
print(tabulate(metrics, headers=["Metric", "Value"], tablefmt="grid"))
+-----------------------+------------+
| Metric | Value |
+=======================+============+
| Initial Cash | $10,000.00 |
+-----------------------+------------+
| Final Portfolio Value | $18,000.74 |
+-----------------------+------------+
| Total Return (%) | 80.01% |
+-----------------------+------------+
| Total Trades | 86 |
+-----------------------+------------+
| Buy Trades | 43 |
+-----------------------+------------+
| Sell Trades | 43 |
+-----------------------+------------+
Analysis of Results
Portfolio Growth: The portfolio grew from $10,000 to $18,000, an 80% total return over the tested period. This illustrates that an adaptive SMA, when optimized with simulated annealing, can capture trends effectively and improve consistency compared to a static SMA.
Trading Activity: The strategy executed 86 trades in total, evenly split between buys and sells. This balance indicates that the model reacts appropriately to market changes without excessive trading, which could incur high transaction costs.
Adaptive SMA Behavior: By dynamically adjusting the SMA window, the system was able to respond to changing market regimes, shortening during volatile periods to catch trends and lengthening during quieter periods to reduce noise.
Realistic Performance Simulation: Using a walk-forward approach ensures these results mimic realistic trading conditions, where parameters are optimized on past data and applied to unseen future data. This minimizes overfitting and gives a reliable view of potential performance in live markets.
Conclusion from Metrics
The combination of adaptive SMA windows, simulated annealing optimization, and walk-forward testing produced strong portfolio growth with controlled trade frequency.
These results highlight the value of adaptive approaches over static indicators in evolving markets.