Skip to content

Commit 6f61d5a

Browse files
committed
Merge branch 'dev'
2 parents 888a152 + 7c5179c commit 6f61d5a

File tree

6 files changed

+32
-36
lines changed

6 files changed

+32
-36
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22
<PropertyGroup>
33
<!-- Common settings for all projects -->
4-
<TargetFramework>net9.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<LangVersion>latestmajor</LangVersion>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>

README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![NuGet](https://img.shields.io/nuget/v/Backtest.Net.svg)](https://www.nuget.org/packages/Backtest.Net/)
44
[![NuGet Downloads](https://img.shields.io/nuget/dt/Backtest.Net.svg)](https://www.nuget.org/packages/Backtest.Net/)
55
[![Build Status](https://github.com/islero/High-Performance-Backtest.Net/actions/workflows/ci.yml/badge.svg)](https://github.com/islero/High-Performance-Backtest.Net/actions/workflows/ci.yml)
6-
[![.NET](https://img.shields.io/badge/.NET-9.0-512BD4)](https://dotnet.microsoft.com/)
6+
[![.NET](https://img.shields.io/badge/.NET-10.0-512BD4)](https://dotnet.microsoft.com/)
77

88
A high-performance backtesting engine for algorithmic trading strategies in .NET.
99

@@ -61,7 +61,7 @@ dotnet add package Backtest.Net
6161
```
6262

6363
> [!NOTE]
64-
> Requires .NET 9.0 or later.
64+
> Requires .NET 10.0 or later.
6565
6666
---
6767

@@ -165,28 +165,29 @@ await engine.RunAsync(splitData, cts.Token);
165165

166166
## Benchmarks
167167

168-
Performance benchmarks run on Apple M3 Max with .NET 9.0, processing **4 million candlesticks** (1 symbol × 4 timeframes × 1,000,000 candles each):
168+
Performance benchmarks run on Apple M3 Max with .NET 10.0, processing **4 million candlesticks** (1 symbol × 4 timeframes × 1,000,000 candles each):
169169

170-
| Method | Mean | Error | StdDev | Gen0 | Allocated |
171-
|------------- |---------:|--------:|--------:|-------:|----------:|
172-
| EngineV8Run | 125.7 ns | 0.45 ns | 0.42 ns | 0.0620 | 520 B |
173-
| EngineV9Run | 128.4 ns | 2.31 ns | 2.16 ns | 0.0629 | 528 B |
174-
| EngineV10Run | 102.0 ns | 2.00 ns | 1.96 ns | 0.0545 | 456 B |
170+
| Method | Mean | Error | StdDev | Gen0 | Allocated |
171+
|------------- |---------:|---------:|---------:|-------:|----------:|
172+
| EngineV8Run | 99.78 ns | 1.564 ns | 1.463 ns | 0.0621 | 520 B |
173+
| EngineV9Run | 96.69 ns | 1.933 ns | 2.148 ns | 0.0631 | 528 B |
174+
| EngineV10Run | 80.16 ns | 1.553 ns | 1.453 ns | 0.0545 | 456 B |
175175

176176
**Key findings:**
177-
- **EngineV10** is ~19% faster than EngineV8 and ~21% faster than EngineV9
177+
- **EngineV10** is ~20% faster than EngineV8 and ~17% faster than EngineV9
178178
- **EngineV10** allocates 12% less memory than EngineV8
179-
- All engines maintain sub-microsecond per-tick latency
179+
- All engines maintain sub-100ns per-tick latency
180+
- **.NET 10 migration** improved all engines by ~20% compared to .NET 9
180181

181-
> Benchmarks run with BenchmarkDotNet v0.15.8 on macOS Tahoe 26.2, Apple M3 Max, .NET 9.0.8
182+
> Benchmarks run with BenchmarkDotNet v0.15.8 on macOS Tahoe 26.2, Apple M3 Max, .NET 10.0
182183
183184
---
184185

185186
## Development
186187

187188
### Prerequisites
188189

189-
- [.NET 9.0 SDK](https://dotnet.microsoft.com/download/dotnet/9.0) or later
190+
- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) or later
190191
- Git
191192

192193
### Build

benchmarks/Backtest.Net.Benchmarks/Backtest.Net.Benchmarks.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<ItemGroup>
99
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
10-
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
10+
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.102" />
1111
</ItemGroup>
1212

1313
<ItemGroup>

src/Backtest.Net/Backtest.Net.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
<ItemGroup>
2626
<PackageReference Include="SimdLinq" Version="1.3.2" />
27+
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.102" />
2728
</ItemGroup>
2829

2930
</Project>

src/Backtest.Net/SymbolDataSplitters/SymbolDataSplitterV2.cs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,12 @@ public class SymbolDataSplitterV2(
2929
/// <returns></returns>
3030
public Task<List<List<SymbolDataV2>>> SplitAsyncV2(List<SymbolDataV2> symbolsData)
3131
{
32-
// --- Enumerating Symbols Data
33-
var symbolsDataList = symbolsData.ToList();
34-
3532
// --- Quick Symbol Data validation
36-
if (!QuickSymbolDataValidationV2(symbolsDataList))
33+
if (!QuickSymbolDataValidationV2(symbolsData))
3734
throw new ArgumentException("symbolsData argument contains invalid or not properly sorted data");
3835

3936
// --- Symbol or timeframe duplicates validation
40-
if (IsThereSymbolTimeframeDuplicatesV2(symbolsDataList))
37+
if (IsThereSymbolTimeframeDuplicatesV2(symbolsData))
4138
throw new ArgumentException("symbolsData contain duplicated symbols or timeframes");
4239

4340
// --- Creating Result Split Symbols Data
@@ -65,19 +62,19 @@ public Task<List<List<SymbolDataV2>>> SplitAsyncV2(List<SymbolDataV2> symbolsDat
6562
}
6663

6764
// --- Getting the correct warmup timeframe
68-
WarmupTimeframe = GetWarmupTimeframeV2(symbolsDataList);
65+
WarmupTimeframe = GetWarmupTimeframeV2(symbolsData);
6966

7067
DateTime ongoingBacktestingTime = BacktestingStartDateTime;
71-
while (!AreAllSymbolDataReachedHistoryEndV2(symbolsDataList))
68+
while (!AreAllSymbolDataReachedHistoryEndV2(symbolsData))
7269
{
7370
var symbolsDataPart = new List<SymbolDataV2>();
74-
foreach (SymbolDataV2 symbol in symbolsDataList)
71+
foreach (SymbolDataV2 symbol in symbolsData)
7572
{
7673
// --- Checking if there is any symbol with no more history
7774
if (symbol.Timeframes.Any(x => x.NoMoreHistory))
7875
{
7976
// --- Adding days per split to ongoing backtesting time
80-
ongoingBacktestingTime = AddDaysToOngoingBacktestingTime(ongoingBacktestingTime, symbol == symbolsDataList.Last());
77+
ongoingBacktestingTime = AddDaysToOngoingBacktestingTime(ongoingBacktestingTime, symbol == symbolsData.Last());
8178

8279
continue;
8380
}
@@ -127,8 +124,8 @@ public Task<List<List<SymbolDataV2>>> SplitAsyncV2(List<SymbolDataV2> symbolsDat
127124
// --- Deleting source candles and readjusting indexes
128125
if (timeframe.StartIndex > 0)
129126
{
130-
// --- Note that candles readjusting is corrupting source candles perform readjusting
131-
timeframe.Candlesticks = timeframe.Candlesticks.Skip(timeframe.StartIndex).ToList();
127+
// --- Remove source candles in-place (more efficient than Skip().ToList())
128+
timeframe.Candlesticks.RemoveRange(0, timeframe.StartIndex);
132129

133130
// --- Perform reindexing
134131
timeframe.Index -= timeframe.StartIndex;
@@ -159,7 +156,7 @@ public Task<List<List<SymbolDataV2>>> SplitAsyncV2(List<SymbolDataV2> symbolsDat
159156
symbolsDataPart.Add(symbolDataPart);
160157

161158
// --- Adding days per split to ongoing backtesting time
162-
ongoingBacktestingTime = AddDaysToOngoingBacktestingTime(ongoingBacktestingTime, symbol == symbolsDataList.Last());
159+
ongoingBacktestingTime = AddDaysToOngoingBacktestingTime(ongoingBacktestingTime, symbol == symbolsData.Last());
163160
}
164161

165162
// --- Append symbolsDataPart if it contains any record
@@ -218,10 +215,9 @@ private static bool QuickSymbolDataValidationV2(List<SymbolDataV2> symbolsData)
218215
/// <returns></returns>
219216
private static bool IsThereSymbolTimeframeDuplicatesV2(List<SymbolDataV2> symbolsData)
220217
{
221-
var symbolDataList = symbolsData.ToList();
222-
bool symbolDuplicatesExist = symbolDataList.GroupBy(x => x.Symbol).Any(symbol => symbol.Count() > 1);
218+
bool symbolDuplicatesExist = symbolsData.GroupBy(x => x.Symbol).Any(symbol => symbol.Count() > 1);
223219
bool timeframeDuplicatesExist = false;
224-
foreach (SymbolDataV2 symbol in symbolDataList)
220+
foreach (SymbolDataV2 symbol in symbolsData)
225221
{
226222
// Validating
227223
if (timeframeDuplicatesExist) continue;
@@ -242,8 +238,7 @@ private static bool IsThereSymbolTimeframeDuplicatesV2(List<SymbolDataV2> symbol
242238
/// <returns></returns>
243239
private static int GetCandlesticksIndexByOpenTimeV2(List<CandlestickV2> candlesticks, DateTime targetDateTime)
244240
{
245-
var candlesticksList = candlesticks.ToList();
246-
int index = candlesticksList.FindIndex(candle => candle.OpenTime >= targetDateTime);
241+
int index = candlesticks.FindIndex(candle => candle.OpenTime >= targetDateTime);
247242

248243
if (index < 0) index = 0;
249244

@@ -263,10 +258,9 @@ private CandlestickInterval GetWarmupTimeframeV2(List<SymbolDataV2> symbolsData)
263258
return WarmupTimeframe.Value;
264259

265260
// --- Setting the lowest symbolsData timeframe
266-
var symbolDataList = symbolsData.ToList();
267-
CandlestickInterval potentialWarmupTimeframe = symbolDataList.Min(x => x.Timeframes.Min(y => y.Timeframe));
261+
CandlestickInterval potentialWarmupTimeframe = symbolsData.Min(x => x.Timeframes.Min(y => y.Timeframe));
268262

269-
foreach (SymbolDataV2 symbol in symbolDataList)
263+
foreach (SymbolDataV2 symbol in symbolsData)
270264
{
271265
foreach (TimeframeV2 timeframe in symbol.Timeframes)
272266
{
@@ -305,7 +299,6 @@ private static bool AreAllSymbolDataReachedHistoryEndV2(List<SymbolDataV2> symbo
305299
/// <returns></returns>
306300
private static int GetCandlesticksIndexByCloseTimeV2(List<CandlestickV2> candlesticks, DateTime targetDateTime)
307301
{
308-
var candlesticksList = candlesticks.ToList();
309-
return candlesticksList.FindIndex(candle => candle.CloseTime >= targetDateTime);
302+
return candlesticks.FindIndex(candle => candle.CloseTime >= targetDateTime);
310303
}
311304
}

tests/Backtest.Net.Tests/Backtest.Net.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
<PrivateAssets>all</PrivateAssets>
1818
</PackageReference>
19+
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.102" />
1920
</ItemGroup>
2021

2122
<ItemGroup>

0 commit comments

Comments
 (0)