From 839313c9f23cfc9ebfcf8013ff07b175e99a93a2 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Thu, 10 Oct 2024 16:23:23 -0700 Subject: [PATCH] Pwma --- Tests/test_eventing.cs | 3 +- Tests/test_iTValue.cs | 1 + docs/indicators/indicators.md | 2 +- lib/averages/Pwma.cs | 85 ++++++++++++++++++++++++++++ quantower/Averages/AlmaIndicator.cs | 6 +- quantower/Averages/DemaIndicator.cs | 2 +- quantower/Averages/DsmaIndicator.cs | 2 +- quantower/Averages/DwmaIndicator.cs | 2 +- quantower/Averages/EmaIndicator.cs | 2 +- quantower/Averages/EpmaIndicator.cs | 2 +- quantower/Averages/FramaIndicator.cs | 2 +- quantower/Averages/FwmaIndicator.cs | 2 +- quantower/Averages/GmaIndicator.cs | 2 +- quantower/Averages/PwmaIndicator.cs | 23 ++++++++ 14 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 lib/averages/Pwma.cs create mode 100644 quantower/Averages/PwmaIndicator.cs diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs index f51e50b0..395cd4b8 100644 --- a/Tests/test_eventing.cs +++ b/Tests/test_eventing.cs @@ -28,6 +28,7 @@ public class EventingTests ("Dwma", new Dwma(p), new Dwma(input, p)), ("Ema", new Ema(p), new Ema(input, p)), ("Epma", new Epma(p), new Epma(input, p)), + ("Pwma", new Pwma(p), new Pwma(input, p)), ("Frama", new Frama(p), new Frama(input, p)), ("Fwma", new Fwma(p), new Fwma(input, p)), ("Gma", new Gma(p), new Gma(input, p)), @@ -81,4 +82,4 @@ public class EventingTests rng.GetBytes(bytes); return (double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue; } -} \ No newline at end of file +} diff --git a/Tests/test_iTValue.cs b/Tests/test_iTValue.cs index 887c78cf..2886c5bc 100644 --- a/Tests/test_iTValue.cs +++ b/Tests/test_iTValue.cs @@ -35,6 +35,7 @@ public class IndicatorTests new Dsma(period: 14), new Dwma(period: 14), new Epma(period: 14), + new Pwma(period: 14), new Frama(period: 14), new Fwma(period: 14), new Gma(period: 14), diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md index d11d04e8..16cffcb4 100644 --- a/docs/indicators/indicators.md +++ b/docs/indicators/indicators.md @@ -72,7 +72,7 @@ |MGDI - McGinley Dynamic Indicator|`Mgdi`|`✔️`||| |MMA - Modified Moving Average|`Mma`|||| |PPMA - Pivot Point Moving Average||||| -|PWMA - Pascal's Weighted Moving Average||||| +|PWMA - Pascal's Weighted Moving Average|`Pwma`|||| |QEMA - Quad Exponential Moving Average|`Qema`|||| |RMA - WildeR's Moving Average|`Rma`|||| |SINEMA - Sine Weighted Moving Average|`Sinema`|||| diff --git a/lib/averages/Pwma.cs b/lib/averages/Pwma.cs new file mode 100644 index 00000000..fc2aac76 --- /dev/null +++ b/lib/averages/Pwma.cs @@ -0,0 +1,85 @@ +namespace QuanTAlib; + +public class Pwma : AbstractBase +{ + private readonly int _period; + private readonly Convolution _convolution; + + public Pwma(int period) + { + if (period < 1) + { + throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); + } + _period = period; + _convolution = new Convolution(GenerateKernel(_period)); + Name = "Pwma"; + WarmupPeriod = period; + Init(); + } + + public Pwma(object source, int period) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + private new void Init() + { + base.Init(); + _convolution.Init(); + } + + protected override void ManageState(bool isNew) + { + if (isNew) + { + _lastValidValue = Input.Value; + _index++; + } + } + + protected override double Calculation() + { + ManageState(Input.IsNew); + + // Use Convolution for calculation + TValue convolutionResult = _convolution.Calc(Input); + + double result = convolutionResult.Value; + + // Adjust for partial periods during warmup + if (_index < _period) + { + double[] partialKernel = GenerateKernel(_index); + result /= partialKernel.Sum(); + } + + IsHot = _index >= WarmupPeriod; + + return result; + } + + public static double[] GenerateKernel(int period) + { + double[] kernel = new double[period]; + kernel[0] = 1; + + for (int i = 1; i < period; i++) + { + for (int j = i; j > 0; j--) + { + kernel[j] += kernel[j - 1]; + } + } + + // Normalize the kernel + double weightSum = kernel.Sum(); + for (int i = 0; i < period; i++) + { + kernel[i] /= weightSum; + } + + return kernel; + } +} diff --git a/quantower/Averages/AlmaIndicator.cs b/quantower/Averages/AlmaIndicator.cs index 251ad0f1..f1daddab 100644 --- a/quantower/Averages/AlmaIndicator.cs +++ b/quantower/Averages/AlmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class AlmaIndicator : IndicatorBase { @@ -7,10 +7,10 @@ public class AlmaIndicator : IndicatorBase public int Period { get; set; } = 10; [InputParameter("Offset", sortIndex: 5)] - public double Offset = 0.85; + public double Offset { get; set; } = 0.85; [InputParameter("Sigma", sortIndex: 6)] - public double Sigma = 6.0; + public double Sigma { get; set; } = 6.0; private Alma? ma; protected override AbstractBase QuanTAlib => ma!; public override string ShortName => $"ALMA {Period} : {Offset:F2} : {Sigma:F0} : {SourceName}"; diff --git a/quantower/Averages/DemaIndicator.cs b/quantower/Averages/DemaIndicator.cs index 185cf6a5..f943f289 100644 --- a/quantower/Averages/DemaIndicator.cs +++ b/quantower/Averages/DemaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class DemaIndicator : IndicatorBase { diff --git a/quantower/Averages/DsmaIndicator.cs b/quantower/Averages/DsmaIndicator.cs index 9bf1dc6d..e4259399 100644 --- a/quantower/Averages/DsmaIndicator.cs +++ b/quantower/Averages/DsmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class DsmaIndicator : IndicatorBase { diff --git a/quantower/Averages/DwmaIndicator.cs b/quantower/Averages/DwmaIndicator.cs index 5b80c398..2126c023 100644 --- a/quantower/Averages/DwmaIndicator.cs +++ b/quantower/Averages/DwmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class DwmaIndicator : IndicatorBase { diff --git a/quantower/Averages/EmaIndicator.cs b/quantower/Averages/EmaIndicator.cs index 7adecd30..ab07c5b0 100644 --- a/quantower/Averages/EmaIndicator.cs +++ b/quantower/Averages/EmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class EmaIndicator : IndicatorBase { diff --git a/quantower/Averages/EpmaIndicator.cs b/quantower/Averages/EpmaIndicator.cs index 1a92d33c..239fba4f 100644 --- a/quantower/Averages/EpmaIndicator.cs +++ b/quantower/Averages/EpmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class EpmaIndicator : IndicatorBase { diff --git a/quantower/Averages/FramaIndicator.cs b/quantower/Averages/FramaIndicator.cs index 865f4a53..0e5e8d1b 100644 --- a/quantower/Averages/FramaIndicator.cs +++ b/quantower/Averages/FramaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class FramaIndicator : IndicatorBase { diff --git a/quantower/Averages/FwmaIndicator.cs b/quantower/Averages/FwmaIndicator.cs index b4a9a389..5d24849b 100644 --- a/quantower/Averages/FwmaIndicator.cs +++ b/quantower/Averages/FwmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class FwmaIndicator : IndicatorBase { diff --git a/quantower/Averages/GmaIndicator.cs b/quantower/Averages/GmaIndicator.cs index 5d85e499..4d794534 100644 --- a/quantower/Averages/GmaIndicator.cs +++ b/quantower/Averages/GmaIndicator.cs @@ -1,5 +1,5 @@ using TradingPlatform.BusinessLayer; -using QuanTAlib; +namespace QuanTAlib; public class GmaIndicator : IndicatorBase { diff --git a/quantower/Averages/PwmaIndicator.cs b/quantower/Averages/PwmaIndicator.cs new file mode 100644 index 00000000..29d7bdcc --- /dev/null +++ b/quantower/Averages/PwmaIndicator.cs @@ -0,0 +1,23 @@ +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class PwmaIndicator : IndicatorBase +{ + [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)] + public int Period { get; set; } = 10; + + private Pwma? ma; + protected override AbstractBase QuanTAlib => ma!; + public override string ShortName => $"PWMA {Period} : {SourceName}"; + + public PwmaIndicator() : base() + { + Name = "PWMA - Pascal's Weighted Moving Average"; + } + + protected override void InitIndicator() + { + base.InitIndicator(); + ma = new Pwma(period: Period); + } +}