From b5881b9bb47818a12d31066a9c5131eeee1744bf Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Tue, 25 Nov 2025 20:40:18 -0800 Subject: [PATCH] feat: add CSV feed and GBM examples - Add CsvFeedExample console app demonstrating CSV data loading and streaming - Add GbmExample console app for geometric Brownian motion feed - Add CoreTypes example project - Include daily_IBM.csv test data file - Configure project files for .NET 10.0 with quantalib dependency --- examples/CoreTypes/CoreTypes.csproj | 14 ++++ examples/CoreTypes/Program.cs | 119 +++++++++++++++++++++++++++ examples/feeds/CsvFeedExample.cs | 47 +++++++++++ examples/feeds/CsvFeedExample.csproj | 20 +++++ examples/feeds/GbmExample.cs | 77 +++++++++++++++++ examples/feeds/GbmExample.csproj | 15 ++++ examples/feeds/daily_IBM.csv | 101 +++++++++++++++++++++++ 7 files changed, 393 insertions(+) create mode 100644 examples/CoreTypes/CoreTypes.csproj create mode 100644 examples/CoreTypes/Program.cs create mode 100644 examples/feeds/CsvFeedExample.cs create mode 100644 examples/feeds/CsvFeedExample.csproj create mode 100644 examples/feeds/GbmExample.cs create mode 100644 examples/feeds/GbmExample.csproj create mode 100644 examples/feeds/daily_IBM.csv diff --git a/examples/CoreTypes/CoreTypes.csproj b/examples/CoreTypes/CoreTypes.csproj new file mode 100644 index 00000000..89091b79 --- /dev/null +++ b/examples/CoreTypes/CoreTypes.csproj @@ -0,0 +1,14 @@ + + + + + + + + Exe + net10.0 + enable + enable + + + diff --git a/examples/CoreTypes/Program.cs b/examples/CoreTypes/Program.cs new file mode 100644 index 00000000..29be166e --- /dev/null +++ b/examples/CoreTypes/Program.cs @@ -0,0 +1,119 @@ +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."); + } + } +} diff --git a/examples/feeds/CsvFeedExample.cs b/examples/feeds/CsvFeedExample.cs new file mode 100644 index 00000000..6379cc87 --- /dev/null +++ b/examples/feeds/CsvFeedExample.cs @@ -0,0 +1,47 @@ +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 ==="); diff --git a/examples/feeds/CsvFeedExample.csproj b/examples/feeds/CsvFeedExample.csproj new file mode 100644 index 00000000..94efec9e --- /dev/null +++ b/examples/feeds/CsvFeedExample.csproj @@ -0,0 +1,20 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + PreserveNewest + + + + diff --git a/examples/feeds/GbmExample.cs b/examples/feeds/GbmExample.cs new file mode 100644 index 00000000..e55fdb5e --- /dev/null +++ b/examples/feeds/GbmExample.cs @@ -0,0 +1,77 @@ +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."); + } +} diff --git a/examples/feeds/GbmExample.csproj b/examples/feeds/GbmExample.csproj new file mode 100644 index 00000000..dece856e --- /dev/null +++ b/examples/feeds/GbmExample.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + enable + enable + 73a533c9-b5d2-4762-9ac9-5017fcffa8d1 + + + + + + + diff --git a/examples/feeds/daily_IBM.csv b/examples/feeds/daily_IBM.csv new file mode 100644 index 00000000..111591d2 --- /dev/null +++ b/examples/feeds/daily_IBM.csv @@ -0,0 +1,101 @@ +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