diff --git a/.github/workflows/main_automation.yml b/.github/workflows/main_automation.yml index e84ae92e..53c6ac2a 100644 --- a/.github/workflows/main_automation.yml +++ b/.github/workflows/main_automation.yml @@ -57,10 +57,13 @@ jobs: run: dotnet build ./Quantower/Quantower.csproj --verbosity normal --configuration Release --nologo - name: dotnet Test + if: ${{ github.ref == 'refs/heads/dev' }} run: dotnet test ./Tests/Tests.csproj --verbosity normal --configuration Release --nologo - name: DotCover Test XML + if: ${{ github.ref == 'refs/heads/dev' }} run: dotnet dotcover test ./Tests/Tests.csproj --verbosity normal --framework net7.0 --dcReportType=DetailedXML --dcoutput=./coveragereport.xml - name: DotCover Test HTML + if: ${{ github.ref == 'refs/heads/dev' }} run: dotnet dotcover test ./Tests/Tests.csproj --verbosity normal --framework net7.0 --dcReportType=HTML --dcoutput=./coveragereport.html # - name: dotnet-coverage # run: dotnet-coverage collect 'dotnet test' -f xml -o './coverage.xml' @@ -72,15 +75,18 @@ jobs: run: dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" - name: CodeCov run + if: ${{ github.ref == 'refs/heads/dev' }} run: codecov -f ./coveragereport.xml -v -t ${{ secrets.CODECOV_TOKEN }} - name: Codacy coverage reporter + if: ${{ github.ref == 'refs/heads/dev' }} uses: codacy/codacy-coverage-reporter-action@v1 with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: ./coveragereport.xml - name: Release + if: ${{ github.ref == 'refs/heads/main' }} uses: marvinpinto/action-automatic-releases@latest with: repo_token: "${{ secrets.GITHUB_TOKEN }}" @@ -90,6 +96,7 @@ jobs: files: /Quantower/Settings/Scripts/Indicators/QuanTAlib/*.dll - name: Authenticate to Github packages source + if: ${{ github.ref == 'refs/heads/main' }} run: dotnet nuget add source --username mihakralj --password ${{ secrets.GITHUB_TOKEN }} @@ -97,6 +104,7 @@ jobs: --name github "https://nuget.pkg.github.com/mihakralj/index.json" - name: Push package to github + if: ${{ github.ref == 'refs/heads/main' }} run: dotnet nuget push '.\Source\bin\Release\QuanTAlib.*.nupkg' --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/mihakralj/index.json diff --git a/Source/Trends/HWMA_Series.cs b/Source/Trends/HWMA_Series.cs new file mode 100644 index 00000000..50754285 --- /dev/null +++ b/Source/Trends/HWMA_Series.cs @@ -0,0 +1,57 @@ +namespace QuanTAlib; +using System; + +/* +HWMA: Holt-Winter Moving Average + Indicator HWMA (Holt-Winter Moving Average) is a three-parameter moving + average by the Holt-Winter method; Holt-Winters Exponential Smoothing is + used for forecasting time series data that exhibits both a trend and a + seasonal variation. + + +Sources: + https://timeseriesreasoning.com/contents/holt-winters-exponential-smoothing/ + https://www.mql5.com/en/code/20856 + +nA - smoothed series (from 0 to 1) +nB - assess the trend (from 0 to 1) +nC - assess seasonality (from 0 to 1) + +F[i] = (1-nA) * (F[i-1] + V[i-1] + 0.5 * A[i-1]) + nA * Price[i] +V[i] = (1-nB) * (V[i-1] + A[i-1]) + nB * (F[i] - F[i-1]) +A[i] = (1-nC) * A[i-1] + nC * (V[i] - V[i-1]) +HWMA[i] = F[i] + V[i] + 0.5 * A[i] + + */ + +public class HWMA_Series : Single_TSeries_Indicator { + double _nA, _nB, _nC; + double _pF, _pV, _pA; + double _ppF, _ppV, _ppA; + + public HWMA_Series(TSeries source, double nA = 0.2, double nB = 0.1, double nC = 0.1, bool useNaN = false) : base(source, 0, useNaN) { + + _nA = nA; + _nB = nB; + _nC = nC; + if (this._data.Count > 0) { base.Add(this._data); } + } + public override void Add((DateTime t, double v) TValue, bool update) { + double _F, _V, _A; + if (this.Count == 0) { _pF = TValue.v; _pA = _pV = 0; } + + if (update) { _pF = _ppF; _pV = _ppV; _pA = _ppA; } + else { _ppF = _pF; _ppV = _pV; _ppA = _pA; } + + _F = (1 - _nA) * (_pF + _pV + 0.5 * _pA) + _nA * TValue.v; + _V = (1 - _nB) * (_pV + _pA) + _nB * (_F - _pF); + _A = (1 - _nC) * _pA + _nC * (_V - _pV); + + double _hwma = _F + _V + 0.5 * _A; + _pF = _F; + _pV = _V; + _pA = _A; + + base.Add((TValue.t, _hwma), update, _NaN); + } +} diff --git a/Source/Trends/JMA_Series.cs b/Source/Trends/JMA_Series.cs index af37b43d..0a8b201d 100644 --- a/Source/Trends/JMA_Series.cs +++ b/Source/Trends/JMA_Series.cs @@ -91,9 +91,8 @@ public class JMA_Series : Single_TSeries_Indicator { /// from avolty to rolty double rvolty = (avolty != 0) ? volty / avolty : 0; - double len1 = (Math.Log(Math.Sqrt(0.5 * (_p - 1))) / Math.Log(2.0)) + 2; - if (len1 < 0) - len1 = 0; + double len1 = (Math.Log(Math.Sqrt(2.0 * _p)) / Math.Log(2.0)) + 2; + if (len1 < 0) len1 = 0; double pow1 = Math.Max(len1 - 2.0, 0.5); if (rvolty > Math.Pow(len1, 1.0 / pow1)) rvolty = Math.Pow(len1, 1.0 / pow1); @@ -102,7 +101,7 @@ public class JMA_Series : Single_TSeries_Indicator { //// from rvolty to second smoothing double pow2 = Math.Pow(rvolty, pow1); - double len2 = Math.Sqrt(0.5 * (_p - 1)) * len1; + double len2 = Math.Sqrt(0.5 * (_p - 2)) * len1; Kv = Math.Pow(len2 / (len2 + 2), Math.Sqrt(pow2)); double beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2); double alpha = Math.Pow(beta * 1.1, pow2); @@ -120,6 +119,6 @@ public class JMA_Series : Single_TSeries_Indicator { double jma = prev_jma + det1; prev_jma = jma; - base.Add((TValue.t, ma1), update, _NaN); + base.Add((TValue.t, jma), update, _NaN); } } \ No newline at end of file diff --git a/Tests/Series/Update.cs b/Tests/Series/Update.cs index 80335697..e477da9f 100644 --- a/Tests/Series/Update.cs +++ b/Tests/Series/Update.cs @@ -175,7 +175,18 @@ public class Update { Assert.Equal(lastLen, QL.Count); // same size Assert.Equal(lastCalc, QL.Last()); // same data } - [Fact] public void JMA() { + [Fact] + public void HWMA() { + HWMA_Series QL = new(source: bars.Close); + var lastData = bars.Close.Last(); + var lastCalc = QL.Last(); + int lastLen = QL.Count; + QL.Add((DateTime.Today, 0), update: true); + QL.Add(lastData, update: true); + Assert.Equal(lastLen, QL.Count); // same size + Assert.Equal(lastCalc, QL.Last()); // same data + } + [Fact] public void JMA() { JMA_Series QL = new(source: bars.Close, period: period); var lastData = bars.Close.Last(); var lastCalc = QL.Last(); diff --git a/Tests/Validations/Trends/Pandas_TA.cs b/Tests/Validations/Trends/Pandas_TA.cs index 5549a5c2..7da08900 100644 --- a/Tests/Validations/Trends/Pandas_TA.cs +++ b/Tests/Validations/Trends/Pandas_TA.cs @@ -165,7 +165,18 @@ public class PandasTA : IDisposable Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); } - } + } + [Fact] void HWMA() { + HWMA_Series QL = new(bars.Close, useNaN: false); + var pta = df.ta.hwma(close: df.close); + for (int i = QL.Length; i > QL.Length-sample; i--) + { + double QL_item = QL[i - 1].v; + double PanTA_item = (double)pta[i - 1]; + Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); + } + + } [Fact] void KAMA() { KAMA_Series QL = new(bars.Close, period); var pta = df.ta.kama(close: df.close, length: period); diff --git a/docs/HWMA.md b/docs/HWMA.md new file mode 100644 index 00000000..6102933e --- /dev/null +++ b/docs/HWMA.md @@ -0,0 +1,4 @@ +# HWMA: Holt-Winter Moving Average +nA = 0.5; nB = 0.3; nC = 0.01; + +![Alt text](./img/HWMA_chart.svg) \ No newline at end of file diff --git a/docs/MAMA.md b/docs/MAMA.md new file mode 100644 index 00000000..19dacbb0 --- /dev/null +++ b/docs/MAMA.md @@ -0,0 +1,4 @@ +# MAMA: MESA Adaptive Moving Average +period = 10 + +![Alt text](./img/MAMA_chart.svg) \ No newline at end of file diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 52a8a6dd..f59cf77c 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -3,19 +3,20 @@ * [List of all Indicators](indicators.md "Indicators coverage") * [SMA - Simple Moving Average](SMA.md) - * [RMA - WildeR Moving Average](RMA.md) * [EMA - Exponential Moving Average](EMA.md) * [WMA - Weighted Moving Average](WMA.md) + * [T3 - Tillson T3 Exponential MA](T3.md) * [SMMA - Smoothed Moving Average](SMMA.md) - * [DWMA - Double Weighted Moving Average](DWMA.md) * [TRIMA - Triangular Moving Average](TRIMA.md) + * [DWMA - Double Weighted Moving Average](DWMA.md) * [DEMA - Double Exponential MA](DEMA.md) * [TEMA - Triple Exponential MA](TEMA.md) - * [T3 - Tillson T3 Exponential MA](T3.md) + * [ALMA - Arnaud Legoux Moving Average](ALMA.md) * [HMA - Hull Moving Average](HMA.md) * [HEMA - Hull/Exponential Moving Average](HEMA.md) + * [HWMA - Holt-Winter Moving Average](HWMA.md) + * [MAMA - MESA Adaptive Moving Average](MAMA.md) * [KAMA - Kaufman Adaptive Moving Average](KAMA.md) - * [ALMA - Arnaud Legoux Moving Average](ALMA.md) * [ZLEMA - Zero-Lag Exponential MA](ZLEMA.md) * [JMA - Jurik Moving Average](JMA.md) diff --git a/docs/img/HWMA_chart.svg b/docs/img/HWMA_chart.svg new file mode 100644 index 00000000..660fa53f --- /dev/null +++ b/docs/img/HWMA_chart.svg @@ -0,0 +1 @@ +020406000.20.40.60.81020406000.510204060−100102030020406001020300204060−1−0.500.510204060−1010204060−1010204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.511.50204060−100102030400204060−1010204060−10120204060170175 \ No newline at end of file diff --git a/docs/img/MAMA_chart.svg b/docs/img/MAMA_chart.svg index 3a17f18e..fd2cef35 100644 --- a/docs/img/MAMA_chart.svg +++ b/docs/img/MAMA_chart.svg @@ -1 +1 @@ -020406000.5×10​621×10​62020406002×10​594×10​596×10​598×10​590204060−3×10​45−2×10​45−1×10​450020406001×10​402×10​400204060−1.5×10​19−1×10​19−0.5×10​1900204060−5×10​3805×10​380204060−5×10​2205×10​22020406001×10​482×10​48020406000.5×10​331×10​331.5×10​33020406001×10​692×10​693×10​694×10​690204060−1×10​37−0.5×10​3700204060−1×10​34−0.5×10​3400204060−3×10​33−2×10​33−1×10​3300204060−1×10​28−0.5×10​280020406002×10​244×10​246×10​24020406002×10​224×10​226×10​22 \ No newline at end of file +020406000.20.40.60.81020406000.20.40.60.8102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/docs/img/T3_chart.svg b/docs/img/T3_chart.svg index c1d87c5f..906a89ab 100644 --- a/docs/img/T3_chart.svg +++ b/docs/img/T3_chart.svg @@ -1 +1 @@ -020406000.20.40.60.81020406000.5102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file +020406000.20.40.60.81020406000.5102040600102030020406001020300204060−1−0.500.510204060−1−0.500.510204060−1−0.500.510204060−0.4−0.200.20.40204060−1−0.500.50204060−1−0.500.510204060−1−0.500.51020406000.51020406001020300204060−1010204060−1010204060170172174176178 \ No newline at end of file diff --git a/docs/indicators.md b/docs/indicators.md index bec75679..514e1c35 100644 --- a/docs/indicators.md +++ b/docs/indicators.md @@ -62,7 +62,7 @@ |HEMA - Hull/EMA Average|`HEMA_Series`|||| |Hilbert Transform Instantaneous Trendline||HT_TRENDLINE|GetHtTrendline|| |⭐HMA - Hull Moving Average|`HMA_Series`||✔️GetHma|✔️hma|✔️hma| -|HWMA - Holt-Winter Moving Average||||hwma| +|HWMA - Holt-Winter Moving Average|`HWMA_Series`|||✔️hwma| |JMA - Jurik Moving Average|`JMA_Series`|||jma|| |KAMA - Kaufman's Adaptive Moving Average|`KAMA_Series`|✔️KAMA|✔️GetKama|✔️kama|✔️kama| |KDJ - KDJ Indicator (trend reversal)||||kdj|