Fixed ATR/ATRP and improved SMA

This commit is contained in:
Miha Kralj
2023-01-03 18:03:09 -08:00
21 changed files with 272 additions and 174 deletions
+8
View File
@@ -57,10 +57,13 @@ jobs:
run: dotnet build ./Quantower/Quantower.csproj --verbosity normal --configuration Release --nologo run: dotnet build ./Quantower/Quantower.csproj --verbosity normal --configuration Release --nologo
- name: dotnet Test - name: dotnet Test
if: ${{ github.ref == 'refs/heads/dev' }}
run: dotnet test ./Tests/Tests.csproj --verbosity normal --configuration Release --nologo run: dotnet test ./Tests/Tests.csproj --verbosity normal --configuration Release --nologo
- name: DotCover Test XML - 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 run: dotnet dotcover test ./Tests/Tests.csproj --verbosity normal --framework net7.0 --dcReportType=DetailedXML --dcoutput=./coveragereport.xml
- name: DotCover Test HTML - 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 run: dotnet dotcover test ./Tests/Tests.csproj --verbosity normal --framework net7.0 --dcReportType=HTML --dcoutput=./coveragereport.html
# - name: dotnet-coverage # - name: dotnet-coverage
# run: dotnet-coverage collect 'dotnet test' -f xml -o './coverage.xml' # 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 }}" run: dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
- name: CodeCov run - name: CodeCov run
if: ${{ github.ref == 'refs/heads/dev' }}
run: codecov -f ./coveragereport.xml -v -t ${{ secrets.CODECOV_TOKEN }} run: codecov -f ./coveragereport.xml -v -t ${{ secrets.CODECOV_TOKEN }}
- name: Codacy coverage reporter - name: Codacy coverage reporter
if: ${{ github.ref == 'refs/heads/dev' }}
uses: codacy/codacy-coverage-reporter-action@v1 uses: codacy/codacy-coverage-reporter-action@v1
with: with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
coverage-reports: ./coveragereport.xml coverage-reports: ./coveragereport.xml
- name: Release - name: Release
if: ${{ github.ref == 'refs/heads/main' }}
uses: marvinpinto/action-automatic-releases@latest uses: marvinpinto/action-automatic-releases@latest
with: with:
repo_token: "${{ secrets.GITHUB_TOKEN }}" repo_token: "${{ secrets.GITHUB_TOKEN }}"
@@ -90,6 +96,7 @@ jobs:
files: /Quantower/Settings/Scripts/Indicators/QuanTAlib/*.dll files: /Quantower/Settings/Scripts/Indicators/QuanTAlib/*.dll
- name: Authenticate to Github packages source - name: Authenticate to Github packages source
if: ${{ github.ref == 'refs/heads/main' }}
run: dotnet nuget add source run: dotnet nuget add source
--username mihakralj --username mihakralj
--password ${{ secrets.GITHUB_TOKEN }} --password ${{ secrets.GITHUB_TOKEN }}
@@ -97,6 +104,7 @@ jobs:
--name github "https://nuget.pkg.github.com/mihakralj/index.json" --name github "https://nuget.pkg.github.com/mihakralj/index.json"
- name: Push package to github - name: Push package to github
if: ${{ github.ref == 'refs/heads/main' }}
run: dotnet nuget push '.\Source\bin\Release\QuanTAlib.*.nupkg' run: dotnet nuget push '.\Source\bin\Release\QuanTAlib.*.nupkg'
--api-key ${{ secrets.GITHUB_TOKEN }} --api-key ${{ secrets.GITHUB_TOKEN }}
--source https://nuget.pkg.github.com/mihakralj/index.json --source https://nuget.pkg.github.com/mihakralj/index.json
+2 -2
View File
@@ -2,9 +2,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Title>QuanTAlib</Title> <Title>QuanTAlib</Title>
<Version>0.1.24</Version> <Version>0.1.25</Version>
<Product>Library of Technical Indicators for .NET</Product> <Product>Library of Technical Indicators for .NET</Product>
<Description>Quantitative Technical Analysis library for both real-time (streaming) and historical data analysis</Description> <Description>Quantitative Technical Analysis library for real-time (streaming) data analysis</Description>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/mihakralj/QuanTAlib</RepositoryUrl> <RepositoryUrl>https://github.com/mihakralj/QuanTAlib</RepositoryUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
+24 -28
View File
@@ -2,38 +2,34 @@
using System; using System;
/* <summary> /* <summary>
DWMA: Double (linearly) Weighted Moving Average DWMA: Double Weighted Moving Average
The weights are linearly decreasing over the period and the most recent data has The weights are decreasing over the period with p^2 decay
the heaviest weight. and the most recent data has the heaviest weight.
Sources:
</summary> */ </summary> */
public class DWMA_Series : Single_TSeries_Indicator public class DWMA_Series : Single_TSeries_Indicator {
{ public DWMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN) {
public DWMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN) for (int i = 0; i < this._p; i++) {
{ double _weight = (i + 1) * (i + 1);
for (int i = 0; i < this._p; i++) { this._weights.Add(i + 1); } this._weights.Add(_weight);
if (base._data.Count > 0) { base.Add(base._data); } }
}
private readonly System.Collections.Generic.List<double> _buffer1 = new(); if (base._data.Count > 0) { base.Add(base._data); }
private readonly System.Collections.Generic.List<double> _buffer2 = new(); }
private readonly System.Collections.Generic.List<double> _buffer1 = new();
private readonly System.Collections.Generic.List<double> _weights = new(); private readonly System.Collections.Generic.List<double> _weights = new();
public override void Add((System.DateTime t, double v) TValue, bool update) public override void Add((System.DateTime t, double v) TValue, bool update) {
{ Add_Replace_Trim(_buffer1, TValue.v, _p, update);
Add_Replace_Trim(_buffer1, TValue.v, _p, update); double _wma1 = 0;
double _wma = 0; double _wsum = 0;
for (int i = 0; i < _buffer1.Count; i++) { _wma += _buffer1[i] * this._weights[i]; } for (int i = 0; i < _buffer1.Count; i++) {
_wma /= (this._buffer1.Count * (this._buffer1.Count + 1)) * 0.5; _wma1 += _buffer1[i] * this._weights[i];
_wsum += this._weights[i];
}
_wma1 /= _wsum;
Add_Replace_Trim(_buffer2, TValue.v, _p, update); base.Add((TValue.t, _wma1), update, _NaN);
double _dwma = 0; }
for (int i = 0; i < _buffer2.Count; i++) { _dwma += _buffer2[i] * this._weights[i]; }
_dwma /= (this._buffer2.Count * (this._buffer2.Count + 1)) * 0.5;
base.Add((TValue.t, 2*_wma - _dwma), update, _NaN);
}
} }
+57
View File
@@ -0,0 +1,57 @@
namespace QuanTAlib;
using System;
/* <summary>
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]
</summary> */
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);
}
}
+6 -7
View File
@@ -79,11 +79,11 @@ public class JMA_Series : Single_TSeries_Indicator {
//// from volty to avolty //// from volty to avolty
if (update) { volty_10[volty_10.Count - 1] = volty; } if (update) { volty_10[volty_10.Count - 1] = volty; }
else { volty_10.Add(volty); } else { volty_10.Add(volty); }
if (volty_10.Count > _p) { volty_10.RemoveAt(0); } if (volty_10.Count > 10) { volty_10.RemoveAt(0); }
vsum = prev_vsum + 0.1 * (volty - volty_10.First()); vsum = prev_vsum + 0.1 * (volty - volty_10.First());
if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; } if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; }
else { vsum_buff.Add(vsum); } else { vsum_buff.Add(vsum); }
if (vsum_buff.Count > (65)) if (vsum_buff.Count > (10*_p))
vsum_buff.RemoveAt(0); vsum_buff.RemoveAt(0);
double avolty = 0; double avolty = 0;
for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; } for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; }
@@ -91,9 +91,8 @@ public class JMA_Series : Single_TSeries_Indicator {
/// from avolty to rolty /// from avolty to rolty
double rvolty = (avolty != 0) ? volty / avolty : 0; double rvolty = (avolty != 0) ? volty / avolty : 0;
double len1 = (Math.Log(Math.Sqrt(0.5 * (_p - 1))) / Math.Log(2.0)) + 2; double len1 = (Math.Log(Math.Sqrt(2.0 * _p)) / Math.Log(2.0)) + 2;
if (len1 < 0) if (len1 < 0) len1 = 0;
len1 = 0;
double pow1 = Math.Max(len1 - 2.0, 0.5); double pow1 = Math.Max(len1 - 2.0, 0.5);
if (rvolty > Math.Pow(len1, 1.0 / pow1)) if (rvolty > Math.Pow(len1, 1.0 / pow1))
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 //// from rvolty to second smoothing
double pow2 = Math.Pow(rvolty, pow1); 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)); Kv = Math.Pow(len2 / (len2 + 2), Math.Sqrt(pow2));
double beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2); double beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
double alpha = Math.Pow(beta * 1.1, pow2); double alpha = Math.Pow(beta * 1.1, pow2);
@@ -120,6 +119,6 @@ public class JMA_Series : Single_TSeries_Indicator {
double jma = prev_jma + det1; double jma = prev_jma + det1;
prev_jma = jma; prev_jma = jma;
base.Add((TValue.t, ma1), update, _NaN); base.Add((TValue.t, jma), update, _NaN);
} }
} }
+25 -1
View File
@@ -16,6 +16,30 @@ Remark:
</summary> */ </summary> */
public class SMA_Series : Single_TSeries_Indicator {
private double _sum, _oldsum;
private int _len, _oldlen;
public SMA_Series(TSeries source, int period = 0, bool useNaN = false) : base(source, period, false) {
Reset();
if (this._data.Count > 0) { base.Add(this._data); }
}
public override void Add((DateTime t, double v) TValue, bool update) {
if (update) { _sum = _oldsum; }
else { _oldsum = _sum; _len++; }
_sum += TValue.v;
if (_period != 0 && _len > _period)
_sum -= (_data[base.Count - _period - (update ? 1 : 0)].v);
double _div = (_period == 0) ? _len : Math.Min(_len, _period);
base.Add((TValue.t, _sum / _div), update, _NaN);
}
public void Reset() {
_sum = _oldsum = 0;
_len = _oldlen = 0;
}
}
/*
public class SMA_Series : Single_TSeries_Indicator public class SMA_Series : Single_TSeries_Indicator
{ {
private readonly System.Collections.Generic.List<double> _buffer = new(); private readonly System.Collections.Generic.List<double> _buffer = new();
@@ -59,4 +83,4 @@ public class SMA_Series : Single_TSeries_Indicator
base.Add((TValue.t, _sma), update, _NaN); base.Add((TValue.t, _sma), update, _NaN);
} }
} }*/
+31 -45
View File
@@ -11,53 +11,39 @@ Sources:
</summary> */ </summary> */
public class ATRP_Series : Single_TBars_Indicator public class ATRP_Series : Single_TBars_Indicator {
{ private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly System.Collections.Generic.List<double> _buffer = new(); private readonly double _k;
private readonly double _k, _k1m; private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum;
private double _lastema, _lastlastema, _lastcm1; private readonly int _period;
private double _cm1 = double.NaN;
public ATRP_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) public ATRP_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) {
{ _period = period;
this._k = 1.0 / (double)(this._p); _k = 1.0 / (double)(_p);
this._k1m = 1.0 - this._k; _lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0;
this._lastema = this._lastlastema = double.NaN; if (this._bars.Count > 0) { base.Add(this._bars); }
if (_bars.Count > 0) { base.Add(_bars); } }
}
public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) {
{ if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; }
if (update) { else { _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; }
this._lastema = this._lastlastema;
this._cm1 = this._lastcm1;
}
if (_cm1 is double.NaN) { _cm1 = TBar.c; } if (this.Count == 0)
double d1 = Math.Abs(TBar.h - TBar.l); _cm1 = TBar.c;
double d2 = Math.Abs(_cm1 - TBar.h); double d1 = Math.Abs(TBar.h - TBar.l);
double d3 = Math.Abs(_cm1 - TBar.l); double d2 = Math.Abs(_cm1 - TBar.h);
(DateTime t, double v)d = (TBar.t, Math.Max(d1,Math.Max(d2,d3))); //TR value for RMA below double d3 = Math.Abs(_cm1 - TBar.l);
_lastcm1 = _cm1; (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3)));
_cm1 = TBar.c; _cm1 = TBar.c;
double _ema = 0; double _atr = 0;
if (this.Count < this._p) if (this.Count == 0) { _atr = d.v; }
{ else if (this.Count < _p + 1) { _sum += d.v; _atr = _sum / (this.Count); }
if (update) { _buffer[_buffer.Count - 1] = d.v; } else { _atr = _k * (d.v - _lastatr) + _lastatr; }
else { _buffer.Add(d.v); } _lastatr = _atr;
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
for (int i = 0; i < _buffer.Count; i++) { _ema += _buffer[i]; }
_ema /= this._buffer.Count;
}
else { _ema = (d.v * _k) + (_lastema * _k1m); }
this._lastlastema = this._lastema; double _atrp = 100 * (_atr / TBar.c);
this._lastema = _ema; var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atrp);
base.Add(ret, update);
double _atrp = 100 * (_ema / TBar.c); }
}
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atrp);
base.Add(ret, update);
}
}
+29 -42
View File
@@ -13,51 +13,38 @@ Sources:
</summary> */ </summary> */
public class ATR_Series : Single_TBars_Indicator public class ATR_Series : Single_TBars_Indicator {
{ private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly System.Collections.Generic.List<double> _buffer = new(); private readonly double _k;
private readonly double _k, _k1m; private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum;
private double _lastema, _lastlastema, _lastcm1; private readonly int _period;
private double _cm1;
public ATR_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) public ATR_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) {
{ _period = period;
this._k = 1.0 / (double)(this._p); _k = 1.0 / (double)(_p);
this._k1m = 1.0 - this._k; _lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0;
this._lastema = this._lastlastema = double.NaN; if (this._bars.Count > 0) { base.Add(this._bars); }
if (this._bars.Count > 0) { base.Add(this._bars); } }
}
public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update) {
{ if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; }
if (update) { else { _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; }
this._lastema = this._lastlastema;
this._cm1 = this._lastcm1;
}
if (this.Count == 0) { this._cm1 = TBar.c; } if (this.Count == 0)
double d1 = Math.Abs(TBar.h - TBar.l); _cm1 = TBar.c;
double d2 = Math.Abs(_cm1 - TBar.h); double d1 = Math.Abs(TBar.h - TBar.l);
double d3 = Math.Abs(_cm1 - TBar.l); double d2 = Math.Abs(_cm1 - TBar.h);
(DateTime t, double v)d = (TBar.t, Math.Max(d1,Math.Max(d2,d3))); double d3 = Math.Abs(_cm1 - TBar.l);
_lastcm1 = _cm1; (DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3)));
_cm1 = TBar.c; _cm1 = TBar.c;
double _ema = 0; double _atr = 0;
if (this.Count < this._p) if (this.Count == 0) { _atr = d.v; }
{ else if (this.Count < _p + 1) { _sum += d.v; _atr = _sum / (this.Count); }
if (update) { _buffer[_buffer.Count - 1] = d.v; } else { _atr = _k * (d.v - _lastatr) + _lastatr; }
else { _buffer.Add(d.v); } _lastatr = _atr;
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
for (int i = 0; i < _buffer.Count; i++) { _ema += _buffer[i]; }
_ema /= this._buffer.Count;
}
else { _ema = (d.v * _k) + (_lastema * _k1m); }
this._lastlastema = this._lastema; var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atr);
this._lastema = _ema; base.Add(ret, update);
}
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
base.Add(ret, update);
}
} }
+12 -1
View File
@@ -175,7 +175,18 @@ public class Update {
Assert.Equal(lastLen, QL.Count); // same size Assert.Equal(lastLen, QL.Count); // same size
Assert.Equal(lastCalc, QL.Last()); // same data 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); JMA_Series QL = new(source: bars.Close, period: period);
var lastData = bars.Close.Last(); var lastData = bars.Close.Last();
var lastCalc = QL.Last(); var lastCalc = QL.Last();
+15 -2
View File
@@ -1,3 +1,4 @@
/*
using Xunit; using Xunit;
using System; using System;
using QuanTAlib; using QuanTAlib;
@@ -165,7 +166,18 @@ public class PandasTA : IDisposable
Assert.InRange(PanTA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); 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() { [Fact] void KAMA() {
KAMA_Series QL = new(bars.Close, period); KAMA_Series QL = new(bars.Close, period);
var pta = df.ta.kama(close: df.close, length: period); var pta = df.ta.kama(close: df.close, length: period);
@@ -391,4 +403,5 @@ public class PandasTA : IDisposable
} }
} }
} }
*/
+36 -35
View File
@@ -16,7 +16,7 @@ public class Skender
{ {
bars = new(Bars: 10000, Volatility: 0.5, Drift: 0.0, Precision: 2); bars = new(Bars: 10000, Volatility: 0.5, Drift: 0.0, Precision: 2);
period = rnd.Next(30) + 5; period = rnd.Next(30) + 5;
digits = 5; //minimizing rounding errors in type conversions digits = 6; //minimizing rounding errors in type conversions
skip = period+2; skip = period+2;
quotes = bars.Select(q => new Quote quotes = bars.Select(q => new Quote
@@ -206,20 +206,21 @@ public class Skender
{ {
HMA_Series QL = new(bars.Close, period, useNaN: false); HMA_Series QL = new(bars.Close, period, useNaN: false);
var SK = quotes.GetHma(period).Select(i => i.Hma.Null2NaN()!); var SK = quotes.GetHma(period).Select(i => i.Hma.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip*2; i--)
{ {
double QL_item = QL[i - 1].v; double QL_item = QL[i - 1].v;
double SK_item = SK.ElementAt(i - 1); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits));
} }
} }
[Fact] [Fact]
public void KAMA() public void KAMA()
{ {
// TODO: check precision of KAMA() // TODO: check precision of KAMA()
KAMA_Series QL = new(bars.Close, period, useNaN: false); KAMA_Series QL = new(bars.Close, period, useNaN: false);
var SK = quotes.GetKama(period).Select(i => i.Kama.Null2NaN()!); var SK = quotes.GetKama(period).Select(i => i.Kama.Null2NaN()!);
for (int i = QL.Length; i > 250; i--) for (int i = QL.Length; i > skip+2; i--)
{ {
double QL_item = QL[i - 1].v; double QL_item = QL[i - 1].v;
double SK_item = SK.ElementAt(i - 1); double SK_item = SK.ElementAt(i - 1);
@@ -269,8 +270,8 @@ public class Skender
var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mad.Null2NaN()!); var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mad.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -281,11 +282,11 @@ public class Skender
var SK = quotes.GetMama(fastLimit: 0.5, slowLimit: 0.05); var SK = quotes.GetMama(fastLimit: 0.5, slowLimit: 0.05);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1).Mama.Null2NaN()!, digits: digits); double SK_item = SK.ElementAt(i - 1).Mama.Null2NaN()!;
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
QL_item = Math.Round(QL.Fama[i - 1].v, digits: digits); QL_item = QL.Fama[i - 1].v;
SK_item = Math.Round(SK.ElementAt(i - 1).Fama.Null2NaN()!, digits: digits); SK_item = SK.ElementAt(i - 1).Fama.Null2NaN()!;
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -296,8 +297,8 @@ public class Skender
var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mape.Null2NaN()!); var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mape.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -308,8 +309,8 @@ public class Skender
var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mse.Null2NaN()!); var SK = quotes.GetSmaAnalysis(period).Select(i => i.Mse.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -319,9 +320,9 @@ public class Skender
OBV_Series QL = new(bars, period, false); OBV_Series QL = new(bars, period, false);
var SK = quotes.GetObv(period).Select(i => i.Obv!); var SK = quotes.GetObv(period).Select(i => i.Obv!);
for (int i = QL.Length; i > skip; i--) { for (int i = QL.Length; i > skip; i--) {
double QL_item = Math.Round(QL.Last().v, digits: digits); double QL_item = QL.Last().v;
// adding volume[0] to OBV to pass the test and keep compatibility with TA-LIB // adding volume[0] to OBV to pass the test and keep compatibility with TA-LIB
double SK_item = Math.Round(SK.Last()! + (double)quotes.First().Volume!, digits: digits); double SK_item = SK.Last()! + (double)quotes.First().Volume!;
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -368,8 +369,8 @@ public class Skender
var SK = quotes.GetRsi(period).Select(i => i.Rsi.Null2NaN()!); var SK = quotes.GetRsi(period).Select(i => i.Rsi.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -380,8 +381,8 @@ public class Skender
var SK = quotes.GetStdDev(period).Select(i => i.StdDev.Null2NaN()!); var SK = quotes.GetStdDev(period).Select(i => i.StdDev.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -392,8 +393,8 @@ public class Skender
var SK = quotes.GetSma(period).Select(i => i.Sma.Null2NaN()!); var SK = quotes.GetSma(period).Select(i => i.Sma.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -404,8 +405,8 @@ public class Skender
var SK = quotes.GetSmma(period).Select(i => i.Smma.Null2NaN()!); var SK = quotes.GetSmma(period).Select(i => i.Smma.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -416,8 +417,8 @@ public class Skender
var SK = quotes.GetT3(lookbackPeriods: period, volumeFactor: 0.7).Select(i => i.T3.Null2NaN()!); var SK = quotes.GetT3(lookbackPeriods: period, volumeFactor: 0.7).Select(i => i.T3.Null2NaN()!);
for (int i = QL.Length; i > period*15; i--) for (int i = QL.Length; i > period*15; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -426,8 +427,8 @@ public class Skender
TRIX_Series QL = new(bars.Close, period, false); TRIX_Series QL = new(bars.Close, period, false);
var SK = quotes.GetTrix(period).Select(i => i.Trix.Null2NaN()!); var SK = quotes.GetTrix(period).Select(i => i.Trix.Null2NaN()!);
for (int i = QL.Length; i > period*12; i--) { for (int i = QL.Length; i > period*12; i--) {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10, -digits), Math.Pow(10, -digits));
} }
} }
@@ -438,8 +439,8 @@ public class Skender
var SK = quotes.GetTema(period).Select(i => i.Tema.Null2NaN()!); var SK = quotes.GetTema(period).Select(i => i.Tema.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -450,8 +451,8 @@ public class Skender
var SK = quotes.GetTr().Select(i => i.Tr.Null2NaN()!); var SK = quotes.GetTr().Select(i => i.Tr.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -462,8 +463,8 @@ public class Skender
var SK = quotes.GetWma(period).Select(i => i.Wma.Null2NaN()!); var SK = quotes.GetWma(period).Select(i => i.Wma.Null2NaN()!);
for (int i = QL.Length; i > skip*2; i--) for (int i = QL.Length; i > skip*2; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
@@ -474,8 +475,8 @@ public class Skender
var SK = quotes.GetStdDev(period).Select(i => i.ZScore.Null2NaN()!); var SK = quotes.GetStdDev(period).Select(i => i.ZScore.Null2NaN()!);
for (int i = QL.Length; i > skip; i--) for (int i = QL.Length; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i - 1].v, digits: digits); double QL_item = QL[i - 1].v;
double SK_item = Math.Round(SK.ElementAt(i - 1), digits: digits); double SK_item = SK.ElementAt(i - 1);
Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits)); Assert.InRange(SK_item! - QL_item, -Math.Pow(10,-digits), Math.Pow(10,-digits));
} }
} }
+3 -1
View File
@@ -74,7 +74,7 @@ public class Ta_Lib
{ {
ATR_Series QL = new(bars, period, false); ATR_Series QL = new(bars, period, false);
Core.Atr(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period); Core.Atr(inhigh, inlow, inclose, 0, bars.Count - 1, TALIB, out int outBegIdx, out _, period);
for (int i = QL.Length - 1; i > skip * 15; i--) for (int i = QL.Length - 1; i > skip; i--)
{ {
double QL_item = Math.Round(QL[i].v, digits: digits); double QL_item = Math.Round(QL[i].v, digits: digits);
double TA_item = Math.Round(TALIB[i - outBegIdx], digits: digits); double TA_item = Math.Round(TALIB[i - outBegIdx], digits: digits);
@@ -114,6 +114,7 @@ public class Ta_Lib
Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits));
} }
} }
[Fact] [Fact]
public void CMO() { public void CMO() {
CMO_Series QL = new(bars.Close, period, false); CMO_Series QL = new(bars.Close, period, false);
@@ -124,6 +125,7 @@ public class Ta_Lib
Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); Assert.InRange(TA_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits));
} }
} }
[Fact] [Fact]
public void CORR() public void CORR()
{ {
+6 -2
View File
@@ -20,8 +20,8 @@ public class Tulip_Test
{ {
bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3); bars = new(Bars: 5000, Volatility: 0.8, Drift: 0.0, Precision: 3);
period = rnd.Next(28) + 3; period = rnd.Next(28) + 3;
skip = period+1; skip = period+5;
digits = 10; digits = 8;
outdata = new double[bars.Count]; outdata = new double[bars.Count];
inopen = bars.Open.v.ToArray(); inopen = bars.Open.v.ToArray();
@@ -124,6 +124,7 @@ public class Tulip_Test
Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits));
} }
} }
[Fact] [Fact]
public void CMO() { public void CMO() {
double[][] arrin = { inclose }; double[][] arrin = { inclose };
@@ -214,6 +215,7 @@ public class Tulip_Test
Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits));
} }
} }
[Fact] [Fact]
public void HMA() { public void HMA() {
double[][] arrin = { inclose }; double[][] arrin = { inclose };
@@ -226,6 +228,7 @@ public class Tulip_Test
Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits));
} }
} }
[Fact] [Fact]
public void KAMA() { public void KAMA() {
double[][] arrin = { inclose }; double[][] arrin = { inclose };
@@ -238,6 +241,7 @@ public class Tulip_Test
Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits)); Assert.InRange(TU_item! - QL_item, -Math.Exp(-digits), Math.Exp(-digits));
} }
} }
[Fact] [Fact]
public void LINREG() { public void LINREG() {
double[][] arrin = { inclose }; double[][] arrin = { inclose };
+4
View File
@@ -0,0 +1,4 @@
# HWMA: Holt-Winter Moving Average
nA = 0.5; nB = 0.3; nC = 0.01;
![Alt text](./img/HWMA_chart.svg)
+4
View File
@@ -0,0 +1,4 @@
# MAMA: MESA Adaptive Moving Average
period = 10
![Alt text](./img/MAMA_chart.svg)
+5 -4
View File
@@ -3,19 +3,20 @@
* [List of all Indicators](indicators.md "Indicators coverage") * [List of all Indicators](indicators.md "Indicators coverage")
* [SMA - Simple Moving Average](SMA.md) * [SMA - Simple Moving Average](SMA.md)
* [RMA - WildeR Moving Average](RMA.md)
* [EMA - Exponential Moving Average](EMA.md) * [EMA - Exponential Moving Average](EMA.md)
* [WMA - Weighted Moving Average](WMA.md) * [WMA - Weighted Moving Average](WMA.md)
* [T3 - Tillson T3 Exponential MA](T3.md)
* [SMMA - Smoothed Moving Average](SMMA.md) * [SMMA - Smoothed Moving Average](SMMA.md)
* [DWMA - Double Weighted Moving Average](DWMA.md)
* [TRIMA - Triangular Moving Average](TRIMA.md) * [TRIMA - Triangular Moving Average](TRIMA.md)
* [DWMA - Double Weighted Moving Average](DWMA.md)
* [DEMA - Double Exponential MA](DEMA.md) * [DEMA - Double Exponential MA](DEMA.md)
* [TEMA - Triple Exponential MA](TEMA.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) * [HMA - Hull Moving Average](HMA.md)
* [HEMA - Hull/Exponential Moving Average](HEMA.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) * [KAMA - Kaufman Adaptive Moving Average](KAMA.md)
* [ALMA - Arnaud Legoux Moving Average](ALMA.md)
* [ZLEMA - Zero-Lag Exponential MA](ZLEMA.md) * [ZLEMA - Zero-Lag Exponential MA](ZLEMA.md)
* [JMA - Jurik Moving Average](JMA.md) * [JMA - Jurik Moving Average](JMA.md)
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 152 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 154 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

+1 -1
View File
@@ -62,7 +62,7 @@
|HEMA - Hull/EMA Average|`HEMA_Series`|||| |HEMA - Hull/EMA Average|`HEMA_Series`||||
|Hilbert Transform Instantaneous Trendline||HT_TRENDLINE|GetHtTrendline|| |Hilbert Transform Instantaneous Trendline||HT_TRENDLINE|GetHtTrendline||
|⭐HMA - Hull Moving Average|`HMA_Series`||✔️GetHma|✔️hma|✔️hma| |⭐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|| |JMA - Jurik Moving Average|`JMA_Series`|||jma||
|KAMA - Kaufman's Adaptive Moving Average|`KAMA_Series`|✔️KAMA|✔️GetKama|✔️kama|✔️kama| |KAMA - Kaufman's Adaptive Moving Average|`KAMA_Series`|✔️KAMA|✔️GetKama|✔️kama|✔️kama|
|KDJ - KDJ Indicator (trend reversal)||||kdj| |KDJ - KDJ Indicator (trend reversal)||||kdj|