mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-20 11:38:05 +00:00
Merge branch 'dev' into main
This commit is contained in:
@@ -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. |
|
||||
|
||||
@@ -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.
|
||||
|
||||
+7
-7
@@ -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
|
||||
|
||||

|
||||
+1
-1
@@ -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 |
+6
-6
@@ -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
|
||||
+37
-37
@@ -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]);
|
||||
+18
-18
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
# HOLT Signature
|
||||
|
||||

|
||||
Reference in New Issue
Block a user