Add TBar, TBarSeries, TSeries, TValue, and IFeed implementations with comprehensive documentation and examples

- Introduced TBar struct for efficient OHLCV data representation.
- Implemented TBarSeries class for high-performance collection of TBar instances using Structure of Arrays (SoA) layout.
- Added TSeries class for time-series data management with zero-copy access.
- Created TValue struct for time-value pairs with implicit conversions.
- Defined IFeed interface for consistent data feed implementations.
- Developed CsvFeed class for loading historical OHLCV data from CSV files.
- Implemented GBM class for generating synthetic financial data using Geometric Brownian Motion.
- Added Quantower project files for Averages indicator with necessary dependencies and configurations.
- Included extensive usage examples and notebooks for TBar, TBarSeries, TSeries, TValue, and feed implementations.
This commit is contained in:
Miha Kralj
2025-11-27 19:51:43 -08:00
parent 1c8f514756
commit 74b49d2bb4
37 changed files with 1379 additions and 462 deletions
-1
View File
@@ -1,6 +1,5 @@
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>preview</LangVersion>
<NoWarn>$(NoWarn);NU1903;NU5104;NETSDK1057</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
+31 -50
View File
@@ -1,21 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "quantalib", "lib\quantalib.csproj", "{F455234B-2A3C-140A-17C3-683D7820A733}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B36A84DF-456D-A817-6EDD-3EC3E7F6E11F}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{3A8DF596-E814-FECC-DD4B-D8EF8AAC1A0D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreTypes", "examples\CoreTypes\CoreTypes.csproj", "{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuanTAlib.Tests", "lib\QuanTAlib.Tests.csproj", "{953F0406-DD9B-406E-993D-6D988D5F5423}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "quantower", "quantower", "{6CF592EE-4302-E72F-3CB4-AB1D314DD5A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuanTAlib.Tests", "tests\QuanTAlib.Tests\QuanTAlib.Tests.csproj", "{43CA2584-D4AD-4082-AFF4-68B3D1239221}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "feeds", "feeds", "{2B942E44-74DA-CD21-D337-7A5E9D347C1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GbmExample", "examples\feeds\GbmExample.csproj", "{B27145AF-B255-4D6E-827B-3512952FB29C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Averages.csproj", "{D8F03B19-F99F-475F-8951-85C9D2258B73}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -39,51 +34,37 @@ Global
{F455234B-2A3C-140A-17C3-683D7820A733}.Release|x64.Build.0 = Release|Any CPU
{F455234B-2A3C-140A-17C3-683D7820A733}.Release|x86.ActiveCfg = Release|Any CPU
{F455234B-2A3C-140A-17C3-683D7820A733}.Release|x86.Build.0 = Release|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Debug|x64.ActiveCfg = Debug|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Debug|x64.Build.0 = Debug|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Debug|x86.ActiveCfg = Debug|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Debug|x86.Build.0 = Debug|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Release|Any CPU.Build.0 = Release|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Release|x64.ActiveCfg = Release|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Release|x64.Build.0 = Release|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Release|x86.ActiveCfg = Release|Any CPU
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782}.Release|x86.Build.0 = Release|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Debug|x64.ActiveCfg = Debug|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Debug|x64.Build.0 = Debug|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Debug|x86.ActiveCfg = Debug|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Debug|x86.Build.0 = Debug|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Release|Any CPU.Build.0 = Release|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Release|x64.ActiveCfg = Release|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Release|x64.Build.0 = Release|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Release|x86.ActiveCfg = Release|Any CPU
{43CA2584-D4AD-4082-AFF4-68B3D1239221}.Release|x86.Build.0 = Release|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Debug|x64.ActiveCfg = Debug|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Debug|x64.Build.0 = Debug|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Debug|x86.ActiveCfg = Debug|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Debug|x86.Build.0 = Debug|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Release|Any CPU.Build.0 = Release|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Release|x64.ActiveCfg = Release|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Release|x64.Build.0 = Release|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Release|x86.ActiveCfg = Release|Any CPU
{B27145AF-B255-4D6E-827B-3512952FB29C}.Release|x86.Build.0 = Release|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Debug|Any CPU.Build.0 = Debug|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Debug|x64.ActiveCfg = Debug|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Debug|x64.Build.0 = Debug|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Debug|x86.ActiveCfg = Debug|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Debug|x86.Build.0 = Debug|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Release|Any CPU.ActiveCfg = Release|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Release|Any CPU.Build.0 = Release|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Release|x64.ActiveCfg = Release|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Release|x64.Build.0 = Release|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Release|x86.ActiveCfg = Release|Any CPU
{953F0406-DD9B-406E-993D-6D988D5F5423}.Release|x86.Build.0 = Release|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Debug|x64.ActiveCfg = Debug|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Debug|x64.Build.0 = Debug|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Debug|x86.ActiveCfg = Debug|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Debug|x86.Build.0 = Debug|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Release|Any CPU.Build.0 = Release|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Release|x64.ActiveCfg = Release|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Release|x64.Build.0 = Release|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Release|x86.ActiveCfg = Release|Any CPU
{D8F03B19-F99F-475F-8951-85C9D2258B73}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8AB1BE0C-06AE-4EE2-B45A-4F8CE6381782} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F}
{43CA2584-D4AD-4082-AFF4-68B3D1239221} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{2B942E44-74DA-CD21-D337-7A5E9D347C1B} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F}
{B27145AF-B255-4D6E-827B-3512952FB29C} = {2B942E44-74DA-CD21-D337-7A5E9D347C1B}
{953F0406-DD9B-406E-993D-6D988D5F5423} = {3A8DF596-E814-FECC-DD4B-D8EF8AAC1A0D}
{D8F03B19-F99F-475F-8951-85C9D2258B73} = {6CF592EE-4302-E72F-3CB4-AB1D314DD5A8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E6DB434C-508E-4231-B8A6-5EDD7FF87E22}
-14
View File
@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\lib\quantalib.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
-119
View File
@@ -1,119 +0,0 @@
using System;
using QuanTAlib;
namespace CoreTypesExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("QuanTAlib Core Types Example");
Console.WriteLine("============================");
// 1. TValue Example
Console.WriteLine("\n1. TValue Usage");
long now = DateTime.UtcNow.Ticks;
var val1 = new TValue(now, 100.5);
Console.WriteLine($"Created TValue: Time={val1.AsDateTime}, Value={val1.Value}");
// 2. TSeries Example (Streaming)
Console.WriteLine("\n2. TSeries Usage (Streaming)");
var series = new TSeries();
// Add new values
series.Add(now, 10.0, isNew: true);
Console.WriteLine($"Added 10.0 (New): Count={series.Count}, Last={series.Last.Value}");
// Update last value (streaming update)
series.Add(now, 11.0, isNew: false);
Console.WriteLine($"Updated to 11.0 (Update): Count={series.Count}, Last={series.Last.Value}");
// Add another new value
series.Add(now + TimeSpan.TicksPerMinute, 12.0, isNew: true);
Console.WriteLine($"Added 12.0 (New): Count={series.Count}, Last={series.Last.Value}");
// 3. TBar Example
Console.WriteLine("\n3. TBar Usage");
var bar1 = new TBar(now, 100, 105, 95, 102, 1000);
Console.WriteLine($"Created TBar: {bar1}");
Console.WriteLine($"Computed HL2: {bar1.HL2}");
Console.WriteLine($"TValue Access: O={bar1.O}, H={bar1.H}, L={bar1.L}, C={bar1.C}, V={bar1.V}");
// 4. TBarSeries Example
Console.WriteLine("\n4. TBarSeries Usage");
var bars = new TBarSeries();
// Add a bar
bars.Add(bar1, isNew: true);
Console.WriteLine($"Added Bar 1: Count={bars.Count}, Close={bars.Last.Close}");
// Update the bar (e.g. price changed within the same minute)
var bar1Update = new TBar(now, 100, 106, 95, 104, 1500);
bars.Add(bar1Update, isNew: false);
Console.WriteLine($"Updated Bar 1: Count={bars.Count}, Close={bars.Last.Close}, High={bars.Last.High}");
// Accessing Views (Zero-Copy)
Console.WriteLine("\n5. TBarSeries Views (Zero-Copy)");
Console.WriteLine($"Bars Count: {bars.Count}");
Console.WriteLine($"Close Series Count: {bars.Close.Count}");
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value}");
// Verify view updates automatically
Console.WriteLine("Adding new bar...");
bars.Add(now + TimeSpan.TicksPerMinute, 104, 108, 103, 107, 2000, isNew: true);
Console.WriteLine($"Bars Count: {bars.Count}");
Console.WriteLine($"Close Series Count: {bars.Close.Count} (Should match Bars Count)");
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value} (Should be 107)");
Console.WriteLine($"Alias Access: C.Last={bars.C.Last.Value}");
Console.WriteLine($"Direct Last Access: LastClose={bars.LastClose}, LastTime={bars.LastTime}");
// 6. GBM Generator Example
Console.WriteLine("\n6. GBM Generator Usage");
var gbm = new GBM(startPrice: 100.0);
// 6a. Batch generation
long startTime = DateTime.UtcNow.Ticks;
var interval = TimeSpan.FromMinutes(1);
var randomBars = gbm.Fetch(5, startTime, interval);
Console.WriteLine($"Generated {randomBars.Count} bars in batch:");
for (int i = 0; i < randomBars.Count; i++)
Console.WriteLine($" Bar {i}: C={randomBars[i].Close:F2}");
// 6b. Individual streaming bars
Console.WriteLine("\n6b. Streaming individual bars:");
var streamBar = gbm.Next(isNew: true);
Console.WriteLine($" New bar: C={streamBar.Close:F2}");
// Simulate 3 intra-bar updates
for (int i = 0; i < 3; i++)
{
streamBar = gbm.Next(isNew: false);
Console.WriteLine($" Update {i + 1}: C={streamBar.Close:F2}, H={streamBar.High:F2}, L={streamBar.Low:F2}");
}
// Finalize and start new bar
streamBar = gbm.Next(isNew: true);
Console.WriteLine($" New bar: C={streamBar.Close:F2}");
// 6c. Manual streaming into TBarSeries
Console.WriteLine("\n6c. Manual streaming into TBarSeries:");
var streamSeries = new TBarSeries();
var bar = gbm.Next(isNew: true);
streamSeries.Add(bar, isNew: true);
Console.WriteLine($" Added bar: Count={streamSeries.Count}, C={streamSeries.LastClose:F2}");
for (int i = 0; i < 2; i++)
{
bar = gbm.Next(isNew: false);
streamSeries.Add(bar, isNew: false);
Console.WriteLine($" Update {i + 1}: Count={streamSeries.Count}, C={streamSeries.LastClose:F2}");
}
bar = gbm.Next(isNew: true);
streamSeries.Add(bar, isNew: true);
Console.WriteLine($" Added bar: Count={streamSeries.Count}, C={streamSeries.LastClose:F2}");
Console.WriteLine("\nExample complete.");
}
}
}
-47
View File
@@ -1,47 +0,0 @@
using QuanTAlib;
// Example: Loading and streaming IBM daily data from CSV
var csvPath = "daily_IBM.csv";
// Create feed from CSV file
var feed = new CsvFeed(csvPath);
Console.WriteLine("=== CSV Feed Example: IBM Daily Data ===\n");
// Example 1: Stream through first 5 bars
Console.WriteLine("1. Streaming first 5 bars:");
for (int i = 0; i < 5; i++)
{
var bar = feed.Next(isNew: true);
Console.WriteLine($" {bar.AsDateTime:yyyy-MM-dd}: O={bar.Open:F2}, H={bar.High:F2}, L={bar.Low:F2}, C={bar.Close:F2}, V={bar.Volume:F0}");
}
// Example 2: Fetch a specific date range
Console.WriteLine("\n2. Fetching 10 bars starting from July 2025:");
var startTime = new DateTime(2025, 7, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
var series = feed.Fetch(10, startTime, TimeSpan.FromDays(1));
Console.WriteLine($" Retrieved {series.Count} bars");
foreach (var bar in series)
{
Console.WriteLine($" {bar.AsDateTime:yyyy-MM-dd}: Close={bar.Close:F2}");
}
// Example 3: Demonstrate isNew parameter
Console.WriteLine("\n3. Demonstrating isNew parameter (new bar vs update):");
var bar1 = feed.Next(isNew: true);
Console.WriteLine($" New bar: {bar1.AsDateTime:yyyy-MM-dd} Close={bar1.Close:F2}");
var bar1Update = feed.Next(isNew: false);
Console.WriteLine($" Update (same bar): {bar1Update.AsDateTime:yyyy-MM-dd} Close={bar1Update.Close:F2}");
var bar2 = feed.Next(isNew: true);
Console.WriteLine($" Next bar: {bar2.AsDateTime:yyyy-MM-dd} Close={bar2.Close:F2}");
// Example 4: Working with TBarSeries views
Console.WriteLine("\n4. Accessing OHLCV components via TSeries:");
var batch = feed.Fetch(5, startTime, TimeSpan.FromDays(1));
Console.WriteLine($" Close prices: [{string.Join(", ", batch.Close.Take(5).Select(c => c.Value.ToString("F2")))}]");
Console.WriteLine($" High prices: [{string.Join(", ", batch.High.Take(5).Select(h => h.Value.ToString("F2")))}]");
Console.WriteLine($" Volumes: [{string.Join(", ", batch.Volume.Take(5).Select(v => v.Value.ToString("F0")))}]");
Console.WriteLine("\n=== Example Complete ===");
-24
View File
@@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\quantalib.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="GbmExample.cs" />
</ItemGroup>
<ItemGroup>
<None Update="daily_IBM.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
-77
View File
@@ -1,77 +0,0 @@
using System;
using QuanTAlib;
namespace FeedsExample;
class GbmExample
{
static void Main(string[] args)
{
Console.WriteLine("QuanTAlib GBM Feed Example");
Console.WriteLine("===========================\n");
// Create GBM generator
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2);
var series = new TBarSeries();
// 1. Batch generation: 20 bars with 1-hour interval, starting 24 hours ago
Console.WriteLine("1. Batch Generation (20 bars, 1-hour interval)");
Console.WriteLine("------------------------------------------------");
long startTime = DateTime.UtcNow.AddHours(-24).Ticks;
var interval = TimeSpan.FromHours(1);
var batchBars = gbm.Fetch(20, startTime, interval);
// Add batch to series
for (int i = 0; i < batchBars.Count; i++)
{
series.Add(batchBars[i], isNew: true);
}
Console.WriteLine($"Generated {batchBars.Count} bars");
Console.WriteLine($"First bar: Time={new DateTime(batchBars[0].Time):yyyy-MM-dd HH:mm:ss}, Close={batchBars[0].Close:F2}");
Console.WriteLine($"Last bar: Time={new DateTime(batchBars[19].Time):yyyy-MM-dd HH:mm:ss}, Close={batchBars[19].Close:F2}");
Console.WriteLine($"Series has {series.Count} bars\n");
// 2. Streaming: Add 4 more bars (1 new + 3 intra-bar updates each)
Console.WriteLine("2. Streaming Generation (4 new bars with intra-bar updates)");
Console.WriteLine("------------------------------------------------------------");
for (int barNum = 1; barNum <= 4; barNum++)
{
Console.WriteLine($"\nBar #{barNum + 20}:");
// New bar
var bar = gbm.Next(isNew: true);
series.Add(bar, isNew: true);
Console.WriteLine($" New: Time={new DateTime(bar.Time):HH:mm:ss}, O={bar.Open:F2}, H={bar.High:F2}, L={bar.Low:F2}, C={bar.Close:F2}");
// Three intra-bar updates
for (int update = 1; update <= 3; update++)
{
bar = gbm.Next(isNew: false);
series.Add(bar, isNew: false);
Console.WriteLine($" Update {update}: Time={new DateTime(bar.Time):HH:mm:ss}, O={bar.Open:F2}, H={bar.High:F2}, L={bar.Low:F2}, C={bar.Close:F2}");
}
}
// 3. Summary
Console.WriteLine("\n3. Final Summary");
Console.WriteLine("----------------");
Console.WriteLine($"Total bars in series: {series.Count}");
Console.WriteLine($"Expected: 24 bars (20 batch + 4 streaming)");
Console.WriteLine($"\nFirst bar: Time={series[0].AsDateTime:yyyy-MM-dd HH:mm:ss}, Close={series[0].Close:F2}");
Console.WriteLine($"Last bar: Time={series.Last.AsDateTime:yyyy-MM-dd HH:mm:ss}, Close={series.Last.Close:F2}");
Console.WriteLine($"\nPrice change: {series.Last.Close - series[0].Close:F2} ({(series.Last.Close / series[0].Close - 1) * 100:F2}%)");
// Statistics using SIMD
var closeValues = series.Close.Values;
Console.WriteLine($"\nStatistics (Close prices):");
Console.WriteLine($" Average: {closeValues.AverageSIMD():F2}");
Console.WriteLine($" Min: {closeValues.MinSIMD():F2}");
Console.WriteLine($" Max: {closeValues.MaxSIMD():F2}");
Console.WriteLine($" StdDev: {closeValues.StdDevSIMD():F2}");
Console.WriteLine("\nExample complete.");
}
}
-19
View File
@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>73a533c9-b5d2-4762-9ac9-5017fcffa8d1</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\quantalib.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="CsvFeedExample.cs" />
</ItemGroup>
</Project>
-101
View File
@@ -1,101 +0,0 @@
timestamp,open,high,low,close,volume
2025-11-25,304.1250,306.0000,297.0600,304.4800,2825322
2025-11-24,299.1800,307.1800,297.5100,304.1200,6050640
2025-11-21,293.4800,300.4800,291.8900,297.4400,5710903
2025-11-20,294.6400,300.7100,290.1600,290.4000,5597028
2025-11-19,290.5000,291.1099,288.0700,288.5300,3595912
2025-11-18,297.0000,297.0000,289.9200,289.9500,4861928
2025-11-17,305.5900,306.0000,296.5100,297.1700,3909741
2025-11-14,300.0000,307.7200,297.5900,305.6900,3592455
2025-11-13,312.2900,314.6000,303.6800,304.8600,5310150
2025-11-12,319.8900,324.9000,314.5324,314.9800,6042686
2025-11-11,309.0000,317.9100,308.4300,313.7200,4381913
2025-11-10,306.8200,309.9400,304.2300,309.1300,2975188
2025-11-07,309.6800,310.0000,302.6301,306.3800,5070773
2025-11-06,306.7500,315.4400,301.0900,312.4200,6818521
2025-11-05,301.3800,307.2000,299.7100,306.7700,4633195
2025-11-04,300.0000,303.1700,296.0000,300.8500,5677330
2025-11-03,308.0000,312.1411,304.2300,304.7300,4957958
2025-10-31,312.0000,313.5000,301.6300,307.4100,7697499
2025-10-30,306.6500,313.7500,305.0200,310.0600,4694275
2025-10-29,312.7900,314.3300,307.5200,308.2100,4135948
2025-10-28,312.6000,319.3500,311.4100,312.5700,6044770
2025-10-27,307.8000,313.5000,302.8800,313.0900,9868151
2025-10-24,283.7700,310.7500,282.2100,307.4600,16914243
2025-10-23,264.9500,285.5791,263.5623,285.0000,16676394
2025-10-22,281.9900,289.1700,281.3500,287.5100,10538480
2025-10-21,283.3100,285.3100,281.6000,282.0500,4080981
2025-10-20,281.2500,285.5000,280.9600,283.6500,3494336
2025-10-17,276.1500,283.4000,275.3500,281.2800,5309565
2025-10-16,281.1100,282.5600,275.6000,275.9700,2956923
2025-10-15,278.3800,285.4500,277.0000,280.7500,3346753
2025-10-14,275.5200,277.5300,272.5469,276.1500,3058149
2025-10-13,279.7900,282.4399,274.6400,277.2200,4333836
2025-10-10,288.9700,290.3850,277.5000,277.8200,4508506
2025-10-09,289.8200,290.1300,283.3200,288.2300,4912375
2025-10-08,294.1600,294.2000,286.4730,289.4600,5297030
2025-10-07,295.5500,301.0425,293.2850,293.8700,7190126
2025-10-06,288.6100,291.4500,287.8000,289.4200,2881947
2025-10-03,287.5000,293.3200,287.3000,288.3700,4375082
2025-10-02,285.7900,288.5400,282.7900,286.7200,3814232
2025-10-01,280.2000,286.5900,280.1500,286.4900,4381338
2025-09-30,280.8800,286.0250,280.5200,282.1600,5926924
2025-09-29,286.0000,286.0000,279.6600,279.8000,6022125
2025-09-26,280.5100,288.8500,280.1100,284.3100,9063938
2025-09-25,272.9350,284.2300,271.1480,281.4400,11506192
2025-09-24,272.6200,273.6499,267.3000,267.5300,3159924
2025-09-23,272.7000,273.2962,269.2650,272.2400,5394121
2025-09-22,266.6200,272.3100,266.0000,271.3700,5030540
2025-09-19,266.0500,267.8700,263.6400,266.4000,9858112
2025-09-18,258.8600,265.2300,256.8004,265.0000,4988421
2025-09-17,257.4950,260.9644,257.0100,259.0800,3974785
2025-09-16,256.2600,258.0000,254.4100,257.5200,2719918
2025-09-15,254.0200,259.0500,254.0000,256.2400,4028365
2025-09-12,256.9500,257.2500,252.4250,253.4400,3433300
2025-09-11,257.5600,258.5450,255.6550,257.0100,3576048
2025-09-10,259.6500,260.0800,254.5600,256.8800,5185420
2025-09-09,256.1200,260.6600,254.8800,259.1100,4931105
2025-09-08,248.6300,257.1500,247.0200,256.0900,6940270
2025-09-05,248.2300,249.0300,245.4500,248.5300,3147478
2025-09-04,245.4200,249.2800,242.8500,247.1800,4765087
2025-09-03,240.0200,244.2500,239.4100,244.1000,3156289
2025-09-02,240.9000,241.5500,238.2500,241.5000,3469501
2025-08-29,245.2300,245.4599,241.7200,243.4900,2967558
2025-08-28,245.4300,245.8800,243.3600,245.7300,2820817
2025-08-27,242.8700,245.9600,242.0000,244.8400,3698372
2025-08-26,241.0200,244.9800,240.3800,242.6300,5386582
2025-08-25,242.5650,242.5650,239.4300,239.4300,3513327
2025-08-22,240.7400,243.6800,240.2200,242.0900,3134882
2025-08-21,242.2100,242.5000,238.6500,239.4000,2991902
2025-08-20,242.1100,242.8800,240.3400,242.5500,3240064
2025-08-19,240.0000,242.8300,239.4900,241.2800,3328305
2025-08-18,239.5700,241.4200,239.1158,239.4500,3569594
2025-08-15,237.6100,240.6200,236.7700,239.7200,4344322
2025-08-14,238.2500,239.0000,235.6200,237.1100,4556725
2025-08-13,236.2000,240.8411,236.2000,240.0700,5663562
2025-08-12,236.5300,237.9600,233.3600,234.7700,8800597
2025-08-11,242.2400,243.1500,234.7000,236.3000,9381960
2025-08-08,248.8800,249.4800,241.6500,242.2700,6828390
2025-08-07,252.8100,255.0000,248.8750,250.1600,6251285
2025-08-06,251.5300,254.3200,249.2800,252.2800,3692105
2025-08-05,252.0000,252.8000,248.9950,250.6700,5823016
2025-08-04,251.0500,252.0800,248.1100,251.9800,5280588
2025-08-01,251.4050,251.4791,245.6100,250.0500,9683404
2025-07-31,259.5700,259.9900,252.2200,253.1500,6739092
2025-07-30,261.6000,262.0000,258.9000,260.2600,3718290
2025-07-29,264.3000,265.7999,261.0200,262.4100,4627265
2025-07-28,260.3000,264.0000,259.6100,263.2100,5192516
2025-07-25,260.0200,260.8000,256.3500,259.7200,7758653
2025-07-24,261.2500,262.0486,252.7500,260.5100,22647720
2025-07-23,284.3000,288.0800,281.4400,282.0100,8105906
2025-07-22,284.7400,284.8800,281.2500,281.9600,4824219
2025-07-21,286.2900,287.7300,284.3800,284.7100,3051791
2025-07-18,283.3800,287.1600,282.2200,285.8700,4478165
2025-07-17,281.5000,283.4566,280.9000,282.0000,3337168
2025-07-16,282.7500,283.8700,279.8700,281.9200,2804831
2025-07-15,283.7700,284.1550,280.7301,282.7000,2864106
2025-07-14,282.8300,284.9250,281.7100,283.7900,2857401
2025-07-11,285.0100,287.4300,282.9200,283.5900,3790679
2025-07-10,288.9000,288.9000,282.2100,287.4300,3489068
2025-07-09,291.3900,291.6000,288.6300,290.1400,2971309
2025-07-08,293.1000,295.6100,289.4900,290.4200,2925329
1 timestamp open high low close volume
2 2025-11-25 304.1250 306.0000 297.0600 304.4800 2825322
3 2025-11-24 299.1800 307.1800 297.5100 304.1200 6050640
4 2025-11-21 293.4800 300.4800 291.8900 297.4400 5710903
5 2025-11-20 294.6400 300.7100 290.1600 290.4000 5597028
6 2025-11-19 290.5000 291.1099 288.0700 288.5300 3595912
7 2025-11-18 297.0000 297.0000 289.9200 289.9500 4861928
8 2025-11-17 305.5900 306.0000 296.5100 297.1700 3909741
9 2025-11-14 300.0000 307.7200 297.5900 305.6900 3592455
10 2025-11-13 312.2900 314.6000 303.6800 304.8600 5310150
11 2025-11-12 319.8900 324.9000 314.5324 314.9800 6042686
12 2025-11-11 309.0000 317.9100 308.4300 313.7200 4381913
13 2025-11-10 306.8200 309.9400 304.2300 309.1300 2975188
14 2025-11-07 309.6800 310.0000 302.6301 306.3800 5070773
15 2025-11-06 306.7500 315.4400 301.0900 312.4200 6818521
16 2025-11-05 301.3800 307.2000 299.7100 306.7700 4633195
17 2025-11-04 300.0000 303.1700 296.0000 300.8500 5677330
18 2025-11-03 308.0000 312.1411 304.2300 304.7300 4957958
19 2025-10-31 312.0000 313.5000 301.6300 307.4100 7697499
20 2025-10-30 306.6500 313.7500 305.0200 310.0600 4694275
21 2025-10-29 312.7900 314.3300 307.5200 308.2100 4135948
22 2025-10-28 312.6000 319.3500 311.4100 312.5700 6044770
23 2025-10-27 307.8000 313.5000 302.8800 313.0900 9868151
24 2025-10-24 283.7700 310.7500 282.2100 307.4600 16914243
25 2025-10-23 264.9500 285.5791 263.5623 285.0000 16676394
26 2025-10-22 281.9900 289.1700 281.3500 287.5100 10538480
27 2025-10-21 283.3100 285.3100 281.6000 282.0500 4080981
28 2025-10-20 281.2500 285.5000 280.9600 283.6500 3494336
29 2025-10-17 276.1500 283.4000 275.3500 281.2800 5309565
30 2025-10-16 281.1100 282.5600 275.6000 275.9700 2956923
31 2025-10-15 278.3800 285.4500 277.0000 280.7500 3346753
32 2025-10-14 275.5200 277.5300 272.5469 276.1500 3058149
33 2025-10-13 279.7900 282.4399 274.6400 277.2200 4333836
34 2025-10-10 288.9700 290.3850 277.5000 277.8200 4508506
35 2025-10-09 289.8200 290.1300 283.3200 288.2300 4912375
36 2025-10-08 294.1600 294.2000 286.4730 289.4600 5297030
37 2025-10-07 295.5500 301.0425 293.2850 293.8700 7190126
38 2025-10-06 288.6100 291.4500 287.8000 289.4200 2881947
39 2025-10-03 287.5000 293.3200 287.3000 288.3700 4375082
40 2025-10-02 285.7900 288.5400 282.7900 286.7200 3814232
41 2025-10-01 280.2000 286.5900 280.1500 286.4900 4381338
42 2025-09-30 280.8800 286.0250 280.5200 282.1600 5926924
43 2025-09-29 286.0000 286.0000 279.6600 279.8000 6022125
44 2025-09-26 280.5100 288.8500 280.1100 284.3100 9063938
45 2025-09-25 272.9350 284.2300 271.1480 281.4400 11506192
46 2025-09-24 272.6200 273.6499 267.3000 267.5300 3159924
47 2025-09-23 272.7000 273.2962 269.2650 272.2400 5394121
48 2025-09-22 266.6200 272.3100 266.0000 271.3700 5030540
49 2025-09-19 266.0500 267.8700 263.6400 266.4000 9858112
50 2025-09-18 258.8600 265.2300 256.8004 265.0000 4988421
51 2025-09-17 257.4950 260.9644 257.0100 259.0800 3974785
52 2025-09-16 256.2600 258.0000 254.4100 257.5200 2719918
53 2025-09-15 254.0200 259.0500 254.0000 256.2400 4028365
54 2025-09-12 256.9500 257.2500 252.4250 253.4400 3433300
55 2025-09-11 257.5600 258.5450 255.6550 257.0100 3576048
56 2025-09-10 259.6500 260.0800 254.5600 256.8800 5185420
57 2025-09-09 256.1200 260.6600 254.8800 259.1100 4931105
58 2025-09-08 248.6300 257.1500 247.0200 256.0900 6940270
59 2025-09-05 248.2300 249.0300 245.4500 248.5300 3147478
60 2025-09-04 245.4200 249.2800 242.8500 247.1800 4765087
61 2025-09-03 240.0200 244.2500 239.4100 244.1000 3156289
62 2025-09-02 240.9000 241.5500 238.2500 241.5000 3469501
63 2025-08-29 245.2300 245.4599 241.7200 243.4900 2967558
64 2025-08-28 245.4300 245.8800 243.3600 245.7300 2820817
65 2025-08-27 242.8700 245.9600 242.0000 244.8400 3698372
66 2025-08-26 241.0200 244.9800 240.3800 242.6300 5386582
67 2025-08-25 242.5650 242.5650 239.4300 239.4300 3513327
68 2025-08-22 240.7400 243.6800 240.2200 242.0900 3134882
69 2025-08-21 242.2100 242.5000 238.6500 239.4000 2991902
70 2025-08-20 242.1100 242.8800 240.3400 242.5500 3240064
71 2025-08-19 240.0000 242.8300 239.4900 241.2800 3328305
72 2025-08-18 239.5700 241.4200 239.1158 239.4500 3569594
73 2025-08-15 237.6100 240.6200 236.7700 239.7200 4344322
74 2025-08-14 238.2500 239.0000 235.6200 237.1100 4556725
75 2025-08-13 236.2000 240.8411 236.2000 240.0700 5663562
76 2025-08-12 236.5300 237.9600 233.3600 234.7700 8800597
77 2025-08-11 242.2400 243.1500 234.7000 236.3000 9381960
78 2025-08-08 248.8800 249.4800 241.6500 242.2700 6828390
79 2025-08-07 252.8100 255.0000 248.8750 250.1600 6251285
80 2025-08-06 251.5300 254.3200 249.2800 252.2800 3692105
81 2025-08-05 252.0000 252.8000 248.9950 250.6700 5823016
82 2025-08-04 251.0500 252.0800 248.1100 251.9800 5280588
83 2025-08-01 251.4050 251.4791 245.6100 250.0500 9683404
84 2025-07-31 259.5700 259.9900 252.2200 253.1500 6739092
85 2025-07-30 261.6000 262.0000 258.9000 260.2600 3718290
86 2025-07-29 264.3000 265.7999 261.0200 262.4100 4627265
87 2025-07-28 260.3000 264.0000 259.6100 263.2100 5192516
88 2025-07-25 260.0200 260.8000 256.3500 259.7200 7758653
89 2025-07-24 261.2500 262.0486 252.7500 260.5100 22647720
90 2025-07-23 284.3000 288.0800 281.4400 282.0100 8105906
91 2025-07-22 284.7400 284.8800 281.2500 281.9600 4824219
92 2025-07-21 286.2900 287.7300 284.3800 284.7100 3051791
93 2025-07-18 283.3800 287.1600 282.2200 285.8700 4478165
94 2025-07-17 281.5000 283.4566 280.9000 282.0000 3337168
95 2025-07-16 282.7500 283.8700 279.8700 281.9200 2804831
96 2025-07-15 283.7700 284.1550 280.7301 282.7000 2864106
97 2025-07-14 282.8300 284.9250 281.7100 283.7900 2857401
98 2025-07-11 285.0100 287.4300 282.9200 283.5900 3790679
99 2025-07-10 288.9000 288.9000 282.2100 287.4300 3489068
100 2025-07-09 291.3900 291.6000 288.6300 290.1400 2971309
101 2025-07-08 293.1000 295.6100 289.4900 290.4200 2925329
+13
View File
@@ -0,0 +1,13 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(MSBuildProjectName)' == 'QuanTAlib.Tests'">
<BaseIntermediateOutputPath>obj\tests\</BaseIntermediateOutputPath>
<BaseOutputPath>bin\tests\</BaseOutputPath>
<OutputPath>bin\tests\$(Configuration)\</OutputPath>
</PropertyGroup>
</Project>
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net10.0;net8.0</TargetFrameworks>
<TargetFramework>net10.0</TargetFramework>
<TargetFrameworks></TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
@@ -9,6 +10,7 @@
<EnableMicrosoftTestingPlatform>false</EnableMicrosoftTestingPlatform>
<TestingPlatformDotnetTestSupport>false</TestingPlatformDotnetTestSupport>
<NoWarn>$(NoWarn);CS8892</NoWarn>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
@@ -27,15 +29,15 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\quantalib.csproj" />
<ProjectReference Include="quantalib.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\lib\**\*.Tests.cs" LinkBase="LibraryTests" />
<Compile Include="**\*.Tests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\lib\feeds\csv\daily_IBM.csv">
<None Include="feeds\csv\daily_IBM.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>daily_IBM.csv</Link>
</None>
+58
View File
@@ -0,0 +1,58 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class EmaIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
public int Period { get; set; } = 10;
[IndicatorExtensions.DataSourceInput]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Ema? ma;
protected LineSeries? Series;
protected string? SourceName;
public int MinHistoryDepths => Period;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName => $"EMA {Period}:{SourceName}";
public EmaIndicator()
{
OnBackGround = true;
SeparateWindow = false;
SourceName = Source.ToString();
Name = "EMA - Exponential Moving Average";
Description = "Exponential Moving Average";
Series = new(name: $"EMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
protected override void OnInit()
{
ma = new Ema(Period);
SourceName = Source.ToString();
base.OnInit();
}
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
TValue result = ma!.Update(input, isNew);
Series!.SetValue(result.Value);
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
}
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
this.PaintSmoothCurve(args, Series!, 0, showColdValues: ShowColdValues, tension: 0.2);
}
}
+91
View File
@@ -0,0 +1,91 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"name":"csharp","languageName":"csharp"},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"html","languageName":"HTML"},{"name":"http","languageName":"HTTP"},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"mermaid","languageName":"Mermaid"},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"value"}]}}
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using System;
using System.Linq;
using System.Numerics;
using QuanTAlib;
// 1. Check Hardware Support
Console.WriteLine($"SIMD Hardware Acceleration: {Vector.IsHardwareAccelerated}");
Console.WriteLine($"Vector<double> Count: {Vector<double>.Count}");
#!csharp
// 2. Basic Operations
// Demonstrate Sum, Min, Max, Average using SIMD extensions
// Define data locally in this cell
double[] data = new double[1000];
for (int i = 0; i < data.Length; i++) data[i] = i;
// We use explicit static method calls with .AsSpan() to ensure correct overload resolution
// and avoid creating top-level ReadOnlySpan variables (which causes CS8345).
double sum = SimdExtensions.SumSIMD(data.AsSpan());
double minVal = SimdExtensions.MinSIMD(data.AsSpan());
double maxVal = SimdExtensions.MaxSIMD(data.AsSpan());
double avg = SimdExtensions.AverageSIMD(data.AsSpan());
Console.WriteLine($"Sum: {sum}");
Console.WriteLine($"Min: {minVal}");
Console.WriteLine($"Max: {maxVal}");
Console.WriteLine($"Average: {avg}");
#!csharp
// 3. Advanced Statistics
double[] dataStats = new double[1000];
for (int i = 0; i < dataStats.Length; i++) dataStats[i] = i;
double variance = SimdExtensions.VarianceSIMD(dataStats.AsSpan());
double stdDev = SimdExtensions.StdDevSIMD(dataStats.AsSpan());
Console.WriteLine($"Variance: {variance:F4}");
Console.WriteLine($"Standard Deviation: {stdDev:F4}");
#!csharp
// 4. Combined Operations
double[] dataComb = new double[1000];
for (int i = 0; i < dataComb.Length; i++) dataComb[i] = i;
var (min, max) = SimdExtensions.MinMaxSIMD(dataComb.AsSpan());
Console.WriteLine($"Min: {min}, Max: {max}");
#!csharp
// 5. Performance Comparison (Simple Benchmark)
int size = 1_000_000;
double[] largeData = new double[size];
Random rnd = new Random(42);
for (int i = 0; i < size; i++) largeData[i] = rnd.NextDouble();
// Warmup
SimdExtensions.SumSIMD(largeData.AsSpan());
// Measure SIMD
long start = DateTime.UtcNow.Ticks;
double sumSimd = SimdExtensions.SumSIMD(largeData.AsSpan());
long end = DateTime.UtcNow.Ticks;
double simdTime = (end - start) / 10000.0; // ms
// Measure Scalar (LINQ Sum as proxy for scalar loop)
start = DateTime.UtcNow.Ticks;
double sumScalar = largeData.Sum();
end = DateTime.UtcNow.Ticks;
double scalarTime = (end - start) / 10000.0; // ms
Console.WriteLine($"Array Size: {size:N0}");
Console.WriteLine($"SIMD Time: {simdTime:F4} ms");
Console.WriteLine($"Scalar Time: {scalarTime:F4} ms");
Console.WriteLine($"Speedup: {scalarTime / simdTime:F2}x");
+43
View File
@@ -0,0 +1,43 @@
# SimdExtensions Class
`SimdExtensions` provides high-performance, SIMD-accelerated extension methods for `ReadOnlySpan<double>`. It leverages .NET's `Vector<T>` to achieve 4-8x speedups on supported hardware (AVX2, AVX-512) while automatically falling back to scalar implementations on older hardware.
## Key Features
- **Hardware Acceleration**: Uses CPU vector registers to process multiple elements in parallel.
- **Automatic Fallback**: Gracefully handles non-SIMD hardware or small arrays.
- **Zero-Allocation**: Operates directly on spans without creating new arrays.
- **Aggressive Inlining**: Methods are marked for inlining to minimize call overhead.
## Available Methods
| Method | Description |
|--------|-------------|
| `SumSIMD()` | Calculates the sum of elements. |
| `MinSIMD()` | Finds the minimum value. |
| `MaxSIMD()` | Finds the maximum value. |
| `MinMaxSIMD()` | Finds both min and max in a single pass (more efficient than separate calls). |
| `AverageSIMD()` | Calculates the arithmetic mean. |
| `VarianceSIMD()` | Calculates the sample variance. |
| `StdDevSIMD()` | Calculates the sample standard deviation. |
## Performance
On modern CPUs (e.g., Intel Core i7/i9, AMD Ryzen), these methods typically outperform standard LINQ or scalar loops by a factor of 4 to 8 for large arrays.
## Usage
```csharp
using QuanTAlib;
double[] data = { 1.0, 2.0, 3.0, 4.0, 5.0, ... };
ReadOnlySpan<double> span = data;
// Calculate sum
double sum = span.SumSIMD();
// Calculate min and max in one pass
var (min, max) = span.MinMaxSIMD();
// Calculate standard deviation
double stdDev = span.StdDevSIMD();
+74
View File
@@ -0,0 +1,74 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"}]}}
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using QuanTAlib;
// 1. Creating a TBar
// TBar represents a single OHLCV bar (Open, High, Low, Close, Volume)
// It is an immutable struct optimized for memory and performance
long now = DateTime.UtcNow.Ticks;
var bar = new TBar(now, 100.0, 105.0, 95.0, 102.0, 1000.0);
Console.WriteLine($"Created TBar: {bar}");
Console.WriteLine($"Time: {bar.AsDateTime}");
Console.WriteLine($"Open: {bar.Open}");
Console.WriteLine($"High: {bar.High}");
Console.WriteLine($"Low: {bar.Low}");
Console.WriteLine($"Close: {bar.Close}");
Console.WriteLine($"Volume: {bar.Volume}");
#!csharp
// 2. Computed Properties
// TBar provides on-demand calculation of common price averages
// These are calculated when accessed, saving storage space
Console.WriteLine($"HL2 (High+Low)/2: {bar.HL2}");
Console.WriteLine($"OC2 (Open+Close)/2: {bar.OC2}");
Console.WriteLine($"OHL3 (Open+High+Low)/3: {bar.OHL3}");
Console.WriteLine($"HLC3 (High+Low+Close)/3: {bar.HLC3}");
Console.WriteLine($"OHLC4 (Open+High+Low+Close)/4: {bar.OHLC4}");
Console.WriteLine($"HLCC4 (High+Low+Close+Close)/4: {bar.HLCC4}");
#!csharp
// 3. TValue Accessors
// You can efficiently access individual components as TValue (Time-Value pair)
// This is useful when you need to treat a specific price component as a time series point
Console.WriteLine($"Open TValue: {bar.O}");
Console.WriteLine($"High TValue: {bar.H}");
Console.WriteLine($"Low TValue: {bar.L}");
Console.WriteLine($"Close TValue: {bar.C}");
Console.WriteLine($"Volume TValue: {bar.V}");
#!csharp
// 4. Implicit Conversions
// TBar supports implicit conversions to double (Close price), TValue (Close), and DateTime
double closePrice = bar;
TValue value = bar;
DateTime dt = bar;
Console.WriteLine($"Implicit double (Close): {closePrice}");
Console.WriteLine($"Implicit TValue (Close): {value}");
Console.WriteLine($"Implicit DateTime: {dt}");
#!csharp
// 5. Equality and Immutability
// Being a struct, TBar has value semantics
var bar2 = new TBar(now, 100.0, 105.0, 95.0, 102.0, 1000.0);
var bar3 = new TBar(now, 101.0, 106.0, 96.0, 103.0, 1100.0);
Console.WriteLine($"bar equals bar2? {bar == bar2}"); // True, same values
Console.WriteLine($"bar equals bar3? {bar == bar3}"); // False, different values
+68
View File
@@ -0,0 +1,68 @@
# TBar Struct
`TBar` is a lightweight, immutable struct representing a single OHLCV (Open, High, Low, Close, Volume) bar. It is designed for high-performance financial data processing with minimal memory overhead.
## Key Features
- **Memory Efficient**: Pure data type occupying exactly 48 bytes (1 `long` + 5 `double`s).
- **Immutable**: Thread-safe by design.
- **Zero-Copy Conversions**: Efficiently converts to `TValue` for individual price components (Open, High, Low, Close, Volume).
- **Computed Properties**: Provides on-demand calculation of common price averages (HL2, HLC3, etc.) without storage overhead.
- **SIMD Compatible**: Layout is optimized for potential vectorization in collection types.
## Structure Definition
```csharp
public readonly struct TBar : IEquatable<TBar>
{
public readonly long Time; // Unix ticks
public readonly double Open;
public readonly double High;
public readonly double Low;
public readonly double Close;
public readonly double Volume;
}
```
## Properties
| Property | Type | Description |
|----------|------|-------------|
| `Time` | `long` | Timestamp in ticks. |
| `Open` | `double` | Opening price. |
| `High` | `double` | Highest price. |
| `Low` | `double` | Lowest price. |
| `Close` | `double` | Closing price. |
| `Volume` | `double` | Traded volume. |
| `AsDateTime` | `DateTime` | `Time` converted to UTC DateTime. |
### Computed Averages
These properties are calculated on the fly:
- `HL2`: (High + Low) / 2
- `OC2`: (Open + Close) / 2
- `OHL3`: (Open + High + Low) / 3
- `HLC3`: (High + Low + Close) / 3
- `OHLC4`: (Open + High + Low + Close) / 4
- `HLCC4`: (High + Low + Close + Close) / 4
### TValue Accessors
Efficiently access components as `TValue` (Time-Value pair):
- `O`: (Time, Open)
- `H`: (Time, High)
- `L`: (Time, Low)
- `C`: (Time, Close)
- `V`: (Time, Volume)
## Usage
### Creating a TBar
```csharp
long now = DateTime.UtcNow.Ticks;
var bar = new TBar(now, 100.0, 105.0, 95.0, 102.0, 1000.0);
```
### Implicit Conversions
```csharp
double closePrice = bar; // Implicitly converts to Close price
TValue value = bar; // Implicitly converts to (Time, Close)
DateTime dt = bar; // Implicitly converts to DateTime
@@ -0,0 +1,82 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"}]}}
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using QuanTAlib;
// 1. Creating a TBarSeries
// TBarSeries is a collection of bars stored in Structure of Arrays (SoA) format
// This layout is optimized for performance and SIMD operations
var bars = new TBarSeries();
long now = DateTime.UtcNow.Ticks;
// Add a new bar
var bar1 = new TBar(now, 100, 105, 95, 102, 1000);
bars.Add(bar1, isNew: true);
Console.WriteLine($"Added Bar 1: Count={bars.Count}");
Console.WriteLine($"Last Close: {bars.Last.Close}");
#!csharp
// 2. Streaming Updates
// TBarSeries supports updating the last bar in place
// This is crucial for real-time feeds where the current bar changes until it closes
// Update the bar (e.g. price changed within the same minute)
var bar1Update = new TBar(now, 100, 106, 95, 104, 1500);
bars.Add(bar1Update, isNew: false);
Console.WriteLine($"Updated Bar 1: Count={bars.Count} (Count should not increase)");
Console.WriteLine($"Last Close: {bars.Last.Close}");
Console.WriteLine($"Last High: {bars.Last.High}");
#!csharp
// 3. Zero-Copy Views
// You can access individual components (Open, High, Low, Close, Volume) as TSeries
// These views share the underlying memory, so no copying is involved
Console.WriteLine($"Bars Count: {bars.Count}");
Console.WriteLine($"Close Series Count: {bars.Close.Count}");
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value}");
// Verify view updates automatically
Console.WriteLine("\nAdding new bar...");
bars.Add(now + TimeSpan.TicksPerMinute, 104, 108, 103, 107, 2000, isNew: true);
Console.WriteLine($"Bars Count: {bars.Count}");
Console.WriteLine($"Close Series Count: {bars.Close.Count} (Should match Bars Count)");
Console.WriteLine($"Close Series Last: {bars.Close.Last.Value} (Should be 107)");
#!csharp
// 4. Aliases and Direct Access
// TBarSeries provides short aliases (O, H, L, C, V) and direct access properties
Console.WriteLine($"Alias Access (C.Last): {bars.C.Last.Value}");
Console.WriteLine($"Direct Last Access (LastClose): {bars.LastClose}");
Console.WriteLine($"Direct Last Time (LastTime): {new DateTime(bars.LastTime)}");
#!csharp
// 5. Iteration
// You can iterate over the bars or individual series
Console.WriteLine("\nIterating over bars:");
foreach (var bar in bars)
{
Console.WriteLine($" {bar}");
}
Console.WriteLine("\nIterating over Close prices:");
for (int i = 0; i < bars.Count; i++)
{
Console.WriteLine($" Bar {i}: Close={bars.Close[i].Value}");
}
+70
View File
@@ -0,0 +1,70 @@
# TBarSeries Class
`TBarSeries` is a high-performance collection of OHLCV bars implemented using a Structure of Arrays (SoA) layout. This design optimizes memory access patterns and enables efficient SIMD operations while providing convenient object-oriented views.
## Key Features
- **Structure of Arrays (SoA)**: Stores Time, Open, High, Low, Close, and Volume in separate contiguous arrays rather than an array of structs. This improves cache locality for operations that only need specific components (e.g., calculating SMA on Close prices).
- **Zero-Copy Views**: Exposes `TSeries` properties (`Open`, `High`, `Low`, `Close`, `Volume`) that view the underlying data without copying.
- **Streaming Support**: Efficiently handles real-time data updates with `Add(bar, isNew: false)`.
- **Memory Efficient**: Minimizes object overhead by using shared internal lists.
## Class Definition
```csharp
public class TBarSeries : IReadOnlyList<TBar>
{
// Views
public TSeries Open { get; }
public TSeries High { get; }
public TSeries Low { get; }
public TSeries Close { get; }
public TSeries Volume { get; }
// Aliases
public TSeries O => Open;
public TSeries H => High;
public TSeries L => Low;
public TSeries C => Close;
public TSeries V => Volume;
}
```
## Core Methods
| Method | Description |
|--------|-------------|
| `Add(TBar bar, bool isNew = true)` | Adds a new bar or updates the last one. |
| `Add(DateTime time, double o, double h, double l, double c, double v, bool isNew)` | Adds raw values directly. |
| `Count` | Returns the number of bars. |
| `Last` | Returns the most recent `TBar`. |
## Usage
### Creating and Populating
```csharp
var bars = new TBarSeries();
// Add a new bar
long now = DateTime.UtcNow.Ticks;
bars.Add(new TBar(now, 100, 105, 95, 102, 1000), isNew: true);
// Update the last bar (e.g., real-time feed update)
bars.Add(new TBar(now, 100, 106, 95, 104, 1500), isNew: false);
```
### Accessing Data
```csharp
// Access entire bar
TBar lastBar = bars.Last;
// Access specific component series (Zero-Copy)
TSeries closes = bars.Close;
double lastClose = closes.Last.Value;
// Access via indexer
TBar firstBar = bars[0];
```
### Performance Note
Because `TBarSeries` uses SoA layout, iterating over a single component (like `Close` prices) is extremely cache-efficient. The CPU prefetcher can load contiguous doubles without loading the interleaved Open, High, Low, or Volume data.
+84
View File
@@ -0,0 +1,84 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"name":"csharp"},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"html","languageName":"HTML"},{"name":"http","languageName":"HTTP"},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"mermaid","languageName":"Mermaid"},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"value"}]}}
#!markdown
# TSeries Examples
This notebook demonstrates the usage of `TSeries`, the high-performance time series container in QuanTAlib.
For detailed documentation, see [TSeries.md](TSeries.md).
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using System;
using QuanTAlib;
#!markdown
## Creating and Adding Data
`TSeries` supports adding data via `DateTime` or `ticks`.
#!csharp
var series = new TSeries();
var now = DateTime.UtcNow;
// Add new values
series.Add(now, 10.0);
series.Add(now.AddMinutes(1), 11.0);
series.Add(now.AddMinutes(2), 12.0);
Console.WriteLine($"Count: {series.Count}");
Console.WriteLine($"Last Value: {series.Last.Value}");
#!markdown
## Streaming Updates (`isNew`)
In real-time scenarios, you often receive updates for the *current* bar before it closes. `TSeries` handles this via the `isNew` parameter.
#!csharp
var streamSeries = new TSeries();
long t = DateTime.UtcNow.Ticks;
// 1. New Bar
streamSeries.Add(t, 100.0, isNew: true);
Console.WriteLine($"New Bar: Count={streamSeries.Count}, Last={streamSeries.Last.Value}");
// 2. Update Current Bar (Price moves to 101.0)
streamSeries.Add(t, 101.0, isNew: false);
Console.WriteLine($"Update: Count={streamSeries.Count}, Last={streamSeries.Last.Value}");
// 3. Update Current Bar (Price moves to 100.5)
streamSeries.Add(t, 100.5, isNew: false);
Console.WriteLine($"Update: Count={streamSeries.Count}, Last={streamSeries.Last.Value}");
// 4. New Bar (Next minute)
streamSeries.Add(t + TimeSpan.TicksPerMinute, 102.0, isNew: true);
Console.WriteLine($"New Bar: Count={streamSeries.Count}, Last={streamSeries.Last.Value}");
#!markdown
## Zero-Copy Access (Spans)
You can access the underlying data arrays directly as `ReadOnlySpan<T>` for high-performance processing.
#!csharp
// Access Values as Span
Console.WriteLine("Values in Span:");
foreach (var v in series.Values)
{
Console.Write($"{v} ");
}
Console.WriteLine();
// Access Times as Span
Console.WriteLine($"First Time: {new DateTime(series.Times[0])}");
+56
View File
@@ -0,0 +1,56 @@
# TSeries: Time Series Data
## Overview
`TSeries` is a high-performance container for time-series data. Unlike a standard `List<TValue>`, it uses a **Structure of Arrays (SoA)** layout internally. This means it stores timestamps and values in separate contiguous arrays (`List<long>` and `List<double>`).
This layout is critical for performance because it allows:
1. **SIMD Optimization**: The `Values` property returns a `ReadOnlySpan<double>` that can be directly processed by CPU vector instructions (AVX/SSE).
2. **Cache Locality**: Iterating over values doesn't load timestamps into the CPU cache, and vice versa.
## Structure
```csharp
public class TSeries : IReadOnlyList<TValue>
{
// Internal SoA storage
protected readonly List<long> _t;
protected readonly List<double> _v;
// Public accessors
public ReadOnlySpan<double> Values => ...; // Zero-copy access
public ReadOnlySpan<long> Times => ...; // Zero-copy access
public TValue Last { get; }
public int Count { get; }
}
```
## Key Features
* **SoA Layout**: Optimized for numerical computing and SIMD.
* **Zero-Copy Access**: `Values` and `Times` properties expose internal storage as Spans without copying.
* **Streaming Support**: The `Add` method supports `isNew` parameter to handle intra-bar updates (replacing the last value instead of appending).
* **Event Publishing**: Optional `Pub` event for reactive pipelines.
## Usage
### Creating and Adding Data
```csharp
var series = new TSeries();
series.Add(DateTime.Now, 100.0); // isNew=true by default
```
### Streaming Updates
```csharp
// New bar
series.Add(time, 100.0, isNew: true);
// Update current bar (e.g. price change within same minute)
series.Add(time, 101.0, isNew: false);
```
### SIMD Processing
```csharp
// Calculate average using SIMD
double avg = series.Values.AverageSIMD();
+69
View File
@@ -0,0 +1,69 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"name":"csharp"},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"html","languageName":"HTML"},{"name":"http","languageName":"HTTP"},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"mermaid","languageName":"Mermaid"},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"value"}]}}
#!markdown
# TValue Examples
This notebook demonstrates the usage of `TValue`, the fundamental data structure in QuanTAlib.
For detailed documentation, see [TValue.md](TValue.md).
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using System;
using QuanTAlib;
#!markdown
## Creating TValue
You can create a `TValue` using `DateTime` or `ticks`.
#!csharp
// Using DateTime
var now = DateTime.UtcNow;
var val1 = new TValue(now, 100.5);
Console.WriteLine($"Created TValue: Time={val1.AsDateTime}, Value={val1.Value}");
// Using Ticks
long ticks = now.AddMinutes(1).Ticks;
var val2 = new TValue(ticks, 101.0);
Console.WriteLine($"Created TValue: Time={val2.AsDateTime}, Value={val2.Value}");
#!markdown
## Implicit Conversions
`TValue` supports implicit conversions to `double` and `DateTime` for convenience.
#!csharp
double d = val1; // Implicitly gets Value
DateTime t = val1; // Implicitly gets Time (as DateTime)
Console.WriteLine($"Double: {d}");
Console.WriteLine($"DateTime: {t}");
// Arithmetic operations using implicit conversion
double result = val1 + 5.0;
Console.WriteLine($"Result (100.5 + 5.0): {result}");
#!markdown
## Immutability
`TValue` is immutable. You cannot change its properties after creation.
#!csharp
// val1.Value = 200; // Error: Property or indexer 'TValue.Value' cannot be assigned to -- it is read only
// To "change" a value, create a new instance
var val3 = new TValue(val1.Time, 200.0);
Console.WriteLine($"New TValue: {val3.Value}");
+37
View File
@@ -0,0 +1,37 @@
# TValue: Time-Value Pair
## Overview
`TValue` is the fundamental building block of QuanTAlib. It represents a single data point in a time series, consisting of a timestamp and a double-precision floating-point value.
It is implemented as a lightweight `readonly struct` to ensure immutability and high performance (stack allocation, no GC overhead).
## Structure
```csharp
public readonly struct TValue
{
public readonly long Time; // Ticks (UTC)
public readonly double Value; // Data value
public readonly bool IsNew; // Metadata for streaming (optional usage)
}
```
## Key Features
* **Lightweight**: 24 bytes (long + double + bool + padding).
* **Immutable**: Thread-safe by design.
* **Implicit Conversions**: Can be implicitly converted to `double` (returns Value) and `DateTime` (returns Time).
* **Performance**: Designed for high-frequency trading and large dataset processing.
## Usage
`TValue` is used throughout the library for:
* Input to indicators (`Update(TValue)`).
* Output from indicators (`Value` property).
* Elements in `TSeries`.
## Constructors
* `new TValue(long time, double value, bool isNew = true)`
* `new TValue(DateTime time, double value, bool isNew = true)`
+45
View File
@@ -0,0 +1,45 @@
# IFeed Interface
`IFeed` defines the standard contract for all data feeds in QuanTAlib, ensuring consistent behavior across different data sources (synthetic, file-based, or live API).
## Key Concepts
- **Bidirectional Control**: The `Next(ref bool isNew)` method allows the consumer to request a new bar (`isNew = true`) or an update to the current bar (`isNew = false`).
- **Streaming**: Designed for bar-by-bar processing, simulating real-time data flow.
- **Batching**: Supports fetching historical data ranges via `Fetch()`.
## Interface Definition
```csharp
public interface IFeed
{
/// <summary>
/// Gets the next bar with full control over new/update state.
/// </summary>
TBar Next(ref bool isNew);
/// <summary>
/// Convenience overload for simple next-bar requests.
/// </summary>
TBar Next(bool isNew = true);
/// <summary>
/// Retrieves a batch of historical bars.
/// </summary>
TBarSeries Fetch(int count, long startTime, TimeSpan interval);
}
```
## Implementation Guidelines
When implementing `IFeed`:
1. **State Management**: Maintain the current position in the data source.
2. **End of Data**: When data is exhausted, `Next` should return the last valid bar and set `isNew` to `false`.
3. **Intra-bar Updates**: If the source supports it (e.g., live ticks), `Next(isNew: false)` should return the updated state of the current bar. If not supported (e.g., CSV), it should return the current bar unchanged.
4. **Thread Safety**: Implementations are generally not required to be thread-safe unless specified.
## Implementations
- **`GBM`**: Geometric Brownian Motion generator (Synthetic).
- **`CsvFeed`**: Reads OHLCV data from CSV files (Historical).
+68
View File
@@ -0,0 +1,68 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"name":"csharp","languageName":"csharp"},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"html","languageName":"HTML"},{"name":"http","languageName":"HTTP"},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"mermaid","languageName":"Mermaid"},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"value"}]}}
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using QuanTAlib;
using System.IO;
// 1. Setup: Use existing CSV file
// CsvFeed expects a CSV with header: timestamp,open,high,low,close,volume
// Timestamp format: YYYY-MM-DD
string csvPath = "daily_IBM.csv";
Console.WriteLine($"Using CSV file: {csvPath}");
#!csharp
// 2. Initialize CsvFeed
// The feed loads the data and prepares it for streaming
var feed = new CsvFeed(csvPath);
Console.WriteLine("CsvFeed initialized.");
#!csharp
// 3. Streaming Data
// Simulate processing historical data bar by bar
Console.WriteLine("\nStreaming data (first 5 bars):");
int count = 0;
bool isNew = true;
// Get first bar
var bar = feed.Next(isNew: true);
while (isNew && count < 5)
{
count++;
Console.WriteLine($" Bar {count}: {bar}");
// Get next bar
bar = feed.Next(ref isNew);
}
Console.WriteLine($"Streamed {count} bars.");
#!csharp
// 4. Batch Fetching
// Retrieve a specific range of data
Console.WriteLine("\nBatch fetching:");
// Using a date range present in daily_IBM.csv (July 2025)
long startTime = new DateTime(2025, 7, 8).Ticks;
var interval = TimeSpan.FromDays(1);
// Fetch 3 bars starting from July 8th, 2025
var batch = feed.Fetch(5, startTime, interval);
Console.WriteLine($"Fetched {batch.Count} bars:");
foreach (var b in batch)
{
Console.WriteLine($" {b}");
}
+68
View File
@@ -0,0 +1,68 @@
# CsvFeed Class
`CsvFeed` is a file-based feed implementation that loads historical OHLCV data from CSV files. It supports both streaming access (simulating real-time playback) and batch retrieval.
## Key Features
- **Historical Data Loading**: Reads standard OHLCV CSV files.
- **Chronological Ordering**: Automatically reverses data if needed (assumes newest-first in file, provides oldest-first).
- **Streaming Interface**: Implements `IFeed` for consistent usage with other feed types.
- **Batch Retrieval**: Supports fetching specific time ranges via `Fetch()`.
## CSV Format Requirements
The file must have a header row and follow this column order:
`timestamp, open, high, low, close, volume`
- **Timestamp**: `YYYY-MM-DD` (assumed UTC midnight)
- **Prices/Volume**: Numeric values
Example:
```csv
Date,Open,High,Low,Close,Volume
2024-01-01,100.0,105.0,99.0,102.5,10000
2024-01-02,102.5,103.0,101.0,101.5,8500
```
## Class Definition
```csharp
public class CsvFeed : IFeed
{
public CsvFeed(string filePath);
public TBar Next(bool isNew = true);
public TBarSeries Fetch(int count, long startTime, TimeSpan interval);
}
```
## Usage
### 1. Loading Data
```csharp
var feed = new CsvFeed("path/to/data.csv");
```
### 2. Streaming Data (Simulation)
```csharp
// Get first bar
var bar = feed.Next(isNew: true);
// Loop through all data
while (true)
{
// Process bar...
Console.WriteLine(bar);
// Get next bar
bool isNew = true;
bar = feed.Next(ref isNew);
// Stop if no more new data
if (!isNew) break;
}
```
### 3. Fetching a Batch
```csharp
long startTime = new DateTime(2024, 1, 1).Ticks;
var batch = feed.Fetch(10, startTime, TimeSpan.FromDays(1));
+69
View File
@@ -0,0 +1,69 @@
#!meta
{"kernelInfo":{"defaultKernelName":"csharp","items":[{"name":"csharp","languageName":"csharp"},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"html","languageName":"HTML"},{"name":"http","languageName":"HTTP"},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"mermaid","languageName":"Mermaid"},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"value"}]}}
#!csharp
// Reference the library
#r "..\..\bin\QuanTAlib.dll"
using QuanTAlib;
// 1. Initialize GBM Generator
// GBM simulates price movements using Geometric Brownian Motion
// Parameters: Start Price, Drift (mu), Volatility (sigma)
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2);
Console.WriteLine("GBM Generator initialized (Start=100, Drift=5%, Vol=20%)");
#!csharp
// 2. Batch Generation
// Generate a sequence of bars at once
// Useful for backtesting or initializing indicators
long startTime = DateTime.UtcNow.Ticks;
var interval = TimeSpan.FromMinutes(1);
var history = gbm.Fetch(10, startTime, interval);
Console.WriteLine($"Generated {history.Count} bars:");
for (int i = 0; i < history.Count; i++)
{
Console.WriteLine($" Bar {i}: Time={history[i].AsDateTime:HH:mm}, Close={history[i].Close:F2}");
}
#!csharp
// 3. Streaming Generation
// Simulate real-time data feed bar by bar
Console.WriteLine("\nStreaming new bars:");
for (int i = 0; i < 3; i++)
{
var bar = gbm.Next(isNew: true);
Console.WriteLine($" New Bar: {bar.Close:F2}");
}
#!csharp
// 4. Intra-bar Updates
// Simulate real-time price ticks within a single bar
// The High/Low will expand, and Close will update
Console.WriteLine("\nSimulating intra-bar updates:");
// Start a new bar
var liveBar = gbm.Next(isNew: true);
Console.WriteLine($" Open: {liveBar.Open:F2}, Close: {liveBar.Close:F2}");
// Simulate 5 ticks
for (int i = 1; i <= 5; i++)
{
liveBar = gbm.Next(isNew: false);
Console.WriteLine($" Tick {i}: Close={liveBar.Close:F2}, High={liveBar.High:F2}, Low={liveBar.Low:F2}");
}
// Finalize bar
liveBar = gbm.Next(isNew: true);
Console.WriteLine($" Finalized Previous, Started New: {liveBar.Open:F2}");
+67
View File
@@ -0,0 +1,67 @@
# GBM Class
`GBM` (Geometric Brownian Motion) is a synthetic data generator that simulates realistic financial price movements. It is useful for testing indicators, strategies, and system performance without relying on external data files.
## Key Features
- **Geometric Brownian Motion**: Uses the standard mathematical model for asset price dynamics.
- **Configurable Parameters**: Control drift (trend) and volatility (noise).
- **Stateless Design**: Minimal memory footprint; only maintains state needed for continuity.
- **Dual Modes**: Supports both streaming (bar-by-bar) and batch generation.
- **Intra-bar Updates**: Can simulate real-time price updates within a single bar.
## Mathematical Model
The price evolution follows the stochastic differential equation:
$$ dS_t = \mu S_t dt + \sigma S_t dW_t $$
Where:
- $S_t$: Asset price at time $t$
- $\mu$: Drift (expected return)
- $\sigma$: Volatility (standard deviation of returns)
- $W_t$: Wiener process (Brownian motion)
## Class Definition
```csharp
public class GBM : IFeed
{
public GBM(double startPrice = 100.0, double mu = 0.05, double sigma = 0.2, TimeSpan? defaultTimeframe = null);
public TBar Next(bool isNew = true);
public TBarSeries Fetch(int count, long startTime, TimeSpan interval);
}
```
## Usage
### 1. Initialization
```csharp
// Default: Start at 100, 5% drift, 20% volatility
var gbm = new GBM();
// Custom: Start at 50, 10% drift, 50% volatility
var volatileGbm = new GBM(startPrice: 50.0, mu: 0.10, sigma: 0.50);
```
### 2. Streaming Generation
```csharp
// Generate a new bar
var bar = gbm.Next(isNew: true);
// Simulate intra-bar updates (e.g., real-time ticks)
for (int i = 0; i < 5; i++)
{
var updatedBar = gbm.Next(isNew: false);
Console.WriteLine($"Update: {updatedBar.Close}");
}
```
### 3. Batch Generation
```csharp
long startTime = DateTime.UtcNow.Ticks;
var interval = TimeSpan.FromMinutes(1);
// Generate 1000 bars
var history = gbm.Fetch(1000, startTime, interval);
+10 -5
View File
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<Title>QuanTAlib</Title>
<Product>Library of TA Calculations, Charts and Strategies for Quantower</Product>
<Description>Quantitative Technical Analysis Library in C# for Quantower</Description>
@@ -8,7 +8,6 @@
<RepositoryUrl>https://github.com/mihakralj/QuanTAlib</RepositoryUrl>
<Authors>Miha Kralj</Authors>
<Copyright>Miha Kralj</Copyright>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<RootNamespace>QuanTAlib</RootNamespace>
<AssemblyName>QuanTAlib</AssemblyName>
<IsPublishable>true</IsPublishable>
@@ -34,12 +33,18 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" Exclude="**\*.Tests.cs;**\*.Benchmarks.cs;**\*.benchmark.cs;obj\**\*.cs" />
<PackageReference Include="System.Text.Encodings.Web" Version="10.0.0-rc.1.25451.107" />
<Compile Include="**\*.cs" Exclude="**\*.Tests.cs;**\*.Quantower.cs;obj\**\*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GitVersion.MsBuild" Version="5.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="..\docs\readme.md" Pack="true" PackagePath="" />
<None Include="..\.github\QuanTAlib2.png" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
+32
View File
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>Averages</AssemblyName>
<AlgoType>Indicator</AlgoType>
<OutputPath>bin\$(Configuration)\</OutputPath>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="*.cs" />
<Compile Include="..\lib\core\**\*.cs" Exclude="..\lib\core\**\*.Tests.cs" />
<Compile Include="..\lib\averages\**\*.cs" Exclude="..\lib\averages\**\*.Tests.cs" />
<Reference Include="TradingPlatform.BusinessLayer">
<HintPath>..\.github\TradingPlatform.BusinessLayer.dll</HintPath>
</Reference>
<None Include="..\.github\TradingPlatform.BusinessLayer.xml">
<Link>TradingPlatform.BusinessLayer.xml</Link>
</None>
</ItemGroup>
<Target Name="CopyCustomContent" AfterTargets="AfterBuild" Condition="'$(IsLocalBuild)' == 'true' AND $([MSBuild]::IsOSPlatform('Windows'))">
<Copy SourceFiles="$(OutputPath)\Averages.dll" DestinationFolder="$(QuantowerRoot)\Settings\Scripts\Indicators\QuanTAlib\Averages" />
</Target>
</Project>
+237
View File
@@ -0,0 +1,237 @@
using TradingPlatform.BusinessLayer;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace QuanTAlib;
public enum SourceType
{
Open, High, Low, Close, HL2, OC2, OHL3, HLC3, OHLC4, HLCC4
}
public enum MaType
{
Alma, Dema, Dsma, Dwma, Ema, Epma, Frama, Fwma, Gma, Hma, Hwma, Jma, Kama, Maaf, Mgdi, MMa, Pwma, Rema, Rma, Sinema, Sma, Smma, T3, Tema, Trima, Vidya, Wma, Zlema
}
public static class IndicatorExtensions
{
public static readonly Color Averages = Color.FromArgb(255, 255, 128); // #FFFF80 - Yellow
public static readonly Color Volume = Color.FromArgb(128, 255, 128); // #80FF80 - Green
public static readonly Color Volatility = Color.FromArgb(255, 128, 128); // #FF8080 - Red
public static readonly Color Statistics = Color.FromArgb(128, 128, 255); // #8080FF - Blue
public static readonly Color Oscillators = Color.FromArgb(255, 128, 255); // #FF80FF - Magenta
public static readonly Color Momentum = Color.FromArgb(128, 255, 255); // #80FFFF - Cyan
public static readonly Color Experiments = Color.FromArgb(255, 165, 0); // #FFA500 - Orange
[AttributeUsage(AttributeTargets.Property)]
public class DataSourceInputAttribute : InputParameterAttribute
{
public DataSourceInputAttribute(string label = "Data source", int sortIndex = 20)
: base(label, sortIndex, variants: new object[]
{
"Open", SourceType.Open,
"High", SourceType.High,
"Low", SourceType.Low,
"Close", SourceType.Close,
"HL/2 (Median)", SourceType.HL2,
"OC/2 (Midpoint)", SourceType.OC2,
"OHL/3 (Mean)", SourceType.OHL3,
"HLC/3 (Typical)", SourceType.HLC3,
"OHLC/4 (Average)", SourceType.OHLC4,
"HLCC/4 (Weighted)", SourceType.HLCC4
})
{ }
}
public static TValue GetInputValue(this Indicator indicator, UpdateArgs args, SourceType source)
{
var historicalData = indicator.HistoricalData;
TBar bar = new TBar(
time: historicalData.Time(),
open: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Open],
high: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.High],
low: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Low],
close: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Close],
volume: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Volume]
);
double price = source switch
{
SourceType.Open => bar.Open,
SourceType.High => bar.High,
SourceType.Low => bar.Low,
SourceType.Close => bar.Close,
SourceType.HL2 => bar.HL2,
SourceType.OC2 => bar.OC2,
SourceType.OHL3 => bar.OHL3,
SourceType.HLC3 => bar.HLC3,
SourceType.OHLC4 => bar.OHLC4,
SourceType.HLCC4 => bar.HLCC4,
_ => bar.Close
};
return new TValue(bar.Time, price);
}
public static TBar GetInputBar(this Indicator indicator, UpdateArgs args)
{
var historicalData = indicator.HistoricalData;
return new TBar(
time: historicalData.Time(),
open: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Open],
high: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.High],
low: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Low],
close: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Close],
volume: historicalData[indicator.Count - 1, SeekOriginHistory.Begin][PriceType.Volume]
);
}
#pragma warning disable CA1416 // Validate platform compatibility
public static void PaintHLine(this Indicator indicator, PaintChartEventArgs args, double value, Pen pen)
{
if (indicator.CurrentChart == null)
return;
Graphics gr = args.Graphics;
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
var converter = mainWindow.CoordinatesConverter;
var clientRect = mainWindow.ClientRectangle;
gr.SetClip(clientRect);
int leftX = clientRect.Left;
int rightX = clientRect.Right;
int Y = (int)converter.GetChartY(value);
using (pen)
{
gr.DrawLine(pen, new Point(leftX, Y), new Point(rightX, Y));
}
}
public static void PaintSmoothCurve(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true, double tension = 0.2)
{
if (!series.Visible || indicator.CurrentChart == null)
return;
Graphics gr = args.Graphics;
gr.SmoothingMode = SmoothingMode.AntiAlias;
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
var converter = mainWindow.CoordinatesConverter;
var clientRect = mainWindow.ClientRectangle;
gr.SetClip(clientRect);
DateTime leftTime = new[] { converter.GetTime(clientRect.Left), indicator.HistoricalData.Time(indicator!.Count - 1) }.Max();
DateTime rightTime = new[] { converter.GetTime(clientRect.Right), indicator.HistoricalData.Time(0) }.Min();
int leftIndex = (int)indicator.HistoricalData.GetIndexByTime(leftTime.Ticks) + 1;
int rightIndex = (int)indicator.HistoricalData.GetIndexByTime(rightTime.Ticks);
List<Point> allPoints = new List<Point>();
for (int i = rightIndex; i < leftIndex; i++)
{
int barX = (int)converter.GetChartX(indicator.HistoricalData.Time(i));
int barY = (int)converter.GetChartY(series[i]);
int halfBarWidth = indicator.CurrentChart.BarsWidth / 2;
Point point = new Point(barX + halfBarWidth, barY);
allPoints.Add(point);
}
if (allPoints.Count > 1)
{
if (allPoints.Count < 2) return;
using (Pen defaultPen = new(series.Color, series.Width) { DashStyle = ConvertLineStyleToDashStyle(series.Style) })
using (Pen coldPen = new(series.Color, series.Width) { DashStyle = DashStyle.Dot })
{
int hotCount = indicator.Count - warmupPeriod - rightIndex;
// Draw the hot part
if (hotCount > 0)
{
var hotPoints = allPoints.Take(Math.Min(hotCount + 1, allPoints.Count)).ToArray();
gr.DrawCurve(defaultPen, hotPoints, 0, hotPoints.Length - 1, (float)tension);
}
// Draw the cold part
if (showColdValues && hotCount < allPoints.Count)
{
var coldPoints = allPoints.Skip(Math.Max(0, hotCount)).ToArray();
gr.DrawCurve(coldPen, coldPoints, 0, coldPoints.Length - 1, (float)tension);
}
}
}
}
public static void PaintHistogram(this Indicator indicator, PaintChartEventArgs args, LineSeries series, int warmupPeriod, bool showColdValues = true)
{
if (!series.Visible || indicator.CurrentChart == null)
return;
Graphics gr = args.Graphics;
gr.SmoothingMode = SmoothingMode.AntiAlias;
var mainWindow = indicator.CurrentChart.Windows[args.WindowIndex];
var converter = mainWindow.CoordinatesConverter;
var clientRect = mainWindow.ClientRectangle;
gr.SetClip(clientRect);
DateTime leftTime = new[] { converter.GetTime(clientRect.Left), indicator.HistoricalData.Time(indicator!.Count - 1) }.Max();
DateTime rightTime = new[] { converter.GetTime(clientRect.Right), indicator.HistoricalData.Time(0) }.Min();
int leftIndex = (int)indicator.HistoricalData.GetIndexByTime(leftTime.Ticks) + 1;
int rightIndex = (int)indicator.HistoricalData.GetIndexByTime(rightTime.Ticks);
for (int i = rightIndex; i < leftIndex; i++)
{
int barX = (int)converter.GetChartX(indicator.HistoricalData.Time(i));
int barY = (int)converter.GetChartY(series[i]);
int barY0 = (int)converter.GetChartY(0);
int HistBarWidth = indicator.CurrentChart.BarsWidth - 2;
if (series[i] > 0)
{
using (Brush hist = new SolidBrush(Color.FromArgb(150, 0, 255, 0)))
{
gr.FillRectangle(hist, barX, barY, HistBarWidth, Math.Abs(barY - barY0));
}
}
else
{
using (Brush hist = new SolidBrush(Color.FromArgb(150, 255, 0, 0)))
{
gr.FillRectangle(hist, barX, barY0, HistBarWidth, Math.Abs(barY0 - barY));
}
}
}
}
public static void DrawText(this Indicator indicator, PaintChartEventArgs args, string text)
{
if (indicator.CurrentChart == null)
return;
Graphics gr = args.Graphics;
var clientRect = indicator.CurrentChart.MainWindow.ClientRectangle;
Font font = new Font("Inter", 8);
SizeF textSize = gr.MeasureString(text, font);
RectangleF textRect = new RectangleF(clientRect.Left + 5,
clientRect.Bottom - textSize.Height - 10,
textSize.Width + 10, textSize.Height + 10);
gr.FillRectangle(Brushes.DarkBlue, textRect);
gr.DrawString(text, font, Brushes.White, new PointF(textRect.X + 6, textRect.Y + 5));
}
private static DashStyle ConvertLineStyleToDashStyle(LineStyle lineStyle)
{
return lineStyle switch
{
LineStyle.Solid => DashStyle.Solid,
LineStyle.Dash => DashStyle.Dash,
LineStyle.Dot => DashStyle.Dot,
LineStyle.DashDot => DashStyle.DashDot,
_ => DashStyle.Solid,
};
}
}