mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-24 05:28:05 +00:00
python wrapper
This commit is contained in:
@@ -1,114 +0,0 @@
|
||||
using TradingPlatform.BusinessLayer;
|
||||
using QuanTAlib;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
public sealed class HurstIndicatorTests
|
||||
{
|
||||
[Fact]
|
||||
public void HurstIndicator_Constructor_SetsDefaults()
|
||||
{
|
||||
var indicator = new HurstIndicator();
|
||||
|
||||
Assert.Equal(100, indicator.Period);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
Assert.Equal("Hurst - Hurst Exponent", indicator.Name);
|
||||
Assert.True(indicator.SeparateWindow);
|
||||
Assert.True(indicator.OnBackGround);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HurstIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new HurstIndicator { Period = 100 };
|
||||
|
||||
Assert.Equal(0, HurstIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HurstIndicator_Initialize_CreatesInternalHurst()
|
||||
{
|
||||
var indicator = new HurstIndicator { Period = 20 };
|
||||
|
||||
// Initialize should not throw
|
||||
indicator.Initialize();
|
||||
|
||||
// After init, line series should exist (Hurst line + 0.5 reference line)
|
||||
Assert.Equal(2, indicator.LinesSeries.Count);
|
||||
Assert.Equal("Hurst", indicator.LinesSeries[0].Name);
|
||||
Assert.Equal("0.5", indicator.LinesSeries[1].Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HurstIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
|
||||
{
|
||||
var indicator = new HurstIndicator { Period = 20 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100 + i, 110 + i, 90 + i, 105 + i);
|
||||
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
}
|
||||
|
||||
double hurst = indicator.LinesSeries[0].GetValue(0);
|
||||
Assert.True(double.IsFinite(hurst));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HurstIndicator_DifferentSourceTypes()
|
||||
{
|
||||
var indicator = new HurstIndicator { Period = 20, Source = SourceType.Open };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100 + i, 110 + i, 90 + i, 105 + i);
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
}
|
||||
|
||||
double hurst = indicator.LinesSeries[0].GetValue(0);
|
||||
Assert.True(double.IsFinite(hurst));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HurstIndicator_ShortName_IncludesPeriod()
|
||||
{
|
||||
var indicator = new HurstIndicator { Period = 50 };
|
||||
Assert.Equal("Hurst 50", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HurstIndicator_NewBar_UpdatesValue()
|
||||
{
|
||||
var indicator = new HurstIndicator { Period = 20 };
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100 + i, 110 + i, 90 + i, 105 + i);
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
}
|
||||
|
||||
_ = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
// Add a new bar with a very different value
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(30), 200, 210, 190, 205);
|
||||
var newArgs = new UpdateArgs(UpdateReason.NewBar);
|
||||
indicator.ProcessUpdate(newArgs);
|
||||
|
||||
double valueAfter = indicator.LinesSeries[0].GetValue(0);
|
||||
Assert.True(double.IsFinite(valueAfter));
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public sealed class HurstIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 20, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 100;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
public SourceType Source { get; set; } = SourceType.Close;
|
||||
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Hurst _hurst = null!;
|
||||
private readonly LineSeries _series;
|
||||
private readonly LineSeries _halfLine;
|
||||
private Func<IHistoryItem, double> _priceSelector = null!;
|
||||
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"Hurst {Period}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/statistics/hurst/Hurst.Quantower.cs";
|
||||
|
||||
public HurstIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = true;
|
||||
Name = "Hurst - Hurst Exponent";
|
||||
Description = "Measures long-range dependence using Rescaled Range (R/S) analysis. H > 0.5 = trending, H < 0.5 = mean-reverting, H ≈ 0.5 = random walk";
|
||||
|
||||
_series = new LineSeries(name: "Hurst", color: IndicatorExtensions.Statistics, width: 2, style: LineStyle.Solid);
|
||||
_halfLine = new LineSeries(name: "0.5", color: Color.Gray, width: 1, style: LineStyle.Dash);
|
||||
AddLineSeries(_series);
|
||||
AddLineSeries(_halfLine);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_hurst = new Hurst(Period);
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
var item = this.HistoricalData[this.Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _priceSelector(item);
|
||||
var time = this.HistoricalData.Time();
|
||||
|
||||
var input = new TValue(time, value);
|
||||
TValue result = _hurst.Update(input, args.IsNewBar());
|
||||
|
||||
_series.SetValue(result.Value, _hurst.IsHot, ShowColdValues);
|
||||
_halfLine.SetValue(0.5);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
using Skender.Stock.Indicators;
|
||||
using QuanTAlib.Tests;
|
||||
|
||||
// HURST Validation Tests - Hurst Exponent via Rescaled Range (R/S) Analysis
|
||||
// Validated against self-consistency and known mathematical properties
|
||||
// No external library provides a direct R/S-based Hurst exponent equivalent
|
||||
@@ -181,4 +184,40 @@ public sealed class HurstValidationTests
|
||||
Assert.Equal(h1.Last.Value, h2.Last.Value, 1e-15);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Structural comparison with Skender GetHurst — both compute Hurst exponent
|
||||
/// but may use different R/S subdivision strategies and regression methods.
|
||||
/// Validates that Skender produces finite results in the same range.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Validate_Skender_Hurst_Structural()
|
||||
{
|
||||
const int period = 20;
|
||||
using var data = new ValidationTestData(10000);
|
||||
|
||||
// QuanTAlib streaming
|
||||
var indicator = new Hurst(period);
|
||||
foreach (var tv in data.Data)
|
||||
{
|
||||
indicator.Update(tv);
|
||||
}
|
||||
|
||||
// Skender
|
||||
var sResult = data.SkenderQuotes.GetHurst(period).ToList();
|
||||
|
||||
// QuanTAlib produces finite output
|
||||
Assert.True(double.IsFinite(indicator.Last.Value), "QuanTAlib Hurst last must be finite");
|
||||
|
||||
// Skender produces finite Hurst exponents
|
||||
int sFinite = sResult.Count(r => r.HurstExponent is not null && double.IsFinite(r.HurstExponent.Value));
|
||||
Assert.True(sFinite > 50, $"Skender produced only {sFinite} finite Hurst values");
|
||||
|
||||
// Both Hurst exponents should be finite
|
||||
foreach (var r in sResult.Where(r => r.HurstExponent is not null))
|
||||
{
|
||||
Assert.True(double.IsFinite(r.HurstExponent!.Value),
|
||||
$"Skender Hurst value {r.HurstExponent.Value} is not finite");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user