![]() |
VOOZH | about |
dotnet add package Backtest.Net --version 4.2.2
NuGet\Install-Package Backtest.Net -Version 4.2.2
<PackageReference Include="Backtest.Net" Version="4.2.2" />
<PackageVersion Include="Backtest.Net" Version="4.2.2" />Directory.Packages.props
<PackageReference Include="Backtest.Net" />Project file
paket add Backtest.Net --version 4.2.2
#r "nuget: Backtest.Net, 4.2.2"
#:package Backtest.Net@4.2.2
#addin nuget:?package=Backtest.Net&version=4.2.2Install as a Cake Addin
#tool nuget:?package=Backtest.Net&version=4.2.2Install as a Cake Tool
👁 NuGet
👁 NuGet Downloads
👁 Build Status
👁 .NET
A high-performance backtesting engine for algorithmic trading strategies in .NET.
High-Performance Backtest.Net is a specialized backtesting library designed for quantitative trading applications. It processes multi-timeframe candlestick data across multiple symbols, simulating tick-by-tick strategy execution with proper warmup periods and OHLC handling.
| Feature | Description |
|---|---|
| EngineV10 | Stable optimized engine with Span-based iteration, binary search, and parallel processing |
| EngineV11 | Reusable feed-buffer engine designed to avoid per-tick feed graph allocations |
| SymbolDataSplitter | Partitions large datasets into memory-efficient chunks |
| Warmup Handling | Configurable warmup candle counts per timeframe |
| OHLC Simulation | Accurate current-candle OHLC handling during backtest |
| Progress Tracking | Real-time progress from 0-100% |
| SIMD Acceleration | Leverages SimdLinq for vectorized operations |
The library implements focused performance techniques:
EngineV11 reuses feed buffers instead of cloning the strategy feed graph on every tickSpan<T> and CollectionsMarshal.AsSpan in tight loops where it is safeEngineV9 and EngineV10 use Parallel.ForEach for symbol-level work[MethodImpl(MethodImplOptions.AggressiveInlining)]dotnet add package Backtest.Net
<PackageReference Include="Backtest.Net" Version="4.2.0" />
Requires .NET 10.0 or later.
using Backtest.Net.Engines;
using Backtest.Net.SymbolsData;
using Backtest.Net.SymbolDataSplitters;
using Backtest.Net.Timeframes;
using Backtest.Net.Enums;
// 1. Prepare your symbol data (candlesticks per timeframe)
// Candlestick properties: OpenTime, Open, High, Low, Close, CloseTime, Volume
var symbolsData = new List<SymbolDataV2>
{
new SymbolDataV2
{
Symbol = "BTCUSDT",
Timeframes = new List<TimeframeV2>
{
new TimeframeV2
{
Timeframe = CandlestickInterval.M1,
Candlesticks = yourOneMinuteCandles
},
new TimeframeV2
{
Timeframe = CandlestickInterval.H1,
Candlesticks = yourOneHourCandles
}
}
}
};
// 2. Split data for efficient processing
var splitter = new SymbolDataSplitterV2(
daysPerSplit: 30,
warmupCandlesCount: 100,
backtestingStartDateTime: new DateTime(2024, 1, 1)
);
var splitData = await splitter.SplitAsyncV2(symbolsData);
// 3. Create and configure the engine
var engine = new EngineV10(
warmupCandlesCount: 100,
sortCandlesInDescOrder: false,
useFullCandleForCurrent: false
);
// 4. Set up your strategy callback
engine.OnTick = async (symbolData) =>
{
foreach (var symbol in symbolData)
{
var latestCandle = symbol.Timeframes[0].Candlesticks[^1];
// Your strategy logic here
Console.WriteLine($"{symbol.Symbol}: Close = {latestCandle.Close}");
}
};
// 5. Run the backtest
await engine.RunAsync(splitData);
Console.WriteLine($"Progress: {engine.GetProgress()}%");
using var cts = new CancellationTokenSource();
engine.OnCancellationFinishedDelegate = () =>
{
Console.WriteLine("Backtest cancelled gracefully");
};
// Cancel after 30 seconds
cts.CancelAfter(TimeSpan.FromSeconds(30));
await engine.RunAsync(splitData, cts.Token);
| Engine | Description | Use Case |
|---|---|---|
EngineV8 |
SymbolDataV2 support | Standard workloads |
EngineV9 |
Optimized OHLC handling | Memory-sensitive scenarios |
EngineV10 |
Full optimization suite with the existing per-tick feed contract | Safe default for new projects |
EngineV11 |
Reusable feed buffers and steady-state allocation reduction | Allocation-sensitive strategies that do not retain OnTick references |
Use EngineV10 when you want the safest compatibility profile. Use EngineV11 when your strategy only reads OnTick data during the callback and does not store references to the supplied array, symbols, timeframes, candle lists, or current-candle objects.
This library works seamlessly with History-Vault.Net - a high-performance historical market data storage solution. Both libraries use identical data structures (SymbolDataV2, TimeframeV2, CandlestickV2) with the same properties, differing only in namespaces:
| Library | Namespace |
|---|---|
| Backtest.Net | Backtest.Net.SymbolsData, Backtest.Net.Timeframes, Backtest.Net.Candlesticks |
| History-Vault.Net | HistoryVault.Models |
The easiest way to convert between the two libraries' types is using JSON serialization:
using System.Text.Json;
using Backtest.Net.SymbolsData;
// Convert from History-Vault.Net to Backtest.Net
public static List<SymbolDataV2> ConvertFromHistoryVault(List<HistoryVault.Models.SymbolDataV2> historyVaultData)
{
var json = JsonSerializer.Serialize(historyVaultData);
return JsonSerializer.Deserialize<List<SymbolDataV2>>(json)!;
}
// Convert from Backtest.Net to History-Vault.Net
public static List<HistoryVault.Models.SymbolDataV2> ConvertToHistoryVault(List<SymbolDataV2> backtestData)
{
var json = JsonSerializer.Serialize(backtestData);
return JsonSerializer.Deserialize<List<HistoryVault.Models.SymbolDataV2>>(json)!;
}
using Backtest.Net.Engines;
using Backtest.Net.SymbolsData;
using Backtest.Net.SymbolDataSplitters;
using System.Text.Json;
using HistoryVault;
using HistoryVault.Configuration;
using HistoryVault.Models;
using HistoryVault.Storage;
// Configure the vault (paths are auto-detected based on OS and scope)
var options = new HistoryVaultOptions
{
DefaultScope = StorageScope.Local
};
await using var vault = new HistoryVaultStorage(options);
// Save candlestick data
var symbolData = new SymbolDataV2
{
Symbol = "BTCUSDT",
Timeframes = new List<TimeframeV2>
{
new TimeframeV2
{
Timeframe = CandlestickInterval.M1,
Candlesticks = candlesticks // Your candlestick list
}
}
};
// Load candlestick data
var loadOptions = LoadOptions.ForSymbol(
"BTCUSDT",
new DateTime(2025, 1, 1),
new DateTime(2025, 1, 31),
CandlestickInterval.M1
);
var historyData = await vault.LoadAsync(loadOptions);
// 2. Convert to Backtest.Net types via JSON
var json = JsonSerializer.Serialize(historyData);
var backtestData = JsonSerializer.Deserialize<List<SymbolDataV2>>(json)!;
// 3. Run backtest
var splitter = new SymbolDataSplitterV2(daysPerSplit: 30, warmupCandlesCount: 100);
var splitData = await splitter.SplitAsyncV2(backtestData);
var engine = new EngineV10(warmupCandlesCount: 100);
engine.OnTick = async (symbolData) =>
{
// Your strategy logic
};
await engine.RunAsync(splitData);
JSON conversion is the recommended approach as it cleanly handles namespace differences without requiring manual mapping or shared assemblies.
Performance benchmarks run on Apple M3 Max with .NET 10.0, processing 4 million candlesticks (1 symbol × 4 timeframes × 1,000,000 candles each):
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|---|---|---|---|---|---|---|
| EngineV11Run | 84.82 ms | 0.539 ms | 0.478 ms | - | - | 1.62 KB |
Key findings:
EngineV11 is designed to avoid per-tick feed graph allocations after setup; the measured allocation reflects remaining setup/runtime overhead, not the old per-tick clone path.EngineV10 for broad compatibility and EngineV11 for allocation-sensitive strategies that follow the borrowed-snapshot callback contract.Benchmarks run with BenchmarkDotNet v0.15.8 on macOS Tahoe 26.2, Apple M3 Max, .NET 10.0
git clone https://github.com/islero/High-Performance-Backtest.Net.git
cd High-Performance-Backtest.Net
dotnet build
dotnet test
cd benchmarks/Backtest.Net.Benchmarks
dotnet run -c Release
dotnet format
This project follows Semantic Versioning.
| Branch | Version Format | NuGet Feed |
|---|---|---|
master |
X.Y.Z (stable) |
nuget.org |
beta |
X.Y.Z-beta.N (prerelease) |
nuget.org |
src/Backtest.Net/Backtest.Net.csprojvX.Y.ZContributions are welcome! Please read for guidelines.
This project is licensed under the .
<p align="center"> Built for the algorithmic trading community </p>
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
Added EngineV11 with reusable feed buffers and a borrowed-snapshot OnTick contract; added EngineV11 unit tests and benchmark coverage; corrected engine benchmark input reset so runs start from fresh indexes; updated README benchmark and engine guidance.