Skip to content

damraka/quant-trading-simulation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Quant Trading Simulation – SMA Crossover Backtester (NVDA & More)

This repo contains a simple but realistic backtesting script for a moving-average (SMA) crossover strategy.

Key features:

  • Uses yfinance to download historical daily data.
  • Runs a grid search over short/long SMA windows on NVDA (train period: 2020–2022).
  • Evaluates the selected parameters on a test period (2023–2025).
  • Executes trades at the next day’s open to avoid lookahead bias.
  • Includes transaction costs via a configurable fee rate.
  • Computes key risk metrics:
    • Total return
    • CAGR
    • Max drawdown
    • Sharpe ratio
    • Volatility
  • Produces a trade-level log (date, side, price, size, fee, cash).
  • Compares the strategy vs Buy & Hold.
  • Applies the same SMA parameters to multiple tickers (AAPL, MSFT, AMD, META, TSLA) to check out-of-sample robustness.
  • Plots price with SMAs and portfolio equity curve.

⚠️ Disclaimer: This project is for research and educational purposes only.
It is not financial advice and should not be used directly for live trading.

1. Idea in One Sentence

Take a very simple long-only moving-average crossover strategy,
make the backtest less fake (no lookahead, trading at next open, fees, train/test split),
and see how it behaves on NVDA and a few other large-cap names.


2. Strategy Description

Universe

  • Main asset: NVDA (2020-01-01 → 2025-01-01)
  • Additional tickers for out-of-sample sanity check:
    AAPL, MSFT, AMD, META, TSLA

Data

  • Source: yfinance
  • Adjusted for splits/dividends (auto_adjust=True)
  • Used columns: Open, Close

Signal Logic (SMA Crossover)

We compute two simple moving averages on the Close price:

  • Short SMA: Short_SMA (e.g. 50-day)
  • Long SMA: Long_SMA (e.g. 100-day)

Daily trading signal:

  • Signal = 1 if Short_SMA > Long_SMAlong
  • Signal = 0 otherwise → flat

Execution Logic

  • Signals are generated on day t close.
  • Orders are executed on day t+1 open:
    • Avoids lookahead bias.
  • Long-only, “all-in / all-out” behavior:
    • If we are flat and Signal flips from 0 → 1
      → buy as many shares as possible with available cash.
    • If we are long and Signal flips from 1 → 0
      → sell the entire position.

Transaction Costs

  • Per-trade fee rate: fee_rate = 0.001 (0.1% of trade notional)
  • Applied on both buys and sells.

Mark-to-Market

At each day we compute:

portfolio_value = cash + shares * close_price

The equity curve is built from these daily portfolio values.

3. Risk & Performance Metrics

For any equity curve, the script computes:

Total Return
Total=VfinalV0−1
Total=V0​Vfinal​​−1

CAGR (Annualized Growth)
Uses calendar days between first and last observations.

Max Drawdown
Based on rolling peak of the equity curve.

Annualized Volatility

Sharpe Ratio
Daily returns → annualized, risk-free rate assumed 0.

Number of Trades
Count of long-only entries.

4. Train / Test Setup on NVDA

Backtest window: 2020-01-01 → 2025-01-01

Train period: up to 2023-01-01 (yaklaşık 2020–2022)

Test period: from 2023-01-01 onward (yaklaşık 2023–2024)

4.1. Hyperparameter Grid Search (Train Only)

Grid search over:

Short SMA candidates: [20, 50, 100]

Long SMA candidates: [100, 150, 200, 250]
with constraint short < long.

For each (short, long) pair:

Run the strategy on the train slice (NVDA 2020–2022).

Compute performance metrics.

Select configuration with highest Sharpe on train.

Top 5 configs on NVDA (Train 2020–2022): short long Sharpe CAGR Total MaxDD 50 100 1.43 63.12% 332.42% −34.27% 50 150 1.42 63.54% 335.77% −36.01% 50 200 1.37 63.14% 332.58% −36.29% 50 250 1.18 52.35% 252.46% −49.21% 100 150 0.88 30.39% 121.22% −44.33%

Chosen parameters:

short = 50

long = 100

These are then fixed and used for both the test period and all other tickers.

5. NVDA Results

Initial capital: 100,000 USD

Fee rate: 0.1% per trade

5.1. Train (2020–2022) – NVDA

Strategy (SMA 50/100):

Total Return: 332.42%

CAGR: 63.12%

Max Drawdown: −34.27%

Sharpe: 1.43

Volatility: 39.64%

5.2. Test (2023–2025) – NVDA

Same SMA(50, 100), no re-tuning:

Total Return: 354.61%

CAGR: 113.77%

Max Drawdown: −27.05%

Sharpe: 1.88

Volatility: 46.13%

5.3. Full Period (2020–2025) – Strategy vs Buy & Hold

Initial capital: 100,000 USD Strategy – SMA(50, 100), long-only

Final Value: 3,500,651 USD

Total Return: 3,400.65%

CAGR: 103.72%

Max Drawdown: −34.27%

Sharpe: 1.84

Volatility: 44.03%

Number of trades: 5

Buy & Hold – NVDA

Final Value: 2,248,333 USD

Total Return: 2,148.33%

CAGR: 86.45%

Max Drawdown: −66.33%

Sharpe: 1.43

Volatility: 53.88%

Interpretation (kısaca):

Strategy still captures the huge NVDA trend.

Drawdown is roughly halved vs pure buy-and-hold.

Only 5 trades over 5 years → extremely low turnover.

6. Example Trade Log (NVDA)

The code records each trade with:

date

side (BUY / SELL)

price

shares

fee

cash_after (cash in account immediately after trade)

First few trades:

2020-03-17 – BUY

    Price ≈ 5.00

    Shares ≈ 19,985

    Fee ≈ 100

    Cash after trade ≈ −99 USD (practically fully invested)

2021-02-03 – SELL

    Price ≈ 13.60

    Shares ≈ 19,985

    Fee ≈ 272

    Cash after trade ≈ 271k

2021-02-09 – BUY

    Price ≈ 14.30

    Shares ≈ 18,976

    Fee ≈ 271

    Cash after trade ≈ −257

2022-02-23 – SELL

    Price ≈ 23.76

    Shares ≈ 18,976

    Fee ≈ 451

    Cash after trade ≈ 450k

2022-12-23 – BUY

    Price ≈ 15.18

    Shares ≈ 29,648

    Fee ≈ 450

    Cash after trade ≈ −445

And that’s basically it: 5 trades managing the full 5-year horizon.

7. Out-of-Sample Check on Other Stocks

To test how curve-fitted these parameters might be, we reuse SMA(50, 100) on several other large-cap names over the same period (2020–2025).

For each ticker:

Initial capital: 100,000 USD

Same execution & cost assumptions.

7.1. Summary Table

All metrics are strategy vs buy & hold. AAPL (Apple)

Strategy

    Final: 219,717 USD

    Total Return: 119.72%

    CAGR: 17.06%

    Max Drawdown: −24.43%

    Sharpe: 0.81

    Volatility: 22.49%

Buy & Hold

    Final: 343,841 USD

    Total Return: 243.84%

    CAGR: 28.04%

    Max Drawdown: −31.41%

    Sharpe: 0.94

    Volatility: 31.66%

On AAPL, buy-and-hold wins clearly on return; SMA helps a bit on drawdown/vol.

MSFT (Microsoft)

Strategy

    Final: 258,626 USD

    Total Return: 158.63%

    CAGR: 20.95%

    Max Drawdown: −28.73%

    Sharpe: 0.98

    Volatility: 21.96%

Buy & Hold

    Final: 274,170 USD

    Total Return: 174.17%

    CAGR: 22.37%

    Max Drawdown: −37.13%

    Sharpe: 0.82

    Volatility: 30.47%

On MSFT, returns are similar; SMA reduces drawdown & vol, slightly better Sharpe.

AMD

Strategy

    Final: 303,254 USD

    Total Return: 203.25%

    CAGR: 24.86%

    Max Drawdown: −39.70%

    Sharpe: 0.78

    Volatility: 37.43%

Buy & Hold

    Final: 245,961 USD

    Total Return: 145.96%

    CAGR: 19.74%

    Max Drawdown: −65.44%

    Sharpe: 0.61

    Volatility: 52.45%

On AMD, SMA both increases return and dramatically reduces drawdown.

META (Meta Platforms)

Strategy

    Final: 417,144 USD

    Total Return: 317.14%

    CAGR: 33.09%

    Max Drawdown: −21.32%

    Sharpe: 1.19

    Volatility: 27.00%

Buy & Hold

    Final: 279,937 USD

    Total Return: 179.94%

    CAGR: 22.88%

    Max Drawdown: −76.68%

    Sharpe: 0.69

    Volatility: 44.84%

META is where the trend-following setup really shines vs buy-and-hold.

TSLA (Tesla)

Strategy

    Final: 1,358,754 USD

    Total Return: 1,258.75%

    CAGR: 68.57%

    Max Drawdown: −55.69%

    Sharpe: 1.27

    Volatility: 51.49%

Buy & Hold

    Final: 1,407,794 USD

    Total Return: 1,307.79%

    CAGR: 69.77%

    Max Drawdown: −73.63%

    Sharpe: 1.12

    Volatility: 67.18%

On TSLA both are insane; SMA is slightly less explosive but a bit more controlled.

7.2. Takeaways from Multi-Asset Check

SMA(50, 100) is not globally optimal, but:

    It’s not a total fluke either.

    It often improves drawdown and volatility.

    On strongly trending or crashy names (META, AMD, TSLA), it can be quite helpful.

8. Implementation Notes

Dependencies

yfinance

numpy

pandas

matplotlib

Installation

pip install yfinance numpy pandas matplotlib

How to Run

python quant_trading_simulation.py

The script will:

Download NVDA data.

Run SMA grid search on 2020–2022 (train).

Evaluate train / test / full period on NVDA.

Log all NVDA trades.

Run the same SMA(50, 100) on AAPL, MSFT, AMD, META, TSLA.

Print all metrics and show NVDA price + equity curve plots.

9. Limitations & Possible Next Steps

Limitations

Single-name, single-factor strategy (pure trend).

Grid search on a relatively short window → overfitting risk.

Simple fee model; no:

    slippage,

    borrowing costs,

    market impact.

Only daily data; intraday volatility and overnight gaps are abstracted away.

No portfolio-level risk (no stop-loss / risk-parity / position sizing rules).

Possible Extensions

Try other trend filters: EMA, MACD, Donchian channels, breakout rules.

Add regime filters (e.g. trade only when volatility / VIX is in certain zones).

Move from single-name to portfolio construction:

    dynamic weights,

    volatility targeting,

    correlation-aware position sizing.

Implement walk-forward or rolling re-optimization instead of a single fixed split.

Compare vs more traditional quant baselines (e.g. equal-weight buy-and-hold basket, risk-parity, etc.).

Releases

No releases published

Packages

 
 
 

Contributors

Languages