From c5b583cd0df0a363a5b910c18366f2ae27aa2f76 Mon Sep 17 00:00:00 2001
From: Miha Kralj <31756078+mihakralj@users.noreply.github.com>
Date: Tue, 5 Nov 2024 06:42:31 -0800
Subject: [PATCH 1/8] Create EFI - Elder Ray's Force Index - add tests and xml
comments
---
For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/mihakralj/QuanTAlib?shareId=XXXX-XXXX-XXXX-XXXX).
---
Tests/test_updates_oscillators.cs | 16 +++++
docs/indicators/indicators.md | 6 +-
lib/oscillators/Efi.cs | 104 ++++++++++++++++++++++++++++++
lib/oscillators/_list.md | 4 +-
4 files changed, 125 insertions(+), 5 deletions(-)
create mode 100644 lib/oscillators/Efi.cs
diff --git a/Tests/test_updates_oscillators.cs b/Tests/test_updates_oscillators.cs
index c1630815..24964699 100644
--- a/Tests/test_updates_oscillators.cs
+++ b/Tests/test_updates_oscillators.cs
@@ -321,4 +321,20 @@ public class OscillatorsUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
+
+ [Fact]
+ public void Efi_Update()
+ {
+ var indicator = new Efi(period: 13);
+ TBar r = GetRandomBar(true);
+ double initialValue = indicator.Calc(r);
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ indicator.Calc(GetRandomBar(IsNew: false));
+ }
+ double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
+
+ Assert.Equal(initialValue, finalValue, precision);
+ }
}
diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md
index 3916949a..a88d07f9 100644
--- a/docs/indicators/indicators.md
+++ b/docs/indicators/indicators.md
@@ -5,12 +5,12 @@
| Basic Transforms | 6 of 6 | 100% |
| Averages & Trends | 33 of 33 | 100% |
| Momentum | 16 of 16 | 100% |
-| Oscillators | 21 of 29 | 72% |
+| Oscillators | 22 of 29 | 76% |
| Volatility | 24 of 35 | 69% |
| Volume | 15 of 19 | 79% |
| Numerical Analysis | 13 of 19 | 68% |
| Errors | 16 of 16 | 100% |
-| **Total** | **144 of 173** | **83%** |
+| **Total** | **145 of 173** | **84%** |
|Technical Indicator Name| Class Name|
|-----------|:----------:|
@@ -85,9 +85,9 @@
|COPPOCK - Coppock Curve|`Coppock`|
|CRSI - Connor RSI|`Crsi`|
|🚧 CTI - Ehler's Correlation Trend Indicator|`Cti`|
-|🚧 EFI - Elder Ray's Force Index|`Efi`|
|🚧 FISHER - Fisher Transform|`Fisher`|
|🚧 FOSC - Forecast Oscillator|`Fosc`|
+|EFI - Elder Ray's Force Index|`Efi`|
|🚧 GATOR* - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)|`Gator`|
|🚧 KDJ* - KDJ Indicator (K, D, J lines)|`Kdj`|
|🚧 KRI - Kairi Relative Index|`Kri`|
diff --git a/lib/oscillators/Efi.cs b/lib/oscillators/Efi.cs
new file mode 100644
index 00000000..a22fdde1
--- /dev/null
+++ b/lib/oscillators/Efi.cs
@@ -0,0 +1,104 @@
+using System.Runtime.CompilerServices;
+namespace QuanTAlib;
+
+///
+/// EFI: Elder Ray's Force Index
+/// A volume-based oscillator that measures the strength of price movements using volume.
+/// It helps identify potential trend reversals and confirm price movements.
+///
+///
+/// The EFI calculation process:
+/// 1. Calculate the difference between the current close and the previous close
+/// 2. Multiply the difference by the current volume
+/// 3. Apply an exponential moving average (EMA) to smooth the result
+///
+/// Key characteristics:
+/// - Oscillates above and below zero
+/// - Positive values indicate buying pressure
+/// - Negative values indicate selling pressure
+/// - Crosses above zero suggest buying opportunities
+/// - Crosses below zero suggest selling opportunities
+///
+/// Formula:
+/// EFI = EMA((Close - Close[1]) * Volume, period)
+///
+/// Sources:
+/// Alexander Elder - "Trading for a Living" (1993)
+/// https://www.investopedia.com/terms/f/force-index.asp
+///
+/// Note: Default period is 13
+///
+[SkipLocalsInit]
+public sealed class Efi : AbstractBase
+{
+ private readonly Ema _ema;
+ private double _prevClose;
+ private double _p_prevClose;
+ private const int DefaultPeriod = 13;
+
+ /// The smoothing period for EMA calculation (default 13).
+ /// Thrown when period is less than 1.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Efi(int period = DefaultPeriod)
+ {
+ if (period < 1)
+ throw new ArgumentOutOfRangeException(nameof(period));
+
+ _ema = new(period);
+ WarmupPeriod = period + 1;
+ Name = $"EFI({period})";
+ }
+
+ /// The data source object that publishes updates.
+ /// The smoothing period for EMA calculation.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Efi(object source, int period = DefaultPeriod) : this(period)
+ {
+ var pubEvent = source.GetType().GetEvent("Pub");
+ pubEvent?.AddEventHandler(source, new BarSignal(Sub));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void Init()
+ {
+ base.Init();
+ _ema.Init();
+ _prevClose = double.NaN;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override void ManageState(bool isNew)
+ {
+ if (isNew)
+ {
+ _index++;
+ _p_prevClose = _prevClose;
+ }
+ else
+ {
+ _prevClose = _p_prevClose;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ protected override double Calculation()
+ {
+ ManageState(BarInput.IsNew);
+
+ if (_index == 1)
+ {
+ _prevClose = BarInput.Close;
+ return 0;
+ }
+
+ // Calculate raw force index
+ double priceChange = BarInput.Close - _prevClose;
+ double forceIndex = priceChange * BarInput.Volume;
+
+ // Update previous close
+ _prevClose = BarInput.Close;
+
+ // Apply EMA smoothing
+ return _ema.Calc(forceIndex, BarInput.IsNew);
+ }
+}
diff --git a/lib/oscillators/_list.md b/lib/oscillators/_list.md
index 72b74c86..e2ab8a34 100644
--- a/lib/oscillators/_list.md
+++ b/lib/oscillators/_list.md
@@ -1,5 +1,5 @@
# Oscillators indicators
-Done: 21, Todo: 8
+Done: 22, Todo: 7
✔️ AC - Acceleration Oscillator
✔️ AO - Awesome Oscillator
@@ -14,7 +14,7 @@ Done: 21, Todo: 8
✔️ CRSI - Connor RSI
CTI - Ehler's Correlation Trend Indicator
✔️ DOSC - Derivative Oscillator
-EFI - Elder Ray's Force Index
+✔️ EFI - Elder Ray's Force Index
FISHER - Fisher Transform
FOSC - Forecast Oscillator
*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)
From e934b424c2e794080922d83fb114afffdbbb28f2 Mon Sep 17 00:00:00 2001
From: Miha Kralj
Date: Tue, 5 Nov 2024 07:42:03 -0800
Subject: [PATCH 2/8] fix exception handling
---
lib/oscillators/Efi.cs | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lib/oscillators/Efi.cs b/lib/oscillators/Efi.cs
index a22fdde1..d2f6d9f5 100644
--- a/lib/oscillators/Efi.cs
+++ b/lib/oscillators/Efi.cs
@@ -41,9 +41,7 @@ public sealed class Efi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Efi(int period = DefaultPeriod)
{
- if (period < 1)
- throw new ArgumentOutOfRangeException(nameof(period));
-
+ ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
_ema = new(period);
WarmupPeriod = period + 1;
Name = $"EFI({period})";
From 0bae9ce15b639d21d76e4b98a1add5eb33c30426 Mon Sep 17 00:00:00 2001
From: Miha Kralj
Date: Tue, 5 Nov 2024 15:51:29 -0800
Subject: [PATCH 3/8] sonar fixes
---
Tests/test_eventing.cs | 2 +
Tests/test_updates_statistics.cs | 33 ++++++
docs/indicators/indicators.md | 30 +++--
lib/errors/Huber.cs | 11 +-
lib/momentum/Macd.cs | 14 +--
lib/momentum/_list.md | 2 +-
lib/oscillators/Coppock.cs | 9 +-
lib/oscillators/Rsi.cs | 3 +-
lib/oscillators/Smi.cs | 9 +-
lib/oscillators/Srsi.cs | 22 +---
lib/oscillators/Stc.cs | 27 +----
lib/oscillators/Stoch.cs | 9 +-
lib/oscillators/Uo.cs | 30 +----
lib/statistics/Beta.cs | 159 ++++++++++++++++++++++++++
lib/statistics/Corr.cs | 163 +++++++++++++++++++++++++++
lib/statistics/Percentile.cs | 15 +--
lib/statistics/Theil.cs | 167 ++++++++++++++++++++++++++++
lib/statistics/Tsf.cs | 185 +++++++++++++++++++++++++++++++
lib/statistics/_list.md | 52 +++++----
19 files changed, 801 insertions(+), 141 deletions(-)
create mode 100644 lib/statistics/Beta.cs
create mode 100644 lib/statistics/Corr.cs
create mode 100644 lib/statistics/Theil.cs
create mode 100644 lib/statistics/Tsf.cs
diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs
index 5406876e..b0689b4d 100644
--- a/Tests/test_eventing.cs
+++ b/Tests/test_eventing.cs
@@ -76,6 +76,8 @@ public class EventingTests
("Stddev", new Stddev(p), new Stddev(input, p)),
("Variance", new Variance(p), new Variance(input, p)),
("Zscore", new Zscore(p), new Zscore(input, p)),
+ ("Beta", new Beta(p), new Beta(input, p)),
+ ("Corr", new Corr(p), new Corr(input, p)),
// Volatility indicators (value-based)
("Hv", new Hv(p), new Hv(input, p)),
("Jvolty", new Jvolty(p), new Jvolty(input, p)),
diff --git a/Tests/test_updates_statistics.cs b/Tests/test_updates_statistics.cs
index e1c902aa..ff6ed756 100644
--- a/Tests/test_updates_statistics.cs
+++ b/Tests/test_updates_statistics.cs
@@ -26,6 +26,39 @@ public class StatisticsUpdateTests
return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
}
+ [Fact]
+ public void Beta_Update()
+ {
+ var indicator = new Beta(period: 14);
+ TBar marketBar = GetRandomBar(true);
+ TBar assetBar = GetRandomBar(true);
+ double initialValue = indicator.Calc(marketBar, assetBar);
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ indicator.Calc(GetRandomBar(false), GetRandomBar(false));
+ }
+ double finalValue = indicator.Calc(new TBar(marketBar.Time, marketBar.Open, marketBar.High, marketBar.Low, marketBar.Close, marketBar.Volume, false),
+ new TBar(assetBar.Time, assetBar.Open, assetBar.High, assetBar.Low, assetBar.Close, assetBar.Volume, false));
+
+ Assert.Equal(initialValue, finalValue, precision);
+ }
+
+ [Fact]
+ public void Corr_Update()
+ {
+ var indicator = new Corr(period: 14);
+ double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true), new TValue(DateTime.Now, ReferenceValue, IsNew: true));
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ indicator.Calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false), new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
+ }
+ double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false), new TValue(DateTime.Now, ReferenceValue, IsNew: false));
+
+ Assert.Equal(initialValue, finalValue, precision);
+ }
+
[Fact]
public void Curvature_Update()
{
diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md
index a88d07f9..4fdc43aa 100644
--- a/docs/indicators/indicators.md
+++ b/docs/indicators/indicators.md
@@ -6,11 +6,12 @@
| Averages & Trends | 33 of 33 | 100% |
| Momentum | 16 of 16 | 100% |
| Oscillators | 22 of 29 | 76% |
-| Volatility | 24 of 35 | 69% |
-| Volume | 15 of 19 | 79% |
-| Numerical Analysis | 13 of 19 | 68% |
+| Volatility | 29 of 35 | 83% |
+| Volume | 19 of 19 | 100% |
+| Numerical Analysis | 15 of 19 | 79% |
| Errors | 16 of 16 | 100% |
-| **Total** | **145 of 173** | **84%** |
+| Patterns | 0 of 8 | 0% |
+| **Total** | **156 of 181** | **86%** |
|Technical Indicator Name| Class Name|
|-----------|:----------:|
@@ -85,9 +86,10 @@
|COPPOCK - Coppock Curve|`Coppock`|
|CRSI - Connor RSI|`Crsi`|
|🚧 CTI - Ehler's Correlation Trend Indicator|`Cti`|
+|DOSC - Derivative Oscillator|`Dosc`|
+|EFI - Elder Ray's Force Index|`Efi`|
|🚧 FISHER - Fisher Transform|`Fisher`|
|🚧 FOSC - Forecast Oscillator|`Fosc`|
-|EFI - Elder Ray's Force Index|`Efi`|
|🚧 GATOR* - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)|`Gator`|
|🚧 KDJ* - KDJ Indicator (K, D, J lines)|`Kdj`|
|🚧 KRI - Kairi Relative Index|`Kri`|
@@ -101,7 +103,15 @@
|TSI - True Strength Index|`Tsi`|
|UO - Ultimate Oscillator|`Uo`|
|WILLR - Larry Williams' %R|`Willr`|
-|DOSC - Derivative Oscillator|`Dosc`|
+|**PATTERNS**||
+|🚧 DOJI - Doji Candlestick Pattern|`Doji`|
+|🚧 ER* - Elder Ray Pattern (Bull Power, Bear Power)|`Er`|
+|🚧 MARU - Marubozu Candlestick Pattern|`Maru`|
+|🚧 PIV* - Pivot Points (Support 1-3, Pivot, Resistance 1-3)|`Piv`|
+|🚧 PP* - Price Pivots (Support 1-3, Pivot, Resistance 1-3)|`Pp`|
+|🚧 RPP* - Rolling Pivot Points (Support 1-3, Pivot, Resistance 1-3)|`Rpp`|
+|🚧 WF - Williams Fractal|`Wf`|
+|🚧 ZZ - Zig Zag Pattern|`Zz`|
|**VOLATILITY INDICATORS**||
|ADR - Average Daily Range|`Adr`|
|AP - Andrew's Pitchfork|`Ap`|
@@ -159,12 +169,12 @@
|VWAP - Volume Weighted Average Price|`Vwap`|
|VWMA - Volume Weighted Moving Average|`Vwma`|
|**NUMERICAL ANALYSIS**||
-|🚧 BETA* - Beta coefficient (Beta, R-squared)|`Beta`|
-|🚧 CORR* - Correlation Coefficient (Correlation, P-value)|`Corr`|
+|BETA* - Beta coefficient (Beta, R-squared)|`Beta`|
+|CORR* - Correlation Coefficient (Correlation, P-value)|`Corr`|
|CURVATURE - Rate of Change in Direction or Slope|`Curvature`|
|ENTROPY - Measure of Uncertainty or Disorder|`Entropy`|
-|🚧 HUBER - Huber Loss|`Huber`|
-|🚧 HURST - Hurst Exponent|`Hurst`|
+|HUBER - Huber Loss|`Huber`|
+|HURST - Hurst Exponent|`Hurst`|
|KURTOSIS - Measure of Tails/Peakedness|`Kurtosis`|
|MAX - Maximum with exponential decay|`Max`|
|MEDIAN - Middle value|`Median`|
diff --git a/lib/errors/Huber.cs b/lib/errors/Huber.cs
index 12c5779b..ffe7a0a1 100644
--- a/lib/errors/Huber.cs
+++ b/lib/errors/Huber.cs
@@ -43,14 +43,9 @@ public sealed class Huber : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Huber(int period, double delta = 1.0)
{
- if (period < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
- }
- if (delta <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(delta), "Delta must be greater than 0.");
- }
+ ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(delta, 0);
+
WarmupPeriod = period;
_actualBuffer = new CircularBuffer(period);
_predictedBuffer = new CircularBuffer(period);
diff --git a/lib/momentum/Macd.cs b/lib/momentum/Macd.cs
index 67c4d589..263c80ec 100644
--- a/lib/momentum/Macd.cs
+++ b/lib/momentum/Macd.cs
@@ -60,14 +60,14 @@ public sealed class Macd : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Macd(int fastPeriod = DefaultFastPeriod, int slowPeriod = DefaultSlowPeriod, int signalPeriod = DefaultSignalPeriod)
{
- if (fastPeriod < 1)
- throw new ArgumentOutOfRangeException(nameof(fastPeriod));
- if (slowPeriod < 1)
- throw new ArgumentOutOfRangeException(nameof(slowPeriod));
- if (signalPeriod < 1)
- throw new ArgumentOutOfRangeException(nameof(signalPeriod));
+ ArgumentOutOfRangeException.ThrowIfLessThan(fastPeriod, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(slowPeriod, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(signalPeriod, 1);
+
if (fastPeriod >= slowPeriod)
- throw new ArgumentException("Fast period must be less than slow period");
+ {
+ throw new ArgumentOutOfRangeException(nameof(fastPeriod), "Fast period must be less than slow period");
+ }
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
diff --git a/lib/momentum/_list.md b/lib/momentum/_list.md
index 1d01f144..85cc2072 100644
--- a/lib/momentum/_list.md
+++ b/lib/momentum/_list.md
@@ -4,7 +4,7 @@ Done: 15, Todo: 2
✔️ ADX - Average Directional Movement Index
✔️ ADXR - Average Directional Movement Index Rating
✔️ APO - Absolute Price Oscillator
-✔️ *DMI - Directional Movement Index (DI+, DI-)
+✔️ DMI - Directional Movement Index (DI+, DI-)
✔️ DMX - Jurik Directional Movement Index
✔️ DPO - Detrended Price Oscillator
✔️ *MACD - Moving Average Convergence/Divergence (MACD, Signal, Histogram)
diff --git a/lib/oscillators/Coppock.cs b/lib/oscillators/Coppock.cs
index 9cd6231c..ed905b4c 100644
--- a/lib/oscillators/Coppock.cs
+++ b/lib/oscillators/Coppock.cs
@@ -51,12 +51,9 @@ public sealed class Coppock : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Coppock(int roc1Period = DefaultRoc1Period, int roc2Period = DefaultRoc2Period, int wmaPeriod = DefaultWmaPeriod)
{
- if (roc1Period < 1)
- throw new ArgumentOutOfRangeException(nameof(roc1Period), "ROC1 period must be greater than 0");
- if (roc2Period < 1)
- throw new ArgumentOutOfRangeException(nameof(roc2Period), "ROC2 period must be greater than 0");
- if (wmaPeriod < 1)
- throw new ArgumentOutOfRangeException(nameof(wmaPeriod), "WMA period must be greater than 0");
+ ArgumentOutOfRangeException.ThrowIfLessThan(roc1Period, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(roc2Period, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(wmaPeriod, 1);
_roc1Period = roc1Period;
_roc2Period = roc2Period;
diff --git a/lib/oscillators/Rsi.cs b/lib/oscillators/Rsi.cs
index c18f70aa..3db9f953 100644
--- a/lib/oscillators/Rsi.cs
+++ b/lib/oscillators/Rsi.cs
@@ -48,8 +48,7 @@ public sealed class Rsi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rsi(int period = DefaultPeriod)
{
- if (period < 1)
- throw new ArgumentOutOfRangeException(nameof(period));
+ ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
_avgGain = new(period, useSma: true);
_avgLoss = new(period, useSma: true);
_index = 0;
diff --git a/lib/oscillators/Smi.cs b/lib/oscillators/Smi.cs
index 27e6e635..989218d1 100644
--- a/lib/oscillators/Smi.cs
+++ b/lib/oscillators/Smi.cs
@@ -57,12 +57,9 @@ public sealed class Smi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Smi(int period = DefaultPeriod, int smooth1 = DefaultSmooth1, int smooth2 = DefaultSmooth2)
{
- if (period < 1)
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
- if (smooth1 < 1)
- throw new ArgumentOutOfRangeException(nameof(smooth1), "Smooth1 must be greater than 0");
- if (smooth2 < 1)
- throw new ArgumentOutOfRangeException(nameof(smooth2), "Smooth2 must be greater than 0");
+ ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(smooth1, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(smooth2, 1);
_highs = new(period);
_lows = new(period);
diff --git a/lib/oscillators/Srsi.cs b/lib/oscillators/Srsi.cs
index c58d3fe9..bdb2c3df 100644
--- a/lib/oscillators/Srsi.cs
+++ b/lib/oscillators/Srsi.cs
@@ -40,7 +40,6 @@ public sealed class Srsi : AbstractBase
private readonly CircularBuffer _srsiValues;
private readonly Sma _signal;
private readonly int _rsiPeriod;
- private readonly int _stochPeriod;
private const int DefaultRsiPeriod = 14;
private const int DefaultStochPeriod = 14;
private const int DefaultSmoothK = 3;
@@ -56,25 +55,12 @@ public sealed class Srsi : AbstractBase
public Srsi(int rsiPeriod = DefaultRsiPeriod, int stochPeriod = DefaultStochPeriod,
int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
- if (rsiPeriod < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(rsiPeriod), "Period must be greater than 0");
- }
- if (stochPeriod < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(stochPeriod), "Period must be greater than 0");
- }
- if (smoothK < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(smoothK), "Period must be greater than 0");
- }
- if (smoothD < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(smoothD), "Period must be greater than 0");
- }
+ ArgumentOutOfRangeException.ThrowIfLessThan(rsiPeriod, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(stochPeriod, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_rsiPeriod = rsiPeriod;
- _stochPeriod = stochPeriod;
_rsi = new(rsiPeriod);
_rsiValues = new(stochPeriod);
_srsiValues = new(smoothK);
diff --git a/lib/oscillators/Stc.cs b/lib/oscillators/Stc.cs
index b1ab2560..8b755952 100644
--- a/lib/oscillators/Stc.cs
+++ b/lib/oscillators/Stc.cs
@@ -62,32 +62,17 @@ public sealed class Stc : AbstractBase
int slowPeriod = DefaultSlowPeriod, int d1Period = DefaultD1Period,
int stcPeriod = DefaultStcPeriod)
{
- string err = "All periods must be greater than 0";
+ ArgumentOutOfRangeException.ThrowIfLessThan(cyclePeriod, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(fastPeriod, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(slowPeriod, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(d1Period, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(stcPeriod, 1);
- if (cyclePeriod < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(cyclePeriod), err);
- }
- if (fastPeriod < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(fastPeriod), err);
- }
- if (slowPeriod < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(slowPeriod), err);
- }
- if (d1Period < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(d1Period), err);
- }
- if (stcPeriod < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(stcPeriod), err);
- }
if (fastPeriod >= slowPeriod)
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), "Fast period must be less than slow period");
}
+
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
_macdValues = new(cyclePeriod);
diff --git a/lib/oscillators/Stoch.cs b/lib/oscillators/Stoch.cs
index ab10d0b6..1f5d30a0 100644
--- a/lib/oscillators/Stoch.cs
+++ b/lib/oscillators/Stoch.cs
@@ -52,12 +52,9 @@ public sealed class Stoch : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Stoch(int period = DefaultPeriod, int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
- if (period < 1)
- throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
- if (smoothK < 1)
- throw new ArgumentOutOfRangeException(nameof(smoothK), "%K smoothing period must be greater than 0");
- if (smoothD < 1)
- throw new ArgumentOutOfRangeException(nameof(smoothD), "%D smoothing period must be greater than 0");
+ ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_highs = new(period);
_lows = new(period);
diff --git a/lib/oscillators/Uo.cs b/lib/oscillators/Uo.cs
index ec247ebd..38a98129 100644
--- a/lib/oscillators/Uo.cs
+++ b/lib/oscillators/Uo.cs
@@ -67,30 +67,12 @@ public sealed class Uo : AbstractBase
public Uo(int period1 = DefaultPeriod1, int period2 = DefaultPeriod2, int period3 = DefaultPeriod3,
double weight1 = DefaultWeight1, double weight2 = DefaultWeight2, double weight3 = DefaultWeight3)
{
- if (period1 < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(period1), "Period1 must be greater than 0");
- }
- if (period2 < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(period2), "Period2 must be greater than 0");
- }
- if (period3 < 1)
- {
- throw new ArgumentOutOfRangeException(nameof(period3), "Period3 must be greater than 0");
- }
- if (weight1 <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(weight1), "Weight1 must be greater than 0");
- }
- if (weight2 <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(weight2), "Weight2 must be greater than 0");
- }
- if (weight3 <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(weight3), "Weight3 must be greater than 0");
- }
+ ArgumentOutOfRangeException.ThrowIfLessThan(period1, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(period2, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThan(period3, 1);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight1, 0);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight2, 0);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight3, 0);
_weight1 = weight1;
_weight2 = weight2;
diff --git a/lib/statistics/Beta.cs b/lib/statistics/Beta.cs
new file mode 100644
index 00000000..97e065f2
--- /dev/null
+++ b/lib/statistics/Beta.cs
@@ -0,0 +1,159 @@
+using System.Runtime.CompilerServices;
+namespace QuanTAlib;
+
+///
+/// BETA: Beta Coefficient
+/// A statistical measure that quantifies the volatility of an asset or portfolio
+/// in relation to the overall market. Beta is used to assess the risk and return
+/// characteristics of an investment.
+///
+///
+/// The Beta calculation process:
+/// 1. Calculates covariance between asset and market returns
+/// 2. Computes variance of market returns
+/// 3. Divides covariance by market variance
+///
+/// Key characteristics:
+/// - Measures relative volatility
+/// - Beta > 1: More volatile than market
+/// - Beta < 1: Less volatile than market
+/// - Beta = 1: Same volatility as market
+/// - Beta < 0: Inverse relationship with market
+///
+/// Formula:
+/// β = Cov(Ra, Rm) / Var(Rm)
+/// where:
+/// Ra = asset returns
+/// Rm = market returns
+///
+/// Market Applications:
+/// - Risk assessment
+/// - Portfolio management
+/// - Asset allocation
+/// - Performance analysis
+/// - Hedging strategies
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Beta_(finance)
+/// "Modern Portfolio Theory" - Harry Markowitz
+///
+/// Note: Assumes linear relationship between asset and market returns
+///
+[SkipLocalsInit]
+public sealed class Beta : AbstractBase
+{
+ private readonly int Period;
+ private readonly CircularBuffer _assetReturns;
+ private readonly CircularBuffer _marketReturns;
+ private const double Epsilon = 1e-10;
+ private const int MinimumPoints = 2;
+
+ /// The number of points to consider for beta calculation.
+ /// Thrown when period is less than 2.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Beta(int period)
+ {
+ if (period < MinimumPoints)
+ {
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2 for beta calculation.");
+ }
+ Period = period;
+ WarmupPeriod = MinimumPoints;
+ _assetReturns = new CircularBuffer(period);
+ _marketReturns = new CircularBuffer(period);
+ Name = $"Beta(period={period})";
+ Init();
+ }
+
+ /// The data source object that publishes updates.
+ /// The number of points to consider for beta calculation.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Beta(object source, int period) : this(period)
+ {
+ var pubEvent = source.GetType().GetEvent("Pub");
+ pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void Init()
+ {
+ base.Init();
+ _assetReturns.Clear();
+ _marketReturns.Clear();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override void ManageState(bool isNew)
+ {
+ if (isNew)
+ {
+ _lastValidValue = Input.Value;
+ _index++;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateMean(ReadOnlySpan values)
+ {
+ double sum = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ sum += values[i];
+ }
+ return sum / values.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateCovariance(ReadOnlySpan assetReturns, ReadOnlySpan marketReturns, double assetMean, double marketMean)
+ {
+ double covariance = 0;
+ for (int i = 0; i < assetReturns.Length; i++)
+ {
+ covariance += (assetReturns[i] - assetMean) * (marketReturns[i] - marketMean);
+ }
+ return covariance / assetReturns.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateVariance(ReadOnlySpan values, double mean)
+ {
+ double variance = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ double diff = values[i] - mean;
+ variance += diff * diff;
+ }
+ return variance / values.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ protected override double Calculation()
+ {
+ ManageState(Input.IsNew);
+
+ _assetReturns.Add(Input.Value, Input.IsNew);
+ _marketReturns.Add(Input2.Value, Input.IsNew);
+
+ double beta = 0;
+ if (_assetReturns.Count >= MinimumPoints && _marketReturns.Count >= MinimumPoints)
+ {
+ ReadOnlySpan assetValues = _assetReturns.GetSpan();
+ ReadOnlySpan marketValues = _marketReturns.GetSpan();
+
+ double assetMean = CalculateMean(assetValues);
+ double marketMean = CalculateMean(marketValues);
+
+ double covariance = CalculateCovariance(assetValues, marketValues, assetMean, marketMean);
+ double marketVariance = CalculateVariance(marketValues, marketMean);
+
+ if (marketVariance > Epsilon)
+ {
+ beta = covariance / marketVariance;
+ }
+ }
+
+ IsHot = _assetReturns.Count >= Period && _marketReturns.Count >= Period;
+ return beta;
+ }
+}
diff --git a/lib/statistics/Corr.cs b/lib/statistics/Corr.cs
new file mode 100644
index 00000000..0eb18c50
--- /dev/null
+++ b/lib/statistics/Corr.cs
@@ -0,0 +1,163 @@
+using System.Runtime.CompilerServices;
+namespace QuanTAlib;
+
+///
+/// CORR: Correlation Coefficient
+/// A statistical measure that quantifies the strength and direction of the relationship
+/// between two variables. The correlation coefficient ranges from -1 to 1, where 1 indicates
+/// a perfect positive correlation, -1 indicates a perfect negative correlation, and 0 indicates
+/// no correlation.
+///
+///
+/// The Correlation calculation process:
+/// 1. Calculates mean of both variables
+/// 2. Computes covariance between variables
+/// 3. Calculates standard deviation of both variables
+/// 4. Divides covariance by product of standard deviations
+///
+/// Key characteristics:
+/// - Measures linear relationship strength
+/// - Symmetric around zero
+/// - Scale-independent measure
+/// - Sensitive to outliers
+/// - Useful for portfolio diversification
+///
+/// Formula:
+/// ρ = Cov(X, Y) / (σX * σY)
+/// where:
+/// X, Y = variables
+/// Cov = covariance
+/// σ = standard deviation
+///
+/// Market Applications:
+/// - Portfolio diversification
+/// - Risk management
+/// - Pairs trading
+/// - Performance analysis
+/// - Market sentiment analysis
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Correlation_coefficient
+/// "Modern Portfolio Theory" - Harry Markowitz
+///
+/// Note: Assumes linear relationship between variables
+///
+[SkipLocalsInit]
+public sealed class Corr : AbstractBase
+{
+ private readonly int Period;
+ private readonly CircularBuffer _xValues;
+ private readonly CircularBuffer _yValues;
+ private const double Epsilon = 1e-10;
+ private const int MinimumPoints = 2;
+
+ /// The number of points to consider for correlation calculation.
+ /// Thrown when period is less than 2.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Corr(int period)
+ {
+ if (period < MinimumPoints)
+ {
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2 for correlation calculation.");
+ }
+ Period = period;
+ WarmupPeriod = MinimumPoints;
+ _xValues = new CircularBuffer(period);
+ _yValues = new CircularBuffer(period);
+ Name = $"Corr(period={period})";
+ Init();
+ }
+
+ /// The data source object that publishes updates.
+ /// The number of points to consider for correlation calculation.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Corr(object source, int period) : this(period)
+ {
+ var pubEvent = source.GetType().GetEvent("Pub");
+ pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void Init()
+ {
+ base.Init();
+ _xValues.Clear();
+ _yValues.Clear();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override void ManageState(bool isNew)
+ {
+ if (isNew)
+ {
+ _lastValidValue = Input.Value;
+ _index++;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateMean(ReadOnlySpan values)
+ {
+ double sum = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ sum += values[i];
+ }
+ return sum / values.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateCovariance(ReadOnlySpan xValues, ReadOnlySpan yValues, double xMean, double yMean)
+ {
+ double covariance = 0;
+ for (int i = 0; i < xValues.Length; i++)
+ {
+ covariance += (xValues[i] - xMean) * (yValues[i] - yMean);
+ }
+ return covariance / xValues.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateStandardDeviation(ReadOnlySpan values, double mean)
+ {
+ double sumSquaredDeviations = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ double deviation = values[i] - mean;
+ sumSquaredDeviations += deviation * deviation;
+ }
+ return Math.Sqrt(sumSquaredDeviations / values.Length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ protected override double Calculation()
+ {
+ ManageState(Input.IsNew);
+
+ _xValues.Add(Input.Value, Input.IsNew);
+ _yValues.Add(Input2.Value, Input.IsNew);
+
+ double correlation = 0;
+ if (_xValues.Count >= MinimumPoints && _yValues.Count >= MinimumPoints)
+ {
+ ReadOnlySpan xValues = _xValues.GetSpan();
+ ReadOnlySpan yValues = _yValues.GetSpan();
+
+ double xMean = CalculateMean(xValues);
+ double yMean = CalculateMean(yValues);
+
+ double covariance = CalculateCovariance(xValues, yValues, xMean, yMean);
+ double xStdDev = CalculateStandardDeviation(xValues, xMean);
+ double yStdDev = CalculateStandardDeviation(yValues, yMean);
+
+ if (xStdDev > Epsilon && yStdDev > Epsilon)
+ {
+ correlation = covariance / (xStdDev * yStdDev);
+ }
+ }
+
+ IsHot = _xValues.Count >= Period && _yValues.Count >= Period;
+ return correlation;
+ }
+}
diff --git a/lib/statistics/Percentile.cs b/lib/statistics/Percentile.cs
index 635f6680..c8ef7be9 100644
--- a/lib/statistics/Percentile.cs
+++ b/lib/statistics/Percentile.cs
@@ -45,7 +45,6 @@ public sealed class Percentile : AbstractBase
private readonly int Period;
private readonly double Percent;
private readonly CircularBuffer _buffer;
- private const double Epsilon = 1e-10;
private const int MinimumPoints = 2;
/// The number of points to consider for percentile calculation.
@@ -56,16 +55,10 @@ public sealed class Percentile : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Percentile(int period, double percent)
{
- if (period < MinimumPoints)
- {
- throw new ArgumentOutOfRangeException(nameof(period),
- "Period must be greater than or equal to 2 for percentile calculation.");
- }
- if (percent < 0 || percent > 100)
- {
- throw new ArgumentOutOfRangeException(nameof(percent),
- "Percent must be between 0 and 100.");
- }
+ ArgumentOutOfRangeException.ThrowIfLessThan(period, MinimumPoints);
+ ArgumentOutOfRangeException.ThrowIfLessThan(percent, 0);
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(percent, 100);
+
Period = period;
Percent = percent;
WarmupPeriod = MinimumPoints; // Minimum number of points needed for percentile calculation
diff --git a/lib/statistics/Theil.cs b/lib/statistics/Theil.cs
new file mode 100644
index 00000000..3ebcb7b5
--- /dev/null
+++ b/lib/statistics/Theil.cs
@@ -0,0 +1,167 @@
+using System.Runtime.CompilerServices;
+namespace QuanTAlib;
+
+///
+/// THEIL: Theil's U Statistics (U1, U2)
+/// A statistical measure that quantifies the accuracy of forecasts compared to actual values
+/// and naive forecasts.
+///
+///
+/// The Theil's U calculation process:
+/// 1. Calculate U1 statistic (relative accuracy)
+/// 2. Calculate U2 statistic (comparison with naive forecast)
+///
+/// Key characteristics:
+/// - U1 ranges from 0 to 1, with 0 indicating perfect forecast
+/// - U2 < 1: forecast better than naive forecast
+/// - U2 = 1: forecast equal to naive forecast
+/// - U2 > 1: forecast worse than naive forecast
+///
+/// Formula:
+/// U1 = √[Σ(Ft - At)² / Σ(At)²]
+/// U2 = √[Σ(Ft - At)² / Σ(At - At-1)²]
+/// where:
+/// Ft = forecasted value
+/// At = actual value
+/// At-1 = previous actual value
+///
+/// Market Applications:
+/// - Evaluating forecast accuracy
+/// - Comparing forecasting models
+/// - Assessing forecasting methods
+/// - Model selection
+/// - Performance analysis
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Theil%27s_U
+/// "Forecasting: Principles and Practice" - Rob J Hyndman
+///
+/// Note: Should be used alongside other accuracy measures
+///
+[SkipLocalsInit]
+public sealed class Theil : AbstractBase
+{
+ private readonly int Period;
+ private readonly CircularBuffer _actual;
+ private readonly CircularBuffer _forecast;
+ private const int MinimumPoints = 2;
+
+ ///
+ /// Gets the U2 statistic comparing forecast with naive forecast
+ ///
+ public double U2 { get; private set; }
+
+ /// The number of points to consider for Theil's U calculation.
+ /// Thrown when period is less than 2.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Theil(int period)
+ {
+ if (period < MinimumPoints)
+ {
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2 for Theil's U calculation.");
+ }
+ Period = period;
+ WarmupPeriod = MinimumPoints;
+ _actual = new CircularBuffer(period);
+ _forecast = new CircularBuffer(period);
+ Name = $"Theil(period={period})";
+ Init();
+ }
+
+ /// The data source object that publishes updates.
+ /// The number of points to consider for Theil's U calculation.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Theil(object source, int period) : this(period)
+ {
+ var pubEvent = source.GetType().GetEvent("Pub");
+ pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void Init()
+ {
+ base.Init();
+ _actual.Clear();
+ _forecast.Clear();
+ U2 = 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override void ManageState(bool isNew)
+ {
+ if (isNew)
+ {
+ _lastValidValue = Input.Value;
+ _index++;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateSquaredSum(ReadOnlySpan values)
+ {
+ double sum = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ sum += values[i] * values[i];
+ }
+ return sum;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateSquaredErrorSum(ReadOnlySpan forecast, ReadOnlySpan actual)
+ {
+ double sum = 0;
+ for (int i = 0; i < forecast.Length; i++)
+ {
+ double error = forecast[i] - actual[i];
+ sum += error * error;
+ }
+ return sum;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateNaiveSquaredErrorSum(ReadOnlySpan actual)
+ {
+ double sum = 0;
+ for (int i = 1; i < actual.Length; i++)
+ {
+ double error = actual[i] - actual[i - 1];
+ sum += error * error;
+ }
+ return sum;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ protected override double Calculation()
+ {
+ ManageState(Input.IsNew);
+
+ _actual.Add(Input.Value, Input.IsNew);
+ _forecast.Add(Input2.Value, Input.IsNew);
+
+ double u1 = 0;
+ if (_actual.Count >= MinimumPoints && _forecast.Count >= MinimumPoints)
+ {
+ ReadOnlySpan actualValues = _actual.GetSpan();
+ ReadOnlySpan forecastValues = _forecast.GetSpan();
+
+ double squaredErrorSum = CalculateSquaredErrorSum(forecastValues, actualValues);
+ double squaredActualSum = CalculateSquaredSum(actualValues);
+ double naiveSquaredErrorSum = CalculateNaiveSquaredErrorSum(actualValues);
+
+ if (squaredActualSum > double.Epsilon)
+ {
+ u1 = Math.Sqrt(squaredErrorSum / squaredActualSum);
+ }
+
+ if (naiveSquaredErrorSum > double.Epsilon)
+ {
+ U2 = Math.Sqrt(squaredErrorSum / naiveSquaredErrorSum);
+ }
+ }
+
+ IsHot = _actual.Count >= Period && _forecast.Count >= Period;
+ return u1;
+ }
+}
diff --git a/lib/statistics/Tsf.cs b/lib/statistics/Tsf.cs
new file mode 100644
index 00000000..7001e1e0
--- /dev/null
+++ b/lib/statistics/Tsf.cs
@@ -0,0 +1,185 @@
+using System.Runtime.CompilerServices;
+namespace QuanTAlib;
+
+///
+/// TSF: Time Series Forecast
+/// A statistical indicator that provides a linear regression forecast of future values
+/// based on historical data. It includes both the forecast value and a confidence interval.
+///
+///
+/// The Time Series Forecast calculation process:
+/// 1. Calculates linear regression on the input data
+/// 2. Extrapolates the regression line to forecast future values
+/// 3. Computes confidence intervals based on the standard error of the forecast
+///
+/// Key characteristics:
+/// - Provides point forecast and confidence interval
+/// - Based on linear regression principles
+/// - Assumes trend continuity
+/// - Sensitive to recent data changes
+/// - Useful for short-term predictions
+///
+/// Formula:
+/// Forecast = a + b * (n + 1)
+/// where:
+/// a = y-intercept
+/// b = slope
+/// n = number of periods
+///
+/// Confidence Interval = Forecast ± (t * SE)
+/// where:
+/// t = t-value for desired confidence level
+/// SE = Standard Error of the forecast
+///
+/// Market Applications:
+/// - Price target estimation
+/// - Trend analysis
+/// - Risk assessment
+/// - Trading strategy development
+/// - Market behavior prediction
+///
+/// Sources:
+/// https://en.wikipedia.org/wiki/Time_series
+/// "Forecasting: Principles and Practice" - Rob J Hyndman and George Athanasopoulos
+///
+/// Note: Assumes linear trend in the data and may not capture non-linear patterns
+///
+[SkipLocalsInit]
+public sealed class Tsf : AbstractBase
+{
+ private readonly int Period;
+ private readonly CircularBuffer _values;
+ private const int MinimumPoints = 2;
+
+ ///
+ /// The forecasted value for the next period.
+ ///
+ public double Forecast { get; private set; }
+
+ ///
+ /// The lower bound of the confidence interval.
+ ///
+ public double LowerBound { get; private set; }
+
+ ///
+ /// The upper bound of the confidence interval.
+ ///
+ public double UpperBound { get; private set; }
+
+ /// The number of historical data points to consider for forecasting.
+ /// Thrown when period is less than 2.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Tsf(int period)
+ {
+ if (period < MinimumPoints)
+ {
+ throw new ArgumentOutOfRangeException(nameof(period),
+ "Period must be greater than or equal to 2 for time series forecasting.");
+ }
+ Period = period;
+ WarmupPeriod = MinimumPoints;
+ _values = new CircularBuffer(period);
+ Name = $"TSF(period={period})";
+ Init();
+ }
+
+ /// The data source object that publishes updates.
+ /// The number of historical data points to consider for forecasting.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Tsf(object source, int period) : this(period)
+ {
+ var pubEvent = source.GetType().GetEvent("Pub");
+ pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override void Init()
+ {
+ base.Init();
+ _values.Clear();
+ Forecast = 0;
+ LowerBound = 0;
+ UpperBound = 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected override void ManageState(bool isNew)
+ {
+ if (isNew)
+ {
+ _lastValidValue = Input.Value;
+ _index++;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static (double slope, double intercept) CalculateLinearRegression(ReadOnlySpan values)
+ {
+ int n = values.Length;
+ double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
+
+ for (int i = 0; i < n; i++)
+ {
+ double x = i + 1;
+ double y = values[i];
+ sumX += x;
+ sumY += y;
+ sumXY += x * y;
+ sumX2 += x * x;
+ }
+
+ double slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
+ double intercept = (sumY - slope * sumX) / n;
+
+ return (slope, intercept);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static double CalculateStandardError(ReadOnlySpan values, double slope, double intercept)
+ {
+ int n = values.Length;
+ double sumSquaredResiduals = 0;
+
+ for (int i = 0; i < n; i++)
+ {
+ double x = i + 1;
+ double y = values[i];
+ double predicted = slope * x + intercept;
+ double residual = y - predicted;
+ sumSquaredResiduals += residual * residual;
+ }
+
+ return Math.Sqrt(sumSquaredResiduals / (n - 2));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ protected override double Calculation()
+ {
+ ManageState(Input.IsNew);
+
+ _values.Add(Input.Value, Input.IsNew);
+
+ if (_values.Count >= MinimumPoints)
+ {
+ ReadOnlySpan values = _values.GetSpan();
+
+ var (slope, intercept) = CalculateLinearRegression(values);
+
+ // Calculate forecast for the next period
+ Forecast = slope * (Period + 1) + intercept;
+
+ // Calculate standard error
+ double standardError = CalculateStandardError(values, slope, intercept);
+
+ // Calculate confidence interval (using t-distribution with n-2 degrees of freedom)
+ double tValue = 1.96; // Approximation for 95% confidence interval
+ double marginOfError = tValue * standardError * Math.Sqrt(1 + 1.0 / Period);
+
+ LowerBound = Forecast - marginOfError;
+ UpperBound = Forecast + marginOfError;
+ }
+
+ IsHot = _values.Count >= Period;
+ return Forecast;
+ }
+}
diff --git a/lib/statistics/_list.md b/lib/statistics/_list.md
index 4e16b0de..3ca1af8c 100644
--- a/lib/statistics/_list.md
+++ b/lib/statistics/_list.md
@@ -1,22 +1,32 @@
-# Statistics indicators
-Done: 13, Todo: 6
+# Statistics
-*BETA - Beta coefficient (Beta, R-squared)
-*CORR - Correlation Coefficient (Correlation, P-value)
-✔️ CURVATURE - Rate of Change in Direction or Slope
-✔️ ENTROPY - Measure of Uncertainty or Disorder
-✔️ HURST - Hurst Exponent
-✔️ KURTOSIS - Measure of Tails/Peakedness
-✔️ MAX - Maximum with exponential decay
-✔️ MEDIAN - Middle value
-✔️ MIN - Minimum with exponential decay
-✔️ MODE - Most Frequent Value
-✔️ PERCENTILE - Rank Order
-*RSQUARED - Coefficient of Determination (R-squared, Adjusted R-squared)
-✔️ SKEW - Skewness, asymmetry of distribution
-✔️ SLOPE - Rate of Change, Linear Regression
-✔️ STDDEV - Standard Deviation, Measure of Spread
-*THEIL - Theil's U Statistics (U1, U2)
-*TSF - Time Series Forecast (Forecast, Confidence Interval)
-✔️ VARIANCE - Average of Squared Deviations
-✔️ ZSCORE - Standardized Score
+Statistical functions and indicators for financial analysis.
+
+## Implemented
+
+- [Beta](Beta.cs) - Beta coefficient measuring volatility relative to market
+- [Corr](Corr.cs) - Correlation coefficient between two series
+- [Curvature](Curvature.cs) - Curvature of a time series
+- [Entropy](Entropy.cs) - Information entropy of a series
+- [Hurst](Hurst.cs) - Hurst exponent for trend strength
+- [Kurtosis](Kurtosis.cs) - Kurtosis measuring tail extremity
+- [Max](Max.cs) - Maximum value over period
+- [Median](Median.cs) - Median value over period
+- [Min](Min.cs) - Minimum value over period
+- [Mode](Mode.cs) - Mode (most frequent value)
+- [Percentile](Percentile.cs) - Percentile rank calculation
+- [Skew](Skew.cs) - Skewness measuring distribution asymmetry
+- [Slope](Slope.cs) - Linear regression slope
+- [Stddev](Stddev.cs) - Standard deviation
+- [Theil](Theil.cs) - Theil's U statistics for forecast accuracy
+- [Tsf](Tsf.cs) - Time series forecast
+- [Variance](Variance.cs) - Statistical variance
+- [Zscore](Zscore.cs) - Z-score standardization
+
+## Planned
+
+- Cointegration - Test for cointegrated series
+- Granger - Granger causality test
+- Jarque-Bera - Normality test
+- Kendall - Kendall rank correlation
+- Spearman - Spearman rank correlation
From 085c6dc6340f33a8c6ec3787b1e9ebdd2a6f705f Mon Sep 17 00:00:00 2001
From: codefactor-io
Date: Tue, 5 Nov 2024 23:51:44 +0000
Subject: [PATCH 4/8] [CodeFactor] Apply fixes to commit 0bae9ce
---
lib/statistics/Tsf.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/statistics/Tsf.cs b/lib/statistics/Tsf.cs
index 7001e1e0..6aa9b798 100644
--- a/lib/statistics/Tsf.cs
+++ b/lib/statistics/Tsf.cs
@@ -128,8 +128,8 @@ public sealed class Tsf : AbstractBase
sumX2 += x * x;
}
- double slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
- double intercept = (sumY - slope * sumX) / n;
+ double slope = ((n * sumXY) - (sumX * sumY)) / ((n * sumX2) - (sumX * sumX));
+ double intercept = (sumY - (slope * sumX)) / n;
return (slope, intercept);
}
@@ -144,7 +144,7 @@ public sealed class Tsf : AbstractBase
{
double x = i + 1;
double y = values[i];
- double predicted = slope * x + intercept;
+ double predicted = (slope * x) + intercept;
double residual = y - predicted;
sumSquaredResiduals += residual * residual;
}
@@ -166,14 +166,14 @@ public sealed class Tsf : AbstractBase
var (slope, intercept) = CalculateLinearRegression(values);
// Calculate forecast for the next period
- Forecast = slope * (Period + 1) + intercept;
+ Forecast = (slope * (Period + 1)) + intercept;
// Calculate standard error
double standardError = CalculateStandardError(values, slope, intercept);
// Calculate confidence interval (using t-distribution with n-2 degrees of freedom)
double tValue = 1.96; // Approximation for 95% confidence interval
- double marginOfError = tValue * standardError * Math.Sqrt(1 + 1.0 / Period);
+ double marginOfError = tValue * standardError * Math.Sqrt(1 + (1.0 / Period));
LowerBound = Forecast - marginOfError;
UpperBound = Forecast + marginOfError;
From 582a0256ecde7c66afa5b5102f928172d3120011 Mon Sep 17 00:00:00 2001
From: Miha Kralj
Date: Wed, 6 Nov 2024 20:56:32 -0800
Subject: [PATCH 5/8] Momentum
charts for Quantower
---
QuanTAlib.sln | 39 ++++-
lib/momentum/Dmi.cs | 113 ++++++--------
lib/momentum/Dmx.cs | 145 ++++--------------
lib/momentum/Dpo.cs | 11 +-
lib/momentum/_list.md | 1 -
quantower/Averages/AfirmaIndicator.cs | 2 +-
quantower/Averages/AlmaIndicator.cs | 2 +-
quantower/Averages/DemaIndicator.cs | 2 +-
quantower/Averages/DsmaIndicator.cs | 2 +-
quantower/Averages/DwmaIndicator.cs | 2 +-
quantower/Averages/EmaIndicator.cs | 4 +-
quantower/Averages/EpmaIndicator.cs | 2 +-
quantower/Averages/FramaIndicator.cs | 2 +-
quantower/Averages/FwmaIndicator.cs | 2 +-
quantower/Averages/GmaIndicator.cs | 2 +-
quantower/Averages/HmaIndicator.cs | 2 +-
quantower/Averages/HtitIndicator.cs | 2 +-
quantower/Averages/HwmaIndicator.cs | 2 +-
quantower/Averages/JmaIndicator.cs | 6 +-
quantower/Averages/KamaIndicator.cs | 2 +-
quantower/Averages/LtmaIndicator.cs | 2 +-
quantower/Averages/MaafIndicator.cs | 2 +-
quantower/Averages/MamaIndicator.cs | 2 +-
quantower/Averages/MgdiIndicator.cs | 2 +-
quantower/Averages/MmaIndicator.cs | 2 +-
quantower/Averages/PwmaIndicator.cs | 2 +-
quantower/Averages/QemaIndicator.cs | 2 +-
quantower/Averages/RemaIndicator.cs | 2 +-
quantower/Averages/RmaIndicator.cs | 2 +-
quantower/Averages/SinemaIndicator.cs | 2 +-
quantower/Averages/SmaIndicator.cs | 2 +-
quantower/Averages/SmmaIndicator.cs | 2 +-
quantower/Averages/T3Indicator.cs | 2 +-
quantower/Averages/TemaIndicator.cs | 2 +-
quantower/Averages/TrimaIndicator.cs | 2 +-
quantower/Averages/VidyaIndicator.cs | 2 +-
quantower/Averages/WmaIndicator.cs | 2 +-
quantower/Averages/ZlemaIndicator.cs | 2 +-
.../FlowIndicator.cs | 0
.../TestIndicator.cs | 2 +-
quantower/Experiments/_Experiments.csproj | 30 ++++
quantower/IndicatorExtensions.cs | 11 +-
quantower/Momentum/AdxIndicator.cs | 53 +++++++
quantower/Momentum/AdxrIndicator.cs | 55 +++++++
quantower/Momentum/ApoIndicator.cs | 71 +++++++++
quantower/Momentum/DmiIndicator.cs | 59 +++++++
quantower/Momentum/DmxIndicator.cs | 68 ++++++++
quantower/Momentum/DpoIndicator.cs | 67 ++++++++
.../{Averages => Momentum}/MacdIndicator.cs | 31 ++--
quantower/Momentum/_Momentum.csproj | 2 +-
.../RsiIndicator.cs | 2 +-
.../RsxIndicator.cs | 2 +-
quantower/Statistics/CurvatureIndicator.cs | 2 +-
quantower/Statistics/EntropyIndicator.cs | 2 +-
quantower/Statistics/KurtosisIndicator.cs | 2 +-
quantower/Statistics/MaxIndicator.cs | 2 +-
quantower/Statistics/MedianIndicator.cs | 2 +-
quantower/Statistics/MinIndicator.cs | 2 +-
quantower/Statistics/ModeIndicator.cs | 2 +-
quantower/Statistics/PercentileIndicator.cs | 2 +-
quantower/Statistics/SkewIndicator.cs | 2 +-
quantower/Statistics/SlopeIndicator.cs | 2 +-
quantower/Statistics/StddevIndicator.cs | 2 +-
quantower/Statistics/VarianceIndicator.cs | 2 +-
quantower/Statistics/ZscoreIndicator.cs | 2 +-
quantower/Volatility/AtrIndicator.cs | 2 +-
quantower/Volatility/CmoIndicator.cs | 2 +-
quantower/Volatility/CviIndicator.cs | 4 +-
quantower/Volatility/HistoricalIndicator.cs | 2 +-
quantower/Volatility/JbandsIndicator.cs | 4 +-
quantower/Volatility/JvoltyIndicator.cs | 2 +-
quantower/Volatility/RealizedIndicator.cs | 2 +-
quantower/Volatility/RviIndicator.cs | 2 +-
quantower/Volume/ObvIndicator.cs | 51 ++++++
quantower/Volume/_Volume.csproj | 2 +-
75 files changed, 652 insertions(+), 281 deletions(-)
rename quantower/{Volatility => Experiments}/FlowIndicator.cs (100%)
rename quantower/{Volatility => Experiments}/TestIndicator.cs (94%)
create mode 100644 quantower/Experiments/_Experiments.csproj
create mode 100644 quantower/Momentum/AdxIndicator.cs
create mode 100644 quantower/Momentum/AdxrIndicator.cs
create mode 100644 quantower/Momentum/ApoIndicator.cs
create mode 100644 quantower/Momentum/DmiIndicator.cs
create mode 100644 quantower/Momentum/DmxIndicator.cs
create mode 100644 quantower/Momentum/DpoIndicator.cs
rename quantower/{Averages => Momentum}/MacdIndicator.cs (85%)
rename quantower/{Volatility => Oscillators}/RsiIndicator.cs (95%)
rename quantower/{Volatility => Oscillators}/RsxIndicator.cs (95%)
create mode 100644 quantower/Volume/ObvIndicator.cs
diff --git a/QuanTAlib.sln b/QuanTAlib.sln
index 068fcd79..ad664505 100644
--- a/QuanTAlib.sln
+++ b/QuanTAlib.sln
@@ -12,6 +12,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Averages", "quantower\Avera
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volatility", "quantower\Volatility\_Volatility.csproj", "{B7DC44F7-D3A3-4C70-9025-513E0182B646}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Oscillators", "quantower\Oscillators\_Oscillators.csproj", "{C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volume", "quantower\Volume\_Volume.csproj", "{D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Momentum", "quantower\Momentum\_Momentum.csproj", "{E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Experiments", "quantower\Experiments\_Experiments.csproj", "{F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyntheticVendor", "SyntheticVendor\SyntheticVendor.csproj", "{1CF111D9-33E6-4A11-8FEC-F23300A78D15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{2D97C971-20BF-40DB-94AA-3279F787D3CB}"
@@ -40,12 +48,27 @@ Global
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release | Any CPU.ActiveCfg = Release | Any CPU
- {B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release | Any CPU.Build.0 = Release | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug | Any CPU.ActiveCfg = Debug | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug | Any CPU.Build.0 = Debug | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release | Any CPU.ActiveCfg = Release | Any CPU
- {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release | Any CPU.Build.0 = Release | Any CPU
+ {B7DC44F7-D3A3-4C70-9025-513E0182B646}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1CF111D9-33E6-4A11-8FEC-F23300A78D15}.Release|Any CPU.Build.0 = Release|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D97C971-20BF-40DB-94AA-3279F787D3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -55,5 +78,9 @@ Global
{2E9427C7-144F-488E-A29D-789ACC1C32AE} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{6BE10C39-4127-446C-818B-7976FCDD51D5} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
{B7DC44F7-D3A3-4C70-9025-513E0182B646} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {C4D8F5D0-E6A7-4B7D-B8E9-F55C3F8D9D01} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {D5E9F6D1-B8A8-4C7E-9FA0-F66C3F8D9D02} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {E6F0F7D2-C9B9-4D8F-0FA1-F77C4F9D9D03} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
+ {F7F1F8D3-DAC0-4E9F-1FB2-F88D5F0E0E04} = {1B9AC248-76F8-44DD-958D-F1DC08EE1E87}
EndGlobalSection
EndGlobal
diff --git a/lib/momentum/Dmi.cs b/lib/momentum/Dmi.cs
index d71b284f..432e2c53 100644
--- a/lib/momentum/Dmi.cs
+++ b/lib/momentum/Dmi.cs
@@ -24,10 +24,13 @@ namespace QuanTAlib;
///
/// Formula:
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
-/// +DM = if(high-prevHigh > prevLow-low) then max(high-prevHigh, 0) else 0
-/// -DM = if(prevLow-low > high-prevHigh) then max(prevLow-low, 0) else 0
-/// +DI = 100 * smoothed(+DM) / smoothed(TR)
-/// -DI = 100 * smoothed(-DM) / smoothed(TR)
+/// +DM = if(high-prevHigh > prevLow-low && high-prevHigh > 0) then high-prevHigh else 0
+/// -DM = if(prevLow-low > high-prevHigh && prevLow-low > 0) then prevLow-low else 0
+/// Smoothed TR = Wilder's smoothing of TR (ATR)
+/// Smoothed +DM = Wilder's smoothing of +DM
+/// Smoothed -DM = Wilder's smoothing of -DM
+/// +DI = 100 * Smoothed(+DM) / Smoothed(TR)
+/// -DI = 100 * Smoothed(-DM) / Smoothed(TR)
///
/// Sources:
/// J. Welles Wilder Jr. - "New Concepts in Technical Trading Systems" (1978)
@@ -36,49 +39,41 @@ namespace QuanTAlib;
/// Note: Default period of 14 was recommended by Wilder
///
[SkipLocalsInit]
-public sealed class Dmi : AbstractBarBase
+public sealed class Dmi : AbstractBase
{
- private readonly Rma _smoothedTr;
+ private readonly Atr _atr;
private readonly Rma _smoothedPlusDm;
private readonly Rma _smoothedMinusDm;
- private double _prevHigh, _prevLow, _prevClose;
- private double _p_prevHigh, _p_prevLow, _p_prevClose;
+ private double _prevHigh, _prevLow;
+ private double _p_prevHigh, _p_prevLow;
private double _plusDi, _minusDi;
private const double ScalingFactor = 100.0;
private const int DefaultPeriod = 14;
- ///
- /// Gets the most recent +DI value
- ///
public double PlusDI => _plusDi;
-
- ///
- /// Gets the most recent -DI value
- ///
public double MinusDI => _minusDi;
- /// The number of periods used in the DMI calculation (default 14).
- /// Thrown when period is less than 1.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dmi(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
- _smoothedTr = new(period, useSma: true);
- _smoothedPlusDm = new(period, useSma: true);
- _smoothedMinusDm = new(period, useSma: true);
- _index = 0;
+ _atr = new(period);
+ _smoothedPlusDm = new(period);
+ _smoothedMinusDm = new(period);
WarmupPeriod = period + 1;
Name = $"DMI({period})";
}
- /// The data source object that publishes updates.
- /// The number of periods used in the DMI calculation.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Dmi(object source, int period) : this(period)
+ public override void Init()
{
- var pubEvent = source.GetType().GetEvent("Pub");
- pubEvent?.AddEventHandler(source, new BarSignal(Sub));
+ base.Init();
+ _atr.Init();
+ _smoothedPlusDm.Init();
+ _smoothedMinusDm.Init();
+ _prevHigh = _prevLow = double.NaN;
+ _p_prevHigh = _p_prevLow = double.NaN;
+ _plusDi = _minusDi = 0;
+ _index = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -89,25 +84,14 @@ public sealed class Dmi : AbstractBarBase
_index++;
_p_prevHigh = _prevHigh;
_p_prevLow = _prevLow;
- _p_prevClose = _prevClose;
}
else
{
_prevHigh = _p_prevHigh;
_prevLow = _p_prevLow;
- _prevClose = _p_prevClose;
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- private static double CalculateTrueRange(double high, double low, double prevClose)
- {
- double hl = high - low;
- double hpc = Math.Abs(high - prevClose);
- double lpc = Math.Abs(low - prevClose);
- return Math.Max(hl, Math.Max(hpc, lpc));
- }
-
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double plusDm, double minusDm) CalculateDirectionalMovement(
double high, double low, double prevHigh, double prevLow)
@@ -115,13 +99,8 @@ public sealed class Dmi : AbstractBarBase
double upMove = high - prevHigh;
double downMove = prevLow - low;
- double plusDm = 0.0;
- double minusDm = 0.0;
-
- if (upMove > downMove && upMove > 0)
- plusDm = upMove;
- else if (downMove > upMove && downMove > 0)
- minusDm = downMove;
+ double plusDm = (upMove > downMove && upMove > 0) ? upMove : 0;
+ double minusDm = (downMove > upMove && downMove > 0) ? downMove : 0;
return (plusDm, minusDm);
}
@@ -129,38 +108,36 @@ public sealed class Dmi : AbstractBarBase
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
- ManageState(Input.IsNew);
+ ManageState(BarInput.IsNew);
- if (_index == 1)
+ if (double.IsNaN(_prevHigh))
{
- _prevHigh = Input.High;
- _prevLow = Input.Low;
- _prevClose = Input.Close;
+ _prevHigh = BarInput.High;
+ _prevLow = BarInput.Low;
return 0.0;
}
- // Calculate True Range and Directional Movement
- double tr = CalculateTrueRange(Input.High, Input.Low, _prevClose);
+ // Calculate ATR
+ double atr = _atr.Calc(BarInput).Value;
+
+ // Calculate Directional Movement
var (plusDm, minusDm) = CalculateDirectionalMovement(
- Input.High, Input.Low, _prevHigh, _prevLow);
+ BarInput.High, BarInput.Low, _prevHigh, _prevLow);
- // Update previous values
- _prevHigh = Input.High;
- _prevLow = Input.Low;
- _prevClose = Input.Close;
+ // Update previous values for next calculation
+ _prevHigh = BarInput.High;
+ _prevLow = BarInput.Low;
- // Smooth the indicators using Wilder's method
- _smoothedTr.Calc(tr, Input.IsNew);
- _smoothedPlusDm.Calc(plusDm, Input.IsNew);
- _smoothedMinusDm.Calc(minusDm, Input.IsNew);
+ // Smooth DM values using Wilder's method
+ double smoothedPlusDm = _smoothedPlusDm.Calc(plusDm, BarInput.IsNew).Value;
+ double smoothedMinusDm = _smoothedMinusDm.Calc(minusDm, BarInput.IsNew).Value;
- // Calculate +DI and -DI
- double smoothedTr = _smoothedTr.Value;
- if (smoothedTr > 0)
+ // Calculate DI values
+ if (atr > 0)
{
- _plusDi = ScalingFactor * _smoothedPlusDm.Value / smoothedTr;
- _minusDi = ScalingFactor * _smoothedMinusDm.Value / smoothedTr;
- return _plusDi - _minusDi; // Return the difference as main value
+ _plusDi = ScalingFactor * smoothedPlusDm / atr;
+ _minusDi = ScalingFactor * smoothedMinusDm / atr;
+ return _plusDi - _minusDi;
}
_plusDi = 0.0;
diff --git a/lib/momentum/Dmx.cs b/lib/momentum/Dmx.cs
index b3c3efbf..a5acf5e6 100644
--- a/lib/momentum/Dmx.cs
+++ b/lib/momentum/Dmx.cs
@@ -4,16 +4,13 @@ namespace QuanTAlib;
///
/// DMX: Enhanced Directional Movement Index using JMA smoothing
/// An improvement over the traditional DMI indicator that uses Jurik Moving Average (JMA)
-/// for smoothing instead of Wilder's moving average. This enhancement provides better
-/// noise reduction while maintaining responsiveness to significant price movements.
+/// for smoothing. This enhancement provides better noise reduction while maintaining
+/// responsiveness to significant price movements.
///
///
/// The DMX calculation process:
-/// 1. Calculate True Range (TR)
-/// 2. Calculate +DM (Positive Directional Movement)
-/// 3. Calculate -DM (Negative Directional Movement)
-/// 4. Smooth TR, +DM, and -DM using JMA instead of Wilder's smoothing
-/// 5. Calculate +DI and -DI as percentages
+/// 1. Calculate DMI using the standard Dmi class
+/// 2. Apply JMA smoothing to the +DI and -DI values
///
/// Key improvements over DMI:
/// - Uses JMA's adaptive volatility-based smoothing
@@ -22,11 +19,9 @@ namespace QuanTAlib;
/// - Reduced lag through JMA's phase-shifting
///
/// Formula:
-/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
-/// +DM = if(high-prevHigh > prevLow-low) then max(high-prevHigh, 0) else 0
-/// -DM = if(prevLow-low > high-prevHigh) then max(prevLow-low, 0) else 0
-/// +DI = 100 * JMA(+DM) / JMA(TR)
-/// -DI = 100 * JMA(-DM) / JMA(TR)
+/// DMI calculation as per standard DMI
+/// DMX +DI = JMA(DMI +DI)
+/// DMX -DI = JMA(DMI -DI)
///
/// Sources:
/// Original DMI by J. Welles Wilder Jr. - "New Concepts in Technical Trading Systems" (1978)
@@ -35,53 +30,40 @@ namespace QuanTAlib;
[SkipLocalsInit]
public sealed class Dmx : AbstractBarBase
{
- private readonly Jma _smoothedTr;
- private readonly Jma _smoothedPlusDm;
- private readonly Jma _smoothedMinusDm;
- private double _prevHigh, _prevLow, _prevClose;
- private double _p_prevHigh, _p_prevLow, _p_prevClose;
+ private readonly Dmi _dmi;
+ private readonly Jma _smoothedPlusDi;
+ private readonly Jma _smoothedMinusDi;
private double _plusDi, _minusDi;
- private const double ScalingFactor = 100.0;
- private const int DefaultPeriod = 10;
+ private const int DefaultDmiPeriod = 14;
+ private const int DefaultJmaPeriod = 7;
private const int DefaultPhase = 100;
private const double DefaultFactor = 0.25;
///
- /// Gets the most recent +DI value
+ /// Gets the most recent smoothed +DI value
///
public double PlusDI => _plusDi;
///
- /// Gets the most recent -DI value
+ /// Gets the most recent smoothed -DI value
///
public double MinusDI => _minusDi;
- /// The number of periods used in the DMX calculation (default 14).
- /// The phase for the JMA smoothing (default 0).
- /// The factor for the JMA smoothing (default 0.45).
+ /// The number of periods used in the DMI calculation (default 14).
+ /// The number of periods used in the JMA smoothing (default 10).
+ /// The phase for the JMA smoothing (default 100).
+ /// The factor for the JMA smoothing (default 0.25).
/// Thrown when period is less than 1.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Dmx(int period = DefaultPeriod, int phase = DefaultPhase, double factor = DefaultFactor)
+ public Dmx(int period = DefaultDmiPeriod, int jmaPeriod = DefaultJmaPeriod, int phase = DefaultPhase, double factor = DefaultFactor)
{
- if (period < 1)
- throw new ArgumentOutOfRangeException(nameof(period));
- _smoothedTr = new(period, phase, factor);
- _smoothedPlusDm = new(period, phase, factor);
- _smoothedMinusDm = new(period, phase, factor);
- _index = 0;
- WarmupPeriod = period * 2; // JMA needs more warmup periods than RMA
- Name = $"DMX({period})";
- }
-
- /// The data source object that publishes updates.
- /// The number of periods used in the DMX calculation.
- /// The phase for the JMA smoothing.
- /// The factor for the JMA smoothing.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Dmx(object source, int period, int phase = DefaultPhase, double factor = DefaultFactor) : this(period, phase, factor)
- {
- var pubEvent = source.GetType().GetEvent("Pub");
- pubEvent?.AddEventHandler(source, new BarSignal(Sub));
+ if (period < 1 || jmaPeriod < 1)
+ throw new ArgumentOutOfRangeException(nameof(period), "Periods must be greater than or equal to 1.");
+ _dmi = new(period);
+ _smoothedPlusDi = new(jmaPeriod, phase, factor);
+ _smoothedMinusDi = new(jmaPeriod, phase, factor);
+ WarmupPeriod = period + jmaPeriod;
+ Name = $"DMX({period},{jmaPeriod})";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -90,43 +72,7 @@ public sealed class Dmx : AbstractBarBase
if (isNew)
{
_index++;
- _p_prevHigh = _prevHigh;
- _p_prevLow = _prevLow;
- _p_prevClose = _prevClose;
}
- else
- {
- _prevHigh = _p_prevHigh;
- _prevLow = _p_prevLow;
- _prevClose = _p_prevClose;
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- private static double CalculateTrueRange(double high, double low, double prevClose)
- {
- double hl = high - low;
- double hpc = Math.Abs(high - prevClose);
- double lpc = Math.Abs(low - prevClose);
- return Math.Max(hl, Math.Max(hpc, lpc));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
- private static (double plusDm, double minusDm) CalculateDirectionalMovement(
- double high, double low, double prevHigh, double prevLow)
- {
- double upMove = high - prevHigh;
- double downMove = prevLow - low;
-
- double plusDm = 0.0;
- double minusDm = 0.0;
-
- if (upMove > downMove && upMove > 0)
- plusDm = upMove;
- else if (downMove > upMove && downMove > 0)
- minusDm = downMove;
-
- return (plusDm, minusDm);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@@ -134,40 +80,13 @@ public sealed class Dmx : AbstractBarBase
{
ManageState(Input.IsNew);
- if (_index == 1)
- {
- _prevHigh = Input.High;
- _prevLow = Input.Low;
- _prevClose = Input.Close;
- return 0.0;
- }
+ // Calculate DMI
+ _dmi.Calc(Input);
- // Calculate True Range and Directional Movement
- double tr = CalculateTrueRange(Input.High, Input.Low, _prevClose);
- var (plusDm, minusDm) = CalculateDirectionalMovement(
- Input.High, Input.Low, _prevHigh, _prevLow);
+ // Smooth the DMI values using JMA
+ _plusDi = _smoothedPlusDi.Calc(_dmi.PlusDI, Input.IsNew).Value;
+ _minusDi = _smoothedMinusDi.Calc(_dmi.MinusDI, Input.IsNew).Value;
- // Update previous values
- _prevHigh = Input.High;
- _prevLow = Input.Low;
- _prevClose = Input.Close;
-
- // Smooth the indicators using JMA
- _smoothedTr.Calc(tr, Input.IsNew);
- _smoothedPlusDm.Calc(plusDm, Input.IsNew);
- _smoothedMinusDm.Calc(minusDm, Input.IsNew);
-
- // Calculate +DI and -DI
- double smoothedTr = _smoothedTr.Value;
- if (smoothedTr > 0)
- {
- _plusDi = ScalingFactor * _smoothedPlusDm.Value / smoothedTr;
- _minusDi = ScalingFactor * _smoothedMinusDm.Value / smoothedTr;
- return _plusDi - _minusDi; // Return the difference as main value
- }
-
- _plusDi = 0.0;
- _minusDi = 0.0;
- return 0.0;
+ return _plusDi - _minusDi; // Return the difference as main value
}
}
diff --git a/lib/momentum/Dpo.cs b/lib/momentum/Dpo.cs
index 87273cb4..1e19bed6 100644
--- a/lib/momentum/Dpo.cs
+++ b/lib/momentum/Dpo.cs
@@ -82,12 +82,13 @@ public sealed class Dpo : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
+
ManageState(BarInput.IsNew);
// Add current price to buffer
- _prices.Add(BarInput.Close);
-
+ _prices.Add(BarInput.Close, BarInput.IsNew);
// Need enough prices for the shifted SMA calculation
+
if (_index <= _shift)
{
return 0;
@@ -96,12 +97,6 @@ public sealed class Dpo : AbstractBase
// Add price from shift periods ago to SMA buffer
_sma.Add(_prices[_shift]);
- // Need enough prices for full calculation
- if (_index <= WarmupPeriod)
- {
- return 0;
- }
-
// Calculate DPO
double dpo = BarInput.Close - _sma.Average();
diff --git a/lib/momentum/_list.md b/lib/momentum/_list.md
index 85cc2072..ccfdf582 100644
--- a/lib/momentum/_list.md
+++ b/lib/momentum/_list.md
@@ -1,5 +1,4 @@
# Momentum indicators
-Done: 15, Todo: 2
✔️ ADX - Average Directional Movement Index
✔️ ADXR - Average Directional Movement Index Rating
diff --git a/quantower/Averages/AfirmaIndicator.cs b/quantower/Averages/AfirmaIndicator.cs
index a094c320..486052da 100644
--- a/quantower/Averages/AfirmaIndicator.cs
+++ b/quantower/Averages/AfirmaIndicator.cs
@@ -50,7 +50,7 @@ public class AfirmaIndicator : Indicator, IWatchlistIndicator
Name = "AFIRMA - Adaptive Finite Impulse Response Moving Average";
Description = "Adaptive Finite Impulse Response Moving Average with ARMA component";
- Series = new(name: $"AFIRMA {Taps}:{Periods}:{Window}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"AFIRMA {Taps}:{Periods}:{Window}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/AlmaIndicator.cs b/quantower/Averages/AlmaIndicator.cs
index 50b49d30..95bee6b7 100644
--- a/quantower/Averages/AlmaIndicator.cs
+++ b/quantower/Averages/AlmaIndicator.cs
@@ -46,7 +46,7 @@ public class AlmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "ALMA - Arnaud Legoux Moving Average";
Description = "Arnaud Legoux Moving Average";
- Series = new(name: $"ALMA {Period}:{Offset:F2}:{Sigma:F0}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"ALMA {Period}:{Offset:F2}:{Sigma:F0}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/DemaIndicator.cs b/quantower/Averages/DemaIndicator.cs
index 7ca1824f..6db92ee5 100644
--- a/quantower/Averages/DemaIndicator.cs
+++ b/quantower/Averages/DemaIndicator.cs
@@ -40,7 +40,7 @@ public class DemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "DEMA - Double Exponential Moving Average";
Description = "A faster-responding moving average that reduces lag by applying the EMA twice.";
- Series = new(name: $"DEMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"DEMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/DsmaIndicator.cs b/quantower/Averages/DsmaIndicator.cs
index 8c22a636..f26fc389 100644
--- a/quantower/Averages/DsmaIndicator.cs
+++ b/quantower/Averages/DsmaIndicator.cs
@@ -43,7 +43,7 @@ public class DsmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "DSMA - Deviation Scaled Moving Average";
Description = "A moving average that adjusts its responsiveness based on price deviations from the mean.";
- Series = new(name: $"DSMA {Period}:{Scale:F2}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"DSMA {Period}:{Scale:F2}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/DwmaIndicator.cs b/quantower/Averages/DwmaIndicator.cs
index 6b195215..6f88e496 100644
--- a/quantower/Averages/DwmaIndicator.cs
+++ b/quantower/Averages/DwmaIndicator.cs
@@ -40,7 +40,7 @@ public class DwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "DWMA - Double Weighted Moving Average";
Description = "A moving average that applies double weighting to recent prices for increased responsiveness.";
- Series = new(name: $"DWMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"DWMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/EmaIndicator.cs b/quantower/Averages/EmaIndicator.cs
index 3e16cd9b..a50eaf48 100644
--- a/quantower/Averages/EmaIndicator.cs
+++ b/quantower/Averages/EmaIndicator.cs
@@ -7,7 +7,7 @@ public class EmaIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Periods", sortIndex: 1, 1, 1000, 1, 0)]
public int Periods { get; set; } = 10;
- [InputParameter("Use SMA for warmup period", sortIndex: 2)]
+ [InputParameter("Use SMA for warmup period", sortIndex: 2)]
public bool UseSMA { get; set; } = false;
[InputParameter("Data source", sortIndex: 3, variants: [
@@ -42,7 +42,7 @@ public class EmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "EMA - Exponential Moving Average";
Description = "Exponential Moving Average";
- Series = new(name: $"EMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"EMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/EpmaIndicator.cs b/quantower/Averages/EpmaIndicator.cs
index 39aad553..eaa32fd0 100644
--- a/quantower/Averages/EpmaIndicator.cs
+++ b/quantower/Averages/EpmaIndicator.cs
@@ -40,7 +40,7 @@ public class EpmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "EPMA - Exponential Percentage Moving Average";
Description = "Exponential Percentage Moving Average";
- Series = new(name: $"EPMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"EPMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/FramaIndicator.cs b/quantower/Averages/FramaIndicator.cs
index ddcceede..038165ae 100644
--- a/quantower/Averages/FramaIndicator.cs
+++ b/quantower/Averages/FramaIndicator.cs
@@ -40,7 +40,7 @@ public class FramaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "FRAMA - Fractal Adaptive Moving Average";
Description = "Fractal Adaptive Moving Average";
- Series = new(name: $"FRAMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"FRAMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/FwmaIndicator.cs b/quantower/Averages/FwmaIndicator.cs
index bb9c89a1..2a6a90a2 100644
--- a/quantower/Averages/FwmaIndicator.cs
+++ b/quantower/Averages/FwmaIndicator.cs
@@ -40,7 +40,7 @@ public class FwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "FWMA - Fibonacci Weighted Moving Average";
Description = "Fibonacci Weighted Moving Average";
- Series = new(name: $"FWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"FWMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/GmaIndicator.cs b/quantower/Averages/GmaIndicator.cs
index f4cfce92..ab93afe3 100644
--- a/quantower/Averages/GmaIndicator.cs
+++ b/quantower/Averages/GmaIndicator.cs
@@ -40,7 +40,7 @@ public class GmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "GMA - Gaussian Moving Average";
Description = "Gaussian Moving Average";
- Series = new(name: $"GMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"GMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/HmaIndicator.cs b/quantower/Averages/HmaIndicator.cs
index e9241249..d1d159da 100644
--- a/quantower/Averages/HmaIndicator.cs
+++ b/quantower/Averages/HmaIndicator.cs
@@ -40,7 +40,7 @@ public class HmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "HMA - Hull Moving Average";
Description = "Hull Moving Average";
- Series = new(name: $"HMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"HMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/HtitIndicator.cs b/quantower/Averages/HtitIndicator.cs
index 411d8afa..04daa1b4 100644
--- a/quantower/Averages/HtitIndicator.cs
+++ b/quantower/Averages/HtitIndicator.cs
@@ -37,7 +37,7 @@ public class HtitIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "HTIT - Hilbert Transform Instantaneous Trendline";
Description = "Hilbert Transform Instantaneous Trendline (Note: This indicator may not be fully functional)";
- Series = new(name: "HTIT", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: "HTIT", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/HwmaIndicator.cs b/quantower/Averages/HwmaIndicator.cs
index de81b817..b201a20a 100644
--- a/quantower/Averages/HwmaIndicator.cs
+++ b/quantower/Averages/HwmaIndicator.cs
@@ -49,7 +49,7 @@ public class HwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "HWMA - Holt-Winter Moving Average";
Description = "Holt-Winter Moving Average";
- Series = new(name: $"HWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"HWMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/JmaIndicator.cs b/quantower/Averages/JmaIndicator.cs
index 5050d3da..382e93c0 100644
--- a/quantower/Averages/JmaIndicator.cs
+++ b/quantower/Averages/JmaIndicator.cs
@@ -11,7 +11,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
[InputParameter("Phase", sortIndex: 2, -100, 100, 1, 0)]
public int Phase { get; set; } = 0;
- [InputParameter("Beta factor", sortIndex: 3, minimum: 0, maximum:5 , increment: 0.01, decimalPlaces: 2)]
+ [InputParameter("Beta factor", sortIndex: 3, minimum: 0, maximum: 5, increment: 0.01, decimalPlaces: 2)]
public double Factor { get; set; } = 0.45;
[InputParameter("Data source", sortIndex: 4, variants: [
@@ -34,7 +34,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
private Jma? ma;
protected LineSeries? Series;
protected string? SourceName;
- public int MinHistoryDepths => Math.Max(65,Periods * 2);
+ public int MinHistoryDepths => Math.Max(65, Periods * 2);
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName => $"JMA {Periods}:{Phase}:{Factor:F2}:{SourceName}";
@@ -46,7 +46,7 @@ public class JmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "JMA - Jurik Moving Average";
Description = "Jurik Moving Average (Note: This indicator may have consistency issues)";
- Series = new(name: $"JMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"JMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/KamaIndicator.cs b/quantower/Averages/KamaIndicator.cs
index dfc14a90..f84c87df 100644
--- a/quantower/Averages/KamaIndicator.cs
+++ b/quantower/Averages/KamaIndicator.cs
@@ -46,7 +46,7 @@ public class KamaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "KAMA - Kaufman's Adaptive Moving Average";
Description = "Kaufman's Adaptive Moving Average";
- Series = new(name: $"KAMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"KAMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/LtmaIndicator.cs b/quantower/Averages/LtmaIndicator.cs
index 77f2f868..ce1c4ad4 100644
--- a/quantower/Averages/LtmaIndicator.cs
+++ b/quantower/Averages/LtmaIndicator.cs
@@ -40,7 +40,7 @@ public class LtmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "LTMA - Laguerre Time Moving Average";
Description = "Laguerre Time Moving Average";
- Series = new(name: $"LTMA {Gamma}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"LTMA {Gamma}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/MaafIndicator.cs b/quantower/Averages/MaafIndicator.cs
index fc474704..3a5a9647 100644
--- a/quantower/Averages/MaafIndicator.cs
+++ b/quantower/Averages/MaafIndicator.cs
@@ -43,7 +43,7 @@ public class MaafIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MAAF - Median Adaptive Averaging Filter";
Description = "Median Adaptive Averaging Filter (Note: This indicator may have consistency issues)";
- Series = new(name: $"MAAF {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"MAAF {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/MamaIndicator.cs b/quantower/Averages/MamaIndicator.cs
index 39b31438..09b9e086 100644
--- a/quantower/Averages/MamaIndicator.cs
+++ b/quantower/Averages/MamaIndicator.cs
@@ -44,7 +44,7 @@ public class MamaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MAMA - MESA Adaptive Moving Average";
Description = "MESA Adaptive Moving Average";
- MamaSeries = new(name: "MAMA", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ MamaSeries = new(name: "MAMA", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
FamaSeries = new(name: "FAMA", color: Color.Red, width: 2, style: LineStyle.Solid);
AddLineSeries(MamaSeries);
AddLineSeries(FamaSeries);
diff --git a/quantower/Averages/MgdiIndicator.cs b/quantower/Averages/MgdiIndicator.cs
index 7a865b5b..7e63ffa2 100644
--- a/quantower/Averages/MgdiIndicator.cs
+++ b/quantower/Averages/MgdiIndicator.cs
@@ -43,7 +43,7 @@ public class MgdiIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MGDI - McGinley Dynamic Indicator";
Description = "McGinley Dynamic Indicator";
- Series = new(name: $"MGDI {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"MGDI {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/MmaIndicator.cs b/quantower/Averages/MmaIndicator.cs
index c7701c74..e4c4fb50 100644
--- a/quantower/Averages/MmaIndicator.cs
+++ b/quantower/Averages/MmaIndicator.cs
@@ -40,7 +40,7 @@ public class MmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MMA - Modified Moving Average";
Description = "Modified Moving Average";
- Series = new(name: $"MMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"MMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/PwmaIndicator.cs b/quantower/Averages/PwmaIndicator.cs
index 14eb349d..52d9ea72 100644
--- a/quantower/Averages/PwmaIndicator.cs
+++ b/quantower/Averages/PwmaIndicator.cs
@@ -40,7 +40,7 @@ public class PwmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "PWMA - Pascal's Weighted Moving Average";
Description = "Pascal's Weighted Moving Average";
- Series = new(name: $"PWMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"PWMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/QemaIndicator.cs b/quantower/Averages/QemaIndicator.cs
index d9a114eb..1c0e908f 100644
--- a/quantower/Averages/QemaIndicator.cs
+++ b/quantower/Averages/QemaIndicator.cs
@@ -49,7 +49,7 @@ public class QemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "QEMA - Quadruple Exponential Moving Average";
Description = "Quadruple Exponential Moving Average";
- Series = new(name: $"QEMA {K1},{K2},{K3},{K4}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"QEMA {K1},{K2},{K3},{K4}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/RemaIndicator.cs b/quantower/Averages/RemaIndicator.cs
index 3197b9bf..b48147d3 100644
--- a/quantower/Averages/RemaIndicator.cs
+++ b/quantower/Averages/RemaIndicator.cs
@@ -43,7 +43,7 @@ public class RemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "REMA - Regularized Exponential Moving Average";
Description = "Regularized Exponential Moving Average";
- Series = new(name: $"REMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"REMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/RmaIndicator.cs b/quantower/Averages/RmaIndicator.cs
index b0cf4306..5265517e 100644
--- a/quantower/Averages/RmaIndicator.cs
+++ b/quantower/Averages/RmaIndicator.cs
@@ -40,7 +40,7 @@ public class RmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "RMA - Relative Moving Average (Wilder's Moving Average)";
Description = "Relative Moving Average, also known as Wilder's Moving Average";
- Series = new(name: $"RMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"RMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/SinemaIndicator.cs b/quantower/Averages/SinemaIndicator.cs
index 0aa8feb8..8c788f8d 100644
--- a/quantower/Averages/SinemaIndicator.cs
+++ b/quantower/Averages/SinemaIndicator.cs
@@ -40,7 +40,7 @@ public class SinemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "SINEMA - Sine-Weighted Moving Average";
Description = "Sine-Weighted Moving Average";
- Series = new(name: $"SINEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"SINEMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/SmaIndicator.cs b/quantower/Averages/SmaIndicator.cs
index 692b5545..66b3d853 100644
--- a/quantower/Averages/SmaIndicator.cs
+++ b/quantower/Averages/SmaIndicator.cs
@@ -39,7 +39,7 @@ public class SmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "SMA - Simple Moving Average";
Description = "Simple Moving Average";
- Series = new(name: $"SMA {Period}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"SMA {Period}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/SmmaIndicator.cs b/quantower/Averages/SmmaIndicator.cs
index befb1816..eae89e0a 100644
--- a/quantower/Averages/SmmaIndicator.cs
+++ b/quantower/Averages/SmmaIndicator.cs
@@ -40,7 +40,7 @@ public class SmmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "SMMA - Smoothed Moving Average";
Description = "Smoothed Moving Average";
- Series = new(name: $"SMMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"SMMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/T3Indicator.cs b/quantower/Averages/T3Indicator.cs
index 7ab6d14e..953cc773 100644
--- a/quantower/Averages/T3Indicator.cs
+++ b/quantower/Averages/T3Indicator.cs
@@ -46,7 +46,7 @@ public class T3Indicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "T3 - Tillson T3 Moving Average";
Description = "Tillson T3 Moving Average";
- Series = new(name: $"T3 {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"T3 {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/TemaIndicator.cs b/quantower/Averages/TemaIndicator.cs
index 23862874..5fe12a90 100644
--- a/quantower/Averages/TemaIndicator.cs
+++ b/quantower/Averages/TemaIndicator.cs
@@ -40,7 +40,7 @@ public class TemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "TEMA - Triple Exponential Moving Average";
Description = "Triple Exponential Moving Average";
- Series = new(name: $"TEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"TEMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/TrimaIndicator.cs b/quantower/Averages/TrimaIndicator.cs
index 14e477df..73e138db 100644
--- a/quantower/Averages/TrimaIndicator.cs
+++ b/quantower/Averages/TrimaIndicator.cs
@@ -40,7 +40,7 @@ public class TrimaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "TRIMA - Triangular Moving Average";
Description = "Triangular Moving Average";
- Series = new(name: $"TRIMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"TRIMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/VidyaIndicator.cs b/quantower/Averages/VidyaIndicator.cs
index 5e411693..54a6b18f 100644
--- a/quantower/Averages/VidyaIndicator.cs
+++ b/quantower/Averages/VidyaIndicator.cs
@@ -46,7 +46,7 @@ public class VidyaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "VIDYA - Variable Index Dynamic Average";
Description = "Variable Index Dynamic Average";
- Series = new(name: $"VIDYA {ShortPeriod}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"VIDYA {ShortPeriod}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/WmaIndicator.cs b/quantower/Averages/WmaIndicator.cs
index ba8d8d60..ef1653dd 100644
--- a/quantower/Averages/WmaIndicator.cs
+++ b/quantower/Averages/WmaIndicator.cs
@@ -40,7 +40,7 @@ public class WmaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "WMA - Weighted Moving Average";
Description = "Weighted Moving Average";
- Series = new(name: $"WMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"WMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Averages/ZlemaIndicator.cs b/quantower/Averages/ZlemaIndicator.cs
index a6ffd28e..268d142b 100644
--- a/quantower/Averages/ZlemaIndicator.cs
+++ b/quantower/Averages/ZlemaIndicator.cs
@@ -41,7 +41,7 @@ public class ZlemaIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "ZLEMA - Zero Lag Exponential Moving Average";
Description = "Zero Lag Exponential Moving Average";
- Series = new(name: $"ZLEMA {Periods}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"ZLEMA {Periods}", color: IndicatorExtensions.Averages, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Volatility/FlowIndicator.cs b/quantower/Experiments/FlowIndicator.cs
similarity index 100%
rename from quantower/Volatility/FlowIndicator.cs
rename to quantower/Experiments/FlowIndicator.cs
diff --git a/quantower/Volatility/TestIndicator.cs b/quantower/Experiments/TestIndicator.cs
similarity index 94%
rename from quantower/Volatility/TestIndicator.cs
rename to quantower/Experiments/TestIndicator.cs
index fbb60c9a..665f2b09 100644
--- a/quantower/Volatility/TestIndicator.cs
+++ b/quantower/Experiments/TestIndicator.cs
@@ -37,7 +37,7 @@ public class TestIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
Name = "TEST";
Description = "test and test and test and more test.";
- Series = new(name: $"{Name}", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ Series = new(name: $"{Name}", color: IndicatorExtensions.Volatility, width: 2, style: LineStyle.Solid);
AddLineSeries(Series);
}
diff --git a/quantower/Experiments/_Experiments.csproj b/quantower/Experiments/_Experiments.csproj
new file mode 100644
index 00000000..a788a51e
--- /dev/null
+++ b/quantower/Experiments/_Experiments.csproj
@@ -0,0 +1,30 @@
+
+
+ Experiments
+ Indicator
+ bin\$(Configuration)\
+ false
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\.github\TradingPlatform.BusinessLayer.dll
+
+
+ TradingPlatform.BusinessLayer.xml
+
+
+
+
+
+
+
+
diff --git a/quantower/IndicatorExtensions.cs b/quantower/IndicatorExtensions.cs
index 1bfd2d5f..7bcbc61d 100644
--- a/quantower/IndicatorExtensions.cs
+++ b/quantower/IndicatorExtensions.cs
@@ -16,6 +16,12 @@ public enum MaType
public static class IndicatorExtensions
{
+ public static readonly Color Averages = Color.FromArgb(255, 255, 128); // #FFFF80 - Yellow
+ public static readonly Color Volume = Color.FromArgb(128, 255, 128); // #80FF80 - Green
+ public static readonly Color Volatility = Color.FromArgb(255, 128, 128); // #FF8080 - Red
+ public static readonly Color Statistics = Color.FromArgb(128, 128, 255); // #8080FF - Blue
+ public static readonly Color Oscillators = Color.FromArgb(255, 128, 255); // #FF80FF - Magenta
+ public static readonly Color Momentum = Color.FromArgb(128, 255, 255); // #80FFFF - Cyan
public static TValue GetInputValue(this Indicator indicator, UpdateArgs args, SourceType source)
{
var historicalData = indicator.HistoricalData;
@@ -179,7 +185,6 @@ public static class IndicatorExtensions
}
}
-
public static void DrawText(this Indicator indicator, PaintChartEventArgs args, string text)
{
if (indicator.CurrentChart == null)
@@ -210,7 +215,3 @@ public static class IndicatorExtensions
};
}
}
-
-
-
-
diff --git a/quantower/Momentum/AdxIndicator.cs b/quantower/Momentum/AdxIndicator.cs
new file mode 100644
index 00000000..d376d60d
--- /dev/null
+++ b/quantower/Momentum/AdxIndicator.cs
@@ -0,0 +1,53 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class AdxIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
+ public int Periods { get; set; } = 14;
+
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Adx? adx;
+ protected LineSeries? AdxSeries;
+ public int MinHistoryDepths => Math.Max(5, Periods * 3); // Need extra periods for ADX calculation
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public AdxIndicator()
+ {
+ Name = "ADX - Average Directional Movement Index";
+ Description = "Measures the strength of a trend, regardless of its direction.";
+ SeparateWindow = true;
+
+ AdxSeries = new($"ADX {Periods}", color: IndicatorExtensions.Momentum, 2, LineStyle.Solid);
+ AddLineSeries(AdxSeries);
+ }
+
+ protected override void OnInit()
+ {
+ adx = new Adx(Periods);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TBar input = IndicatorExtensions.GetInputBar(this, args);
+ TValue result = adx!.Calc(input);
+
+ AdxSeries!.SetValue(result.Value);
+ AdxSeries!.SetMarker(0, Color.Transparent);
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+
+ public override string ShortName => $"ADX ({Periods})";
+
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintSmoothCurve(args, AdxSeries!, adx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Momentum/AdxrIndicator.cs b/quantower/Momentum/AdxrIndicator.cs
new file mode 100644
index 00000000..2a55689a
--- /dev/null
+++ b/quantower/Momentum/AdxrIndicator.cs
@@ -0,0 +1,55 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class AdxrIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
+ public int Periods { get; set; } = 14;
+
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Adxr? adxr;
+ protected LineSeries? AdxrSeries;
+ public int MinHistoryDepths => Math.Max(5, Periods * 4); // Need extra periods for ADXR calculation
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public AdxrIndicator()
+ {
+ Name = "ADXR - Average Directional Movement Index Rating";
+ Description = "Measures trend strength by comparing current ADX with historical ADX values.";
+ SeparateWindow = true;
+
+ AdxrSeries = new($"ADXR {Periods}", Color.Blue, 2, LineStyle.Solid);
+ AddLineSeries(AdxrSeries);
+ }
+
+ protected override void OnInit()
+ {
+ adxr = new Adxr(Periods);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TBar input = IndicatorExtensions.GetInputBar(this, args);
+ TValue result = adxr!.Calc(input);
+
+ AdxrSeries!.SetValue(result.Value);
+ AdxrSeries!.SetMarker(0, Color.Transparent);
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+
+ public override string ShortName => $"ADXR ({Periods})";
+
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintHLine(args, 25, new Pen(color: IndicatorExtensions.Momentum, width: 1)); // Strong trend line
+ this.PaintHLine(args, 20, new Pen(color: IndicatorExtensions.Momentum, width: 1)); // Weak trend line
+ this.PaintSmoothCurve(args, AdxrSeries!, adxr!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Momentum/ApoIndicator.cs b/quantower/Momentum/ApoIndicator.cs
new file mode 100644
index 00000000..23748888
--- /dev/null
+++ b/quantower/Momentum/ApoIndicator.cs
@@ -0,0 +1,71 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class ApoIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Fast Period", sortIndex: 1, 1, 2000, 1, 0)]
+ public int FastPeriod { get; set; } = 12;
+
+ [InputParameter("Slow Period", sortIndex: 2, 1, 2000, 1, 0)]
+ public int SlowPeriod { get; set; } = 26;
+
+ [InputParameter("Data source", sortIndex: 4, variants: [
+ "Open", SourceType.Open,
+ "High", SourceType.High,
+ "Low", SourceType.Low,
+ "Close", SourceType.Close,
+ "HL/2 (Median)", SourceType.HL2,
+ "OC/2 (Midpoint)", SourceType.OC2,
+ "OHL/3 (Mean)", SourceType.OHL3,
+ "HLC/3 (Typical)", SourceType.HLC3,
+ "OHLC/4 (Average)", SourceType.OHLC4,
+ "HLCC/4 (Weighted)", SourceType.HLCC4
+ ])]
+ public SourceType Source { get; set; } = SourceType.Close;
+
+
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Apo? apo;
+ protected LineSeries? ApoSeries;
+ public int MinHistoryDepths => Math.Max(FastPeriod, SlowPeriod) * 2;
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public ApoIndicator()
+ {
+ Name = "APO - Absolute Price Oscillator";
+ Description = "Shows the difference between two moving averages of different periods.";
+ SeparateWindow = true;
+
+ ApoSeries = new($"APO {FastPeriod},{SlowPeriod}", color: IndicatorExtensions.Momentum, 2, LineStyle.Solid);
+ AddLineSeries(ApoSeries);
+ }
+
+ protected override void OnInit()
+ {
+ apo = new Apo(FastPeriod, SlowPeriod);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TValue input = this.GetInputValue(args, Source);
+ TValue result = apo!.Calc(input);
+
+ ApoSeries!.SetValue(result.Value);
+ ApoSeries!.SetMarker(0, Color.Transparent);
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+
+ public override string ShortName => $"APO ({FastPeriod},{SlowPeriod})";
+
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintSmoothCurve(args, ApoSeries!, apo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Momentum/DmiIndicator.cs b/quantower/Momentum/DmiIndicator.cs
new file mode 100644
index 00000000..7cd72f7b
--- /dev/null
+++ b/quantower/Momentum/DmiIndicator.cs
@@ -0,0 +1,59 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class DmiIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
+ public int Periods { get; set; } = 14;
+
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Dmi? dmi;
+ protected LineSeries? PlusDiSeries;
+ protected LineSeries? MinusDiSeries;
+ public int MinHistoryDepths => Math.Max(5, Periods * 2);
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public DmiIndicator()
+ {
+ Name = "DMI - Directional Movement Index";
+ Description = "Identifies the directional movement of a price by comparing successive highs and lows.";
+ SeparateWindow = true;
+
+ PlusDiSeries = new($"+DI {Periods}", color: Color.Red, 2, LineStyle.Solid);
+ MinusDiSeries = new($"-DI {Periods}", color: Color.Blue, 2, LineStyle.Solid);
+ AddLineSeries(PlusDiSeries);
+ AddLineSeries(MinusDiSeries);
+ }
+
+ protected override void OnInit()
+ {
+ dmi = new Dmi(Periods);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TBar input = IndicatorExtensions.GetInputBar(this, args);
+ var result = dmi!.Calc(input);
+
+ PlusDiSeries!.SetValue(dmi.PlusDI);
+ MinusDiSeries!.SetValue(dmi.MinusDI);
+ PlusDiSeries!.SetMarker(0, Color.Transparent);
+ MinusDiSeries!.SetMarker(0, Color.Transparent);
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+
+ public override string ShortName => $"DMI ({Periods})";
+
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintSmoothCurve(args, PlusDiSeries!, dmi!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ this.PaintSmoothCurve(args, MinusDiSeries!, dmi!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Momentum/DmxIndicator.cs b/quantower/Momentum/DmxIndicator.cs
new file mode 100644
index 00000000..fa55606a
--- /dev/null
+++ b/quantower/Momentum/DmxIndicator.cs
@@ -0,0 +1,68 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class DmxIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("DMI Periods", sortIndex: 1, 1, 2000, 1, 0)]
+ public int DmiPeriods { get; set; } = 14;
+
+ [InputParameter("JMA Smoothing Periods", sortIndex: 2, 1, 2000, 1, 0)]
+ public int JmaPeriods { get; set; } = 12;
+
+ [InputParameter("JMA Phase", sortIndex: 3, -100, 100, 1, 0)]
+ public int JmaPhase { get; set; } = 100;
+
+ [InputParameter("JMA Factor", sortIndex: 4, 0.01, 1, 0.01, 2)]
+ public double JmaFactor { get; set; } = 0.3;
+
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Dmx? dmx;
+ protected LineSeries? PlusDiSeries;
+ protected LineSeries? MinusDiSeries;
+ public int MinHistoryDepths => Math.Max(5, (DmiPeriods + JmaPeriods) * 2);
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public DmxIndicator()
+ {
+ Name = "DMX - Enhanced Directional Movement Index";
+ Description = "An enhanced version of DMI using JMA smoothing for better noise reduction and responsiveness.";
+ SeparateWindow = true;
+
+ PlusDiSeries = new($"+DI {DmiPeriods}", color: Color.Red, 2, LineStyle.Solid);
+ MinusDiSeries = new($"-DI {DmiPeriods}", color: Color.Blue, 2, LineStyle.Solid);
+ AddLineSeries(PlusDiSeries);
+ AddLineSeries(MinusDiSeries);
+ }
+
+ protected override void OnInit()
+ {
+ dmx = new Dmx(DmiPeriods, JmaPeriods, JmaPhase, JmaFactor);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TBar input = IndicatorExtensions.GetInputBar(this, args);
+ var result = dmx!.Calc(input);
+
+ PlusDiSeries!.SetValue(dmx.PlusDI);
+ MinusDiSeries!.SetValue(dmx.MinusDI);
+ PlusDiSeries!.SetMarker(0, Color.Transparent);
+ MinusDiSeries!.SetMarker(0, Color.Transparent);
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+
+ public override string ShortName => $"DMX ({DmiPeriods})";
+
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintSmoothCurve(args, PlusDiSeries!, dmx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ this.PaintSmoothCurve(args, MinusDiSeries!, dmx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Momentum/DpoIndicator.cs b/quantower/Momentum/DpoIndicator.cs
new file mode 100644
index 00000000..61ea6f3f
--- /dev/null
+++ b/quantower/Momentum/DpoIndicator.cs
@@ -0,0 +1,67 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class DpoIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
+ public int Period { get; set; } = 20;
+
+ [InputParameter("Data source", sortIndex: 2, variants: [
+ "Open", SourceType.Open,
+ "High", SourceType.High,
+ "Low", SourceType.Low,
+ "Close", SourceType.Close,
+ "HL/2 (Median)", SourceType.HL2,
+ "OC/2 (Midpoint)", SourceType.OC2,
+ "OHL/3 (Mean)", SourceType.OHL3,
+ "HLC/3 (Typical)", SourceType.HLC3,
+ "OHLC/4 (Average)", SourceType.OHLC4,
+ "HLCC/4 (Weighted)", SourceType.HLCC4
+ ])]
+ public SourceType Source { get; set; } = SourceType.Close;
+
+ [InputParameter("Show cold values", sortIndex: 3)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Dpo? Dpo;
+ protected LineSeries? DpoSeries;
+ public int MinHistoryDepths => Period * 2;
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public DpoIndicator()
+ {
+ Name = "DPO - Detrended Price Oscillator";
+ Description = "Removes trend from price by comparing current price to a past moving average, helping identify cycles in the price.";
+ SeparateWindow = true;
+
+ DpoSeries = new($"DPO {Period}", color: IndicatorExtensions.Momentum, 2, LineStyle.Solid);
+ AddLineSeries(DpoSeries);
+ }
+
+ protected override void OnInit()
+ {
+ Dpo = new Dpo(Period);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TBar input = this.GetInputBar(args);
+ TValue result = Dpo!.Calc(input);
+
+ DpoSeries!.SetValue(result.Value);
+ DpoSeries!.SetMarker(0, Color.Transparent);
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+
+ public override string ShortName => $"DPO ({Period})";
+
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintSmoothCurve(args, DpoSeries!, Dpo!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Averages/MacdIndicator.cs b/quantower/Momentum/MacdIndicator.cs
similarity index 85%
rename from quantower/Averages/MacdIndicator.cs
rename to quantower/Momentum/MacdIndicator.cs
index cc98d2c6..fd39ce81 100644
--- a/quantower/Averages/MacdIndicator.cs
+++ b/quantower/Momentum/MacdIndicator.cs
@@ -36,9 +36,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
- private Ema? slow_ma;
- private Ema? fast_ma;
- private Ema? signal_ma;
+ private Macd? macd;
private Slope? histSlope;
protected LineSeries? MainSeries;
protected LineSeries? SignalSeries;
@@ -58,8 +56,8 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
SourceName = Source.ToString();
Name = "MACD - Moving Average Convergence Divergence";
Description = "MACD";
- MainSeries = new(name: $"MAIN", color: Color.Blue, width: 2, style: LineStyle.Solid);
- SignalSeries = new(name: $"SIGNAL", color: Color.Yellow, width: 2, style: LineStyle.Solid);
+ MainSeries = new(name: $"MAIN", color: Color.RoyalBlue, width: 2, style: LineStyle.Solid);
+ SignalSeries = new(name: $"SIGNAL", color: Color.Red, width: 2, style: LineStyle.Solid);
HistogramSeries = new(name: $"HISTOGRAM", color: Color.White, width: 2, style: LineStyle.Solid);
HistSlopeSeries = new(name: $"SLOPE", color: Color.Transparent, width: 2, style: LineStyle.Solid);
HistSlopeSeries.Visible = false;
@@ -72,9 +70,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
protected override void OnInit()
{
- slow_ma = new(Slow, useSma: UseSMA);
- fast_ma = new(Fast, useSma: UseSMA);
- signal_ma = new(Signal, useSma: UseSMA);
+ macd = new(fastPeriod: Fast, slowPeriod: Slow, signalPeriod: Signal);
histSlope = new(2);
SourceName = Source.ToString();
base.OnInit();
@@ -83,19 +79,22 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
protected override void OnUpdate(UpdateArgs args)
{
TValue input = this.GetInputValue(args, Source);
- slow_ma!.Calc(input);
- fast_ma!.Calc(input);
- double main = fast_ma.Value - slow_ma.Value;
- double signal = signal_ma!.Calc(main);
- double histogram = main - signal;
+ macd!.Calc(input);
+
+ double main = macd.MacdLine;
+ double signal = macd.SignalLine;
+ double histogram = macd.Value;
histSlope!.Calc(histogram);
MainSeries!.SetValue(main);
MainSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
+
SignalSeries!.SetValue(signal);
SignalSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
+
HistogramSeries!.SetValue(histogram);
HistogramSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
+
HistSlopeSeries!.SetValue(histSlope.Value);
HistSlopeSeries!.SetMarker(0, Color.Transparent); //OnPaintChart draws the line, hidden here
}
@@ -118,7 +117,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
for (int i = rightIndex; i < leftIndex; i++)
{
int barX = (int)converter.GetChartX(this.HistoricalData.Time(i));
- int barY = (int)converter.GetChartY(HistogramSeries![i]*2.0);
+ int barY = (int)converter.GetChartY(HistogramSeries![i] * 2.0);
int barY0 = (int)converter.GetChartY(0);
int HistBarWidth = this.CurrentChart.BarsWidth - 2;
@@ -139,8 +138,8 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
}
}
- this.PaintSmoothCurve(args, MainSeries!, slow_ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.3);
- this.PaintSmoothCurve(args, SignalSeries!, slow_ma!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ this.PaintSmoothCurve(args, MainSeries!, macd!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.3);
+ this.PaintSmoothCurve(args, SignalSeries!, macd!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
base.OnPaintChart(args);
}
}
diff --git a/quantower/Momentum/_Momentum.csproj b/quantower/Momentum/_Momentum.csproj
index 29adbe09..f82befdd 100644
--- a/quantower/Momentum/_Momentum.csproj
+++ b/quantower/Momentum/_Momentum.csproj
@@ -27,4 +27,4 @@
-
\ No newline at end of file
+
diff --git a/quantower/Volatility/RsiIndicator.cs b/quantower/Oscillators/RsiIndicator.cs
similarity index 95%
rename from quantower/Volatility/RsiIndicator.cs
rename to quantower/Oscillators/RsiIndicator.cs
index fd0a4eec..0f4fd41d 100644
--- a/quantower/Volatility/RsiIndicator.cs
+++ b/quantower/Oscillators/RsiIndicator.cs
@@ -37,7 +37,7 @@ public class RsiIndicator : Indicator, IWatchlistIndicator
Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions.";
SeparateWindow = true;
SourceName = Source.ToString();
- RsiSeries = new($"RSI {Periods}", Color.Blue, 2, LineStyle.Solid);
+ RsiSeries = new($"RSI {Periods}", color: IndicatorExtensions.Oscillators, 2, LineStyle.Solid);
AddLineSeries(RsiSeries);
}
diff --git a/quantower/Volatility/RsxIndicator.cs b/quantower/Oscillators/RsxIndicator.cs
similarity index 95%
rename from quantower/Volatility/RsxIndicator.cs
rename to quantower/Oscillators/RsxIndicator.cs
index 5158bc77..16fb8d43 100644
--- a/quantower/Volatility/RsxIndicator.cs
+++ b/quantower/Oscillators/RsxIndicator.cs
@@ -37,7 +37,7 @@ public class RsxIndicator : Indicator, IWatchlistIndicator
Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions.";
SeparateWindow = true;
SourceName = Source.ToString();
- RsxSeries = new($"RSX {Period}", Color.Blue, 2, LineStyle.Solid);
+ RsxSeries = new($"RSX {Period}", color: IndicatorExtensions.Oscillators, 2, LineStyle.Solid);
AddLineSeries(RsxSeries);
}
diff --git a/quantower/Statistics/CurvatureIndicator.cs b/quantower/Statistics/CurvatureIndicator.cs
index 2180cd61..7bade979 100644
--- a/quantower/Statistics/CurvatureIndicator.cs
+++ b/quantower/Statistics/CurvatureIndicator.cs
@@ -36,7 +36,7 @@ public class CurvatureIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- CurvatureSeries = new("Curvature", Color.Blue, 2, LineStyle.Solid);
+ CurvatureSeries = new("Curvature", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(CurvatureSeries);
}
diff --git a/quantower/Statistics/EntropyIndicator.cs b/quantower/Statistics/EntropyIndicator.cs
index 5fb2a517..cea46fa5 100644
--- a/quantower/Statistics/EntropyIndicator.cs
+++ b/quantower/Statistics/EntropyIndicator.cs
@@ -34,7 +34,7 @@ public class EntropyIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- EntropySeries = new("Entropy", Color.Blue, 2, LineStyle.Solid);
+ EntropySeries = new("Entropy", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(EntropySeries);
}
diff --git a/quantower/Statistics/KurtosisIndicator.cs b/quantower/Statistics/KurtosisIndicator.cs
index 620f53dd..4e8a0e3d 100644
--- a/quantower/Statistics/KurtosisIndicator.cs
+++ b/quantower/Statistics/KurtosisIndicator.cs
@@ -35,7 +35,7 @@ public class KurtosisIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- KurtosisSeries = new("Kurtosis", Color.Blue, 2, LineStyle.Solid);
+ KurtosisSeries = new("Kurtosis", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(KurtosisSeries);
}
diff --git a/quantower/Statistics/MaxIndicator.cs b/quantower/Statistics/MaxIndicator.cs
index 5c696095..ee5edb0f 100644
--- a/quantower/Statistics/MaxIndicator.cs
+++ b/quantower/Statistics/MaxIndicator.cs
@@ -38,7 +38,7 @@ public class MaxIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
- MaxSeries = new("Max", Color.Blue, 2, LineStyle.Solid);
+ MaxSeries = new("Max", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(MaxSeries);
}
diff --git a/quantower/Statistics/MedianIndicator.cs b/quantower/Statistics/MedianIndicator.cs
index 471ace45..c2195066 100644
--- a/quantower/Statistics/MedianIndicator.cs
+++ b/quantower/Statistics/MedianIndicator.cs
@@ -35,7 +35,7 @@ public class MedianIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
- MedianSeries = new("Median", Color.Blue, 2, LineStyle.Solid);
+ MedianSeries = new("Median", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(MedianSeries);
}
diff --git a/quantower/Statistics/MinIndicator.cs b/quantower/Statistics/MinIndicator.cs
index 7f55fcd4..e7545a57 100644
--- a/quantower/Statistics/MinIndicator.cs
+++ b/quantower/Statistics/MinIndicator.cs
@@ -38,7 +38,7 @@ public class MinIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
- MinSeries = new("Min", Color.Blue, 2, LineStyle.Solid);
+ MinSeries = new("Min", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(MinSeries);
}
diff --git a/quantower/Statistics/ModeIndicator.cs b/quantower/Statistics/ModeIndicator.cs
index 7294ed2c..2df23e93 100644
--- a/quantower/Statistics/ModeIndicator.cs
+++ b/quantower/Statistics/ModeIndicator.cs
@@ -35,7 +35,7 @@ public class ModeIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
- ModeSeries = new("Mode", Color.Blue, 2, LineStyle.Solid);
+ ModeSeries = new("Mode", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(ModeSeries);
}
diff --git a/quantower/Statistics/PercentileIndicator.cs b/quantower/Statistics/PercentileIndicator.cs
index a6cc5a2d..96c1cf65 100644
--- a/quantower/Statistics/PercentileIndicator.cs
+++ b/quantower/Statistics/PercentileIndicator.cs
@@ -38,7 +38,7 @@ public class PercentileIndicator : Indicator, IWatchlistIndicator
SeparateWindow = false;
SourceName = Source.ToString();
- PercentileSeries = new("Percentile", Color.Blue, 2, LineStyle.Solid);
+ PercentileSeries = new("Percentile", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(PercentileSeries);
}
diff --git a/quantower/Statistics/SkewIndicator.cs b/quantower/Statistics/SkewIndicator.cs
index 0430c064..decd7444 100644
--- a/quantower/Statistics/SkewIndicator.cs
+++ b/quantower/Statistics/SkewIndicator.cs
@@ -35,7 +35,7 @@ public class SkewIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- SkewSeries = new("Skew", Color.Blue, 2, LineStyle.Solid);
+ SkewSeries = new("Skew", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(SkewSeries);
}
diff --git a/quantower/Statistics/SlopeIndicator.cs b/quantower/Statistics/SlopeIndicator.cs
index c5d32741..e5534d7c 100644
--- a/quantower/Statistics/SlopeIndicator.cs
+++ b/quantower/Statistics/SlopeIndicator.cs
@@ -36,7 +36,7 @@ public class SlopeIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- SlopeSeries = new("Slope", Color.Blue, 2, LineStyle.Solid);
+ SlopeSeries = new("Slope", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
LineSeries = new("Regression Line", Color.Red, 1, LineStyle.Solid);
AddLineSeries(SlopeSeries);
AddLineSeries(LineSeries);
diff --git a/quantower/Statistics/StddevIndicator.cs b/quantower/Statistics/StddevIndicator.cs
index a9139d91..02d5f57a 100644
--- a/quantower/Statistics/StddevIndicator.cs
+++ b/quantower/Statistics/StddevIndicator.cs
@@ -38,7 +38,7 @@ public class StddevIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- StddevSeries = new("StdDev", Color.Blue, 2, LineStyle.Solid);
+ StddevSeries = new("StdDev", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(StddevSeries);
}
diff --git a/quantower/Statistics/VarianceIndicator.cs b/quantower/Statistics/VarianceIndicator.cs
index e2c6ca2e..5f3511f9 100644
--- a/quantower/Statistics/VarianceIndicator.cs
+++ b/quantower/Statistics/VarianceIndicator.cs
@@ -38,7 +38,7 @@ public class VarianceIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- VarianceSeries = new("Variance", Color.Blue, 2, LineStyle.Solid);
+ VarianceSeries = new("Variance", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(VarianceSeries);
}
diff --git a/quantower/Statistics/ZscoreIndicator.cs b/quantower/Statistics/ZscoreIndicator.cs
index e2a52ead..ba70a8a5 100644
--- a/quantower/Statistics/ZscoreIndicator.cs
+++ b/quantower/Statistics/ZscoreIndicator.cs
@@ -35,7 +35,7 @@ public class ZscoreIndicator : Indicator, IWatchlistIndicator
SeparateWindow = true;
SourceName = Source.ToString();
- ZscoreSeries = new("Z-Score", Color.Blue, 2, LineStyle.Solid);
+ ZscoreSeries = new("Z-Score", color: IndicatorExtensions.Statistics, 2, LineStyle.Solid);
AddLineSeries(ZscoreSeries);
}
diff --git a/quantower/Volatility/AtrIndicator.cs b/quantower/Volatility/AtrIndicator.cs
index 614fecdb..0999533a 100644
--- a/quantower/Volatility/AtrIndicator.cs
+++ b/quantower/Volatility/AtrIndicator.cs
@@ -47,7 +47,7 @@ public class AtrIndicator : Indicator, IWatchlistIndicator
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
- this.PaintHLine(args, 0.05, new Pen(Color.DarkRed, width: 2));
+ this.PaintHLine(args, 0.05, new Pen(color: IndicatorExtensions.Volatility, width: 2));
this.PaintSmoothCurve(args, AtrSeries!, atr!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
diff --git a/quantower/Volatility/CmoIndicator.cs b/quantower/Volatility/CmoIndicator.cs
index a5ec940c..a507d652 100644
--- a/quantower/Volatility/CmoIndicator.cs
+++ b/quantower/Volatility/CmoIndicator.cs
@@ -38,7 +38,7 @@ public class CmoIndicator : Indicator, IWatchlistIndicator
Description = "Measures the momentum of price changes using the difference between the sum of recent gains and the sum of recent losses.";
SeparateWindow = true;
SourceName = Source.ToString();
- CmoSeries = new($"CMO {Periods}", Color.Blue, 2, LineStyle.Solid);
+ CmoSeries = new($"CMO {Periods}", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
AddLineSeries(CmoSeries);
}
diff --git a/quantower/Volatility/CviIndicator.cs b/quantower/Volatility/CviIndicator.cs
index 7a43523f..be14de29 100644
--- a/quantower/Volatility/CviIndicator.cs
+++ b/quantower/Volatility/CviIndicator.cs
@@ -22,7 +22,7 @@ public class CviIndicator : Indicator, IWatchlistIndicator
Description = "Measures the volatility of a financial instrument by comparing the spread between the high and low prices.";
SeparateWindow = true;
- CviSeries = new($"CVI {Periods}", Color.Blue, 2, LineStyle.Solid);
+ CviSeries = new($"CVI {Periods}", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
AddLineSeries(CviSeries);
}
@@ -48,7 +48,7 @@ public class CviIndicator : Indicator, IWatchlistIndicator
public override void OnPaintChart(PaintChartEventArgs args)
{
base.OnPaintChart(args);
- this.PaintHLine(args, 0.05, new Pen(Color.DarkRed, width: 2));
+ this.PaintHLine(args, 0.05, new Pen(color: IndicatorExtensions.Volatility, width: 2));
this.PaintSmoothCurve(args, CviSeries!, cvi!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
}
}
diff --git a/quantower/Volatility/HistoricalIndicator.cs b/quantower/Volatility/HistoricalIndicator.cs
index 949b2dde..9637679f 100644
--- a/quantower/Volatility/HistoricalIndicator.cs
+++ b/quantower/Volatility/HistoricalIndicator.cs
@@ -22,7 +22,7 @@ public class HistoricalIndicator : Indicator, IWatchlistIndicator
Description = "Measures price fluctuations over time, indicating market volatility based on past price movements.";
SeparateWindow = true;
- HvSeries = new("HV", Color.Blue, 2, LineStyle.Solid);
+ HvSeries = new("HV", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
AddLineSeries(HvSeries);
}
diff --git a/quantower/Volatility/JbandsIndicator.cs b/quantower/Volatility/JbandsIndicator.cs
index a77312c8..e977bb63 100644
--- a/quantower/Volatility/JbandsIndicator.cs
+++ b/quantower/Volatility/JbandsIndicator.cs
@@ -39,8 +39,8 @@ public class JbandsIndicator : Indicator, IWatchlistIndicator
Description = "Upper and Lower Bands.";
SeparateWindow = false;
- UbSeries = new("UB", Color.Blue, 2, LineStyle.Solid);
- LbSeries = new("LB", Color.Red, 2, LineStyle.Solid);
+ UbSeries = new("UB", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
+ LbSeries = new("LB", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
AddLineSeries(UbSeries);
AddLineSeries(LbSeries);
}
diff --git a/quantower/Volatility/JvoltyIndicator.cs b/quantower/Volatility/JvoltyIndicator.cs
index 7a49d9bb..c3e5146a 100644
--- a/quantower/Volatility/JvoltyIndicator.cs
+++ b/quantower/Volatility/JvoltyIndicator.cs
@@ -35,7 +35,7 @@ public class JvoltyIndicator : Indicator, IWatchlistIndicator
Description = "Measures market volatility according to Mark Jurik.";
SeparateWindow = true;
- JvoltySeries = new("JVOLTY", Color.Blue, 2, LineStyle.Solid);
+ JvoltySeries = new("JVOLTY", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
AddLineSeries(JvoltySeries);
}
diff --git a/quantower/Volatility/RealizedIndicator.cs b/quantower/Volatility/RealizedIndicator.cs
index 6625a71f..661626f5 100644
--- a/quantower/Volatility/RealizedIndicator.cs
+++ b/quantower/Volatility/RealizedIndicator.cs
@@ -22,7 +22,7 @@ public class RealizedIndicator : Indicator, IWatchlistIndicator
Description = "Measures actual price volatility over a specific period, useful for risk assessment and forecasting.";
SeparateWindow = true;
- RvSeries = new("RV", Color.Blue, 2, LineStyle.Solid);
+ RvSeries = new("RV", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
AddLineSeries(RvSeries);
}
diff --git a/quantower/Volatility/RviIndicator.cs b/quantower/Volatility/RviIndicator.cs
index 005ae679..516f4ac6 100644
--- a/quantower/Volatility/RviIndicator.cs
+++ b/quantower/Volatility/RviIndicator.cs
@@ -19,7 +19,7 @@ public class RviIndicator : Indicator, IWatchlistIndicator
Description = "Measures the direction of volatility, helping to identify overbought or oversold conditions in price.";
SeparateWindow = true;
- RviSeries = new("RVI", Color.Blue, 2, LineStyle.Solid);
+ RviSeries = new("RVI", color: IndicatorExtensions.Volatility, 2, LineStyle.Solid);
AddLineSeries(RviSeries);
}
diff --git a/quantower/Volume/ObvIndicator.cs b/quantower/Volume/ObvIndicator.cs
new file mode 100644
index 00000000..bbbdb89b
--- /dev/null
+++ b/quantower/Volume/ObvIndicator.cs
@@ -0,0 +1,51 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class ObvIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Obv? obv;
+ protected LineSeries? ObvSeries;
+ public int MinHistoryDepths => 5;
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public ObvIndicator()
+ {
+ Name = "OBV - On-Balance Volume";
+ Description = "Measures buying and selling pressure by analyzing volume in relation to price changes.";
+ SeparateWindow = true;
+
+ ObvSeries = new("OBV", color: IndicatorExtensions.Volume, 2, LineStyle.Solid);
+ AddLineSeries(ObvSeries);
+ }
+
+ protected override void OnInit()
+ {
+ obv = new Obv();
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TBar input = IndicatorExtensions.GetInputBar(this, args);
+ TValue result = obv!.Calc(input);
+
+ ObvSeries!.SetValue(result.Value);
+ ObvSeries!.SetMarker(0, Color.Transparent);
+ }
+
+#pragma warning disable CA1416 // Validate platform compatibility
+
+ public override string ShortName => "OBV";
+
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintHLine(args, 0, new Pen(color: Color.DimGray, width: 1));
+ this.PaintSmoothCurve(args, ObvSeries!, obv!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Volume/_Volume.csproj b/quantower/Volume/_Volume.csproj
index db6a0d69..d2004efd 100644
--- a/quantower/Volume/_Volume.csproj
+++ b/quantower/Volume/_Volume.csproj
@@ -27,4 +27,4 @@
-
\ No newline at end of file
+
From dd6c68f08f3de3097240de17cb031a7224d0ff13 Mon Sep 17 00:00:00 2001
From: codefactor-io
Date: Thu, 7 Nov 2024 04:56:55 +0000
Subject: [PATCH 6/8] [CodeFactor] Apply fixes to commit 582a025
---
lib/momentum/Dpo.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/lib/momentum/Dpo.cs b/lib/momentum/Dpo.cs
index 1e19bed6..1ba8e4c7 100644
--- a/lib/momentum/Dpo.cs
+++ b/lib/momentum/Dpo.cs
@@ -82,7 +82,6 @@ public sealed class Dpo : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
-
ManageState(BarInput.IsNew);
// Add current price to buffer
From 69aef2b127f8330798513cec71c778aaf3a36b91 Mon Sep 17 00:00:00 2001
From: Miha Kralj
Date: Thu, 7 Nov 2024 09:55:19 -0800
Subject: [PATCH 7/8] Dpo chart + refactored tests
---
Tests/Tests.csproj | 4 +
Tests/UpdateTestBase.cs | 91 ++++++++
Tests/test_eventing.cs | 323 +++++++++++++-------------
Tests/test_quantower.cs | 55 ++++-
Tests/test_updates_oscillators.cs | 246 +++-----------------
Tests/test_updates_statistics.cs | 198 +++-------------
Tests/test_updates_volatility.cs | 355 +++--------------------------
quantower/Momentum/DpoIndicator.cs | 8 +-
8 files changed, 416 insertions(+), 864 deletions(-)
create mode 100644 Tests/UpdateTestBase.cs
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 31f09837..5cef9f7c 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -46,6 +46,10 @@
+
+
+
+
diff --git a/Tests/UpdateTestBase.cs b/Tests/UpdateTestBase.cs
new file mode 100644
index 00000000..7a245277
--- /dev/null
+++ b/Tests/UpdateTestBase.cs
@@ -0,0 +1,91 @@
+using Xunit;
+using System.Security.Cryptography;
+
+namespace QuanTAlib.Tests;
+
+public abstract class UpdateTestBase
+{
+ protected readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
+ protected const int RandomUpdates = 100;
+ protected const double ReferenceValue = 100.0;
+ protected const int precision = 8;
+
+ protected double GetRandomDouble()
+ {
+ byte[] bytes = new byte[8];
+ rng.GetBytes(bytes);
+ return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100
+ }
+
+ protected TBar GetRandomBar(bool IsNew)
+ {
+ double open = GetRandomDouble();
+ double high = open + Math.Abs(GetRandomDouble());
+ double low = open - Math.Abs(GetRandomDouble());
+ double close = low + ((high - low) * GetRandomDouble());
+ return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew);
+ }
+
+ protected void TestTValueUpdate(T indicator, Func calc) where T : class
+ {
+ var initialValue = calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
+ }
+ var finalValue = calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+
+ protected void TestTBarUpdate(T indicator, Func calc) where T : class
+ {
+ TBar r = GetRandomBar(true);
+ var initialValue = calc(r);
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(GetRandomBar(IsNew: false));
+ }
+ var finalValue = calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+
+ protected void TestDualTValueUpdate(T indicator, Func calc) where T : class
+ {
+ var initialValue = calc(
+ new TValue(DateTime.Now, ReferenceValue, IsNew: true),
+ new TValue(DateTime.Now, ReferenceValue, IsNew: true));
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(
+ new TValue(DateTime.Now, GetRandomDouble(), IsNew: false),
+ new TValue(DateTime.Now, GetRandomDouble(), IsNew: false));
+ }
+ var finalValue = calc(
+ new TValue(DateTime.Now, ReferenceValue, IsNew: false),
+ new TValue(DateTime.Now, ReferenceValue, IsNew: false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+
+ protected void TestDualTBarUpdate(T indicator, Func calc) where T : class
+ {
+ TBar bar1 = GetRandomBar(true);
+ TBar bar2 = GetRandomBar(true);
+ var initialValue = calc(bar1, bar2);
+
+ for (int i = 0; i < RandomUpdates; i++)
+ {
+ calc(GetRandomBar(false), GetRandomBar(false));
+ }
+ var finalValue = calc(
+ new TBar(bar1.Time, bar1.Open, bar1.High, bar1.Low, bar1.Close, bar1.Volume, false),
+ new TBar(bar2.Time, bar2.Open, bar2.High, bar2.Low, bar2.Close, bar2.Volume, false));
+
+ Assert.Equal(initialValue.Value, finalValue.Value, precision);
+ }
+}
diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs
index b0689b4d..5e4b1665 100644
--- a/Tests/test_eventing.cs
+++ b/Tests/test_eventing.cs
@@ -1,172 +1,112 @@
using Xunit;
using System.Security.Cryptography;
+using System.Reflection;
-#pragma warning disable S1944, S2053, S2222, S2259, S2583, S2589, S3329, S3655, S3900, S3949, S3966, S4158, S4347, S5773, S6781
-
-namespace QuanTAlib;
+namespace QuanTAlib.Tests;
public class EventingTests
{
- [Fact]
- public void EventBasedCalculations()
+ private const int TestDataPoints = 200;
+ private const int DefaultPeriod = 10;
+ private const double Tolerance = 1e-9;
+
+ private static readonly (string Name, object[] DirectParams, object[] EventParams)[] ValueIndicators = new[]
{
- // Create a cryptographically secure random number generator
- using var rng = RandomNumberGenerator.Create();
+ ("Afirma", new object[] { DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }, new object[] { new TSeries(), DefaultPeriod, DefaultPeriod, Afirma.WindowType.BlackmanHarris }),
+ ("Alma", new object[] { DefaultPeriod, 0.85, 6.0 }, new object[] { new TSeries(), DefaultPeriod, 0.85, 6.0 }),
+ ("Convolution", new object[] { new double[] {1,2,3,2,1} }, new object[] { new TSeries(), new double[] {1,2,3,2,1} }),
+ ("Dema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Dsma", new object[] { DefaultPeriod, 0.9 }, new object[] { new TSeries(), DefaultPeriod, 0.9 }),
+ ("Dwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Ema", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }),
+ ("Epma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Pwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Frama", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Fwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Gma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Hma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Htit", new object[] { }, new object[] { new TSeries() }),
+ ("Hwma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Jma", new object[] { DefaultPeriod, 0, 0.45, 10 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.45, 10 }),
+ ("Kama", new object[] { DefaultPeriod, 2, 30 }, new object[] { new TSeries(), DefaultPeriod, 2, 30 }),
+ ("Ltma", new object[] { 0.2 }, new object[] { new TSeries(), 0.2 }),
+ ("Maaf", new object[] { 39, 0.002 }, new object[] { new TSeries(), 39, 0.002 }),
+ ("Mama", new object[] { 0.5, 0.05 }, new object[] { new TSeries(), 0.5, 0.05 }),
+ ("Mgdi", new object[] { DefaultPeriod, 0.6 }, new object[] { new TSeries(), DefaultPeriod, 0.6 }),
+ ("Mma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Qema", new object[] { 0.2, 0.2, 0.2, 0.2 }, new object[] { new TSeries(), 0.2, 0.2, 0.2, 0.2 }),
+ ("Rema", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }),
+ ("Rma", new object[] { DefaultPeriod, true }, new object[] { new TSeries(), DefaultPeriod, true }),
+ ("Sma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Wma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Tema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Zlema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Sinema", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Smma", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("T3", new object[] { DefaultPeriod, 0.7, true }, new object[] { new TSeries(), DefaultPeriod, 0.7, true }),
+ ("Trima", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Vidya", new object[] { DefaultPeriod, 0, 0.2 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.2 }),
+ ("Apo", new object[] { 12, 26 }, new object[] { new TSeries(), 12, 26 }),
+ ("Macd", new object[] { 12, 26, 9 }, new object[] { new TSeries(), 12, 26, 9 }),
+ ("Rsi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rsx", new object[] { DefaultPeriod, 0, 0.55 }, new object[] { new TSeries(), DefaultPeriod, 0, 0.55 }),
+ ("Cmo", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Cog", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Curvature", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Entropy", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Kurtosis", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Max", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }),
+ ("Median", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Min", new object[] { DefaultPeriod, 0.0 }, new object[] { new TSeries(), DefaultPeriod, 0.0 }),
+ ("Mode", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Percentile", new object[] { DefaultPeriod, 0.5 }, new object[] { new TSeries(), DefaultPeriod, 0.5 }),
+ ("Skew", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Slope", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Stddev", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Variance", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Zscore", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Beta", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Corr", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Hv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Jvolty", new object[] { DefaultPeriod, 0 }, new object[] { new TSeries(), DefaultPeriod, 0 }),
+ ("Rv", new object[] { DefaultPeriod, false }, new object[] { new TSeries(), DefaultPeriod, false }),
+ ("Rvi", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mapd", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mase", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mda", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Me", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mpe", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Mse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Msle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rae", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rmse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rmsle", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rse", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Smape", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Rsquared", new object[] { DefaultPeriod }, new object[] { new TSeries(), DefaultPeriod }),
+ ("Huber", new object[] { DefaultPeriod, 1.0 }, new object[] { new TSeries(), DefaultPeriod, 1.0 })
+ };
- // Create input series to hold our random values
- var input = new TSeries();
- var barInput = new TBarSeries();
- int p = 10;
+ private static readonly (string Name, object[] DirectParams, object[] EventParams)[] BarIndicators = new[]
+ {
+ ("Adl", new object[] { }, new object[] { new TBarSeries() }),
+ ("Adosc", new object[] { 3, 10 }, new object[] { new TBarSeries(), 3, 10 }),
+ ("Aobv", new object[] { }, new object[] { new TBarSeries() }),
+ ("Cmf", new object[] { 20 }, new object[] { new TBarSeries(), 20 }),
+ ("Eom", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
+ ("Kvo", new object[] { 34, 55 }, new object[] { new TBarSeries(), 34, 55 }),
+ ("Atr", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
+ ("Chop", new object[] { 14 }, new object[] { new TBarSeries(), 14 }),
+ ("Dosc", new object[] { }, new object[] { new TBarSeries() })
+ };
- // Create a list of value-based indicator pairs
- var valueIndicators = new List<(string Name, AbstractBase Direct, AbstractBase EventBased)>
- {
- ("Afirma", new Afirma(p,p,Afirma.WindowType.BlackmanHarris), new Afirma(input, p,p,Afirma.WindowType.BlackmanHarris)),
- ("Alma", new Alma(p), new Alma(input, p)),
- ("Convolution", new Convolution(new double[] {1,2,3,2,1}), new Convolution(input, new double[] {1,2,3,2,1})),
- ("Dema", new Dema(p), new Dema(input, p)),
- ("Dsma", new Dsma(p), new Dsma(input, p)),
- ("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)),
- ("Hma", new Hma(p), new Hma(input, p)),
- ("Htit", new Htit(), new Htit(input)),
- ("Hwma", new Hwma(p), new Hwma(input, p)),
- ("Jma", new Jma(p), new Jma(input, p)),
- ("Kama", new Kama(p), new Kama(input, p)),
- ("Ltma", new Ltma(gamma: 0.2), new Ltma(input, gamma: 0.2)),
- ("Maaf", new Maaf(p), new Maaf(input, p)),
- ("Mama", new Mama(p), new Mama(input, p)),
- ("Mgdi", new Mgdi(p, kFactor: 0.6), new Mgdi(input, p, kFactor: 0.6)),
- ("Mma", new Mma(p), new Mma(input, p)),
- ("Qema", new Qema(k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2), new Qema(input, k1: 0.2, k2: 0.2, k3: 0.2, k4: 0.2)),
- ("Rema", new Rema(p), new Rema(input, p)),
- ("Rma", new Rma(p), new Rma(input, p)),
- ("Sma", new Sma(p), new Sma(input, p)),
- ("Wma", new Wma(p), new Wma(input, p)),
- ("Rma", new Rma(p), new Rma(input, p)),
- ("Tema", new Tema(p), new Tema(input, p)),
- ("Kama", new Kama(2, 30, 6), new Kama(input, 2, 30, 6)),
- ("Zlema", new Zlema(p), new Zlema(input, p)),
- ("Sinema", new Sinema(p), new Sinema(input, p)),
- ("Smma", new Smma(p), new Smma(input, p)),
- ("T3", new T3(p), new T3(input, p)),
- ("Trima", new Trima(p), new Trima(input, p)),
- ("Vidya", new Vidya(p), new Vidya(input, p)),
- ("Apo", new Apo(12, 26), new Apo(input, 12, 26)),
- ("Macd", new Macd(12, 26, 9), new Macd(input, 12, 26, 9)),
- ("Rsi", new Rsi(p), new Rsi(input, p)),
- ("Rsx", new Rsx(p), new Rsx(input, p)),
- ("Cmo", new Cmo(p), new Cmo(input, p)),
- ("Cog", new Cog(p), new Cog(input, p)),
- ("Curvature", new Curvature(p), new Curvature(input, p)),
- ("Entropy", new Entropy(p), new Entropy(input, p)),
- ("Kurtosis", new Kurtosis(p), new Kurtosis(input, p)),
- ("Max", new Max(p), new Max(input, p)),
- ("Median", new Median(p), new Median(input, p)),
- ("Min", new Min(p), new Min(input, p)),
- ("Mode", new Mode(p), new Mode(input, p)),
- ("Percentile", new Percentile(p, 0.5), new Percentile(input, p, 0.5)),
- ("Skew", new Skew(p), new Skew(input, p)),
- ("Slope", new Slope(p), new Slope(input, p)),
- ("Stddev", new Stddev(p), new Stddev(input, p)),
- ("Variance", new Variance(p), new Variance(input, p)),
- ("Zscore", new Zscore(p), new Zscore(input, p)),
- ("Beta", new Beta(p), new Beta(input, p)),
- ("Corr", new Corr(p), new Corr(input, p)),
- // Volatility indicators (value-based)
- ("Hv", new Hv(p), new Hv(input, p)),
- ("Jvolty", new Jvolty(p), new Jvolty(input, p)),
- ("Rv", new Rv(p), new Rv(input, p)),
- ("Rvi", new Rvi(p), new Rvi(input, p)),
- // Error classes
- ("Mae", new Mae(p), new Mae(input, p)),
- ("Mapd", new Mapd(p), new Mapd(input, p)),
- ("Mape", new Mape(p), new Mape(input, p)),
- ("Mase", new Mase(p), new Mase(input, p)),
- ("Mda", new Mda(p), new Mda(input, p)),
- ("Me", new Me(p), new Me(input, p)),
- ("Mpe", new Mpe(p), new Mpe(input, p)),
- ("Mse", new Mse(p), new Mse(input, p)),
- ("Msle", new Msle(p), new Msle(input, p)),
- ("Rae", new Rae(p), new Rae(input, p)),
- ("Rmse", new Rmse(p), new Rmse(input, p)),
- ("Rmsle", new Rmsle(p), new Rmsle(input, p)),
- ("Rse", new Rse(p), new Rse(input, p)),
- ("Smape", new Smape(p), new Smape(input, p)),
- ("Rsquared", new Rsquared(p), new Rsquared(input, p)),
- ("Huber", new Huber(p), new Huber(input, p))
- };
+ public static IEnumerable