KAMA, SMMA, ZLEMA

This commit is contained in:
Miha Kralj
2022-04-24 17:29:11 -07:00
parent 6b9aa85aa1
commit 0b57a75eb9
12 changed files with 284 additions and 69 deletions
@@ -2,7 +2,7 @@ using System.Drawing;
using TradingPlatform.BusinessLayer; using TradingPlatform.BusinessLayer;
namespace QuanTAlib; namespace QuanTAlib;
public class ZLEMA_chart : Indicator public class SMMA_chart : Indicator
{ {
#region Parameters #region Parameters
@@ -16,27 +16,28 @@ public class ZLEMA_chart : Indicator
#endregion Parameters #endregion Parameters
private TBars bars; private TBars bars;
/////// ///////
private ZLEMA_Series indicator; private SMMA_Series indicator;
/////// ///////
public ZLEMA_chart() public SMMA_chart()
{ {
this.SeparateWindow = false; this.SeparateWindow = false;
this.Name = "ZLEMA - Zero-lag Exponential Moving Average"; this.Name = "SMMA - Smoothed Moving Average";
this.Description = "Zero-Lag Exponential Moving Average description"; this.Description = "Smoothed Moving Average description";
this.AddLineSeries("ZLEMA", Color.RoyalBlue, 3, LineStyle.Solid); this.AddLineSeries("SMMA", Color.RoyalBlue, 3, LineStyle.Solid);
} }
protected override void OnInit() protected override void OnInit()
{ {
this.bars = new(); this.ShortName = "SMMA (" + TBars.SelectStr(this.DataSource) + ", " + this.Period + ")";
this.ShortName = "ZLEMA (" + TBars.SelectStr(this.DataSource) + ", " + this.Period + ")"; this.bars = new();
this.indicator = new(source: bars.Select(this.DataSource), period: this.Period, useNaN: false); this.indicator = new(source: bars.Select(this.DataSource), period: this.Period, useNaN: false);
} }
protected override void OnUpdate(UpdateArgs args)
protected override void OnUpdate(UpdateArgs args)
{ {
bool update = !(args.Reason == UpdateReason.NewBar || bool update = !(args.Reason == UpdateReason.NewBar ||
args.Reason == UpdateReason.HistoricalBar); args.Reason == UpdateReason.HistoricalBar);
@@ -44,7 +45,6 @@ public class ZLEMA_chart : Indicator
this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low), this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low),
this.GetPrice(PriceType.Close), this.GetPrice(PriceType.Close),
this.GetPrice(PriceType.Volume), update); this.GetPrice(PriceType.Volume), update);
double result = this.indicator[this.indicator.Count - 1].v; double result = this.indicator[this.indicator.Count - 1].v;
this.SetValue(result); this.SetValue(result);
} }
+94
View File
@@ -0,0 +1,94 @@
using System.Collections;
using System.Drawing;
using System.Drawing.Text;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class ZLMA_chart : Indicator
{
#region Parameters
[InputParameter("Smoothing period", 0, 1, 999, 1, 1)]
private int Period = 10;
[InputParameter("Data source", 1, variants: new object[]
{ "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5,
"OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })]
private int DataSource = 3;
[InputParameter("MA algorithm", 2, variants: new object[]
{ "SMA", 0,
"WMA", 1,
"EMA", 2,
"DEMA", 3,
"TEMA", 4,
"HMA", 5,
"KAMA", 6,
"JMA", 7,
"SMMA", 8
})]
private int matype = 2;
#endregion Parameters
private TBars bars;
///////
private ZL_Series zerolag;
private TSeries indicator;
///////
public ZLMA_chart()
{
this.SeparateWindow = false;
this.Name = "ZLMA - Zero-lag Moving Average";
this.Description = "Zero-Lag Moving Average description";
this.AddLineSeries("ZLMA", Color.RoyalBlue, 3, LineStyle.Solid);
}
protected override void OnInit()
{
this.bars = new();
string maname = matype switch
{
0 => "SMA",
1 => "WMA",
2 => "EMA",
3 => "DEMA",
4 => "TEMA",
5 => "HMA",
6 => "KAMA",
7 => "JMA",
8 => "SMMA",
_ => "???"
};
this.ShortName = "ZLMA (" + maname + ", " + TBars.SelectStr(this.DataSource) + ", " + this.Period + ")";
this.zerolag = new(source: bars.Select(this.DataSource), period: this.Period, useNaN: false);
this.indicator = matype switch
{
0 => new SMA_Series(source: zerolag, period: this.Period, useNaN: false),
1 => new WMA_Series(source: zerolag, period: this.Period, useNaN: false),
2 => new EMA_Series(source: zerolag, period: this.Period, useNaN: false),
3 => new DEMA_Series(source: zerolag, period: this.Period, useNaN: false),
4 => new TEMA_Series(source: zerolag, period: this.Period, useNaN: false),
5 => new HMA_Series(source: zerolag, period: this.Period, useNaN: false),
6 => new KAMA_Series(source: zerolag, period: this.Period, useNaN: false),
7 => new JMA_Series(source: zerolag, period: this.Period, useNaN: false),
8 => new SMMA_Series(source: zerolag, period: this.Period, useNaN: false),
_ => new EMA_Series(source: zerolag, period: this.Period, useNaN: false)
};
}
protected override void OnUpdate(UpdateArgs args)
{
bool update = !(args.Reason == UpdateReason.NewBar ||
args.Reason == UpdateReason.HistoricalBar);
this.bars.Add(this.Time(), this.GetPrice(PriceType.Open),
this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low),
this.GetPrice(PriceType.Close),
this.GetPrice(PriceType.Volume), update);
double result = this.indicator[this.indicator.Count - 1].v;
this.SetValue(result);
}
}
+3 -3
View File
@@ -3,12 +3,12 @@ using System;
/* <summary> /* <summary>
ZL: Zero Lag ZL: Zero Lag
Data is de-lagged by removing the data from “lag” days ago, thus removing Data is de-lagged by removing the data from “lag” days ago, thus removing
(or attempting to) the cumulative effect of the moving average. (or attempting to) the cumulative effect of the moving average.
Calculation: Calculation:
Lag = (Period-1)/2 Lag = (Period-1)/2
ZL = Data + (Data - Data(Lag days ago) ) ZL = Data + (Data - Data(Lag days ago) )
Sources: Sources:
https://mudrex.com/blog/zero-lag-ema-trading-strategy/ https://mudrex.com/blog/zero-lag-ema-trading-strategy/
@@ -24,7 +24,7 @@ public class ZL_Series : Single_TSeries_Indicator
public override void Add((DateTime t, double v) TValue, bool update) public override void Add((DateTime t, double v) TValue, bool update)
{ {
int _lag = (int)((_p-1) * 0.5); int _lag = (int)((_p-1) * 0.5);
_lag = (_data.Count-_lag < 0) ? 0 : _data.Count-_lag; _lag = (this.Count-_lag < 0) ? 0 : this.Count-_lag;
double _zl = TValue.v + (TValue.v - _data[_lag].v); double _zl = TValue.v + (TValue.v - _data[_lag].v);
+1 -1
View File
@@ -59,7 +59,7 @@ public class HMA_Series : TSeries
this._buf1.Add(data.v); this._buf1.Add(data.v);
this._buf2.Add(data.v); this._buf2.Add(data.v);
} }
if (this._buf1.Count > (int)(Math.Ceiling((double)this._p / 2))) if (this._buf1.Count > (int)((double)this._p / 2))
{ {
this._buf1.RemoveAt(0); this._buf1.RemoveAt(0);
} }
+22 -24
View File
@@ -35,30 +35,28 @@ public class KAMA_Series : Single_TSeries_Indicator
_scSlow = 2.0 / (slow+1); _scSlow = 2.0 / (slow+1);
if (base._data.Count > 0) { base.Add(base._data); } if (base._data.Count > 0) { base.Add(base._data); }
} }
public override void Add((System.DateTime t, double v) TValue, bool update) public override void Add((System.DateTime t, double v) TValue, bool update)
{ {
if (update){ if (update){
_buffer[_buffer.Count - 1] = TValue.v; _buffer[_buffer.Count - 1] = TValue.v;
this._lastkama = this._lastlastkama; this._lastkama = this._lastlastkama;
} else { } else {
_buffer.Add(TValue.v); _buffer.Add(TValue.v);
} }
if (_buffer.Count > _p + 1) { _buffer.RemoveAt(0); } if (_buffer.Count > _p + 1) { _buffer.RemoveAt(0); }
double _kama = TValue.v; double _kama = 0;
if (this.Count < this._p) { if (this.Count < this._p) {
for (int i = 0; i < this._buffer.Count; i++) { _kama += this._buffer[i]; } for (int i = 0; i < this._buffer.Count; i++) { _kama += this._buffer[i]; }
_kama /= this._buffer.Count; _kama /= this._buffer.Count;
} else { } else {
double _change = Math.Abs(_buffer[_buffer.Count - 1] - _buffer[(_buffer.Count > _p + 1) ? 1 : 0]); double _change = Math.Abs(_buffer[_buffer.Count - 1] - _buffer[(_buffer.Count > _p + 1) ? 1 : 0]);
double _sumpv = 0; double _sumpv = 0;
for (int i = 1; i < _buffer.Count; i++) for (int i = 1; i < _buffer.Count; i++)
{ { _sumpv += Math.Abs(_buffer[(_buffer.Count > 0) ? i : 0] - _buffer[i - 1]); }
_sumpv += Math.Abs(_buffer[(_buffer.Count > 0) ? i : 0] - _buffer[i - 1]); double _er = (_sumpv == 0) ? 0 : _change / _sumpv;
} double _sc = (_er * (_scFast - _scSlow)) + _scSlow;
double _er = (_sumpv == 0) ? 0 : _change / _sumpv; _kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama)));
double _sc = (_er * (_scFast - _scSlow)) + _scSlow; }
_kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama)));
}
_lastlastkama = _lastkama; _lastlastkama = _lastkama;
_lastkama = _kama; _lastkama = _kama;
var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _kama); var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _kama);
+58
View File
@@ -0,0 +1,58 @@
namespace QuanTAlib;
using System;
/* <summary>
SMMA: Smoothed Moving Average
The Smoothed Moving Average (SMMA) is a combination of a SMA and an EMA. It gives the recent prices
an equal weighting as the historic prices as it takes all available price data into account.
The main advantage of a smoothed moving average is that it removes short-term fluctuations.
SMMA(i) = (SMMA-1*(N-1) + CLOSE (i)) / N
Sources:
https://blog.earn2trade.com/smoothed-moving-average
https://guide.traderevolution.com/traderevolution/mobile-applications/phone/android/technical-indicators/moving-averages/smma-smoothed-moving-average
https://www.chartmill.com/documentation/technical-analysis-indicators/217-MOVING-AVERAGES-%7C-The-Smoothed-Moving-Average-%28SMMA%29
</summary> */
public class SMMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> _buffer = new();
private double _lastsmma, _lastlastsmma;
public SMMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
{
this._lastsmma = this._lastlastsmma = double.NaN;
if (this._data.Count > 0) { base.Add(this._data); }
}
public override void Add((DateTime t, double v) TValue, bool update)
{
double _smma = 0;
if (update) { this._lastsmma = this._lastlastsmma; }
if (this.Count < this._p)
{
if (update) { this._buffer[this._buffer.Count - 1] = TValue.v; }
else
{
this._buffer.Add(TValue.v);
}
if (this._buffer.Count > this._p) { this._buffer.RemoveAt(0); }
for (int i = 0; i < this._buffer.Count; i++) { _smma += this._buffer[i]; }
_smma /= this._buffer.Count;
}
else
{
_smma = ((_lastsmma * (_p-1)) + TValue.v) / _p ;
}
this._lastlastsmma = this._lastsmma;
this._lastsmma = _smma;
var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _smma);
base.Add(ret, update);
}
}
+43 -28
View File
@@ -21,36 +21,51 @@ Remark:
public class ZLEMA_Series : Single_TSeries_Indicator public class ZLEMA_Series : Single_TSeries_Indicator
{ {
private readonly double _k, _k1m; private readonly System.Collections.Generic.List<double> _buffer = new();
private double _lastema, _lastlastema; private readonly double _k, _k1m;
private double _lastema, _lastlastema;
public ZLEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN) public ZLEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
{ {
this._k = 2.0 / (double)(period + 1); this._k = 2.0 / (this._p + 1);
this._k1m = 1.0 - this._k; this._k1m = 1.0 - this._k;
this._lastema = this._lastlastema = double.NaN; this._lastema = this._lastlastema = double.NaN;
if (base._data.Count > 0)
{ base.Add(base._data); }
}
public override void Add((System.DateTime t, double v) TValue, bool update)
{
int _lag = (int)((_p - 1) * 0.5);
_lag = (this.Count - _lag < 0) ? 0 : this.Count - _lag;
double _zl = TValue.v + (TValue.v - _data[_lag].v);
double _ema = 0;
if (update)
{ this._lastema = this._lastlastema; }
if (this.Count < this._p)
{
if (update)
{ this._buffer[this._buffer.Count - 1] = _zl; }
else
{
this._buffer.Add(_zl);
}
if (this._buffer.Count > this._p)
{ this._buffer.RemoveAt(0); }
if (base._data.Count > 0) { base.Add(base._data); } for (int i = 0; i < this._buffer.Count; i++)
} { _ema += this._buffer[i]; }
_ema /= this._buffer.Count;
}
else
{
_ema = TValue.v * this._k + this._lastema * this._k1m;
}
public override void Add((System.DateTime t, double v) TValue, bool update) this._lastlastema = this._lastema;
{ this._lastema = _ema;
if (update)
{
this._lastema = this._lastlastema;
}
int _lag = (int)(0.5 * (_p - 1));
int _l = Math.Max(this._data.Count - _lag, 0);
double _lagdata = 1 * TValue.v - this._data[_l].v;
double _ema = System.Double.IsNaN(this._lastema) ? _lagdata : _lagdata * this._k + this._lastema * this._k1m; var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
this._lastlastema = this._lastema; base.Add(ret, update);
this._lastema = _ema; }
}
(System.DateTime t, double v) result =
(TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _ema);
base.Add(result, update);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Version>0.1.11</Version> <Version>0.1.12</Version>
<releaseNotes></releaseNotes> <releaseNotes></releaseNotes>
<Title>QuanTAlib</Title> <Title>QuanTAlib</Title>
<Product>Library of Technical Indicators for .NET</Product> <Product>Library of Technical Indicators for .NET</Product>
+33
View File
@@ -0,0 +1,33 @@
using Xunit;
using System;
using QuanTAlib;
namespace MovingAvg;
public class SMMA_Test
{
[Fact]
public void Add_Test()
{
TSeries a = new() { 0, 1, 2, 3, 4, 5 };
SMMA_Series c = new(a, 3);
Assert.Equal(6, c.Count);
a.Add(5);
Assert.Equal(a.Count, c.Count);
a.Add(0, update: true);
Assert.Equal(a.Count, c.Count);
}
[Fact]
public void Edge_Test()
{
TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
SMMA_Series c = new(a, 3);
Assert.Equal(a.Count, c.Count);
a.Add(double.NaN);
Assert.Equal(a.Count, c.Count);
a.Add(double.PositiveInfinity);
Assert.Equal(a.Count, c.Count);
}
}
+8
View File
@@ -26,6 +26,14 @@
<NoWarn>1701;1702;MSB3270</NoWarn> <NoWarn>1701;1702;MSB3270</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="Validations\Pandas_TA.cstemp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Validations\Pandas_TA.cstemp" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="JetBrains.dotCover.CommandLineTools" Version="2022.1.0-eap10"> <PackageReference Include="JetBrains.dotCover.CommandLineTools" Version="2022.1.0-eap10">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
+9
View File
@@ -117,4 +117,13 @@ public class Skender_Stock
Assert.Equal(Math.Round((double)SK.Last().Kama!, 8), Math.Round(QL.Last().v, 8)); Assert.Equal(Math.Round((double)SK.Last().Kama!, 8), Math.Round(QL.Last().v, 8));
} }
[Fact]
public void SMMA()
{
SMMA_Series QL = new(this.bars.Close, this.period, useNaN: false);
var SK = this.quotes.GetSmma(this.period);
Assert.Equal(Math.Round((double)SK.Last().Smma!, 8), Math.Round(QL.Last().v, 8));
}
} }