mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 04:28:04 +00:00
Add TRIMA implementation and benchmarks; optimize WMA with SIMD
- Introduced `TrimaVector` class for multi-period Triangular Moving Average (TRIMA) calculations, optimized for SIMD. - Implemented last-value substitution for invalid inputs in TRIMA. - Added methods for calculating TRIMA for entire series and individual updates. - Enhanced `Wma` class with periodic resync to prevent floating-point drift and introduced SIMD optimizations for performance. - Updated benchmark suite to include TRIMA calculations alongside existing SMA, EMA, and WMA benchmarks.
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
#!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
|
||||
|
||||
# Triangular Moving Average (TRIMA) Examples
|
||||
|
||||
This is a **.NET Interactive** notebook. To run it, you need the [Polyglot Notebooks](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) extension installed in VS Code.
|
||||
|
||||
The **Triangular Moving Average (TRIMA)** is a weighted moving average where the weights increase linearly to the middle of the period and then decrease. It is equivalent to a double-smoothed SMA (SMA of an SMA).
|
||||
|
||||
**Key characteristics:**
|
||||
- Triangular weighting (emphasis on middle values)
|
||||
- Smoother than SMA
|
||||
- Higher lag than SMA
|
||||
- O(1) update complexity
|
||||
|
||||
#!csharp
|
||||
|
||||
// Reference the library
|
||||
#r "..\..\bin\QuanTAlib.dll"
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using QuanTAlib;
|
||||
|
||||
// Helper to print TSeries
|
||||
void PrintSeries(TSeries series, int count = 5)
|
||||
{
|
||||
Console.WriteLine($"Series Length: {series.Count}");
|
||||
foreach (var item in series.Take(count))
|
||||
{
|
||||
Console.WriteLine($"Time: {item.Time:HH:mm:ss}, Value: {item.Value:F2}");
|
||||
}
|
||||
if (series.Count > count) Console.WriteLine("...");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## 1. Manual Data: Batch vs. Streaming
|
||||
|
||||
We'll start with a small, manually created dataset.
|
||||
|
||||
#!csharp
|
||||
|
||||
// Create a small manual dataset
|
||||
var manualData = new TSeries();
|
||||
manualData.Add(DateTime.Now, 100.0);
|
||||
manualData.Add(DateTime.Now.AddMinutes(1), 102.0);
|
||||
manualData.Add(DateTime.Now.AddMinutes(2), 101.0);
|
||||
manualData.Add(DateTime.Now.AddMinutes(3), 103.0);
|
||||
manualData.Add(DateTime.Now.AddMinutes(4), 105.0);
|
||||
|
||||
Console.WriteLine("--- Input Data ---");
|
||||
PrintSeries(manualData, 5);
|
||||
|
||||
// Batch Calculation
|
||||
Console.WriteLine("\n--- Batch TRIMA (Period 3) ---");
|
||||
var trimaBatch = new Trima(3);
|
||||
var resultBatch = trimaBatch.Update(manualData);
|
||||
|
||||
PrintSeries(resultBatch, 5);
|
||||
|
||||
#!markdown
|
||||
|
||||
### Streaming Processing
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine("\n--- Streaming TRIMA (Period 3) ---");
|
||||
var trimaStream = new Trima(3);
|
||||
|
||||
foreach (var item in manualData)
|
||||
{
|
||||
var result = trimaStream.Update(item);
|
||||
Console.WriteLine($"Time: {item.Time:HH:mm:ss}, Input: {item.Value:F2}, TRIMA: {result.Value:F2}, IsHot: {trimaStream.IsHot}");
|
||||
}
|
||||
|
||||
// Verify that the last values match
|
||||
var batchLast = resultBatch.Last().Value;
|
||||
var streamLast = trimaStream.Value.Value;
|
||||
Console.WriteLine($"\nMatch: {Math.Abs(batchLast - streamLast) < 1e-10} (Batch: {batchLast:F2}, Stream: {streamLast:F2})");
|
||||
|
||||
#!markdown
|
||||
|
||||
## 2. Large Dataset: Geometric Brownian Motion (GBM)
|
||||
|
||||
We'll generate a larger dataset (1000 bars) using a Geometric Brownian Motion generator to simulate realistic market data.
|
||||
|
||||
#!csharp
|
||||
|
||||
// Generate 1000 bars of data
|
||||
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.2);
|
||||
var gbmData = gbm.Fetch(1000, DateTime.Now.Ticks, TimeSpan.FromMinutes(1));
|
||||
var closeSeries = gbmData.Close;
|
||||
|
||||
Console.WriteLine($"Generated {closeSeries.Count} bars of GBM data.");
|
||||
|
||||
#!markdown
|
||||
|
||||
### Batch vs. Streaming Performance on Large Data
|
||||
|
||||
#!csharp
|
||||
|
||||
// Batch
|
||||
var trimaLargeBatch = new Trima(20);
|
||||
var batchLargeResult = trimaLargeBatch.Update(closeSeries);
|
||||
Console.WriteLine($"Batch Last Value: {batchLargeResult.Last().Value:F2}");
|
||||
|
||||
// Streaming
|
||||
var trimaLargeStream = new Trima(20);
|
||||
TValue lastStreamVal = default;
|
||||
foreach(var item in closeSeries)
|
||||
{
|
||||
lastStreamVal = trimaLargeStream.Update(item);
|
||||
}
|
||||
Console.WriteLine($"Streaming Last Value: {lastStreamVal.Value:F2}");
|
||||
|
||||
// Verify match
|
||||
Console.WriteLine($"Match: {Math.Abs(batchLargeResult.Last().Value - lastStreamVal.Value) < 1e-10}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## 3. TRIMA vs SMA Comparison
|
||||
|
||||
TRIMA is smoother than SMA but has more lag. Let's compare them on a volatile dataset.
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine("\n--- TRIMA vs SMA Comparison (Period 10) ---");
|
||||
|
||||
var compareData = new TSeries();
|
||||
var baseTime = DateTime.Now;
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
// Create data with a sudden spike at position 10
|
||||
double value = (i == 10) ? 150.0 : 100.0;
|
||||
compareData.Add(baseTime.AddMinutes(i), value);
|
||||
}
|
||||
|
||||
var trimaCompare = new Trima(10);
|
||||
var smaCompare = new Sma(10);
|
||||
|
||||
Console.WriteLine("Position | Input | TRIMA | SMA | Difference");
|
||||
Console.WriteLine("---------+--------+---------+---------+-----------");
|
||||
|
||||
for (int i = 0; i < compareData.Count; i++)
|
||||
{
|
||||
var trimaVal = trimaCompare.Update(compareData[i]);
|
||||
var smaVal = smaCompare.Update(compareData[i]);
|
||||
var input = compareData[i].Value;
|
||||
var diff = trimaVal.Value - smaVal.Value;
|
||||
|
||||
Console.WriteLine($" {i,2} | {input,6:F0} | {trimaVal.Value,7:F2} | {smaVal.Value,7:F2} | {diff,+9:F2}");
|
||||
}
|
||||
|
||||
Console.WriteLine("\nNote how TRIMA reacts more gradually to the spike compared to SMA.");
|
||||
Reference in New Issue
Block a user