Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

C++ Backtesting Engine — SMA Crossover Strategy

A lightweight, high-performance backtesting engine written in C++17 that simulates a Simple Moving Average (SMA) crossover strategy on historical OHLCV stock data — from raw CSV ingestion through signal generation, trade execution, and full performance analytics.


Table of Contents


Overview

This project simulates a classic Golden Cross / Death Cross trading strategy against historical stock data. It reads OHLCV data from a CSV file, computes two moving averages (a fast and a slow window), detects crossovers between them, executes simulated trades, and reports risk-adjusted performance metrics — all without look-ahead bias.

The goal wasn't to build a strategy that beats the market, but to build a correct, reusable backtesting framework that any strategy could be plugged into.

Features

  • 📂 CSV Data Loading — parses OHLCV-formatted historical price data
  • 📈 SMA Calculation — configurable short and long moving average windows
  • 🔀 Crossover Detection — identifies Golden Cross (buy) and Death Cross (sell) signals
  • 💰 Trade Simulation — executes simulated buys/sells with full position sizing
  • 📊 Equity Tracking — maintains a running equity curve across the backtest
  • 📉 Performance Metrics — total return, max drawdown, win rate, and average profit per trade

Project Structure

StockBacktester/
├── data/
│   └── nvda_us_d.csv          # Historical stock data
├── src/
│   ├── sma_backtester.cpp     # Main backtester source
│   └── sma_backtester.exe     # Compiled executable
├── .gitignore
└── README.md

How It Works

1. Data Loading Closing prices are parsed from a CSV file (Date, Open, High, Low, Close, Volume) into a std::vector<double>.

2. SMA Calculation A lambda computes the moving average for any given day and window size:

auto calculateSMA = [&](int currentDay, int window) -> double {
    if (currentDay < window - 1) return 0.0;
    double sum = 0.0;
    for (int i = currentDay - window + 1; i < currentDay + 1; i++) {
        sum += prices[i];
    }
    return sum / window;
};

3. Signal Generation Each day, the short-window SMA is compared to the long-window SMA:

  • Golden Cross — short SMA crosses above long SMA → BUY
  • Death Cross — short SMA crosses below long SMA → SELL

4. Trade Execution On a buy signal, all available cash is converted into shares at the current price. On a sell signal, the full position is liquidated and profit/loss is recorded.

5. Performance Analysis After the simulation completes, the engine calculates:

Metric Formula
Total Return (Final Equity − Initial Capital) / Initial Capital × 100%
Max Drawdown Largest peak-to-trough decline in the equity curve
Win Rate (Winning Trades / Total Trades) × 100%
Avg Profit/Trade Total Profit / Number of Trades

No look-ahead bias: every calculation on day i only uses price data from day i and earlier — the single most important correctness property of a real backtester.

Getting Started

Prerequisites

  • A C++17-compatible compiler (g++, clang++, or MSVC)

Build & Run

# Navigate to the source directory
cd src

# Compile (Windows)
g++ -std=c++17 -O2 sma_backtester.cpp -o sma_backtester.exe
.\sma_backtester.exe

# Compile (Linux / macOS)
g++ -std=c++17 -O2 sma_backtester.cpp -o backtester
./backtester

Configuration

Adjust the strategy parameters directly in sma_backtester.cpp:

int shortWindow = 20;    // Try 10, 30, 50
int longWindow  = 50;    // Try 30, 100, 200
double cash     = 10000.0;  // Starting capital

To backtest a different stock, replace data/nvda_us_d.csv with any CSV file in the same OHLCV format:

Date,Open,High,Low,Close,Volume
2025-07-21,172.75,173.38,171.00,171.38,123126136
2025-07-22,171.34,171.39,164.58,167.03,193114327

Sample Output

Successfully read 250 closing prices.

BUY executed at $187.62 on day 53
SELL executed at $181.46 on day 94
BUY executed at $187.05 on day 124
SELL executed at $182.48 on day 154
BUY executed at $177.69 on day 158
SELL executed at $184.76 on day 160
BUY executed at $202.50 on day 190
SELL executed at $199.00 on day 233

=== Performance Summary ===
Final Equity:         $9,647.16
Total Return:         -3.53%
Max Drawdown:         -19.31%
Total Trades:         4
Winning Trades:       1
Losing Trades:        3
Win Rate:             25.00%
Total Profit:         -$352.84
Avg Profit/Trade:     -$88.21

Trade Breakdown (20/50 SMA on NVDA)

Trade Buy Day Buy Price Sell Day Sell Price Profit/Loss
1 53 $187.62 94 $181.46 −$326.48
2 124 $187.05 154 $182.48 −$242.21
3 158 $177.69 160 $184.76 +$374.71
4 190 $202.50 233 $199.00 −$158.86

Results & Interpretation

The 20/50 SMA crossover strategy lost money on NVDA over this test period:

  • High risk — a max drawdown of −19.31% signals significant volatility exposure
  • Low consistency — a 25% win rate shows the strategy struggled to catch clean trends
  • Lagging indicator problem — SMA crossover strategies react to price after the move has already happened, which hurts performance on fast-moving, high-growth stocks like NVDA

This isn't a failure of the engine — it's exactly the kind of insight a backtester is supposed to surface.

Key Learnings

  • The framework matters more than the strategy. The engine itself is strategy-agnostic and reusable — the SMA logic is just one plug-in.
  • Eliminating look-ahead bias is the #1 priority. It's the most common mistake in beginner backtesters and silently invalidates results if done wrong.
  • Risk-adjusted performance beats raw returns. A −3.53% return paired with a −19.31% drawdown is a bad trade-off, and professional evaluation always weighs both.
  • Data quality underpins everything. Any properly formatted OHLCV CSV can be dropped in and tested.
  • Simple strategies are the right place to start. SMA crossover is easy to reason about, implement, and debug — a solid foundation before tackling RSI, MACD, or Bollinger Bands.

Why the Strategy Underperformed

  • NVDA is a high-growth, high-volatility stock
  • SMA crossover is inherently a lagging indicator
  • 20/50-day windows are too slow to react to NVDA's rapid price swings
  • The strategy got whipsawed repeatedly in choppy conditions

Roadmap

  • Sharpe Ratio calculation
  • Export equity curve to CSV for external plotting
  • Command-line argument support
  • Additional strategies (RSI, MACD, Bollinger Bands)
  • Transaction costs and slippage modeling
  • Parameter optimization via grid search
  • Multi-stock / portfolio-level backtesting
  • Live data ingestion from a market data API
  • Web dashboard for visualizing results

Built as a hands-on exploration of C++, quantitative finance, and algorithmic trading fundamentals.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages