Merge branch 'dev' into main

This commit is contained in:
Miha Kralj
2026-03-16 12:46:19 -07:00
131 changed files with 1582 additions and 1583 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ Trend indicators based on Infinite Impulse Response (IIR) filters. Recursive arc
| [GDEMA](gdema/Gdema.md) | Generalized Double Exponential MA | Generalized DEMA with configurable volume factor for tunable lag/smoothness trade-off. |
| [HEMA](hema/Hema.md) | Hull Exponential MA | EMA-domain Hull analog using half-life timing and de-lagged EMA cascade. |
| [HOLT](holt/Holt.md) | Holt Exponential Smoothing | Double exponential smoothing with separate level and trend components for adaptive trend-following. |
| [HTIT](htit/Htit.md) | Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE) | Utilizes Hilbert Transform to isolate instantaneous trend component, providing zero-lag trendline with hybrid FIR-in-IIR design. |
| [HT_TRENDLINE](ht_trendline/HtTrendline.md) | Ehlers Hilbert Transform Instantaneous Trend | Utilizes Hilbert Transform to isolate instantaneous trend component, providing zero-lag trendline with hybrid FIR-in-IIR design. |
| [HWMA](hwma/Hwma.md) | Holt-Winters MA | Triple exponential smoothing. Tracks level, velocity, acceleration. Recursive IIR structure. |
| [JMA](jma/Jma.md) | Jurik MA | Adaptive filter achieving high noise reduction and low phase delay through multi-stage volatility normalization and dynamic parameter optimization. |
| [KAMA](kama/Kama.md) | Kaufman Adaptive MA | Automatically adjusts sensitivity based on market volatility using Efficiency Ratio, balancing responsiveness and stability. |
+1 -1
View File
@@ -14,7 +14,7 @@
| **Signature** | [decycler_signature](decycler_signature.md) |
- The Ehlers Decycler extracts the trend component from a price series by subtracting a 2-pole Butterworth high-pass filter from the source signal.
- **Similar:** [EMA](../ema/ema.md), [HTIT](../htit/htit.md) | **Complementary:** Cycle indicators | **Trading note:** Ehlers Decycler; high-pass complement removes cycle components to isolate trend.
- **Similar:** [EMA](../ema/ema.md), [HT_TRENDLINE](../ht_trendline/HtTrendline.md) | **Complementary:** Cycle indicators | **Trading note:** Ehlers Decycler; high-pass complement removes cycle components to isolate trend.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Ehlers Decycler extracts the trend component from a price series by subtracting a 2-pole Butterworth high-pass filter from the source signal. Where most moving averages blur the boundary between trend and cycle, the Decycler defines it with a frequency-domain cutoff: cycles shorter than the specified period are removed, everything longer stays. The result is an overlay that hugs price with near-zero lag during trends and rejects short-term oscillations without the smoothing artifacts of convolution-based averages.
@@ -5,7 +5,7 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
[SkipLocalsInit]
public sealed class HtitIndicator : Indicator, IWatchlistIndicator
public sealed class HtTrendlineIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
public int Period { get; set; } = 50; // Not used in calculation but kept for consistency
@@ -16,7 +16,7 @@ public sealed class HtitIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
private Htit _htit = null!;
private HtTrendline _htit = null!;
private readonly LineSeries _series;
private string _sourceName = null!;
private Func<IHistoryItem, double> _priceSelector = null!;
@@ -24,15 +24,15 @@ public sealed class HtitIndicator : Indicator, IWatchlistIndicator
public static int MinHistoryDepths => 0;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName => $"HTIT:{_sourceName}";
public override string ShortName => $"HT_TRENDLINE:{_sourceName}";
public HtitIndicator()
public HtTrendlineIndicator()
{
OnBackGround = true;
SeparateWindow = false;
Name = "HTIT - Ehlers Hilbert Transform Instantaneous Trend";
Name = "HT_TRENDLINE - Ehlers Hilbert Transform Instantaneous Trend";
Description = "Ehlers Hilbert Transform Instantaneous Trend";
_series = new LineSeries(name: "HTIT", color: Color.Orange, width: 2, style: LineStyle.Solid);
_series = new LineSeries(name: "HT_TRENDLINE", color: Color.Orange, width: 2, style: LineStyle.Solid);
AddLineSeries(_series);
}
@@ -40,7 +40,7 @@ public sealed class HtitIndicator : Indicator, IWatchlistIndicator
{
_priceSelector = Source.GetPriceSelector();
_sourceName = Source.ToString();
_htit = new Htit();
_htit = new HtTrendline();
base.OnInit();
}
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace QuanTAlib;
/// <summary>
/// HTIT: Hilbert Transform Instantaneous Trendline (also known as HT_TRENDLINE)
/// HT_TRENDLINE: Hilbert Transform Instantaneous Trendline (also known as HT_TRENDLINE)
/// </summary>
/// <remarks>
/// Ehlers' adaptive trendline using Hilbert Transform cycle measurement.
@@ -12,10 +12,10 @@ namespace QuanTAlib;
///
/// Key features: homodyne discriminator, period-adaptive averaging window.
/// </remarks>
/// <seealso href="Htit.md">Detailed documentation</seealso>
/// <seealso href="htit.pine">Reference Pine Script implementation</seealso>
/// <seealso href="HtTrendline.md">Detailed documentation</seealso>
/// <seealso href="ht_trendline.pine">Reference Pine Script implementation</seealso>
[SkipLocalsInit]
public sealed class Htit : AbstractBase
public sealed class HtTrendline : AbstractBase
{
public override bool IsHot => _state.Index >= WarmupPeriod;
@@ -48,9 +48,9 @@ public sealed class Htit : AbstractBase
private const double TwoPi = 2.0 * Math.PI;
private const double MinDeltaRadians = Math.PI / 180.0; // 1 degree in radians
public Htit()
public HtTrendline()
{
Name = "Htit";
Name = "HtTrendline";
WarmupPeriod = 12;
_handler = Handle;
@@ -66,7 +66,7 @@ public sealed class Htit : AbstractBase
Init();
}
public Htit(ITValuePublisher source) : this()
public HtTrendline(ITValuePublisher source) : this()
{
source.Pub += _handler;
}
@@ -266,7 +266,7 @@ public sealed class Htit : AbstractBase
/// For high-performance batch-only processing, use the static Calculate method instead.
/// </summary>
/// <param name="source">Input time series</param>
/// <returns>Output time series with HTIT values</returns>
/// <returns>Output time series with HT_TRENDLINE values</returns>
public override TSeries Update(TSeries source)
{
if (source.Count == 0)
@@ -303,8 +303,8 @@ public sealed class Htit : AbstractBase
public static TSeries Batch(TSeries source)
{
var htit = new Htit();
return htit.Update(source);
var httrendline = new HtTrendline();
return httrendline.Update(source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -513,9 +513,9 @@ public sealed class Htit : AbstractBase
}
}
public static (TSeries Results, Htit Indicator) Calculate(TSeries source)
public static (TSeries Results, HtTrendline Indicator) Calculate(TSeries source)
{
var indicator = new Htit();
var indicator = new HtTrendline();
TSeries results = indicator.Update(source);
return (results, indicator);
}
@@ -1,29 +1,29 @@
# HTIT: Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE)
# HT_TRENDLINE: Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE)
> *John Ehlers brought rocket science to trading. Literally. HTIT uses signal processing to find the trend by removing the cycle. It's not smoothing; it's extraction.*
> *John Ehlers brought rocket science to trading. Literally. HT_TRENDLINE uses signal processing to find the trend by removing the cycle. It's not smoothing; it's extraction.*
| Property | Value |
| ---------------- | -------------------------------- |
| **Category** | Trend (IIR MA) |
| **Inputs** | Source (close) |
| **Parameters** | None |
| **Outputs** | Single series (HTIT) |
| **Outputs** | Single series (HT_TRENDLINE) |
| **Output range** | Tracks input |
| **Warmup** | `12` bars |
| **PineScript** | [htit.pine](htit.pine) |
| **Signature** | [htit_signature](htit_signature.md) |
| **PineScript** | [ht_trendline.pine](ht_trendline.pine) |
| **Signature** | [ht_trendline_signature](ht_trendline_signature.md) |
- HTIT (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging.
- HT_TRENDLINE (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging.
- **Similar:** [MAMA](../mama/mama.md), [DEMA](../dema/dema.md) | **Complementary:** HT_DCPeriod | **Trading note:** Hilbert Transform trendline; cycle-adaptive smoothing.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
HTIT (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging. Instead, it uses the Hilbert Transform to measure the dominant cycle period of the market and then computes a trendline that filters out that specific cycle. It adapts to the market's rhythm rather than imposing a fixed period.
HT_TRENDLINE (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging. Instead, it uses the Hilbert Transform to measure the dominant cycle period of the market and then computes a trendline that filters out that specific cycle. It adapts to the market's rhythm rather than imposing a fixed period.
## Historical Context
John Ehlers, a pioneer in applying DSP to trading, introduced this in his book *Rocket Science for Traders*. He recognized that markets have cyclic components (noise) and trend components. By identifying the cycle, you can mathematically subtract it to reveal the pure trend.
Most trend indicators (SMA, EMA) are low-pass filters: they let low frequencies (trend) pass and block high frequencies (noise). The problem is that "noise" in markets isn't random white noise; it's often cyclic. A fixed-period SMA might filter out a 10-day cycle perfectly but amplify a 20-day cycle. HTIT solves this by measuring the cycle first, then tuning the filter to kill exactly that frequency.
Most trend indicators (SMA, EMA) are low-pass filters: they let low frequencies (trend) pass and block high frequencies (noise). The problem is that "noise" in markets isn't random white noise; it's often cyclic. A fixed-period SMA might filter out a 10-day cycle perfectly but amplify a 20-day cycle. HT_TRENDLINE solves this by measuring the cycle first, then tuning the filter to kill exactly that frequency.
## Architecture & Physics
@@ -84,11 +84,11 @@ Where $\text{DC}$ is the integer part of the smoothed dominant cycle period.
The Instantaneous Trend is smoothed again using the same 4-bar WMA to remove any residual stepping artifacts from the integer period changes.
$$ \text{HTIT}_t = \frac{4 \text{IT}_t + 3 \text{IT}_{t-1} + 2 \text{IT}_{t-2} + \text{IT}_{t-3}}{10} $$
$$ \text{HT_TRENDLINE}_t = \frac{4 \text{IT}_t + 3 \text{IT}_{t-1} + 2 \text{IT}_{t-2} + \text{IT}_{t-3}}{10} $$
## Mathematical Precision & Implementation Philosophy
Like our MAMA implementation, QuanTAlib's HTIT prioritizes mathematical correctness over blind porting.
Like our MAMA implementation, QuanTAlib's HT_TRENDLINE prioritizes mathematical correctness over blind porting.
| Aspect | Other Libraries | QuanTAlib | Rationale |
| :----------------------- | :----------------- | :---------------------- | :-------------------------------------------- |
@@ -102,7 +102,7 @@ We use `atan2` for robust phase calculation and maintain full double precision t
## Performance Profile
HTIT is computationally heavier than a simple MA but lighter than MAMA. The main cost is the loop for the Instantaneous Trend calculation, which sums up to 50 past prices.
HT_TRENDLINE is computationally heavier than a simple MA but lighter than MAMA. The main cost is the loop for the Instantaneous Trend calculation, which sums up to 50 past prices.
### Operation Count (Streaming Mode, Scalar)
@@ -144,7 +144,7 @@ HTIT is computationally heavier than a simple MA but lighter than MAMA. The main
### Batch Mode (SIMD Analysis)
HTIT is **not SIMD-parallelizable** across bars due to:
HT_TRENDLINE is **not SIMD-parallelizable** across bars due to:
1. Recursive feedback in Hilbert transforms (I2, Q2 depend on previous values)
2. Period-dependent IT summation loop (variable iteration count)
3. Homodyne discriminator state dependencies
@@ -1,13 +1,13 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
indicator("Ehlers Hilbert Transform Instantaneous Trend (HTIT)", "HTIT", overlay=true)
indicator("Ehlers Hilbert Transform Instantaneous Trend (HT_TRENDLINE)", "HT_TRENDLINE", overlay=true)
//@function Calculates the Hilbert Transform Instantaneous Trendline (HTIT)
//@param source Series to calculate HTIT from
//@returns HTIT value using Hilbert Transform with adaptive period estimation
//@function Calculates the Hilbert Transform Instantaneous Trendline (HT_TRENDLINE)
//@param source Series to calculate HT_TRENDLINE from
//@returns HT_TRENDLINE value using Hilbert Transform with adaptive period estimation
//@optimized Uses Hilbert Transform quadrature components for O(1) complexity per bar
htit(series float source) =>
httrendline(series float source) =>
var float price = na
var float smooth = na
var float detrender = 0.0
@@ -56,7 +56,7 @@ htit(series float source) =>
i_source = input.source(close, "Source")
// Calculation
htit_value = htit(i_source)
htit_value = httrendline(i_source)
// Plot
plot(htit_value, "HTIT", color=color.yellow, linewidth=2)
plot(htit_value, "HT_TRENDLINE", color=color.yellow, linewidth=2)
@@ -0,0 +1,3 @@
# HOLT Signature
![HT_TRENDLINE Signature](ht_trendline_signature.svg)
@@ -19508,7 +19508,7 @@ L 1441.19952 978.875435
</g>
</g>
<g id="text_245">
<!-- HTIT: Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE) -->
<!-- HT_TRENDLINE: Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE) -->
<g transform="translate(348.72851 19.3575) scale(0.16 -0.16)">
<defs>
<path id="DejaVuSans-Bold-3a" d="M 716 3500

Before

Width:  |  Height:  |  Size: 552 KiB

After

Width:  |  Height:  |  Size: 552 KiB

@@ -2,24 +2,24 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib.Quantower.Tests;
public class HtitIndicatorTests
public class HtTrendlineIndicatorTests
{
[Fact]
public void Indicator_Initializes_Correctly()
{
var indicator = new HtitIndicator();
var indicator = new HtTrendlineIndicator();
indicator.Initialize();
Assert.Equal("HTIT - Ehlers Hilbert Transform Instantaneous Trend", indicator.Name);
Assert.StartsWith("HTIT", indicator.ShortName, StringComparison.Ordinal);
Assert.Equal("HT_TRENDLINE - Ehlers Hilbert Transform Instantaneous Trend", indicator.Name);
Assert.StartsWith("HT_TRENDLINE", indicator.ShortName, StringComparison.Ordinal);
Assert.Contains("Close", indicator.ShortName, StringComparison.Ordinal);
Assert.Equal(0, HtitIndicator.MinHistoryDepths);
Assert.Equal(0, HtTrendlineIndicator.MinHistoryDepths);
Assert.Single(indicator.LinesSeries);
}
[Fact]
public void Indicator_Updates_Correctly()
{
var indicator = new HtitIndicator();
var indicator = new HtTrendlineIndicator();
indicator.Initialize();
// Warmup
@@ -1,11 +1,11 @@
namespace QuanTAlib.Tests;
public class HtitTests
public class HtTrendlineTests
{
private readonly GBM _gbm;
public HtitTests()
public HtTrendlineTests()
{
_gbm = new GBM();
}
@@ -13,30 +13,30 @@ public class HtitTests
[Fact]
public void IsHot_BecomesTrue_AfterWarmup()
{
var htit = new Htit();
var httrendline = new HtTrendline();
for (int i = 0; i < 12; i++)
{
Assert.False(htit.IsHot);
htit.Update(new TValue(DateTime.UtcNow.Ticks, 100.0));
Assert.False(httrendline.IsHot);
httrendline.Update(new TValue(DateTime.UtcNow.Ticks, 100.0));
}
Assert.True(htit.IsHot);
Assert.True(httrendline.IsHot);
}
[Fact]
public void Update_Matches_Calculate()
{
var htit = new Htit();
var httrendline = new HtTrendline();
var data = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var series = data;
var resultSeries = htit.Update(series);
var resultSeries = httrendline.Update(series);
// Reset and calculate streaming
htit.Reset();
httrendline.Reset();
var streamingResults = new List<double>();
foreach (var item in data)
{
streamingResults.Add(htit.Update(item).Value);
streamingResults.Add(httrendline.Update(item).Value);
}
for (int i = 0; i < resultSeries.Count; i++)
@@ -48,16 +48,16 @@ public class HtitTests
[Fact]
public void Calculate_Span_Matches_Update()
{
var htit = new Htit();
var httrendline = new HtTrendline();
var data = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var series = data;
var resultSeries = htit.Update(series);
var resultSeries = httrendline.Update(series);
var spanInput = data.Values.ToArray();
var spanOutput = new double[spanInput.Length];
Htit.Batch(spanInput, spanOutput);
HtTrendline.Batch(spanInput, spanOutput);
for (int i = 0; i < resultSeries.Count; i++)
{
@@ -68,38 +68,38 @@ public class HtitTests
[Fact]
public void Handles_NaN()
{
var htit = new Htit();
htit.Update(new TValue(DateTime.UtcNow.Ticks, 100.0));
htit.Update(new TValue(DateTime.UtcNow.Ticks, double.NaN));
var httrendline = new HtTrendline();
httrendline.Update(new TValue(DateTime.UtcNow.Ticks, 100.0));
httrendline.Update(new TValue(DateTime.UtcNow.Ticks, double.NaN));
Assert.Equal(100.0, htit.Last.Value);
Assert.Equal(100.0, httrendline.Last.Value);
}
[Fact]
public void Htit_Calc_IsNew_AcceptsParameter()
{
var htit = new Htit();
htit.Update(new TValue(DateTime.UtcNow, 100), isNew: true);
Assert.Equal(100, htit.Last.Value);
var httrendline = new HtTrendline();
httrendline.Update(new TValue(DateTime.UtcNow, 100), isNew: true);
Assert.Equal(100, httrendline.Last.Value);
}
[Fact]
public void Htit_Reset_ClearsState()
{
var htit = new Htit();
htit.Update(new TValue(DateTime.UtcNow, 100));
htit.Update(new TValue(DateTime.UtcNow, 110));
var httrendline = new HtTrendline();
httrendline.Update(new TValue(DateTime.UtcNow, 100));
httrendline.Update(new TValue(DateTime.UtcNow, 110));
htit.Reset();
httrendline.Reset();
Assert.True(double.IsNaN(htit.Last.Value));
Assert.False(htit.IsHot);
Assert.True(double.IsNaN(httrendline.Last.Value));
Assert.False(httrendline.IsHot);
}
[Fact]
public void Htit_IterativeCorrections_RestoreToOriginalState()
{
var htit = new Htit();
var httrendline = new HtTrendline();
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1);
// Feed 20 new values (needs > 12 for warmup)
@@ -108,21 +108,21 @@ public class HtitTests
{
var bar = gbm.Next(isNew: true);
lastInput = new TValue(bar.Time, bar.Close);
htit.Update(lastInput, isNew: true);
httrendline.Update(lastInput, isNew: true);
}
// Remember state after 20 values
double valueAfterTwenty = htit.Last.Value;
double valueAfterTwenty = httrendline.Last.Value;
// Generate 9 corrections with isNew=false (different values)
for (int i = 0; i < 9; i++)
{
var bar = gbm.Next(isNew: false);
htit.Update(new TValue(bar.Time, bar.Close), isNew: false);
httrendline.Update(new TValue(bar.Time, bar.Close), isNew: false);
}
// Feed the remembered 20th input again with isNew=false
TValue finalValue = htit.Update(lastInput, isNew: false);
TValue finalValue = httrendline.Update(lastInput, isNew: false);
// Should match the original state after 20 values
Assert.Equal(valueAfterTwenty, finalValue.Value, 1e-9);
@@ -134,7 +134,7 @@ public class HtitTests
double[] source = [1, 2, 3, 4, 5];
double[] wrongSizeOutput = new double[3];
Assert.Throws<ArgumentException>(() => Htit.Batch(source.AsSpan(), wrongSizeOutput.AsSpan()));
Assert.Throws<ArgumentException>(() => HtTrendline.Batch(source.AsSpan(), wrongSizeOutput.AsSpan()));
}
[Fact]
@@ -143,7 +143,7 @@ public class HtitTests
double[] source = [100, 110, double.NaN, 120, 130];
double[] output = new double[5];
Htit.Batch(source.AsSpan(), output.AsSpan());
HtTrendline.Batch(source.AsSpan(), output.AsSpan());
foreach (var val in output)
{
@@ -160,18 +160,18 @@ public class HtitTests
var series = bars.Close;
// 1. Batch Mode
var batchSeries = Htit.Batch(series);
var batchSeries = HtTrendline.Batch(series);
double expected = batchSeries.Last.Value;
// 2. Span Mode
var tValues = series.Values.ToArray();
var spanInput = new ReadOnlySpan<double>(tValues);
var spanOutput = new double[tValues.Length];
Htit.Batch(spanInput, spanOutput);
HtTrendline.Batch(spanInput, spanOutput);
double spanResult = spanOutput[^1];
// 3. Streaming Mode
var streamingInd = new Htit();
var streamingInd = new HtTrendline();
for (int i = 0; i < series.Count; i++)
{
streamingInd.Update(series[i]);
@@ -180,7 +180,7 @@ public class HtitTests
// 4. Eventing Mode
var pubSource = new TSeries();
var eventingInd = new Htit(pubSource);
var eventingInd = new HtTrendline(pubSource);
for (int i = 0; i < series.Count; i++)
{
pubSource.Add(series[i]);
@@ -5,12 +5,12 @@ using TALib;
namespace QuanTAlib.Tests;
public sealed class HtitValidationTests : IDisposable
public sealed class HtTrendlineValidationTests : IDisposable
{
private readonly ValidationTestData _data;
private bool _disposed;
public HtitValidationTests()
public HtTrendlineValidationTests()
{
_data = new ValidationTestData(10000);
}
@@ -38,16 +38,16 @@ public sealed class HtitValidationTests : IDisposable
[Fact]
public void Validate_TaLib()
{
// Calculate TA-Lib HTIT
// Calculate TA-Lib HT_TRENDLINE
var input = _data.RawData.Span;
var output = new double[input.Length];
var retCode = TALib.Functions.HtTrendline(input, 0..^0, output, out var outRange);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
// Calculate QuanTAlib HTIT
var htit = new Htit();
var quantalibResults = htit.Update(_data.Data);
// Calculate QuanTAlib HT_TRENDLINE
var httrendline = new HtTrendline();
var quantalibResults = httrendline.Update(_data.Data);
// Compare results
// TA-Lib HT_TRENDLINE has a lookback of 63
@@ -67,13 +67,13 @@ public sealed class HtitValidationTests : IDisposable
[Fact]
public void Validate_Skender_Batch()
{
// Calculate Skender HTIT
// Calculate Skender HT_TRENDLINE
var skenderResults = _data.SkenderQuotes.GetHtTrendline().ToList();
// Calculate QuanTAlib HTIT
var htit = new Htit();
// Calculate QuanTAlib HT_TRENDLINE
var httrendline = new HtTrendline();
var series = _data.Data;
var quantalibResults = htit.Update(series);
var quantalibResults = httrendline.Update(series);
// Compare results
// Skip warmup period (Skender needs 100 periods for convergence, but we can check after 50)
@@ -97,16 +97,16 @@ public sealed class HtitValidationTests : IDisposable
[Fact]
public void Validate_Skender_Streaming()
{
// Calculate Skender HTIT
// Calculate Skender HT_TRENDLINE
var skenderResults = _data.SkenderQuotes.GetHtTrendline().ToList();
// Calculate QuanTAlib HTIT Streaming
var htit = new Htit();
// Calculate QuanTAlib HT_TRENDLINE Streaming
var httrendline = new HtTrendline();
var streamingResults = new List<double>();
foreach (var item in _data.Data)
{
streamingResults.Add(htit.Update(item).Value);
streamingResults.Add(httrendline.Update(item).Value);
}
// Compare results
@@ -139,14 +139,14 @@ public sealed class HtitValidationTests : IDisposable
Volume = (double)q.Volume
}).ToList();
// Calculate Ooples HTIT
// Calculate Ooples HT_TRENDLINE
var stockData = new StockData(ooplesData);
var oResult = stockData.CalculateEhlersInstantaneousTrendlineV1();
var oValues = oResult.OutputValues["Eit"];
// Calculate QuanTAlib HTIT
var htit = new Htit();
var quantalibResults = htit.Update(_data.Data);
// Calculate QuanTAlib HT_TRENDLINE
var httrendline = new HtTrendline();
var quantalibResults = httrendline.Update(_data.Data);
// Compare results
// Ooples might have different warmup or calculation details
-3
View File
@@ -1,3 +0,0 @@
# HOLT Signature
![HTIT Signature](htit_signature.svg)