mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-15 00:58:04 +00:00
Refactor IndicatorExtensions: Remove unused methods and optimize price retrieval
This commit is contained in:
@@ -19,33 +19,15 @@ public class AdxIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
public void AdxIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new AdxIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AdxIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxIndicator_ShortName_IncludesParameters()
|
||||
{
|
||||
var indicator = new AdxIndicator { Period = 20 };
|
||||
indicator.Initialize();
|
||||
|
||||
Assert.Contains("ADX", indicator.ShortName);
|
||||
Assert.Contains("20", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxIndicator_SourceCodeLink_IsValid()
|
||||
{
|
||||
var indicator = new AdxIndicator();
|
||||
|
||||
Assert.Contains("github.com", indicator.SourceCodeLink);
|
||||
Assert.Contains("Adx.Quantower.cs", indicator.SourceCodeLink);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxIndicator_Initialize_CreatesInternalAdx()
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AdxIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class AdxIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -12,15 +14,15 @@ public class AdxIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Adx? _adx;
|
||||
protected LineSeries? AdxSeries;
|
||||
protected LineSeries? DiPlusSeries;
|
||||
protected LineSeries? DiMinusSeries;
|
||||
private readonly LineSeries? _adxSeries;
|
||||
private readonly LineSeries? _diPlusSeries;
|
||||
private readonly LineSeries? _diMinusSeries;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"ADX {Period}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/adx/Adx.Quantower.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/momentum/adx/Adx.Quantower.cs";
|
||||
|
||||
public AdxIndicator()
|
||||
{
|
||||
@@ -29,36 +31,29 @@ public class AdxIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "ADX - Average Directional Index";
|
||||
Description = "Measures the strength of a trend";
|
||||
|
||||
AdxSeries = new(name: "ADX", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
DiPlusSeries = new(name: "+DI", color: Color.Green, width: 1, style: LineStyle.Solid);
|
||||
DiMinusSeries = new(name: "-DI", color: Color.Red, width: 1, style: LineStyle.Solid);
|
||||
_adxSeries = new(name: "ADX", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
_diPlusSeries = new(name: "+DI", color: Color.Green, width: 1, style: LineStyle.Solid);
|
||||
_diMinusSeries = new(name: "-DI", color: Color.Red, width: 1, style: LineStyle.Solid);
|
||||
|
||||
AddLineSeries(AdxSeries);
|
||||
AddLineSeries(DiPlusSeries);
|
||||
AddLineSeries(DiMinusSeries);
|
||||
AddLineSeries(_adxSeries);
|
||||
AddLineSeries(_diPlusSeries);
|
||||
AddLineSeries(_diMinusSeries);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_adx = new Adx(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _adx!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
|
||||
TValue result = _adx!.Update(bar, isNew);
|
||||
|
||||
if (!_adx.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AdxSeries!.SetValue(result.Value);
|
||||
DiPlusSeries!.SetValue(_adx.DiPlus.Value);
|
||||
DiMinusSeries!.SetValue(_adx.DiMinus.Value);
|
||||
_adxSeries!.SetValue(result.Value, _adx.IsHot, ShowColdValues);
|
||||
_diPlusSeries!.SetValue(_adx.DiPlus.Value, _adx.IsHot, ShowColdValues);
|
||||
_diMinusSeries!.SetValue(_adx.DiMinus.Value, _adx.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,33 +19,15 @@ public class AdxrIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxrIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
public void AdxrIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new AdxrIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AdxrIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxrIndicator_ShortName_IncludesParameters()
|
||||
{
|
||||
var indicator = new AdxrIndicator { Period = 20 };
|
||||
indicator.Initialize();
|
||||
|
||||
Assert.Contains("ADXR", indicator.ShortName);
|
||||
Assert.Contains("20", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxrIndicator_SourceCodeLink_IsValid()
|
||||
{
|
||||
var indicator = new AdxrIndicator();
|
||||
|
||||
Assert.Contains("github.com", indicator.SourceCodeLink);
|
||||
Assert.Contains("Adxr.Quantower.cs", indicator.SourceCodeLink);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdxrIndicator_Initialize_CreatesInternalAdxr()
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AdxrIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class AdxrIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -12,9 +14,9 @@ public class AdxrIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Adxr? _adxr;
|
||||
protected LineSeries? AdxrSeries;
|
||||
private readonly LineSeries? _adxrSeries;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"ADXR {Period}";
|
||||
@@ -27,29 +29,22 @@ public class AdxrIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "ADXR - Average Directional Movement Rating";
|
||||
Description = "Quantifies the change in momentum of the ADX";
|
||||
|
||||
AdxrSeries = new(name: "ADXR", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(AdxrSeries);
|
||||
_adxrSeries = new(name: "ADXR", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_adxrSeries);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_adxr = new Adxr(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _adxr!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
|
||||
TValue result = _adxr!.Update(bar, isNew);
|
||||
|
||||
if (!_adxr.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AdxrSeries!.SetValue(result.Value);
|
||||
_adxrSeries!.SetValue(result.Value, _adxr.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,4 +128,21 @@ public class AdxrTests
|
||||
Assert.Throws<ArgumentException>(() => new Adxr(0));
|
||||
Assert.Throws<ArgumentException>(() => new Adxr(-1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Chainability_Works()
|
||||
{
|
||||
var adxr = new Adxr(14);
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(10, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
// Test TBarSeries chain
|
||||
var result = adxr.Update(bars);
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<TSeries>(result);
|
||||
|
||||
// Test TBar chain (returns TValue)
|
||||
var result2 = adxr.Update(bars[0]);
|
||||
Assert.IsType<TValue>(result2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ public class AoIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AoIndicator_MinHistoryDepths_EqualsSlowPeriod()
|
||||
public void AoIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new AoIndicator { SlowPeriod = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AoIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -120,6 +120,6 @@ public class AoIndicatorTests
|
||||
|
||||
Assert.Equal(10, indicator.FastPeriod);
|
||||
Assert.Equal(40, indicator.SlowPeriod);
|
||||
Assert.Equal(40, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AoIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AoIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class AoIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Fast Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int FastPeriod { get; set; } = 5;
|
||||
@@ -15,10 +17,10 @@ public class AoIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Ao? _ao;
|
||||
protected LineSeries? UpSeries;
|
||||
protected LineSeries? DownSeries;
|
||||
private readonly LineSeries? _upSeries;
|
||||
private readonly LineSeries? _downSeries;
|
||||
|
||||
public int MinHistoryDepths => SlowPeriod;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"AO {FastPeriod}:{SlowPeriod}";
|
||||
@@ -31,69 +33,47 @@ public class AoIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "AO - Awesome Oscillator";
|
||||
Description = "Momentum indicator measuring market momentum";
|
||||
|
||||
UpSeries = new(name: "AO Up", color: Color.Green, width: 2, style: LineStyle.Solid);
|
||||
DownSeries = new(name: "AO Down", color: Color.Red, width: 2, style: LineStyle.Solid);
|
||||
_upSeries = new(name: "AO Up", color: Color.Green, width: 2, style: LineStyle.Solid);
|
||||
_downSeries = new(name: "AO Down", color: Color.Red, width: 2, style: LineStyle.Solid);
|
||||
|
||||
AddLineSeries(UpSeries);
|
||||
AddLineSeries(DownSeries);
|
||||
AddLineSeries(_upSeries);
|
||||
AddLineSeries(_downSeries);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_ao = new Ao(FastPeriod, SlowPeriod);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
TValue result = _ao!.Update(bar, isNew);
|
||||
TValue result = _ao!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
if (!_ao.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine color based on momentum
|
||||
// Green if rising, Red if falling
|
||||
// We need previous value to compare.
|
||||
// Since OnUpdate is called multiple times for the same bar (ticks),
|
||||
// we need to be careful about "previous value".
|
||||
// Ideally, we compare with the value of the *previous bar*.
|
||||
// But AO coloring is usually: Current > Previous Bar's AO => Green.
|
||||
// Or Current > Previous Value (intra-bar)?
|
||||
// Standard is: "Green bar if the bar is higher than the previous bar. Red bar if the bar is lower than the previous bar."
|
||||
// "Previous bar" usually means the AO value of the previous period.
|
||||
|
||||
// We can get the previous value from the indicator history if we stored it,
|
||||
// or just use _ao.Last (which is current) and we need the previous one.
|
||||
// But _ao doesn't expose history directly unless we use TSeries.
|
||||
// However, Quantower stores history in the Series.
|
||||
|
||||
// Get previous value from series
|
||||
double prevAo = double.NaN;
|
||||
if (Count > 1)
|
||||
{
|
||||
// Try to get from UpSeries
|
||||
prevAo = UpSeries!.GetValue(1);
|
||||
prevAo = _upSeries!.GetValue(1);
|
||||
if (double.IsNaN(prevAo))
|
||||
{
|
||||
prevAo = DownSeries!.GetValue(1);
|
||||
prevAo = _downSeries!.GetValue(1);
|
||||
}
|
||||
}
|
||||
|
||||
// If first bar, just pick a color (e.g. Green) or NaN
|
||||
if (double.IsNaN(prevAo) || result.Value > prevAo)
|
||||
{
|
||||
UpSeries!.SetValue(result.Value);
|
||||
DownSeries!.SetValue(double.NaN);
|
||||
_upSeries!.SetValue(result.Value);
|
||||
_downSeries!.SetValue(double.NaN);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpSeries!.SetValue(double.NaN);
|
||||
DownSeries!.SetValue(result.Value);
|
||||
_upSeries!.SetValue(double.NaN);
|
||||
_downSeries!.SetValue(result.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class AoTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticBatch_Matches_Streaming()
|
||||
public void StaticCalculate_Matches_Streaming()
|
||||
{
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
@@ -20,13 +20,13 @@ public class ApoIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApoIndicator_MinHistoryDepths_EqualsSlowPeriod()
|
||||
public void ApoIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new ApoIndicator { SlowPeriod = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ApoIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -117,6 +117,6 @@ public class ApoIndicatorTests
|
||||
|
||||
Assert.Equal(10, indicator.FastPeriod);
|
||||
Assert.Equal(40, indicator.SlowPeriod);
|
||||
Assert.Equal(40, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ApoIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class ApoIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class ApoIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Fast Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int FastPeriod { get; set; } = 12;
|
||||
@@ -15,9 +17,9 @@ public class ApoIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Apo? _apo;
|
||||
protected LineSeries? Series;
|
||||
private readonly LineSeries? _series;
|
||||
|
||||
public int MinHistoryDepths => SlowPeriod;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"APO {FastPeriod}:{SlowPeriod}";
|
||||
@@ -30,28 +32,22 @@ public class ApoIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "APO - Absolute Price Oscillator";
|
||||
Description = "Momentum indicator showing the difference between two EMAs";
|
||||
|
||||
Series = new(name: "APO", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: "APO", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_apo = new Apo(FastPeriod, SlowPeriod);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _apo!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
TValue result = _apo!.Update(bar, isNew);
|
||||
|
||||
if (!_apo.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Series!.SetValue(result.Value);
|
||||
_series!.SetValue(result.Value, _apo.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
+120
-38
@@ -1,67 +1,149 @@
|
||||
using Xunit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class ApoTests
|
||||
{
|
||||
private readonly GBM _gbm;
|
||||
|
||||
public ApoTests()
|
||||
{
|
||||
_gbm = new GBM();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ValidatesInput()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new Apo(fastPeriod: 0));
|
||||
Assert.Throws<ArgumentException>(() => new Apo(slowPeriod: 0));
|
||||
Assert.Throws<ArgumentException>(() => new Apo(fastPeriod: 26, slowPeriod: 12)); // Fast >= Slow
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Update_ReturnsValidValue()
|
||||
public void BasicCalculation_DoesNotCrash()
|
||||
{
|
||||
var apo = new Apo(12, 26);
|
||||
var result = apo.Update(new TValue(DateTime.UtcNow, 100));
|
||||
Assert.Equal(0, result.Value); // First value: EMA(100) - EMA(100) = 0
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(1000, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
apo.Update(bars[i]);
|
||||
}
|
||||
|
||||
Assert.True(double.IsFinite(apo.Last.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsHot_BecomesTrue()
|
||||
public void IsNew_Consistency()
|
||||
{
|
||||
var apo = new Apo(12, 26);
|
||||
for (int i = 0; i < 100; i++)
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
// Feed first 99
|
||||
for (int i = 0; i < 99; i++)
|
||||
{
|
||||
apo.Update(new TValue(DateTime.UtcNow, 100));
|
||||
apo.Update(bars[i]);
|
||||
}
|
||||
Assert.True(apo.IsHot);
|
||||
|
||||
// Update with 100th point (isNew=true)
|
||||
apo.Update(bars[99], true);
|
||||
|
||||
// Update with modified 100th point (isNew=false)
|
||||
var modifiedBar = new TBar(bars[99].Time, bars[99].Open, bars[99].High + 1.0, bars[99].Low - 1.0, bars[99].Close, bars[99].Volume);
|
||||
var val2 = apo.Update(modifiedBar, false);
|
||||
|
||||
// Create new instance and feed up to modified
|
||||
var apo2 = new Apo(12, 26);
|
||||
for (int i = 0; i < 99; i++)
|
||||
{
|
||||
apo2.Update(bars[i]);
|
||||
}
|
||||
var val3 = apo2.Update(modifiedBar, true);
|
||||
|
||||
Assert.Equal(val3.Value, val2.Value, 1e-9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Batch_Matches_Streaming()
|
||||
public void Reset_Works()
|
||||
{
|
||||
var source = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var tSeries = new TSeries(source.Close.Count);
|
||||
for (int i = 0; i < source.Close.Count; i++)
|
||||
var apo = new Apo(12, 26);
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
tSeries.Add(source.Close[i]);
|
||||
apo.Update(bars[i]);
|
||||
}
|
||||
|
||||
var apoBatch = Apo.Batch(tSeries, 12, 26);
|
||||
|
||||
var apoStream = new Apo(12, 26);
|
||||
var streamResults = new List<double>();
|
||||
for (int i = 0; i < tSeries.Count; i++)
|
||||
apo.Reset();
|
||||
Assert.Equal(0, apo.Last.Value);
|
||||
Assert.False(apo.IsHot);
|
||||
|
||||
// Feed again
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
streamResults.Add(apoStream.Update(tSeries[i]).Value);
|
||||
apo.Update(bars[i]);
|
||||
}
|
||||
|
||||
Assert.True(double.IsFinite(apo.Last.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TBarSeries_Update_Matches_Streaming()
|
||||
{
|
||||
var apo = new Apo(12, 26);
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
var streamingResults = new List<double>();
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
streamingResults.Add(apo.Update(bars[i]).Value);
|
||||
}
|
||||
|
||||
Assert.Equal(apoBatch.Count, streamResults.Count);
|
||||
for (int i = 0; i < apoBatch.Count; i++)
|
||||
var apo2 = new Apo(12, 26);
|
||||
var seriesResults = apo2.Update(bars.Close);
|
||||
|
||||
Assert.Equal(streamingResults.Count, seriesResults.Count);
|
||||
for (int i = 0; i < seriesResults.Count; i++)
|
||||
{
|
||||
Assert.Equal(apoBatch[i].Value, streamResults[i], precision: 9);
|
||||
Assert.Equal(streamingResults[i], seriesResults.Values[i], 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StaticCalculate_Matches_Streaming()
|
||||
{
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
var apo = new Apo(12, 26);
|
||||
var streamingResults = new List<double>();
|
||||
for (int i = 0; i < bars.Count; i++)
|
||||
{
|
||||
streamingResults.Add(apo.Update(bars[i]).Value);
|
||||
}
|
||||
|
||||
var staticResults = Apo.Batch(bars.Close, 12, 26);
|
||||
|
||||
Assert.Equal(streamingResults.Count, staticResults.Count);
|
||||
for (int i = 0; i < staticResults.Count; i++)
|
||||
{
|
||||
Assert.Equal(streamingResults[i], staticResults.Values[i], 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Chainability_Works()
|
||||
{
|
||||
var apo = new Apo(12, 26);
|
||||
var gbm = new GBM();
|
||||
var bars = gbm.Fetch(10, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
|
||||
// Test TBarSeries chain
|
||||
var result = apo.Update(bars.Close);
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<TSeries>(result);
|
||||
|
||||
// Test TBar chain (returns TValue)
|
||||
var result2 = apo.Update(bars[0]);
|
||||
Assert.IsType<TValue>(result2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_InvalidParameters_ThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new Apo(0, 26));
|
||||
Assert.Throws<ArgumentException>(() => new Apo(12, 0));
|
||||
Assert.Throws<ArgumentException>(() => new Apo(26, 12)); // Fast >= Slow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ public class AroonIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AroonIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
public void AroonIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new AroonIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AroonIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AroonIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class AroonIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -12,11 +14,11 @@ public class AroonIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Aroon? _aroon;
|
||||
protected LineSeries? UpSeries;
|
||||
protected LineSeries? DownSeries;
|
||||
protected LineSeries? OscSeries;
|
||||
private readonly LineSeries? _upSeries;
|
||||
private readonly LineSeries? _downSeries;
|
||||
private readonly LineSeries? _oscSeries;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"Aroon {Period}";
|
||||
@@ -29,36 +31,29 @@ public class AroonIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "Aroon";
|
||||
Description = "Identifies trend changes and strength";
|
||||
|
||||
UpSeries = new(name: "Aroon Up", color: Color.Green, width: 1, style: LineStyle.Solid);
|
||||
DownSeries = new(name: "Aroon Down", color: Color.Red, width: 1, style: LineStyle.Solid);
|
||||
OscSeries = new(name: "Aroon Osc", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
_upSeries = new(name: "Aroon Up", color: Color.Green, width: 1, style: LineStyle.Solid);
|
||||
_downSeries = new(name: "Aroon Down", color: Color.Red, width: 1, style: LineStyle.Solid);
|
||||
_oscSeries = new(name: "Aroon Osc", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
|
||||
AddLineSeries(UpSeries);
|
||||
AddLineSeries(DownSeries);
|
||||
AddLineSeries(OscSeries);
|
||||
AddLineSeries(_upSeries);
|
||||
AddLineSeries(_downSeries);
|
||||
AddLineSeries(_oscSeries);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_aroon = new Aroon(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _aroon!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
|
||||
TValue result = _aroon!.Update(bar, isNew);
|
||||
|
||||
if (!_aroon.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpSeries!.SetValue(_aroon.Up.Value);
|
||||
DownSeries!.SetValue(_aroon.Down.Value);
|
||||
OscSeries!.SetValue(result.Value);
|
||||
_upSeries!.SetValue(_aroon.Up.Value, _aroon.IsHot, ShowColdValues);
|
||||
_downSeries!.SetValue(_aroon.Down.Value, _aroon.IsHot, ShowColdValues);
|
||||
_oscSeries!.SetValue(result.Value, _aroon.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ public class AroonOscIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AroonOscIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
public void AroonOscIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new AroonOscIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AroonOscIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AroonOscIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class AroonOscIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -12,9 +14,9 @@ public class AroonOscIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private AroonOsc? _aroonOsc;
|
||||
protected LineSeries? OscSeries;
|
||||
private readonly LineSeries? _oscSeries;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"AroonOsc {Period}";
|
||||
@@ -27,30 +29,23 @@ public class AroonOscIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "Aroon Oscillator";
|
||||
Description = "Aroon Oscillator";
|
||||
|
||||
OscSeries = new(name: "Aroon Osc", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
_oscSeries = new(name: "Aroon Osc", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
|
||||
AddLineSeries(OscSeries);
|
||||
AddLineSeries(_oscSeries);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_aroonOsc = new AroonOsc(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _aroonOsc!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
|
||||
TValue result = _aroonOsc!.Update(bar, isNew);
|
||||
|
||||
if (!_aroonOsc.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OscSeries!.SetValue(result.Value);
|
||||
_oscSeries!.SetValue(result.Value, _aroonOsc.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class BopIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class BopIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
private Bop? _bop;
|
||||
protected LineSeries? BopSeries;
|
||||
private readonly LineSeries? _bopSeries;
|
||||
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
@@ -21,24 +23,22 @@ public class BopIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "BOP - Balance of Power";
|
||||
Description = "Measures the strength of buyers vs sellers";
|
||||
|
||||
BopSeries = new(name: "BOP", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(BopSeries);
|
||||
_bopSeries = new(name: "BOP", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_bopSeries);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_bop = new Bop();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _bop!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
|
||||
TValue result = _bop!.Update(bar, isNew);
|
||||
|
||||
BopSeries!.SetValue(result.Value);
|
||||
_bopSeries!.SetValue(result.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ public class CfbIndicatorTests
|
||||
{
|
||||
var indicator = new CfbIndicator { MaxLength = 50 };
|
||||
|
||||
Assert.Equal(50, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, CfbIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(50, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -132,17 +132,6 @@ public class CfbIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CfbIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new CfbIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(CfbIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CfbIndicator_DifferentSourceTypes_Work()
|
||||
{
|
||||
@@ -182,6 +171,6 @@ public class CfbIndicatorTests
|
||||
Assert.Equal(10, indicator.MinLength);
|
||||
Assert.Equal(40, indicator.MaxLength);
|
||||
Assert.Equal(10, indicator.Step);
|
||||
Assert.Equal(40, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, CfbIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class CfbIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class CfbIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Min Length", sortIndex: 1, 2, 1000, 1, 0)]
|
||||
public int MinLength { get; set; } = 2;
|
||||
@@ -21,27 +23,28 @@ public class CfbIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Cfb? _cfb;
|
||||
private int _warmupBarIndex = -1;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => MaxLength;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"CFB {MinLength}-{MaxLength}:{SourceName}";
|
||||
public override string ShortName => $"CFB {MinLength}-{MaxLength}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/momentum/cfb/Cfb.Quantower.cs";
|
||||
|
||||
public CfbIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = true;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "CFB - Jurik Composite Fractal Behavior";
|
||||
Description = "Trend Duration Index using fractal efficiency";
|
||||
Series = new(name: "CFB", color: IndicatorExtensions.Statistics, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: "CFB", color: IndicatorExtensions.Statistics, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
// Generate lengths array
|
||||
@@ -53,25 +56,17 @@ public class CfbIndicator : Indicator, IWatchlistIndicator
|
||||
}
|
||||
|
||||
_cfb = new Cfb(lengths);
|
||||
_warmupBarIndex = -1;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _cfb!.Update(input, isNew);
|
||||
if (_warmupBarIndex < 0 && _cfb!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
}
|
||||
TValue result = _cfb!.Update(new TValue(this.GetInputBar(args).Time, _priceSelector!(HistoricalData[Count - 1, SeekOriginHistory.Begin])), args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, _warmupBarIndex, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _cfb.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ public class DmxIndicatorTests
|
||||
{
|
||||
var indicator = new DmxIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, DmxIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -126,17 +126,6 @@ public class DmxIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DmxIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new DmxIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(DmxIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DmxIndicator_Parameters_CanBeChanged()
|
||||
{
|
||||
@@ -146,6 +135,6 @@ public class DmxIndicatorTests
|
||||
indicator.Period = 20;
|
||||
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, DmxIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class DmxIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class DmxIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -12,10 +14,9 @@ public class DmxIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Dmx? _dmx;
|
||||
protected LineSeries? Series;
|
||||
private int _warmupBarIndex = -1;
|
||||
private readonly LineSeries? _series;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"DMX {Period}";
|
||||
@@ -27,36 +28,23 @@ public class DmxIndicator : Indicator, IWatchlistIndicator
|
||||
SeparateWindow = true;
|
||||
Name = "DMX - Jurik Directional Movement Index";
|
||||
Description = "Jurik's smoother, lower-lag alternative to DMI/ADX";
|
||||
Series = new(name: $"DMX {Period}", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"DMX {Period}", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_dmx = new Dmx(Period);
|
||||
_warmupBarIndex = -1;
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _dmx!.Update(this.GetInputBar(args), args.IsNewBar());
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
|
||||
TValue result = _dmx!.Update(bar, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
// DMX doesn't expose IsHot directly, but we can infer warmup
|
||||
if (_warmupBarIndex < 0 && Count > Period * 2) // Rough estimate for JMA warmup
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ public class MacdIndicatorTests
|
||||
};
|
||||
|
||||
// 26 + 9 = 35
|
||||
Assert.Equal(35, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, MacdIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(35, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -41,7 +41,7 @@ public class MacdIndicatorTests
|
||||
var indicator = new MacdIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
Assert.Equal("MACD(12,26,9)", indicator.ShortName);
|
||||
Assert.Equal("MACD(12,26,9):Close", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MacdIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class MacdIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Fast Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int FastPeriod { get; set; } = 12;
|
||||
@@ -14,49 +16,58 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Signal Period", sortIndex: 3, 1, 2000, 1, 0)]
|
||||
public int SignalPeriod { get; set; } = 9;
|
||||
|
||||
private Macd? _macd;
|
||||
protected LineSeries? MacdSeries;
|
||||
protected LineSeries? SignalSeries;
|
||||
protected LineSeries? HistSeries;
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
public SourceType Source { get; set; } = SourceType.Close;
|
||||
|
||||
public int MinHistoryDepths => Math.Max(FastPeriod, SlowPeriod) + SignalPeriod;
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Macd? _macd;
|
||||
private readonly LineSeries? _macdSeries;
|
||||
private readonly LineSeries? _signalSeries;
|
||||
private readonly LineSeries? _histSeries;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"MACD({FastPeriod},{SlowPeriod},{SignalPeriod})";
|
||||
public override string ShortName => $"MACD({FastPeriod},{SlowPeriod},{SignalPeriod}):{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/momentum/macd/Macd.Quantower.cs";
|
||||
|
||||
public MacdIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = true;
|
||||
_sourceName = Source.ToString();
|
||||
Name = "MACD - Moving Average Convergence Divergence";
|
||||
Description = "Trend-following momentum indicator";
|
||||
|
||||
MacdSeries = new(name: "MACD", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
SignalSeries = new(name: "Signal", color: Color.Red, width: 2, style: LineStyle.Solid);
|
||||
HistSeries = new(name: "Histogram", color: Color.Green, width: 2, style: LineStyle.Solid); // Quantower LineStyle doesn't have Histogram, use Solid and we'll paint it manually if needed, or just use Solid for now. Actually, Quantower usually handles Histogram via a different series type or style, but LineSeries only supports lines. Let's stick to Solid for now to fix compilation.
|
||||
_macdSeries = new(name: "MACD", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
_signalSeries = new(name: "Signal", color: Color.Red, width: 2, style: LineStyle.Solid);
|
||||
_histSeries = new(name: "Histogram", color: Color.Green, width: 2, style: LineStyle.Solid);
|
||||
|
||||
AddLineSeries(MacdSeries);
|
||||
AddLineSeries(SignalSeries);
|
||||
AddLineSeries(HistSeries);
|
||||
AddLineSeries(_macdSeries);
|
||||
AddLineSeries(_signalSeries);
|
||||
AddLineSeries(_histSeries);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_macd = new Macd(FastPeriod, SlowPeriod, SignalPeriod);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _macd!.Update(new TValue(this.GetInputBar(args).Time, _priceSelector!(HistoricalData[Count - 1, SeekOriginHistory.Begin])), args.IsNewBar());
|
||||
|
||||
TValue input = this.GetInputValue(args, SourceType.Close);
|
||||
|
||||
_macd!.Update(input, isNew);
|
||||
|
||||
MacdSeries!.SetValue(_macd.Last.Value);
|
||||
SignalSeries!.SetValue(_macd.Signal.Value);
|
||||
HistSeries!.SetValue(_macd.Histogram.Value);
|
||||
_macdSeries!.SetValue(result.Value, _macd.IsHot, ShowColdValues);
|
||||
_signalSeries!.SetValue(_macd.Signal.Value, _macd.IsHot, ShowColdValues);
|
||||
_histSeries!.SetValue(_macd.Histogram.Value, _macd.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ public class RsiIndicatorTests
|
||||
Period = 20
|
||||
};
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, RsiIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -39,7 +39,7 @@ public class RsiIndicatorTests
|
||||
};
|
||||
indicator.Initialize();
|
||||
|
||||
Assert.Equal("RSI(20)", indicator.ShortName);
|
||||
Assert.Contains("RSI(20)", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,47 +1,59 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class RsiIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class RsiIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
|
||||
private Rsi? _rsi;
|
||||
protected LineSeries? RsiSeries;
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
public SourceType Source { get; set; } = SourceType.Close;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Rsi? _rsi;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"RSI({Period})";
|
||||
public override string ShortName => $"RSI({Period}):{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/momentum/rsi/Rsi.Quantower.cs";
|
||||
|
||||
public RsiIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = true;
|
||||
_sourceName = Source.ToString();
|
||||
Name = "RSI - Relative Strength Index";
|
||||
Description = "Measures the speed and change of price movements";
|
||||
|
||||
RsiSeries = new(name: "RSI", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(RsiSeries);
|
||||
_series = new(name: "RSI", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_rsi = new Rsi(Period);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _rsi!.Update(new TValue(this.GetInputBar(args).Time, _priceSelector!(HistoricalData[Count - 1, SeekOriginHistory.Begin])), args.IsNewBar());
|
||||
|
||||
TValue input = this.GetInputValue(args, SourceType.Close);
|
||||
|
||||
TValue result = _rsi!.Update(input, isNew);
|
||||
|
||||
RsiSeries!.SetValue(result.Value);
|
||||
_series!.SetValue(result.Value, _rsi.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ public class RsxIndicatorTests
|
||||
{
|
||||
var indicator = new RsxIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, RsxIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -113,17 +113,6 @@ public class RsxIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RsxIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new RsxIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(RsxIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RsxIndicator_MultipleUpdates_ProducesCorrectRsxSequence()
|
||||
{
|
||||
@@ -174,6 +163,6 @@ public class RsxIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, RsxIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class RsxIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class RsxIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -15,51 +17,42 @@ public class RsxIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Rsx? _rsx;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"RSX {Period}:{SourceName}";
|
||||
public override string ShortName => $"RSX {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/momentum/rsx/Rsx.Quantower.cs";
|
||||
|
||||
public RsxIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = true;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "RSX - Jurik Relative Strength Index";
|
||||
Description = "Jurik's RSI: A noise-free, zero-lag version of RSI";
|
||||
Series = new(name: $"RSX {Period}", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"RSX {Period}", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_rsx = new Rsx(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _rsx!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
TValue result = _rsx!.Update(new TValue(this.GetInputBar(args).Time, _priceSelector!(HistoricalData[Count - 1, SeekOriginHistory.Begin])), args.IsNewBar());
|
||||
|
||||
if (_warmupBarIndex < 0 && _rsx!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _rsx.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ public class VelIndicatorTests
|
||||
{
|
||||
var indicator = new VelIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, VelIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -113,17 +113,6 @@ public class VelIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VelIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new VelIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(VelIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VelIndicator_MultipleUpdates_ProducesCorrectVelSequence()
|
||||
{
|
||||
@@ -174,6 +163,6 @@ public class VelIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, VelIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class VelIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class VelIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -15,49 +17,42 @@ public class VelIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Vel? _vel;
|
||||
private int _warmupBarIndex = -1;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"VEL {Period}:{SourceName}";
|
||||
public override string ShortName => $"VEL {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/momentum/vel/Vel.Quantower.cs";
|
||||
|
||||
public VelIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = true;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "VEL - Jurik Velocity";
|
||||
Description = "Momentum oscillator calculated as PWMA - WMA";
|
||||
Series = new(name: $"VEL {Period}", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"VEL {Period}", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_vel = new Vel(Period);
|
||||
_warmupBarIndex = -1;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _vel!.Update(input, isNew);
|
||||
if (_warmupBarIndex < 0 && _vel!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
}
|
||||
TValue result = _vel!.Update(new TValue(this.GetInputBar(args).Time, _priceSelector!(HistoricalData[Count - 1, SeekOriginHistory.Begin])), args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, _warmupBarIndex, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _vel.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ public class AlmaIndicatorTests
|
||||
{
|
||||
var indicator = new AlmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, AlmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -165,6 +165,6 @@ public class AlmaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AlmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class AlmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
@@ -23,12 +25,13 @@ public class AlmaIndicator : Indicator, IWatchlistIndicator
|
||||
private Alma? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"ALMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/alma/Alma.Quantower.cs";
|
||||
|
||||
public AlmaIndicator()
|
||||
{
|
||||
@@ -45,26 +48,17 @@ public class AlmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
ma = new Alma(Period, Offset, Sigma);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
Series!.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class BesselIndicatorTests
|
||||
{
|
||||
var indicator = new BesselIndicator { Length = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, BesselIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -157,6 +157,6 @@ public class BesselIndicatorTests
|
||||
|
||||
indicator.Length = 20;
|
||||
Assert.Equal(20, indicator.Length);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, BesselIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class BesselIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Length", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
@@ -17,9 +19,9 @@ public class BesselIndicator : Indicator, IWatchlistIndicator
|
||||
private Bessel? _filter;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Length;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"BESSEL {Length}:{SourceName}";
|
||||
@@ -39,27 +41,17 @@ public class BesselIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
_filter = new Bessel(Length);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _filter!.Update(input, isNew);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = _filter!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && _filter!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
Series!.SetValue(result.Value, _filter.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ public class BilateralIndicatorTests
|
||||
{
|
||||
var indicator = new BilateralIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, BilateralIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -113,16 +113,6 @@ public class BilateralIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BilateralIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new BilateralIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(BilateralIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BilateralIndicator_MultipleUpdates_ProducesCorrectSequence()
|
||||
@@ -181,6 +171,6 @@ public class BilateralIndicatorTests
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(1.0, indicator.SigmaSRatio);
|
||||
Assert.Equal(2.0, indicator.SigmaRMult);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, BilateralIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class BilateralIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
@@ -23,9 +25,9 @@ public class BilateralIndicator : Indicator, IWatchlistIndicator
|
||||
private Bilateral? _bilateral;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"Bilateral {Period}:{SourceName}";
|
||||
@@ -46,30 +48,17 @@ public class BilateralIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
_bilateral = new Bilateral(Period, SigmaSRatio, SigmaRMult);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _bilateral!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = _bilateral!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
if (_warmupBarIndex < 0 && _bilateral!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
var savedColor = Series!.Color;
|
||||
Series.Color = Color.Transparent;
|
||||
base.OnPaintChart(args);
|
||||
Series.Color = savedColor;
|
||||
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintLine(args, Series!, warmupPeriod, showColdValues: ShowColdValues);
|
||||
Series!.SetValue(result.Value, _bilateral.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,13 @@ namespace QuanTAlib;
|
||||
|
||||
public class BilateralTests
|
||||
{
|
||||
private readonly GBM _gbm;
|
||||
|
||||
public BilateralTests()
|
||||
{
|
||||
_gbm = new GBM();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ValidatesInput()
|
||||
{
|
||||
@@ -101,22 +108,43 @@ public class BilateralTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TSeries_Update_Matches_Iterative()
|
||||
public void AllModes_ProduceSameResult()
|
||||
{
|
||||
var indicator = new Bilateral(5);
|
||||
var series = new TSeries();
|
||||
for (int i = 0; i < 20; i++)
|
||||
int period = 10;
|
||||
var bars = _gbm.Fetch(1000, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
|
||||
var series = bars.Close;
|
||||
|
||||
// 1. Batch Mode
|
||||
var batchSeries = new Bilateral(period).Update(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];
|
||||
Bilateral.Calculate(spanInput, spanOutput, period);
|
||||
double spanResult = spanOutput[^1];
|
||||
|
||||
// 3. Streaming Mode
|
||||
var streamingInd = new Bilateral(period);
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
series.Add(new TValue(DateTime.UtcNow.AddMinutes(i), i));
|
||||
streamingInd.Update(series[i]);
|
||||
}
|
||||
|
||||
var resultSeries = indicator.Update(series);
|
||||
|
||||
var indicatorIterative = new Bilateral(5);
|
||||
for (int i = 0; i < 20; i++)
|
||||
double streamingResult = streamingInd.Last.Value;
|
||||
|
||||
// 4. Eventing Mode
|
||||
var pubSource = new TSeries();
|
||||
var eventingInd = new Bilateral(pubSource, period);
|
||||
for (int i = 0; i < series.Count; i++)
|
||||
{
|
||||
indicatorIterative.Update(series[i]);
|
||||
Assert.Equal(indicatorIterative.Last.Value, resultSeries[i].Value);
|
||||
pubSource.Add(series[i]);
|
||||
}
|
||||
double eventingResult = eventingInd.Last.Value;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, spanResult, 1e-9);
|
||||
Assert.Equal(expected, streamingResult, 1e-9);
|
||||
Assert.Equal(expected, eventingResult, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,4 +260,106 @@ public sealed class Bilateral : AbstractBase
|
||||
_p_state = default;
|
||||
Last = default;
|
||||
}
|
||||
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> destination, int period, double sigmaSRatio = 0.5, double sigmaRMult = 1.0)
|
||||
{
|
||||
if (period <= 0)
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
|
||||
// Precalculate spatial weights
|
||||
double sigmaS = Math.Max(period * sigmaSRatio, 1e-10);
|
||||
double twoSigmaSSq = 2.0 * sigmaS * sigmaS;
|
||||
Span<double> spatialWeights = period <= 256 ? stackalloc double[period] : new double[period];
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
double diffSpatial = i;
|
||||
spatialWeights[i] = Math.Exp(-(diffSpatial * diffSpatial) / twoSigmaSSq);
|
||||
}
|
||||
|
||||
// Handle NaNs by tracking last valid value
|
||||
double lastValid = double.NaN;
|
||||
// Find initial valid value
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
if (double.IsFinite(source[i]))
|
||||
{
|
||||
lastValid = source[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all NaNs, fill with NaN
|
||||
if (double.IsNaN(lastValid))
|
||||
{
|
||||
destination.Fill(double.NaN);
|
||||
return;
|
||||
}
|
||||
|
||||
Span<double> window = period <= 256 ? stackalloc double[period] : new double[period];
|
||||
int windowIdx = 0;
|
||||
int count = 0;
|
||||
double sum = 0;
|
||||
double sumSq = 0;
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
double val = source[i];
|
||||
if (double.IsNaN(val))
|
||||
{
|
||||
val = lastValid;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastValid = val;
|
||||
}
|
||||
|
||||
// Add to window
|
||||
double removed = 0;
|
||||
if (count >= period)
|
||||
{
|
||||
removed = window[windowIdx];
|
||||
sum -= removed;
|
||||
sumSq -= removed * removed;
|
||||
}
|
||||
|
||||
window[windowIdx] = val;
|
||||
sum += val;
|
||||
sumSq += val * val;
|
||||
|
||||
int currentNewestIdx = windowIdx;
|
||||
windowIdx = (windowIdx + 1) % period;
|
||||
if (count < period) count++;
|
||||
|
||||
// Calculate StDev
|
||||
double variance = Math.Max(0, (sumSq - (sum * sum) / count) / count);
|
||||
double stdev = Math.Sqrt(variance);
|
||||
|
||||
double sigmaR = Math.Max(stdev * sigmaRMult, 1e-10);
|
||||
double twoSigmaRSq = 2.0 * sigmaR * sigmaR;
|
||||
|
||||
double sumWeights = 0.0;
|
||||
double sumWeightedSrc = 0.0;
|
||||
double centerVal = val; // Newest value
|
||||
|
||||
// Iterate backwards through the window
|
||||
for (int k = 0; k < count; k++)
|
||||
{
|
||||
// k=0 is newest (currentNewestIdx)
|
||||
// k=1 is previous...
|
||||
int idx = currentNewestIdx - k;
|
||||
if (idx < 0) idx += period;
|
||||
|
||||
double wVal = window[idx];
|
||||
double diffRange = centerVal - wVal;
|
||||
|
||||
double weightRange = Math.Exp(-(diffRange * diffRange) / twoSigmaRSq);
|
||||
double weight = spatialWeights[k] * weightRange;
|
||||
|
||||
sumWeights += weight;
|
||||
sumWeightedSrc += weight * wVal;
|
||||
}
|
||||
|
||||
destination[i] = sumWeights == 0.0 ? centerVal : sumWeightedSrc / sumWeights;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ public class BlmaIndicatorTests
|
||||
{
|
||||
var indicator = new BlmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, BlmaIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class BlmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
@@ -17,47 +18,41 @@ public class BlmaIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
private Blma? _ma;
|
||||
protected LineSeries? _series;
|
||||
protected string? SourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"BLMA {Period}";
|
||||
public override string ShortName => $"BLMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/blma/Blma.Quantower.cs";
|
||||
|
||||
public BlmaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "BLMA - Blackman Window Moving Average";
|
||||
Description = "A moving average using the Blackman window function for superior noise suppression.";
|
||||
SeparateWindow = false;
|
||||
|
||||
_series = new(name: "BLMA", color: Color.Yellow, width: 2, style: LineStyle.Solid);
|
||||
_series = new(name: $"BLMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_ma = new Blma(Period);
|
||||
SourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _ma!.Update(input, isNew);
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
if (!_ma.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_series!.SetValue(result.Value);
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, _series!, _ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-14
@@ -150,12 +150,22 @@ public sealed class Blma : AbstractBase
|
||||
|
||||
private static double CalculateWeightedSum(RingBuffer buffer, ReadOnlySpan<double> weights)
|
||||
{
|
||||
double sum = 0;
|
||||
for (int i = 0; i < buffer.Count; i++)
|
||||
int start = buffer.StartIndex;
|
||||
int count = buffer.Count;
|
||||
int capacity = buffer.Capacity;
|
||||
|
||||
if (start + count <= capacity)
|
||||
{
|
||||
sum += buffer[i] * weights[i];
|
||||
return buffer.InternalBuffer.Slice(start, count).DotProduct(weights);
|
||||
}
|
||||
return sum;
|
||||
|
||||
int firstPartLength = capacity - start;
|
||||
int secondPartLength = count - firstPartLength;
|
||||
|
||||
double sum1 = buffer.InternalBuffer.Slice(start, firstPartLength).DotProduct(weights[..firstPartLength]);
|
||||
double sum2 = buffer.InternalBuffer.Slice(0, secondPartLength).DotProduct(weights[firstPartLength..]);
|
||||
|
||||
return sum1 + sum2;
|
||||
}
|
||||
|
||||
public static void Calculate(ReadOnlySpan<double> source, Span<double> destination, int period)
|
||||
@@ -200,11 +210,7 @@ public sealed class Blma : AbstractBase
|
||||
}
|
||||
else
|
||||
{
|
||||
double sum = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
sum += source[i - count + 1 + j] * currentWeights[j];
|
||||
}
|
||||
double sum = source.Slice(i - count + 1, count).DotProduct(currentWeights);
|
||||
destination[i] = sum / currentWeightSum;
|
||||
}
|
||||
}
|
||||
@@ -212,11 +218,7 @@ public sealed class Blma : AbstractBase
|
||||
else
|
||||
{
|
||||
// Full period
|
||||
double sum = 0;
|
||||
for (int j = 0; j < period; j++)
|
||||
{
|
||||
sum += source[i - period + 1 + j] * weights[j];
|
||||
}
|
||||
double sum = source.Slice(i - period + 1, period).DotProduct(weights);
|
||||
destination[i] = sum / weightSum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ public class ButterIndicatorTests
|
||||
{
|
||||
var indicator = new ButterIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ButterIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class ButterIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 2, 2000, 1, 0)]
|
||||
@@ -17,47 +18,41 @@ public class ButterIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
private Butter? _ma;
|
||||
protected LineSeries? _series;
|
||||
protected string? SourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"BUTTER {Period}";
|
||||
public override string ShortName => $"BUTTER {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/butter/Butter.Quantower.cs";
|
||||
|
||||
public ButterIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "BUTTER - Butterworth Filter";
|
||||
Description = "A 2nd-order low-pass filter with maximally flat frequency response in the passband.";
|
||||
SeparateWindow = false;
|
||||
|
||||
_series = new(name: "BUTTER", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
_series = new(name: $"BUTTER {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_ma = new Butter(Period);
|
||||
SourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _ma!.Update(input, isNew);
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
if (!_ma.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_series!.SetValue(result.Value);
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, _series!, _ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ public class ConvIndicatorTests
|
||||
var indicator = new ConvIndicator { WeightsInput = "1, 2, 3, 4, 5" };
|
||||
indicator.Initialize(); // Initialize to parse weights
|
||||
|
||||
Assert.Equal(5, indicator.MinHistoryDepths);
|
||||
Assert.Equal(5, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, ConvIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -112,16 +112,6 @@ public class ConvIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new ConvIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(ConvIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvIndicator_MultipleUpdates_ProducesCorrectSequence()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class ConvIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Weights (comma separated)", sortIndex: 1)]
|
||||
@@ -17,11 +17,11 @@ public class ConvIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Conv? _conv;
|
||||
private int _warmupBarIndex = -1;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => _conv != null ? WeightsInput.Split(',').Length : 0;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"CONV:{SourceName}";
|
||||
@@ -60,25 +60,18 @@ public class ConvIndicator : Indicator, IWatchlistIndicator
|
||||
_conv = new Conv([1.0]);
|
||||
}
|
||||
|
||||
_warmupBarIndex = -1;
|
||||
SourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _conv!.Update(input, isNew);
|
||||
if (_warmupBarIndex < 0 && _conv!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
}
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = _conv!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, _warmupBarIndex, showColdValues: ShowColdValues, tension: 0.2);
|
||||
Series!.SetValue(result.Value, _conv.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class DemaIndicatorTests
|
||||
{
|
||||
var indicator = new DemaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, DemaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -111,18 +111,6 @@ public class DemaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DemaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new DemaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
// We can't easily mock PaintChartEventArgs fully, but we can verify the method exists and is callable
|
||||
// if we could mock the args. Since we can't, we skip the actual call but verify the method is overridden.
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(DemaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DemaIndicator_MultipleUpdates_ProducesCorrectDemaSequence()
|
||||
@@ -166,14 +154,4 @@ public class DemaIndicatorTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DemaIndicator_Period_CanBeChanged()
|
||||
{
|
||||
var indicator = new DemaIndicator { Period = 5 };
|
||||
Assert.Equal(5, indicator.Period);
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class DemaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
@@ -17,9 +19,9 @@ public class DemaIndicator : Indicator, IWatchlistIndicator
|
||||
private Dema? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"DEMA {Period}:{SourceName}";
|
||||
@@ -40,26 +42,17 @@ public class DemaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
ma = new Dema(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
Series!.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class DwmaIndicatorTests
|
||||
{
|
||||
var indicator = new DwmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(40, indicator.MinHistoryDepths);
|
||||
Assert.Equal(40, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, DwmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class DwmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
@@ -15,11 +17,11 @@ public class DwmaIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Dwma? ma;
|
||||
private int _warmupBarIndex = -1;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period * 2; // DWMA needs roughly 2x period to warm up
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"DWMA {Period}:{SourceName}";
|
||||
@@ -39,25 +41,18 @@ public class DwmaIndicator : Indicator, IWatchlistIndicator
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Dwma(Period);
|
||||
_warmupBarIndex = -1;
|
||||
SourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
}
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, _warmupBarIndex, showColdValues: ShowColdValues, tension: 0.2);
|
||||
Series!.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ public class EmaIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmaIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
public void EmaIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new EmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, EmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -164,6 +164,6 @@ public class EmaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, EmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class EmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
@@ -17,9 +19,9 @@ public class EmaIndicator : Indicator, IWatchlistIndicator
|
||||
private Ema? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"EMA {Period}:{SourceName}";
|
||||
@@ -39,27 +41,15 @@ public class EmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
ma = new Ema(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1; // Reset warmup tracking when period changes
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
|
||||
// Track when IsHot becomes true for the first time
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
Series!.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ public class HmaIndicatorTests
|
||||
var indicator = new HmaIndicator { Period = 16 };
|
||||
// HMA warmup is roughly Period + Sqrt(Period)
|
||||
// 16 + Sqrt(16) = 16 + 4 = 20
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, HmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -112,16 +112,6 @@ public class HmaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HmaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new HmaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(HmaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HmaIndicator_MultipleUpdates_ProducesCorrectHmaSequence()
|
||||
@@ -174,6 +164,6 @@ public class HmaIndicatorTests
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
// 20 + sqrt(20) = 20 + 4 = 24
|
||||
Assert.Equal(24, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, HmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class HmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 2, 1000, 1, 0)]
|
||||
@@ -15,11 +17,11 @@ public class HmaIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Hma? ma;
|
||||
private int _warmupBarIndex = -1;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period + (int)Math.Sqrt(Period); // Approximate warmup
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"HMA {Period}:{SourceName}";
|
||||
@@ -39,25 +41,18 @@ public class HmaIndicator : Indicator, IWatchlistIndicator
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Hma(Period);
|
||||
_warmupBarIndex = -1;
|
||||
SourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, _warmupBarIndex, showColdValues: ShowColdValues, tension: 0.2);
|
||||
Series!.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ public class HtitIndicatorTests
|
||||
public void Indicator_Initializes_Correctly()
|
||||
{
|
||||
var indicator = new HtitIndicator();
|
||||
indicator.Initialize();
|
||||
Assert.Equal("HTIT - Ehlers Hilbert Transform Instantaneous Trend", indicator.Name);
|
||||
Assert.Equal("HTIT:Close", indicator.ShortName);
|
||||
Assert.Equal(50, HtitIndicator.MinHistoryDepths);
|
||||
Assert.StartsWith("HTIT", indicator.ShortName);
|
||||
Assert.Contains("Close", indicator.ShortName);
|
||||
Assert.Equal(0, HtitIndicator.MinHistoryDepths);
|
||||
Assert.Single(indicator.LinesSeries);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class HtitIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class HtitIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 50; // Not used in calculation but kept for consistency if needed
|
||||
public int Period { get; set; } = 50; // Not used in calculation but kept for consistency
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
public SourceType Source { get; set; } = SourceType.Close;
|
||||
@@ -16,51 +18,39 @@ public class HtitIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Htit? _htit;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public static int MinHistoryDepths => 50;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"HTIT:{SourceName}";
|
||||
public override string ShortName => $"HTIT:{_sourceName}";
|
||||
|
||||
public HtitIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "HTIT - Ehlers Hilbert Transform Instantaneous Trend";
|
||||
Description = "Ehlers Hilbert Transform Instantaneous Trend";
|
||||
Series = new(name: "HTIT", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: "HTIT", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_htit = new Htit();
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
|
||||
TValue result = _htit!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && _htit.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _htit!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _htit.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ public class JmaIndicatorTests
|
||||
|
||||
Assert.Equal(10, indicator.Period);
|
||||
Assert.Equal(0, indicator.Phase);
|
||||
Assert.Equal(0.45, indicator.Power);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
Assert.Equal("JMA - Jurik Moving Average", indicator.Name);
|
||||
@@ -25,19 +24,18 @@ public class JmaIndicatorTests
|
||||
{
|
||||
var indicator = new JmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, JmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JmaIndicator_ShortName_IncludesParameters()
|
||||
{
|
||||
var indicator = new JmaIndicator { Period = 15, Phase = 50, Power = 0.8 };
|
||||
var indicator = new JmaIndicator { Period = 15, Phase = 50 };
|
||||
|
||||
Assert.Contains("JMA", indicator.ShortName);
|
||||
Assert.Contains("15", indicator.ShortName);
|
||||
Assert.Contains("50", indicator.ShortName);
|
||||
Assert.Contains("0.8", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -115,16 +113,6 @@ public class JmaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JmaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new JmaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(JmaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void JmaIndicator_MultipleUpdates_ProducesCorrectSequence()
|
||||
@@ -171,18 +159,15 @@ public class JmaIndicatorTests
|
||||
[Fact]
|
||||
public void JmaIndicator_Parameters_CanBeChanged()
|
||||
{
|
||||
var indicator = new JmaIndicator { Period = 5, Phase = 10, Power = 0.5 };
|
||||
var indicator = new JmaIndicator { Period = 5, Phase = 10 };
|
||||
Assert.Equal(5, indicator.Period);
|
||||
Assert.Equal(10, indicator.Phase);
|
||||
Assert.Equal(0.5, indicator.Power);
|
||||
|
||||
indicator.Period = 20;
|
||||
indicator.Phase = -10;
|
||||
indicator.Power = 0.9;
|
||||
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(-10, indicator.Phase);
|
||||
Assert.Equal(0.9, indicator.Power);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, JmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public class JmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
@@ -11,24 +13,27 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Phase", sortIndex: 2, -100, 100, 1, 0)]
|
||||
public int Phase { get; set; } = 0;
|
||||
|
||||
[InputParameter("Power", sortIndex: 3, 0.1, 10.0, 0.1, 1)]
|
||||
public double Power { get; set; } = 0.45;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
public SourceType Source { get; set; } = SourceType.Close;
|
||||
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
[InputParameter("Color", sortIndex: 22)]
|
||||
public Color LineColor { get; set; } = IndicatorExtensions.Averages;
|
||||
|
||||
[InputParameter("Width", sortIndex: 23)]
|
||||
public int LineWidth { get; set; } = 2;
|
||||
|
||||
private Jma? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"JMA {Period}:{Phase}:{Power}:{SourceName}";
|
||||
public override string ShortName => $"JMA {Period}:{Phase}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/jma/Jma.Quantower.cs";
|
||||
|
||||
public JmaIndicator()
|
||||
@@ -44,28 +49,21 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Jma(Period, Phase, Power);
|
||||
ma = new Jma(Period, Phase);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
Series!.Color = LineColor;
|
||||
Series!.Width = LineWidth;
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
|
||||
TValue result = ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew: args.IsNewBar());
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
Series!.SetValue(result.Value, ma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class KamaIndicatorTests
|
||||
Assert.Equal(30, indicator.SlowPeriod);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
Assert.Equal("KAMA - Kaufman Adaptive Moving Average", indicator.Name);
|
||||
Assert.Equal("KAMA - Kaufman's Adaptive Moving Average", indicator.Name);
|
||||
Assert.False(indicator.SeparateWindow);
|
||||
Assert.True(indicator.OnBackGround);
|
||||
}
|
||||
@@ -25,8 +25,8 @@ public class KamaIndicatorTests
|
||||
{
|
||||
var indicator = new KamaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, KamaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -165,6 +165,6 @@ public class KamaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, KamaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class KamaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class KamaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
|
||||
[InputParameter("Fast Period", sortIndex: 2, 1, 1000, 1, 0)]
|
||||
[InputParameter("Fast Period", sortIndex: 2, 1, 200, 1, 0)]
|
||||
public int FastPeriod { get; set; } = 2;
|
||||
|
||||
[InputParameter("Slow Period", sortIndex: 3, 1, 1000, 1, 0)]
|
||||
[InputParameter("Slow Period", sortIndex: 3, 1, 200, 1, 0)]
|
||||
public int SlowPeriod { get; set; } = 30;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
@@ -20,51 +23,40 @@ public class KamaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Kama? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Kama? _kama;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"KAMA {Period}:{SourceName}";
|
||||
public override string ShortName => $"KAMA {Period}:{_sourceName}";
|
||||
|
||||
public KamaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "KAMA - Kaufman Adaptive Moving Average";
|
||||
Description = "Kaufman Adaptive Moving Average";
|
||||
Series = new(name: $"KAMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
Name = "KAMA - Kaufman's Adaptive Moving Average";
|
||||
Description = "Kaufman's Adaptive Moving Average";
|
||||
_series = new(name: $"KAMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Kama(Period, FastPeriod, SlowPeriod);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_kama = new Kama(Period, FastPeriod, SlowPeriod);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _kama!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _kama.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public class LsmaIndicatorTests
|
||||
{
|
||||
var indicator = new LsmaIndicator();
|
||||
|
||||
Assert.Equal(14, indicator.Period);
|
||||
Assert.Equal(25, indicator.Period);
|
||||
Assert.Equal(0, indicator.Offset);
|
||||
Assert.Equal(SourceType.Close, indicator.Source);
|
||||
Assert.True(indicator.ShowColdValues);
|
||||
@@ -24,8 +24,8 @@ public class LsmaIndicatorTests
|
||||
{
|
||||
var indicator = new LsmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, LsmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -35,7 +35,6 @@ public class LsmaIndicatorTests
|
||||
|
||||
Assert.Contains("LSMA", indicator.ShortName);
|
||||
Assert.Contains("15", indicator.ShortName);
|
||||
Assert.Contains("2", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -113,16 +112,6 @@ public class LsmaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LsmaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new LsmaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(LsmaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LsmaIndicator_MultipleUpdates_ProducesCorrectSequence()
|
||||
@@ -177,6 +166,6 @@ public class LsmaIndicatorTests
|
||||
indicator.Offset = 2;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(2, indicator.Offset);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, LsmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class LsmaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class LsmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 25;
|
||||
|
||||
[InputParameter("Offset", sortIndex: 2, -1000, 1000, 1, 0)]
|
||||
public int Offset { get; set; } = 0;
|
||||
@@ -17,53 +20,41 @@ public class LsmaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Lsma? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Lsma? _lsma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"LSMA {Period}:{Offset}:{SourceName}";
|
||||
public override string ShortName => $"LSMA {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/lsma/Lsma.Quantower.cs";
|
||||
|
||||
public LsmaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "LSMA - Least Squares Moving Average";
|
||||
Description = "Least Squares Moving Average";
|
||||
Series = new(name: $"LSMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"LSMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Lsma(Period, Offset);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1; // Reset warmup tracking when period changes
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_lsma = new Lsma(Period, Offset);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
|
||||
// Track when IsHot becomes true for the first time
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _lsma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _lsma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,22 +20,22 @@ public class MamaIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MamaIndicator_MinHistoryDepths_Equals6()
|
||||
public void MamaIndicator_MinHistoryDepths_Equals50()
|
||||
{
|
||||
var indicator = new MamaIndicator();
|
||||
|
||||
Assert.Equal(6, MamaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(6, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, MamaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MamaIndicator_ShortName_IncludesLimitsAndSource()
|
||||
{
|
||||
var indicator = new MamaIndicator { FastLimit = 0.5, SlowLimit = 0.05 };
|
||||
indicator.Initialize();
|
||||
|
||||
Assert.Contains("MAMA", indicator.ShortName);
|
||||
Assert.Contains("0.50", indicator.ShortName);
|
||||
Assert.Contains("0.05", indicator.ShortName);
|
||||
Assert.Contains("Close", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MamaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class MamaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Fast Limit", sortIndex: 1, 0.01, 0.99, 0.01, 2)]
|
||||
public double FastLimit { get; set; } = 0.5;
|
||||
@@ -17,62 +20,44 @@ public class MamaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Mama? _ma;
|
||||
protected LineSeries? MamaSeries;
|
||||
protected LineSeries? FamaSeries;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Mama? _mama;
|
||||
private readonly LineSeries? _series;
|
||||
private readonly LineSeries? _famaSeries;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public static int MinHistoryDepths => 6;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"MAMA({FastLimit:F2}, {SlowLimit:F2}):{SourceName}";
|
||||
public override string ShortName => $"MAMA:{_sourceName}";
|
||||
|
||||
public MamaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "MAMA - MESA Adaptive Moving Average";
|
||||
Description = "MESA Adaptive Moving Average";
|
||||
|
||||
MamaSeries = new(name: "MAMA", color: Color.Red, width: 2, style: LineStyle.Solid);
|
||||
FamaSeries = new(name: "FAMA", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
|
||||
AddLineSeries(MamaSeries);
|
||||
AddLineSeries(FamaSeries);
|
||||
_series = new(name: "MAMA", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
_famaSeries = new(name: "FAMA", color: Color.Red, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
AddLineSeries(_famaSeries);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_ma = new Mama(FastLimit, SlowLimit);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_mama = new Mama(FastLimit, SlowLimit);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
|
||||
TValue result = _ma!.Update(input, isNew);
|
||||
|
||||
MamaSeries!.SetValue(result.Value);
|
||||
FamaSeries!.SetValue(_ma.Fama.Value);
|
||||
|
||||
MamaSeries!.SetMarker(0, Color.Transparent);
|
||||
FamaSeries!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && _ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, MamaSeries!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
this.PaintSmoothCurve(args, FamaSeries!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _mama!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _mama.IsHot, ShowColdValues);
|
||||
_famaSeries!.SetValue(_mama.Fama.Value, _mama.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MgdiIndicatorTests
|
||||
var indicator = new MgdiIndicator();
|
||||
Assert.Equal("MGDI - McGinley Dynamic Indicator", indicator.Name);
|
||||
Assert.Equal("MGDI(14,0.6):Close", indicator.ShortName);
|
||||
Assert.Equal(14, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, MgdiIndicator.MinHistoryDepths);
|
||||
Assert.Single(indicator.LinesSeries);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MgdiIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class MgdiIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
|
||||
[InputParameter("K Factor", sortIndex: 2, 0.1, 10, 0.1, 1)]
|
||||
[InputParameter("K Factor", sortIndex: 2, 0.1, 10.0, 0.1, 1)]
|
||||
public double K { get; set; } = 0.6;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
@@ -19,51 +21,40 @@ public class MgdiIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Mgdi? _mgdi;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"MGDI({Period},{K}):{SourceName}";
|
||||
public override string ShortName => $"MGDI({Period},{K}):{_sourceName}";
|
||||
|
||||
public MgdiIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "MGDI - McGinley Dynamic Indicator";
|
||||
Description = "McGinley Dynamic Indicator";
|
||||
Series = new(name: "MGDI", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"MGDI {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_mgdi = new Mgdi(Period, K);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
|
||||
TValue result = _mgdi!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && _mgdi.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _mgdi!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _mgdi.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ public class PwmaIndicatorTests
|
||||
{
|
||||
var indicator = new PwmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, PwmaIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -113,17 +113,6 @@ public class PwmaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PwmaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new PwmaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(PwmaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PwmaIndicator_MultipleUpdates_ProducesCorrectPwmaSequence()
|
||||
{
|
||||
@@ -174,6 +163,6 @@ public class PwmaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, PwmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class PwmaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class PwmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -14,50 +17,42 @@ public class PwmaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Pwma? _ma;
|
||||
private int _warmupBarIndex = -1;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private Pwma? _pwma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"PWMA {Period}:{SourceName}";
|
||||
public override string ShortName => $"PWMA {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/pwma/Pwma.Quantower.cs";
|
||||
|
||||
public PwmaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "PWMA - Parabolic Weighted Moving Average";
|
||||
Description = "Weighted Moving Average with parabolic weighting";
|
||||
Series = new(name: $"PWMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
Description = "Parabolic Weighted Moving Average";
|
||||
_series = new(name: $"PWMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_ma = new Pwma(Period);
|
||||
_warmupBarIndex = -1;
|
||||
SourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_pwma = new Pwma(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = _ma!.Update(input, isNew);
|
||||
if (_warmupBarIndex < 0 && _ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _pwma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _pwma.IsHot, ShowColdValues);
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, _warmupBarIndex, showColdValues: ShowColdValues, tension: 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class RmaIndicatorTests
|
||||
{
|
||||
var indicator = new RmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, RmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -164,6 +164,6 @@ public class RmaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, RmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class RmaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class RmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
@@ -14,52 +17,40 @@ public class RmaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Rma? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Rma? _rma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"RMA {Period}:{SourceName}";
|
||||
public override string ShortName => $"RMA {Period}:{_sourceName}";
|
||||
|
||||
public RmaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "RMA - Running Moving Average";
|
||||
Description = "Running Moving Average (Wilder's Smoothing)";
|
||||
Series = new(name: $"RMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"RMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Rma(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1; // Reset warmup tracking when period changes
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_rma = new Rma(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
|
||||
// Track when IsHot becomes true for the first time
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _rma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _rma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ public class SmaIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SmaIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
public void SmaIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new SmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, SmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -36,14 +36,6 @@ public class SmaIndicatorTests
|
||||
Assert.Contains("15", indicator.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SmaIndicator_SourceCodeLink_IsValid()
|
||||
{
|
||||
var indicator = new SmaIndicator();
|
||||
|
||||
Assert.Contains("github.com", indicator.SourceCodeLink);
|
||||
Assert.Contains("Sma.Quantower.cs", indicator.SourceCodeLink);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SmaIndicator_Initialize_CreatesInternalSma()
|
||||
@@ -111,16 +103,6 @@ public class SmaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SmaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new SmaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(SmaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SmaIndicator_MultipleUpdates_ProducesCorrectSmaSequence()
|
||||
@@ -177,6 +159,6 @@ public class SmaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, SmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class SmaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class SmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
@@ -14,57 +17,40 @@ public class SmaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Sma? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Sma? _sma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"SMA {Period}:{SourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/sma/Sma.Quantower.cs";
|
||||
public override string ShortName => $"SMA {Period}:{_sourceName}";
|
||||
|
||||
public SmaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "SMA - Simple Moving Average";
|
||||
Description = "Simple Moving Average";
|
||||
Series = new(name: $"SMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"SMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Sma(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1; // Reset warmup tracking when period changes
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_sma = new Sma(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
|
||||
// Track when IsHot becomes true for the first time
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
var savedColor = Series!.Color;
|
||||
Series.Color = Color.Transparent;
|
||||
base.OnPaintChart(args);
|
||||
Series.Color = savedColor;
|
||||
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintLine(args, Series!, warmupPeriod, showColdValues: ShowColdValues);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _sma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _sma.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class SsfIndicatorTests
|
||||
{
|
||||
var indicator = new SsfIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, SsfIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -164,6 +164,6 @@ public class SsfIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, SsfIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class SsfIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class SsfIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
|
||||
[IndicatorExtensions.DataSourceInput]
|
||||
@@ -14,51 +17,40 @@ public class SsfIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Ssf? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Ssf? _ssf;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"SSF {Period}:{SourceName}";
|
||||
public override string ShortName => $"SSF {Period}:{_sourceName}";
|
||||
|
||||
public SsfIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
Name = "SSF - Super Smooth Filter";
|
||||
Description = "Ehlers Super Smooth Filter";
|
||||
Series = new(name: $"SSF {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"SSF {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Ssf(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
_sourceName = Source.ToString();
|
||||
_ssf = new Ssf(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
bool isNew = args.IsNewBar();
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
double value = _ssf!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), isNew).Value;
|
||||
_series!.SetValue(value, _ssf.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ public class SuperIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuperIndicator_MinHistoryDepths_EqualsPeriod()
|
||||
public void SuperIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new SuperIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, SuperIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -57,8 +57,8 @@ public class SuperIndicatorTests
|
||||
// Initialize should not throw
|
||||
indicator.Initialize();
|
||||
|
||||
// After init, line series should exist (Up and Down)
|
||||
Assert.Equal(2, indicator.LinesSeries.Count);
|
||||
// After init, line series should exist (SuperTrend, Upper, Lower)
|
||||
Assert.Equal(3, indicator.LinesSeries.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -119,6 +119,6 @@ public class SuperIndicatorTests
|
||||
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(4.0, indicator.Multiplier);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, SuperIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,45 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class SuperIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class SuperIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
|
||||
[InputParameter("Multiplier", sortIndex: 2, 0.1, 100, 0.1, 1)]
|
||||
[InputParameter("Multiplier", sortIndex: 2, 0.1, 100.0, 0.1, 1)]
|
||||
public double Multiplier { get; set; } = 3.0;
|
||||
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Super? _super;
|
||||
protected LineSeries? UpSeries;
|
||||
protected LineSeries? DownSeries;
|
||||
private readonly LineSeries? _series;
|
||||
private readonly LineSeries? _upperBand;
|
||||
private readonly LineSeries? _lowerBand;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"Super {Period}:{Multiplier}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/super/Super.Quantower.cs";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/master/lib/trends/super/Super.Quantower.cs";
|
||||
|
||||
public SuperIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
Name = "SuperTrend";
|
||||
Description = "Trend-following indicator using ATR";
|
||||
|
||||
UpSeries = new(name: "SuperTrend Up", color: Color.Green, width: 2, style: LineStyle.Solid);
|
||||
DownSeries = new(name: "SuperTrend Down", color: Color.Red, width: 2, style: LineStyle.Solid);
|
||||
|
||||
AddLineSeries(UpSeries);
|
||||
AddLineSeries(DownSeries);
|
||||
Description = "SuperTrend Indicator";
|
||||
_series = new(name: "SuperTrend", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
_upperBand = new(name: "Upper Band", color: Color.Red, width: 1, style: LineStyle.Dot);
|
||||
_lowerBand = new(name: "Lower Band", color: Color.Green, width: 1, style: LineStyle.Dot);
|
||||
AddLineSeries(_series);
|
||||
AddLineSeries(_upperBand);
|
||||
AddLineSeries(_lowerBand);
|
||||
}
|
||||
|
||||
protected override void OnInit()
|
||||
@@ -44,28 +48,21 @@ public class SuperIndicator : Indicator, IWatchlistIndicator
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
bool isNew = args.IsNewBar();
|
||||
var bar = this.GetInputBar(args);
|
||||
double value = _super!.Update(bar, isNew).Value;
|
||||
|
||||
_series!.SetValue(value, _super.IsHot, ShowColdValues);
|
||||
_upperBand!.SetValue(_super.UpperBand.Value, _super.IsHot, ShowColdValues);
|
||||
_lowerBand!.SetValue(_super.LowerBand.Value, _super.IsHot, ShowColdValues);
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
|
||||
TValue result = _super!.Update(bar, isNew);
|
||||
|
||||
if (!_super.IsHot && !ShowColdValues)
|
||||
// Color logic
|
||||
if (_super.IsHot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_super.IsBullish)
|
||||
{
|
||||
UpSeries!.SetValue(result.Value);
|
||||
DownSeries!.SetValue(double.NaN);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpSeries!.SetValue(double.NaN);
|
||||
DownSeries!.SetValue(result.Value);
|
||||
_series!.SetMarker(0, _super.IsBullish ? Color.Green : Color.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ public class T3IndicatorTests
|
||||
var indicator = new T3Indicator { Period = 10 };
|
||||
|
||||
// MinHistoryDepths is Period * 6 for T3 due to 6 stages
|
||||
Assert.Equal(60, indicator.MinHistoryDepths);
|
||||
Assert.Equal(60, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, T3Indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -168,6 +168,6 @@ public class T3IndicatorTests
|
||||
indicator.VolumeFactor = 0.9;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(0.9, indicator.VolumeFactor);
|
||||
Assert.Equal(120, indicator.MinHistoryDepths); // 20 * 6
|
||||
Assert.Equal(0, T3Indicator.MinHistoryDepths); // 20 * 6
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class T3Indicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class T3Indicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
@@ -17,51 +19,44 @@ public class T3Indicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private T3? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private T3? _ma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period * 6; // Approx warmup for 6 stages
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"T3({Period}, {VolumeFactor:F2}):{SourceName}";
|
||||
public override string ShortName => $"T3({Period}, {VolumeFactor:F2}):{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/t3/T3.Quantower.cs";
|
||||
|
||||
public T3Indicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "T3 - Tillson T3 Moving Average";
|
||||
Description = "Tillson T3 Moving Average";
|
||||
Series = new(name: $"T3 {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"T3 {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new T3(Period, VolumeFactor);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_ma = new T3(Period, VolumeFactor);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), args.IsNewBar());
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class TemaIndicatorTests
|
||||
{
|
||||
var indicator = new TemaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, TemaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -65,14 +65,17 @@ public class TemaIndicatorTests
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
}
|
||||
|
||||
// Line series should have a value
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(indicator.LinesSeries[0].Count > 0);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
@@ -99,9 +102,12 @@ public class TemaIndicatorTests
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
}
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
double firstValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewTick));
|
||||
@@ -111,17 +117,6 @@ public class TemaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TemaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new TemaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(TemaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TemaIndicator_MultipleUpdates_ProducesCorrectTemaSequence()
|
||||
{
|
||||
@@ -172,6 +167,6 @@ public class TemaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, TemaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class TemaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class TemaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
@@ -14,52 +16,47 @@ public class TemaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Tema? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Tema? _ma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"TEMA {Period}:{SourceName}";
|
||||
public override string ShortName => $"TEMA {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/tema/Tema.Quantower.cs";
|
||||
|
||||
public TemaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "TEMA - Triple Exponential Moving Average";
|
||||
Description = "Triple Exponential Moving Average";
|
||||
Series = new(name: $"TEMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"TEMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Tema(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_ma = new Tema(Period);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar && args.Reason != UpdateReason.NewTick)
|
||||
return;
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class TrimaIndicatorTests
|
||||
{
|
||||
var indicator = new TrimaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, TrimaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -65,14 +65,17 @@ public class TrimaIndicatorTests
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
}
|
||||
|
||||
// Line series should have a value
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(indicator.LinesSeries[0].Count > 0);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
@@ -99,9 +102,12 @@ public class TrimaIndicatorTests
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
}
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
double firstValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewTick));
|
||||
@@ -111,17 +117,6 @@ public class TrimaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrimaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new TrimaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(TrimaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TrimaIndicator_MultipleUpdates_ProducesCorrectTrimaSequence()
|
||||
{
|
||||
@@ -176,7 +171,7 @@ public class TrimaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, TrimaIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class TrimaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class TrimaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
@@ -14,52 +16,47 @@ public class TrimaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Trima? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Trima? _ma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"TRIMA {Period}:{SourceName}";
|
||||
public override string ShortName => $"TRIMA {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/trima/Trima.Quantower.cs";
|
||||
|
||||
public TrimaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "TRIMA - Triangular Moving Average";
|
||||
Description = "Triangular Moving Average";
|
||||
Series = new(name: $"TRIMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"TRIMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Trima(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_ma = new Trima(Period);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar && args.Reason != UpdateReason.NewTick)
|
||||
return;
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class UsfIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class UsfIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
|
||||
public int Period { get; set; } = 20;
|
||||
@@ -14,52 +16,47 @@ public class UsfIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Usf? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private int _warmupBarIndex = -1;
|
||||
private Usf? _ma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"USF {Period}:{SourceName}";
|
||||
public override string ShortName => $"USF {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/usf/Usf.Quantower.cs";
|
||||
|
||||
public UsfIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "USF - Ultimate Smoother Filter";
|
||||
Description = "Ehlers Ultimate Smoother Filter";
|
||||
Series = new(name: $"USF {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"USF {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Usf(Period);
|
||||
SourceName = Source.ToString();
|
||||
_warmupBarIndex = -1;
|
||||
_ma = new Usf(Period);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent);
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar)
|
||||
return;
|
||||
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
}
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), args.IsNewBar());
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
int warmupPeriod = _warmupBarIndex > 0 ? _warmupBarIndex : Count;
|
||||
this.PaintSmoothCurve(args, Series!, warmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class VidyaIndicatorTests
|
||||
{
|
||||
var indicator = new VidyaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, VidyaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -56,14 +56,17 @@ public class VidyaIndicatorTests
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
}
|
||||
|
||||
// Line series should have a value
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(indicator.LinesSeries[0].Count > 0);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
@@ -94,10 +97,12 @@ public class VidyaIndicatorTests
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
}
|
||||
|
||||
// Process historical bar first
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
double firstValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
// Update with new tick (same bar data - simulates intrabar update)
|
||||
@@ -164,6 +169,6 @@ public class VidyaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, VidyaIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class VidyaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class VidyaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
@@ -14,45 +16,47 @@ public class VidyaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Vidya? ma;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private Vidya? _ma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"VIDYA {Period}:{SourceName}";
|
||||
public override string ShortName => $"VIDYA {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/vidya/Vidya.Quantower.cs";
|
||||
|
||||
public VidyaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "VIDYA - Variable Index Dynamic Average";
|
||||
Description = "Variable Index Dynamic Average (Chande)";
|
||||
Series = new(name: $"VIDYA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"VIDYA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Vidya(Period);
|
||||
SourceName = Source.ToString();
|
||||
_ma = new Vidya(Period);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
}
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar && args.Reason != UpdateReason.NewTick)
|
||||
return;
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, Period, showColdValues: ShowColdValues, tension: 0.2);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), args.IsNewBar());
|
||||
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ public class WmaIndicatorTests
|
||||
{
|
||||
var indicator = new WmaIndicator { Period = 20 };
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(20, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
Assert.Equal(0, WmaIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -65,14 +65,17 @@ public class WmaIndicatorTests
|
||||
|
||||
// Add historical data
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
|
||||
// Process update
|
||||
var args = new UpdateArgs(UpdateReason.HistoricalBar);
|
||||
indicator.ProcessUpdate(args);
|
||||
}
|
||||
|
||||
// Line series should have a value
|
||||
Assert.Equal(1, indicator.LinesSeries[0].Count);
|
||||
Assert.True(indicator.LinesSeries[0].Count > 0);
|
||||
Assert.True(double.IsFinite(indicator.LinesSeries[0].GetValue(0)));
|
||||
}
|
||||
|
||||
@@ -99,9 +102,12 @@ public class WmaIndicatorTests
|
||||
indicator.Initialize();
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
indicator.HistoricalData.AddBar(now, 100, 105, 95, 102);
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
indicator.HistoricalData.AddBar(now.AddMinutes(i), 100, 105, 95, 102);
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
}
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
|
||||
double firstValue = indicator.LinesSeries[0].GetValue(0);
|
||||
|
||||
indicator.ProcessUpdate(new UpdateArgs(UpdateReason.NewTick));
|
||||
@@ -111,17 +117,6 @@ public class WmaIndicatorTests
|
||||
Assert.True(double.IsFinite(secondValue));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WmaIndicator_OnPaintChart_DoesNotThrow()
|
||||
{
|
||||
var indicator = new WmaIndicator();
|
||||
indicator.Initialize();
|
||||
|
||||
var method = indicator.GetType().GetMethod("OnPaintChart");
|
||||
Assert.NotNull(method);
|
||||
Assert.Equal(typeof(WmaIndicator), method.DeclaringType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WmaIndicator_MultipleUpdates_ProducesCorrectWmaSequence()
|
||||
{
|
||||
@@ -177,7 +172,7 @@ public class WmaIndicatorTests
|
||||
|
||||
indicator.Period = 20;
|
||||
Assert.Equal(20, indicator.Period);
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, WmaIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class WmaIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class WmaIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 10;
|
||||
@@ -14,50 +16,47 @@ public class WmaIndicator : Indicator, IWatchlistIndicator
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Wma? ma;
|
||||
private int _warmupBarIndex = -1;
|
||||
protected LineSeries? Series;
|
||||
protected string? SourceName;
|
||||
private Wma? _ma;
|
||||
private readonly LineSeries? _series;
|
||||
private string? _sourceName;
|
||||
private Func<IHistoryItem, double>? _priceSelector;
|
||||
|
||||
public int MinHistoryDepths => Period;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"WMA {Period}:{SourceName}";
|
||||
public override string ShortName => $"WMA {Period}:{_sourceName}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/trends/wma/Wma.Quantower.cs";
|
||||
|
||||
public WmaIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = false;
|
||||
SourceName = Source.ToString();
|
||||
_sourceName = Source.ToString();
|
||||
Name = "WMA - Weighted Moving Average";
|
||||
Description = "Weighted Moving Average with linear weighting";
|
||||
Series = new(name: $"WMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: $"WMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
ma = new Wma(Period);
|
||||
_warmupBarIndex = -1;
|
||||
SourceName = Source.ToString();
|
||||
_ma = new Wma(Period);
|
||||
_sourceName = Source.ToString();
|
||||
_priceSelector = Source.GetPriceSelector();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TValue input = this.GetInputValue(args, Source);
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
TValue result = ma!.Update(input, isNew);
|
||||
if (_warmupBarIndex < 0 && ma!.IsHot)
|
||||
_warmupBarIndex = Count;
|
||||
Series!.SetValue(result.Value);
|
||||
Series!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
|
||||
}
|
||||
if (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar && args.Reason != UpdateReason.NewTick)
|
||||
return;
|
||||
|
||||
public override void OnPaintChart(PaintChartEventArgs args)
|
||||
{
|
||||
base.OnPaintChart(args);
|
||||
this.PaintSmoothCurve(args, Series!, _warmupBarIndex, showColdValues: ShowColdValues, tension: 0.2);
|
||||
var item = HistoricalData[Count - 1, SeekOriginHistory.Begin];
|
||||
TValue result = _ma!.Update(new TValue(item.TimeLeft.Ticks, _priceSelector!(item)), args.IsNewBar());
|
||||
|
||||
_series!.SetValue(result.Value, _ma.IsHot, ShowColdValues);
|
||||
_series!.SetMarker(0, Color.Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public sealed class AtrIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int Period { get; set; } = 14;
|
||||
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Atr? _atr;
|
||||
private readonly LineSeries? _series;
|
||||
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"ATR {Period}";
|
||||
public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/volatility/atr/Atr.Quantower.cs";
|
||||
|
||||
public AtrIndicator()
|
||||
{
|
||||
OnBackGround = true;
|
||||
SeparateWindow = true;
|
||||
Name = "ATR - Average True Range";
|
||||
Description = "Measures the volatility of an asset";
|
||||
|
||||
_series = new(name: "ATR", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_atr = new Atr(Period);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
TBar bar = this.GetInputBar(args);
|
||||
TValue result = _atr!.Update(bar, args.IsNewBar());
|
||||
|
||||
_series!.SetValue(result.Value, _atr.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,12 @@ public class AdlIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdlIndicator_SourceCodeLink_IsValid()
|
||||
public void AdlIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new AdlIndicator();
|
||||
|
||||
Assert.Contains("github.com", indicator.SourceCodeLink);
|
||||
Assert.Contains("Adl.Quantower.cs", indicator.SourceCodeLink);
|
||||
Assert.Equal(0, AdlIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AdlIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class AdlIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Show cold values", sortIndex: 21)]
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Adl? _adl;
|
||||
protected LineSeries? AdlSeries;
|
||||
private readonly LineSeries? _series;
|
||||
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
@@ -21,23 +26,23 @@ public class AdlIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "ADL - Accumulation/Distribution Line";
|
||||
Description = "Accumulation/Distribution Line";
|
||||
|
||||
AdlSeries = new(name: "ADL", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(AdlSeries);
|
||||
_series = new(name: "ADL", color: Color.Blue, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_adl = new Adl();
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
TValue result = _adl!.Update(bar, isNew);
|
||||
TValue result = _adl!.Update(bar, args.IsNewBar());
|
||||
|
||||
AdlSeries!.SetValue(result.Value);
|
||||
_series!.SetValue(result.Value, _adl.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,24 +20,28 @@ public class AdoscIndicatorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdoscIndicator_MinHistoryDepths_EqualsSlowPeriod()
|
||||
public void AdoscIndicator_MinHistoryDepths_EqualsZero()
|
||||
{
|
||||
var indicator = new AdoscIndicator { SlowPeriod = 20 };
|
||||
var indicator = new AdoscIndicator
|
||||
{
|
||||
SlowPeriod = 20
|
||||
};
|
||||
|
||||
Assert.Equal(20, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AdoscIndicator.MinHistoryDepths);
|
||||
IWatchlistIndicator watchlistIndicator = indicator;
|
||||
Assert.Equal(20, watchlistIndicator.MinHistoryDepths);
|
||||
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdoscIndicator_ShortName_IncludesParameters()
|
||||
public void AdoscIndicator_SlowPeriod_CanBeChanged()
|
||||
{
|
||||
var indicator = new AdoscIndicator { FastPeriod = 10, SlowPeriod = 40 };
|
||||
indicator.Initialize();
|
||||
var indicator = new AdoscIndicator
|
||||
{
|
||||
SlowPeriod = 40
|
||||
};
|
||||
|
||||
Assert.Contains("ADOSC", indicator.ShortName);
|
||||
Assert.Contains("10", indicator.ShortName);
|
||||
Assert.Contains("40", indicator.ShortName);
|
||||
Assert.Equal(40, indicator.SlowPeriod);
|
||||
Assert.Equal(0, AdoscIndicator.MinHistoryDepths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -117,6 +121,6 @@ public class AdoscIndicatorTests
|
||||
|
||||
Assert.Equal(10, indicator.FastPeriod);
|
||||
Assert.Equal(40, indicator.SlowPeriod);
|
||||
Assert.Equal(40, indicator.MinHistoryDepths);
|
||||
Assert.Equal(0, AdoscIndicator.MinHistoryDepths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class AdoscIndicator : Indicator, IWatchlistIndicator
|
||||
[SkipLocalsInit]
|
||||
public sealed class AdoscIndicator : Indicator, IWatchlistIndicator
|
||||
{
|
||||
[InputParameter("Fast Period", sortIndex: 1, 1, 1000, 1, 0)]
|
||||
public int FastPeriod { get; set; } = 3;
|
||||
@@ -15,9 +17,9 @@ public class AdoscIndicator : Indicator, IWatchlistIndicator
|
||||
public bool ShowColdValues { get; set; } = true;
|
||||
|
||||
private Adosc? _adosc;
|
||||
protected LineSeries? Series;
|
||||
private readonly LineSeries? _series;
|
||||
|
||||
public int MinHistoryDepths => SlowPeriod;
|
||||
public static int MinHistoryDepths => 0;
|
||||
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
|
||||
|
||||
public override string ShortName => $"ADOSC {FastPeriod}:{SlowPeriod}";
|
||||
@@ -30,28 +32,23 @@ public class AdoscIndicator : Indicator, IWatchlistIndicator
|
||||
Name = "ADOSC - Accumulation/Distribution Oscillator";
|
||||
Description = "Momentum indicator for the Accumulation/Distribution Line";
|
||||
|
||||
Series = new(name: "ADOSC", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(Series);
|
||||
_series = new(name: "ADOSC", color: Color.Orange, width: 2, style: LineStyle.Solid);
|
||||
AddLineSeries(_series);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnInit()
|
||||
{
|
||||
_adosc = new Adosc(FastPeriod, SlowPeriod);
|
||||
base.OnInit();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void OnUpdate(UpdateArgs args)
|
||||
{
|
||||
bool isNew = args.Reason == UpdateReason.NewBar || args.Reason == UpdateReason.HistoricalBar;
|
||||
|
||||
TBar bar = this.GetInputBar(args);
|
||||
TValue result = _adosc!.Update(bar, isNew);
|
||||
TValue result = _adosc!.Update(bar, args.IsNewBar());
|
||||
|
||||
if (!_adosc.IsHot && !ShowColdValues)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Series!.SetValue(result.Value);
|
||||
_series!.SetValue(result.Value, _adosc.IsHot, ShowColdValues);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user