A GPU-accelerated exhaustive parameter sweep of Moving Average crossover strategies on real market data, powered by Hybridizer.
The program tests 78,804 combinations of (shortMA, longMA) parameters across a configurable price history and produces both a performance benchmark and a visual heatmap of strategy returns.
| Requirement | Details |
|---|---|
| .NET 8.0 SDK | Download |
| NVIDIA GPU | Any CUDA-capable GPU (compute capability auto-detected at build time) |
| CUDA Toolkit 13.0 | Version is set in Directory.Build.props — adjust <CUDAVersion> if needed |
| Hybridizer CLI | Install with dotnet tool install -g hybridizer |
| C++ toolchain | Visual Studio 2022+ with Desktop development with C++ workload (Windows) or GCC (Linux) |
Note
The build system automatically detects your GPU architecture via nvidia-smi — no manual -gencode flags needed.
Always build in Release mode for accurate benchmark results. Debug mode disables compiler optimizations and gives misleading timings.
# Restore NuGet packages (first time only)
dotnet restore
# Build in Release mode
dotnet build --configuration ReleaseThe build pipeline has three stages:
- C# compilation — Standard
dotnet buildcompiles the project - Hybridizer code generation — The
GenerateCUDAtarget invokeshybridizerto transpile[EntryPoint]/[Kernel]methods into CUDA C++ (output ingenerated-sources/) - CUDA compilation — The
CompileCUDAtarget callsnvccto compile the generated C++ into a native GPU DLL (StrategyBacktest_CUDA.dll)
Tip
If you don't have a GPU, the build will fail at step 3. You can disable GPU compilation by removing <CompileCUDA>enable</CompileCUDA> from StrategyBacktest.csproj — the program will then fall back to CPU-only execution at runtime.
dotnet run --configuration Release --no-build -- [options]Important
The -- separator is required before program arguments to distinguish them from dotnet options.
| Option | Description | Default |
|---|---|---|
--file <path> |
Load price data from a local CSV file | (downloads from Yahoo Finance) |
--symbol <ticker> |
Yahoo Finance ticker symbol to download | BTC-USD |
--days <count> |
Number of days of history to download | 3650 (10 years) |
# Download BTC-USD data automatically (default)
dotnet run --configuration Release --no-build
# Use a local CSV file
dotnet run --configuration Release --no-build -- --file btc_history.csv
# Download Apple stock data
dotnet run --configuration Release --no-build -- --symbol AAPL
# Download 5 years of Ethereum data
dotnet run --configuration Release --no-build -- --symbol ETH-USD --days 1825The program tries to download price data from the Yahoo Finance API. If the download fails (network error, rate limit, etc.), it automatically falls back to synthetic price data generated via geometric Brownian motion, so the benchmark always runs.
Standard Yahoo Finance CSV format is supported:
Date,Open,High,Low,Close,Adj Close,Volume
2024-01-02,42681.37,44733.25,42526.00,44156.78,44156.78,6543567891The loader looks for a Close column in the header (falls back to Adj Close, then the second column).
To download a CSV manually:
- Go to https://finance.yahoo.com/quote/BTC-USD/history/
- Set the time period (at least 2 years recommended)
- Click Download
- Run with
--file downloaded.csv
The program produces:
- Benchmark timing — Execution time with GPU (or CPU fallback)
- Best/worst strategy — The parameter combinations with highest and lowest returns
- Heatmap PNG (
strategy_heatmap.png) — Visual map of returns by parameter combination
GPU: NVIDIA GeForce RTX 4070 Laptop GPU
Loaded 3651 price data points
Parameter sweep: short MA [2..200], long MA [5..400]
Total combinations: 78804
Warmup... done
Running benchmark... 84 ms
Best strategy: MA(5,17) -> +432.15% return
Worst strategy: MA(197,398) -> -87.23% return
Heatmap saved to: strategy_heatmap.png
When no GPU is available the program runs on CPU using Parallel.For and reports accordingly.
The program performs an exhaustive sweep over all combinations of short and long moving average periods:
| Parameter | Range | Count |
|---|---|---|
| Short MA | 2 to 200 | 199 values |
| Long MA | 5 to 400 | 396 values |
| Total | 78,804 combinations (~59,500 valid where short < long) |
For each (shortMA, longMA) combination, starting with $10,000:
- Compute both moving averages at each time step (naive O(window) sum)
- Detect golden cross (short MA crosses above long MA) → buy
- Detect death cross (short MA crosses below long MA) → sell
- Track equity and compute total return
| Factor | Why it helps |
|---|---|
| Massive parallelism | Each of the 78,804 combinations runs as an independent GPU thread |
| High arithmetic intensity | Naive SMA = O(window_size) per data point per combination |
| Read-only shared data | Price array (~15 KB) fits entirely in GPU L2 cache |
| Minimal transfers | Prices uploaded once, results downloaded once |
The code uses idiomatic C#: the backtest lives in a [Kernel] method (RunBacktest) called from a [EntryPoint] method (BacktestAll) via Parallel.For. Hybridizer transpiles this into CUDA C++ automatically — no pointer arithmetic, no manual memory management, no explicit thread indexing.
At runtime, the program checks for a compiled GPU DLL (*_CUDA.dll). If found, HybRunner wraps the entry point and dispatches it to the GPU. If not, the same Parallel.For code runs on CPU cores — the same source code powers both paths.
StrategyBacktest/
├── Program.cs # Entry point, GPU wrapper, backtest kernels
├── PriceDataLoader.cs # CSV parser + Yahoo Finance downloader + synthetic fallback
├── HeatmapGenerator.cs # PNG heatmap (ImageSharp) + ANSI console heatmap
├── StrategyBacktest.csproj # Project file (Hybridizer + ImageSharp refs, CUDA build targets)
├── Directory.Build.props # CUDA version setting
├── Directory.Build.targets # GPU arch detection + nvcc compilation logic
└── generated-sources/ # (build output) Hybridizer-generated CUDA C++
No license specified.