mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-17 18:18:04 +00:00
Rebase
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
// ADD - adding TSeries+TSeries together, or TSeries+double, or double+TSeries
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class ADD_Series : Pair_TSeries_Indicator
|
||||
{
|
||||
public ADD_Series(TSeries d1, TSeries d2 ) : base(d1, d2) {
|
||||
if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } }
|
||||
}
|
||||
public ADD_Series(TSeries d1, double dd2 ) : base(d1, dd2) {
|
||||
if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } }
|
||||
}
|
||||
public ADD_Series(double dd1, TSeries d2 ) : base(dd1, d2) {
|
||||
if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update)
|
||||
{
|
||||
(System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t,
|
||||
TValue1.v+TValue2.v);
|
||||
if (update) { base[base.Count - 1] = result; } else { base.Add(result); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
public abstract class Single_TSeries_Indicator : TSeries
|
||||
{
|
||||
protected readonly int _p;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
// Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN)
|
||||
protected Single_TSeries_Indicator(TSeries source, int period, bool useNaN)
|
||||
{
|
||||
this._data = source;
|
||||
this._p = period;
|
||||
this._NaN = useNaN;
|
||||
this._data.Pub += this.Sub;
|
||||
}
|
||||
|
||||
// overridable Add() method to add/update a single item at the end of the list
|
||||
public new virtual void Add((System.DateTime t, double v) TValue, bool update) => base.Add(TValue, update);
|
||||
|
||||
// potentially overridable Add() method for the whole series (could be replaced with faster bulk algo)
|
||||
public virtual void Add(TSeries data)
|
||||
{
|
||||
for (int i = 0; i < data.Count; i++) { this.Add(TValue: data[i], update: false); }
|
||||
}
|
||||
|
||||
public new void Add((System.DateTime t, double v) TValue)
|
||||
=> this.Add(TValue: TValue, update: false);
|
||||
public void Add(bool update)
|
||||
=> this.Add(TValue: this._data[this._data.Count - 1], update: update);
|
||||
public void Add()
|
||||
=> this.Add(TValue: this._data[this._data.Count - 1], update: false);
|
||||
public new void Sub(object source, TSeriesEventArgs e)
|
||||
=> this.Add(TValue: this._data[this._data.Count - 1], update: e.update);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public abstract class Pair_TSeries_Indicator : TSeries
|
||||
{
|
||||
protected readonly TSeries _d1;
|
||||
protected readonly TSeries _d2;
|
||||
protected readonly double _dd1, _dd2;
|
||||
|
||||
// Chainable Constructors - add them at the end of primary constructors if needed
|
||||
protected Pair_TSeries_Indicator(TSeries source1, TSeries source2)
|
||||
{
|
||||
this._d1 = source1;
|
||||
this._d2 = source2;
|
||||
this._dd1 = double.NaN;
|
||||
this._dd2 = double.NaN;
|
||||
this._d1.Pub += this.Sub;
|
||||
this._d2.Pub += this.Sub;
|
||||
}
|
||||
protected Pair_TSeries_Indicator(TSeries source1, double dd2)
|
||||
{
|
||||
this._d1 = source1;
|
||||
this._d2 = new();
|
||||
this._dd1 = double.NaN;
|
||||
this._dd2 = dd2;
|
||||
this._d1.Pub += this.Sub;
|
||||
}
|
||||
protected Pair_TSeries_Indicator(double dd1, TSeries source2)
|
||||
{
|
||||
this._d1 = new();
|
||||
this._d2 = source2;
|
||||
this._dd1 = dd1;
|
||||
this._dd2 = double.NaN;
|
||||
this._d2.Pub += this.Sub;
|
||||
}
|
||||
|
||||
// overridable Add(Tvalue, Tvalue) method to add/update a single value at the end of the list
|
||||
public virtual void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update)
|
||||
=> base.Add(TValue: (TValue1.t, 0), update: update); // default inserts zeros
|
||||
|
||||
|
||||
// potentially overridable Add() bulk variations (could be replaced with faster bulk algos)
|
||||
public virtual void Add(TSeries d1, TSeries d2) {
|
||||
for (int i = 0; i < d1.Count; i++) { this.Add(d1[i], d2[i], update: false); }
|
||||
}
|
||||
public virtual void Add(TSeries d1, double dd2) {
|
||||
for (int i = 0; i < d1.Count; i++) { this.Add(d1[i], (d1[i].t, dd2), update: false); }
|
||||
}
|
||||
public virtual void Add(double dd1, TSeries d2) {
|
||||
for (int i = 0; i < d2.Count; i++) { this.Add((d2[i].t, dd1), d2[i], update: false); }
|
||||
}
|
||||
|
||||
|
||||
public void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2)
|
||||
=> this.Add(TValue1, TValue2, update: false);
|
||||
|
||||
public void Add(bool update)
|
||||
{
|
||||
if ((this._dd1 is double.NaN) && (this._dd2 is double.NaN))
|
||||
{
|
||||
// (Series, Series)
|
||||
if (update || (this._d1.Count > this.Count && this._d2.Count > this.Count))
|
||||
{ this.Add(this._d1[this._d1.Count - 1], this._d2[this._d2.Count - 1], update); }
|
||||
}
|
||||
else if ((this._dd2 is not double.NaN) && (this._dd1 is double.NaN))
|
||||
{
|
||||
// (Series, Double)
|
||||
this.Add(TValue1: this._d1[this._d1.Count - 1], TValue2: (this._d1[this._d1.Count - 1].t, this._dd2), update: update);
|
||||
}
|
||||
else
|
||||
{
|
||||
// (Double, Series)
|
||||
this.Add(TValue1: (this._d2[this._d2.Count - 1].t, this._dd1), TValue2: this._d2[this._d2.Count - 1], update: update);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add() => this.Add(update: false);
|
||||
|
||||
public new void Sub(object source, TSeriesEventArgs e)
|
||||
=> this.Add(e.update);
|
||||
}
|
||||
|
||||
|
||||
public abstract class Single_TBars_Indicator : TSeries
|
||||
{
|
||||
protected readonly int _p;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TBars _bars;
|
||||
|
||||
// Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN)
|
||||
protected Single_TBars_Indicator(TBars source, bool useNaN)
|
||||
{
|
||||
this._bars = source;
|
||||
this._NaN = useNaN;
|
||||
this._bars.Close.Pub += this.Sub;
|
||||
}
|
||||
|
||||
// overridable Add() method to add/update a single item at the end of the list
|
||||
public virtual void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar, bool update) => base.Add(TBar.c, update);
|
||||
|
||||
// potentially overridable Add() method for the whole series (could be replaced with faster bulk algo)
|
||||
public virtual void Add(TBars bars)
|
||||
{
|
||||
for (int i = 0; i < bars.Count; i++) { this.Add(TBar: bars[i], update: false); }
|
||||
}
|
||||
|
||||
public new void Add((System.DateTime t, double v) TValue)
|
||||
=> this.Add(TValue: TValue, update: false);
|
||||
public void Add(bool update)
|
||||
=> this.Add(TBar: this._bars[this._bars.Count - 1], update: update);
|
||||
public void Add()
|
||||
=> this.Add(TBar: this._bars[this._bars.Count - 1], update: false);
|
||||
public new void Sub(object source, TSeriesEventArgs e)
|
||||
=> this.Add(TBar: this._bars[this._bars.Count - 1], update: e.update);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// DIV - divide TSeries/TSeries , or TSeries/double, or double/TSeries
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class DIV_Series : Pair_TSeries_Indicator
|
||||
{
|
||||
public DIV_Series(TSeries d1, TSeries d2 ) : base(d1, d2) {
|
||||
if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } }
|
||||
}
|
||||
public DIV_Series(TSeries d1, double dd2 ) : base(d1, dd2) {
|
||||
if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } }
|
||||
}
|
||||
public DIV_Series(double dd1, TSeries d2 ) : base(dd1, d2) {
|
||||
if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update)
|
||||
{
|
||||
(System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t,
|
||||
(TValue2.v is not 0) ? TValue1.v/TValue2.v : Double.PositiveInfinity);
|
||||
if (update) { base[base.Count - 1] = result; } else { base.Add(result); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// MUL - multiply TSeries*TSeries together, or TSeries*double, or double*TSeries
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MUL_Series : Pair_TSeries_Indicator
|
||||
{
|
||||
public MUL_Series(TSeries d1, TSeries d2 ) : base(d1, d2) {
|
||||
if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } }
|
||||
}
|
||||
public MUL_Series(TSeries d1, double dd2 ) : base(d1, dd2) {
|
||||
if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } }
|
||||
}
|
||||
public MUL_Series(double dd1, TSeries d2 ) : base(dd1, d2) {
|
||||
if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update)
|
||||
{
|
||||
(System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t,
|
||||
TValue1.v*TValue2.v);
|
||||
if (update) { base[base.Count - 1] = result; } else { base.Add(result); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class RND_Feed : TBars
|
||||
{
|
||||
public RND_Feed(int days, double volatility = 0.05, double startvalue = 100.0)
|
||||
{
|
||||
Random rnd = new();
|
||||
double c = startvalue;
|
||||
for (int i = 0; i < days; i++)
|
||||
{
|
||||
double o = Math.Round(c + c * (volatility * 0.1 * rnd.NextDouble() - 0.005), 2);
|
||||
double h = Math.Round(o + c * volatility * rnd.NextDouble(), 2);
|
||||
double l = Math.Round(o - c * volatility * rnd.NextDouble(), 2);
|
||||
c = Math.Round(l + (h - l) * rnd.NextDouble(), 2);
|
||||
double v = Math.Round(1000 * rnd.NextDouble(), 2);
|
||||
this.Add(DateTime.Today.AddDays(i - days), o, h, l, c, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// SUB - subtracting TSeries-TSeries, or TSeries-double, or double-TSeries
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class SUB_Series : Pair_TSeries_Indicator
|
||||
{
|
||||
public SUB_Series(TSeries d1, TSeries d2 ) : base(d1, d2) {
|
||||
if (base._d1.Count > 0 && base._d2.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], base._d2[i], false); } }
|
||||
}
|
||||
public SUB_Series(TSeries d1, double dd2 ) : base(d1, dd2) {
|
||||
if (base._d1.Count > 0) { for (int i=0; i< base._d1.Count; i++) { this.Add(base._d1[i], (base._d1[i].t, dd2), false); } }
|
||||
}
|
||||
public SUB_Series(double dd1, TSeries d2 ) : base(dd1, d2) {
|
||||
if (base._d2.Count > 0) { for (int i=0; i< base._d2.Count; i++) { this.Add((base._d2[i].t, dd1), base._d2[i], false); } }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v)TValue1, (System.DateTime t, double v)TValue2, bool update)
|
||||
{
|
||||
(System.DateTime t, double v) result = ((TValue1.t > TValue2.t) ? TValue1.t : TValue2.t,
|
||||
TValue1.v-TValue2.v);
|
||||
if (update) { base[base.Count - 1] = result; } else { base.Add(result); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
|
||||
public class TBars : System.Collections.Generic.List<(DateTime t, double o, double h, double l, double c, double v)>
|
||||
{
|
||||
private readonly TSeries _open = new();
|
||||
private readonly TSeries _high = new();
|
||||
private readonly TSeries _low = new();
|
||||
private readonly TSeries _close = new();
|
||||
private readonly TSeries _volume = new();
|
||||
private readonly TSeries _hl2 = new();
|
||||
private readonly TSeries _oc2 = new();
|
||||
private readonly TSeries _ohl3 = new();
|
||||
private readonly TSeries _hlc3 = new();
|
||||
private readonly TSeries _ohlc4 = new();
|
||||
private readonly TSeries _hlcc4 = new();
|
||||
|
||||
public TSeries Open => this._open;
|
||||
public TSeries High => this._high;
|
||||
public TSeries Low => this._low;
|
||||
public TSeries Close => this._close;
|
||||
public TSeries Volume => this._volume;
|
||||
public TSeries HL2 => this._hl2;
|
||||
public TSeries OC2 => this._oc2;
|
||||
public TSeries OHL3 => this._ohl3;
|
||||
public TSeries HLC3 => this._hlc3;
|
||||
public TSeries OHLC4 => this._ohlc4;
|
||||
public TSeries HLCC4 => this._hlcc4;
|
||||
|
||||
public TSeries Select(int source)
|
||||
{
|
||||
return source switch
|
||||
{
|
||||
0 => _open,
|
||||
1 => _high,
|
||||
2 => _low,
|
||||
3 => _close,
|
||||
4 => _hl2,
|
||||
5 => _oc2,
|
||||
6 => _ohl3,
|
||||
7 => _hlc3,
|
||||
8 => _ohlc4,
|
||||
_ => _hlcc4,
|
||||
};
|
||||
}
|
||||
public static string SelectStr(int source)
|
||||
{
|
||||
return source switch
|
||||
{
|
||||
0 => "Open",
|
||||
1 => "High",
|
||||
2 => "Low",
|
||||
3 => "Close",
|
||||
4 => "HL2",
|
||||
5 => "OC2",
|
||||
6 => "OHL3",
|
||||
7 => "Typical",
|
||||
8 => "Mean",
|
||||
_ => "Weighted",
|
||||
};
|
||||
}
|
||||
|
||||
public void
|
||||
Add((DateTime t, double o, double h, double l, double c, double v) i, bool update = false)
|
||||
=> Add(i.t, i.o, i.h, i.l, i.c, i.v, update);
|
||||
|
||||
public void Add(DateTime t, decimal o, decimal h, decimal l, decimal c, decimal v, bool update = false)
|
||||
=> Add(t, (double)o, (double)h, (double)l, (double)c, (double)v, update);
|
||||
|
||||
public void Add(DateTime t, double o, double h, double l, double c, double v, bool update = false)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
this[this.Count - 1] = (t, o, h, l, c, v);
|
||||
_open[_open.Count - 1] = (t, o);
|
||||
_high[_high.Count - 1] = (t, h);
|
||||
_low[_low.Count - 1] = (t, l);
|
||||
_close[_close.Count - 1] = (t, c);
|
||||
_volume[_volume.Count - 1] = (t, v);
|
||||
_hl2[_hl2.Count - 1] = (t, (h + l) * 0.5);
|
||||
_oc2[_oc2.Count - 1] = (t, (o + c) * 0.5);
|
||||
_ohl3[_ohl3.Count - 1] = (t, (o + h + l) * 0.333333333333333);
|
||||
_hlc3[_hlc3.Count - 1] = (t, (h + l + c) * 0.333333333333333);
|
||||
_ohlc4[_ohlc4.Count - 1] = (t, (o + h + l + c) * 0.25);
|
||||
_hlcc4[_hlcc4.Count - 1] = (t, (h + l + c + c) * 0.25);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Add((t, o, h, l, c, v));
|
||||
_open.Add((t, o));
|
||||
_high.Add((t, h));
|
||||
_low.Add((t, l));
|
||||
_close.Add((t, c));
|
||||
_volume.Add((t, v));
|
||||
_hl2.Add((t, (h + l) * 0.5));
|
||||
_oc2.Add((t, (o + c) * 0.5));
|
||||
_ohl3.Add((t, (o + h + l) * 0.333333333333333));
|
||||
_hlc3.Add((t, (h + l + c) * 0.333333333333333));
|
||||
_ohlc4.Add((t, (o + h + l + c) * 0.25));
|
||||
_hlcc4.Add((t, (h + l + c + c) * 0.25));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
public class TSeries : System.Collections.Generic.List<(DateTime t, double v)>
|
||||
{
|
||||
// when asked for a (t,v) tuple, return the last (t,v) on the List
|
||||
public static implicit operator (DateTime t, double v)(TSeries l) => l[l.Count - 1];
|
||||
|
||||
// when asked for a (double), return the value part of the last tuple on the list
|
||||
public static implicit operator double(TSeries l) => l[l.Count - 1].v;
|
||||
|
||||
// when asked for a (DateTime), return the DateTime part of the last tuple on the list
|
||||
public static implicit operator DateTime(TSeries l) => l[l.Count - 1].t;
|
||||
|
||||
public System.Collections.Generic.List<DateTime> t =>
|
||||
this.Select(x => (DateTime)x.t).ToList();
|
||||
|
||||
public System.Collections.Generic.List<double> v =>
|
||||
this.Select(x => (double)x.v).ToList();
|
||||
|
||||
public int Length => this.Count;
|
||||
|
||||
// add/update one (t,v) tuple to/at the end of the list
|
||||
public void Add((DateTime t, double v) TValue, bool update = false)
|
||||
{
|
||||
if (update) { this[this.Count - 1] = TValue; }
|
||||
else { base.Add(TValue); }
|
||||
this.OnEvent(update);
|
||||
}
|
||||
|
||||
public void Add(DateTime t, double v, bool update = false) => this.Add((t, v), update);
|
||||
|
||||
public void Add(double v, bool update = false) => this.Add((DateTime.Now, v), update);
|
||||
|
||||
// Broadcast handler - only to valid targets
|
||||
protected virtual void OnEvent(bool update = false)
|
||||
{
|
||||
if (Pub != null && Pub.Target != this)
|
||||
{
|
||||
Pub(this, new TSeriesEventArgs { update = update });
|
||||
}
|
||||
}
|
||||
|
||||
// delegate used by event handler + event handler (Pub == publisher)
|
||||
public delegate
|
||||
void NewDataEventHandler(object source, TSeriesEventArgs args);
|
||||
public event NewDataEventHandler Pub;
|
||||
|
||||
public void Sub(object source, TSeriesEventArgs e)
|
||||
{
|
||||
TSeries ss = (TSeries)source;
|
||||
if (ss.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < ss.Count; i++)
|
||||
{
|
||||
this.Add(ss[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Add(ss[ss.Count - 1], e.update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EventArgs extension - carries the update field
|
||||
public class TSeriesEventArgs : EventArgs
|
||||
{
|
||||
public bool update { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
DEMA: Double Exponential Moving Average
|
||||
DEMA uses EMA(EMA()) to calculate smoother Exponential moving average.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/
|
||||
|
||||
Remark:
|
||||
ema1 = EMA(close, length)
|
||||
ema2 = EMA(ema1, length)
|
||||
DEMA = 2 * ema1 - ema2
|
||||
**/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
public class DEMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly List<double> _buffer = new();
|
||||
private readonly double _k, _k1m;
|
||||
private double _lastema1, _lastlastema1;
|
||||
private double _lastema2, _lastlastema2;
|
||||
|
||||
public DEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._k = 2.0 / (this._p + 1);
|
||||
this._k1m = 1.0 - this._k;
|
||||
if (_data.Count > 0) { base.Add(_data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) d, bool update = false)
|
||||
{
|
||||
|
||||
if (update)
|
||||
{
|
||||
this._lastema1 = this._lastlastema1;
|
||||
this._lastema2 = this._lastlastema2;
|
||||
}
|
||||
|
||||
double _ema1, _ema2;
|
||||
|
||||
if (this.Count < this._p)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else
|
||||
{
|
||||
_buffer.Add(d.v);
|
||||
}
|
||||
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
_ema1 = _ema2 = _sma;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_ema1 = d.v * this._k + this._lastema1 * this._k1m;
|
||||
_ema2 = _ema1 * this._k + this._lastema2 * this._k1m;
|
||||
}
|
||||
|
||||
double _dema = 2 * _ema1 - _ema2;
|
||||
this._lastlastema1 = this._lastema1;
|
||||
this._lastlastema2 = this._lastema2;
|
||||
this._lastema1 = _ema1;
|
||||
this._lastema2 = _ema2;
|
||||
|
||||
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _dema);
|
||||
base.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
EMA: Exponential Moving Average
|
||||
|
||||
EMA needs very short history buffer and calculates the EMA value using just the
|
||||
previous EMA value. The weight of the new datapoint (k) is k = 2 / (period-1)
|
||||
Sources:
|
||||
https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages
|
||||
https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp
|
||||
https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA
|
||||
Issues:
|
||||
There is no consensus what the first EMA value should be - a zero, a first
|
||||
datapoint, or an average of the initial Period bars. All three starting methods
|
||||
converge within 20+ bars to the same moving average. Most implementations (including this one)
|
||||
use SMA() for the first Period bars as a seeding value for EMA.
|
||||
**/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
public class EMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly List<double> _buffer = new();
|
||||
private readonly double _k, _k1m;
|
||||
private double _lastema, _lastlastema;
|
||||
|
||||
public EMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._k = 2.0 / (this._p + 1);
|
||||
this._k1m = 1.0 - this._k;
|
||||
this._lastema = this._lastlastema = double.NaN;
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) d, bool update = false)
|
||||
{
|
||||
double _ema = 0;
|
||||
if (update) { this._lastema = this._lastlastema; }
|
||||
|
||||
if (this.Count < this._p)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else
|
||||
{
|
||||
this._buffer.Add(d.v);
|
||||
}
|
||||
if (this._buffer.Count > this._p) { this._buffer.RemoveAt(0); }
|
||||
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _ema += this._buffer[i]; }
|
||||
_ema /= this._buffer.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ema = d.v * this._k + this._lastema * this._k1m;
|
||||
}
|
||||
|
||||
this._lastlastema = this._lastema;
|
||||
this._lastema = _ema;
|
||||
|
||||
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
|
||||
base.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
HEMA: Hull-EMA Moving Average
|
||||
Modified HUll Moving Average; instead of using WMA (Weighted MA) for a
|
||||
calculation, HEMA uses EMA for Hull's formula:
|
||||
|
||||
EMA1 = EMA(n/2) of price - where k = 4/(n/2 +1)
|
||||
EMA2 = EMA(n) of price - where k = 3/(n+1)
|
||||
Raw HMA = (2 * EMA1) - EMA2
|
||||
EMA3 = EMA(sqrt(n)) of Raw HMA - where k = 2/(sqrt(n)+1)
|
||||
**/
|
||||
|
||||
public class HEMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public HEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._k1 = 4 / ((period * 0.5) + 1);
|
||||
this._k2 = 3 / (double)(period + 1);
|
||||
this._k3 = 2 / (Math.Sqrt(period) + 1);
|
||||
this._lastema1 = this._lastlastema1 = double.NaN;
|
||||
this._lastema2 = this._lastlastema2 = double.NaN;
|
||||
this._lastema3 = this._lastlastema3 = double.NaN;
|
||||
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly double _k1, _k2, _k3;
|
||||
private double _lastema1, _lastlastema1;
|
||||
private double _lastema2, _lastlastema2;
|
||||
private double _lastema3, _lastlastema3;
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
this._lastema1 = this._lastlastema1;
|
||||
this._lastema2 = this._lastlastema2;
|
||||
this._lastema3 = this._lastlastema3;
|
||||
}
|
||||
double _ema1 = System.Double.IsNaN(this._lastema1)
|
||||
? d.v
|
||||
: d.v * this._k1 + this._lastema1 * (1 - this._k1);
|
||||
double _ema2 = System.Double.IsNaN(this._lastema2)
|
||||
? d.v
|
||||
: d.v * this._k2 + this._lastema2 * (1 - this._k2);
|
||||
|
||||
double _rawhema = (2 * _ema1) - _ema2;
|
||||
double _ema3 = System.Double.IsNaN(this._lastema3)
|
||||
? _rawhema
|
||||
: _rawhema * this._k3 + this._lastema3 * (1 - this._k3);
|
||||
|
||||
this._lastlastema1 = this._lastema1;
|
||||
this._lastlastema2 = this._lastema2;
|
||||
this._lastlastema3 = this._lastema3;
|
||||
this._lastema1 = _ema1;
|
||||
this._lastema2 = _ema2;
|
||||
this._lastema3 = _ema3;
|
||||
|
||||
(System.DateTime t, double v) result =
|
||||
(d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _ema3);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
HMA: Hull Moving Average
|
||||
Developed by Alan Hull, an extremely fast and smooth moving average; almost
|
||||
eliminates lag altogether and manages to improve smoothing at the same time.
|
||||
|
||||
Sources:
|
||||
https://alanhull.com/hull-moving-average
|
||||
https://school.stockcharts.com/doku.php?id=technical_indicators:hull_moving_average
|
||||
WMA1 = WMA(n/2) of price
|
||||
WMA2 = WMA(n) of price
|
||||
Raw HMA = (2 * WMA1) - WMA2
|
||||
HMA = WMA(sqrt(n)) of Raw HMA
|
||||
**/
|
||||
|
||||
public class HMA_Series : TSeries
|
||||
{
|
||||
private readonly int _p;
|
||||
private readonly bool _NaN;
|
||||
private readonly TSeries _data;
|
||||
private double _wma1, _wma2;
|
||||
private readonly System.Collections.Generic.List<double> _buf1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buf2 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buf3 = new();
|
||||
private readonly System.Collections.Generic.List<double> _weights = new();
|
||||
|
||||
public HMA_Series(TSeries source, int period, bool useNaN = false)
|
||||
{
|
||||
this._p = period;
|
||||
this._data = source;
|
||||
this._NaN = useNaN;
|
||||
for (int i = 0; i < this._p; i++)
|
||||
{
|
||||
this._weights.Add(i + 1);
|
||||
}
|
||||
|
||||
source.Pub += this.Sub;
|
||||
if (source.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < source.Count; i++)
|
||||
{
|
||||
this.Add(source[i], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
public new void Add((System.DateTime t, double v) data, bool update = false)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
this._buf1[this._buf1.Count - 1] = data.v;
|
||||
this._buf2[this._buf2.Count - 1] = data.v;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._buf1.Add(data.v);
|
||||
this._buf2.Add(data.v);
|
||||
}
|
||||
if (this._buf1.Count > (int)(Math.Ceiling((double)this._p / 2)))
|
||||
{
|
||||
this._buf1.RemoveAt(0);
|
||||
}
|
||||
if (this._buf2.Count > this._p)
|
||||
{
|
||||
this._buf2.RemoveAt(0);
|
||||
}
|
||||
|
||||
this._wma1 = 0;
|
||||
for (int i = 0; i < this._buf1.Count; i++)
|
||||
{
|
||||
this._wma1 += this._buf1[i] * this._weights[i];
|
||||
}
|
||||
|
||||
this._wma1 /= (this._buf1.Count * (this._buf1.Count + 1)) * 0.5;
|
||||
|
||||
this._wma2 = 0;
|
||||
for (int i = 0; i < this._buf2.Count; i++)
|
||||
{
|
||||
this._wma2 += this._buf2[i] * this._weights[i];
|
||||
}
|
||||
|
||||
this._wma2 /= (this._buf2.Count * (this._buf2.Count + 1)) * 0.5;
|
||||
|
||||
if (update)
|
||||
{
|
||||
this._buf3[this._buf3.Count - 1] = 2 * this._wma1 - this._wma2;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._buf3.Add(2 * this._wma1 - this._wma2);
|
||||
}
|
||||
if (this._buf3.Count > (int)Math.Sqrt(this._p))
|
||||
{
|
||||
this._buf3.RemoveAt(0);
|
||||
}
|
||||
|
||||
double _hma = 0;
|
||||
for (int i = 0; i < this._buf3.Count; i++)
|
||||
{
|
||||
_hma += this._buf3[i] * this._weights[i];
|
||||
}
|
||||
|
||||
_hma /= (this._buf3.Count * (this._buf3.Count + 1)) * 0.5;
|
||||
|
||||
(System.DateTime t, double v) result =
|
||||
(data.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _hma);
|
||||
base.Add(result, update);
|
||||
}
|
||||
public void Add(bool update = false)
|
||||
{
|
||||
this.Add(this._data[this._data.Count - 1], update);
|
||||
}
|
||||
public new void Sub(object source, TSeriesEventArgs e)
|
||||
{
|
||||
this.Add(this._data[this._data.Count - 1], e.update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
JMA: Jurik Moving Average
|
||||
Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the
|
||||
underlying activity. It has extremely low lag, is very smooth and is responsive
|
||||
to market gaps.
|
||||
|
||||
Sources:
|
||||
https://c.mql5.com/forextsd/forum/164/jurik_1.pdf
|
||||
https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/
|
||||
|
||||
Issues:
|
||||
Real JMA algorithm is not published and this formula is derived through
|
||||
deduction and reverse analysis of JMA behavior. It is really close, but not
|
||||
exact - published JMA tests against JMA.CSV fail with small deviation. The
|
||||
original algo is slightly different, yet this approximation is close enough.
|
||||
**/
|
||||
public class JMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> vbuffer10;
|
||||
private readonly System.Collections.Generic.List<double> vsum65;
|
||||
|
||||
private double prev_ma1, prev_det0, prev_det1, prev_jma, bsmax, bsmin;
|
||||
private double o_prev_ma1, o_prev_det0, o_prev_det1, o_prev_jma, o_bsmax, o_bsmin;
|
||||
|
||||
private readonly double pr, pow1, len2, beta, rvolty;
|
||||
private readonly int _l;
|
||||
|
||||
public JMA_Series(TSeries source, int period, double phase = 0.0, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this.vbuffer10 = new();
|
||||
this.vsum65 = new();
|
||||
|
||||
// constants
|
||||
this.pr = (phase < -100) ? 0.5 : (phase > 100) ? 2.5 : (phase * 0.01) + 1.5;
|
||||
double len1 = Math.Max((Math.Log(Math.Sqrt(0.5 * (_p - 1))) / Math.Log(2.0)) + 2.0, 0);
|
||||
this.pow1 = Math.Max(len1 - 2, 0.5);
|
||||
this.rvolty = Math.Exp((1 / this.pow1) * Math.Log(len1));
|
||||
this.len2 = Math.Sqrt(0.5 * (_p - 1)) * len1;
|
||||
this.beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
|
||||
this._l = (int)Math.Round(this._p - 1 * 0.5);
|
||||
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (this.Count == 0)
|
||||
{
|
||||
this.prev_ma1 = this.prev_jma = d.v;
|
||||
this.bsmax = this.bsmin = this.prev_det0 = this.prev_det1 = 0;
|
||||
}
|
||||
|
||||
if (update)
|
||||
{
|
||||
this.prev_jma = this.o_prev_jma;
|
||||
this.prev_ma1 = this.o_prev_ma1;
|
||||
this.prev_det0 = this.o_prev_det0;
|
||||
this.prev_det1 = this.o_prev_det1;
|
||||
this.bsmax = this.o_bsmax;
|
||||
this.bsmin = this.o_bsmin;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.o_prev_jma = this.prev_jma;
|
||||
this.o_prev_ma1 = this.prev_ma1;
|
||||
this.o_prev_det0 = this.prev_det0;
|
||||
this.o_prev_det1 = this.prev_det1;
|
||||
this.o_bsmax = this.bsmax;
|
||||
this.o_bsmin = this.bsmin;
|
||||
}
|
||||
|
||||
double hprice = d.v;
|
||||
double lprice = d.v;
|
||||
for (int i = 0; i <= Math.Min(9, this._data.Count - 1); i++)
|
||||
{
|
||||
var _item = this._data[this._data.Count - 1 - i].v;
|
||||
hprice = (_item > hprice) ? _item : hprice;
|
||||
lprice = (_item < lprice) ? _item : lprice;
|
||||
}
|
||||
double del1 = hprice - this.bsmax;
|
||||
double del2 = lprice - this.bsmin;
|
||||
|
||||
double volty = (Math.Abs(del1) != Math.Abs(del2))
|
||||
? Math.Max(Math.Abs(del1), Math.Abs(del2))
|
||||
: 0;
|
||||
if (update)
|
||||
{
|
||||
this.vbuffer10[this.vbuffer10.Count - 1] = volty;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.vbuffer10.Add(volty);
|
||||
}
|
||||
if (this.vbuffer10.Count > 10)
|
||||
{
|
||||
this.vbuffer10.RemoveAt(0);
|
||||
}
|
||||
|
||||
double prevvsum =
|
||||
(this.vsum65.Count > 0) ? this.vsum65[this.vsum65.Count - 1] : 0;
|
||||
double vsumitem = prevvsum + 0.1 * (volty - this.vbuffer10[0]);
|
||||
if (update)
|
||||
{
|
||||
this.vsum65[this.vsum65.Count - 1] = vsumitem;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.vsum65.Add(vsumitem);
|
||||
}
|
||||
if (this.vsum65.Count > 65)
|
||||
{
|
||||
this.vsum65.RemoveAt(0);
|
||||
}
|
||||
|
||||
double avolty = 0;
|
||||
for (int i = 0; i < this.vsum65.Count; i++)
|
||||
{
|
||||
avolty += this.vsum65[i];
|
||||
}
|
||||
|
||||
avolty /= this.vsum65.Count;
|
||||
double dvolty = (avolty > 0) ? volty / avolty : 0;
|
||||
dvolty = Math.Max((dvolty > this.rvolty) ? this.rvolty : dvolty, 1.0);
|
||||
|
||||
double pow2 = Math.Exp(this.pow1 * Math.Log(dvolty));
|
||||
double kv =
|
||||
Math.Exp(Math.Sqrt(pow2) * Math.Log(this.len2 / (this.len2 + 1)));
|
||||
|
||||
this.bsmax = (del1 > 0) ? hprice : hprice - (kv * del1);
|
||||
this.bsmin = (del2 < 0) ? lprice : lprice - (kv * del2);
|
||||
|
||||
// adaptive EMA dynamic factor
|
||||
double pow = Math.Pow(dvolty, this.pow1);
|
||||
double alpha = Math.Pow(this.beta, pow);
|
||||
|
||||
// 1st stage - preliminary smoothing by adaptive EMA
|
||||
double ma1 = d.v * (1 - alpha) + this.prev_ma1 * alpha;
|
||||
this.prev_ma1 = ma1;
|
||||
|
||||
// 2nd stage - one more preliminary smoothing by Kalman filter
|
||||
double det0 = (d.v - ma1) * (1 - this.beta) + this.prev_det0 * this.beta;
|
||||
this.prev_det0 = det0;
|
||||
double ma2 = ma1 + (this.pr * det0);
|
||||
|
||||
// 3rd stage - final smoothing by Jurik adaptive filter
|
||||
double det1 = ((ma2 - this.prev_jma) * (1 - alpha) * (1 - alpha)) +
|
||||
(this.prev_det1 * alpha * alpha);
|
||||
this.prev_det1 = det1;
|
||||
var jma = this.prev_jma + det1;
|
||||
this.prev_jma = jma;
|
||||
|
||||
(System.DateTime t, double v) result =
|
||||
(d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : jma);
|
||||
base.Add(result, update);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
RMA: wildeR Moving Average
|
||||
|
||||
J. Welles Wilder introduced RMA as an alternative to EMA. RMA's weight (k) is
|
||||
set as 1/period, giving less weight to the new data compared to EMA. Sources:
|
||||
https://archive.org/details/newconceptsintec00wild/page/23/mode/2up
|
||||
|
||||
https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing
|
||||
https://www.incrediblecharts.com/indicators/wilder_moving_average.php
|
||||
|
||||
Issues:
|
||||
Pandas-TA library calculates RMA using straight Exponential Weighted Mean:
|
||||
pandas.ewm().mean() and returns incorrect first (period) of bars compared to
|
||||
published formula. This implementation passess the validation test in Wilder's book.
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
public class RMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly List<double> _buffer = new();
|
||||
private readonly double _k, _k1m;
|
||||
private double _lastema, _lastlastema;
|
||||
|
||||
public RMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._k = 1.0 / (double)(this._p);
|
||||
this._k1m = 1.0 - this._k;
|
||||
this._lastema = this._lastlastema = double.NaN;
|
||||
if (_data.Count > 0) { base.Add(_data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) d, bool update = false)
|
||||
{
|
||||
double _ema = 0;
|
||||
if (update) { this._lastema = this._lastlastema; }
|
||||
|
||||
if (this.Count < this._p)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else
|
||||
{
|
||||
_buffer.Add(d.v);
|
||||
}
|
||||
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;
|
||||
this._lastema = _ema;
|
||||
|
||||
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
|
||||
base.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
SMA: Simple Moving Average
|
||||
The weights are equally distributed across the period, resulting in a mean() of
|
||||
the data within the period/
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/
|
||||
https://stats.stackexchange.com/a/24739
|
||||
|
||||
Remark:
|
||||
This calc doesn't use LINQ or SUM() or any of iterative methods.
|
||||
**/
|
||||
|
||||
public class SMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _sma);
|
||||
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
TEMA: Triple Exponential Moving Average
|
||||
TEMA uses EMA(EMA(EMA())) to calculate less laggy Exponential moving average.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/
|
||||
|
||||
Remark:
|
||||
ema1 = EMA(close, length)
|
||||
ema2 = EMA(ema1, length)
|
||||
ema3 = EMA(ema2, length)
|
||||
TEMA = 3 * (ema1 - ema2) + ema3
|
||||
**/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
public class TEMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly List<double> _buffer = new();
|
||||
private readonly double _k, _k1m;
|
||||
private double _lastema1, _lastlastema1;
|
||||
private double _lastema2, _lastlastema2;
|
||||
private double _lastema3, _lastlastema3;
|
||||
|
||||
public TEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._k = 2.0 / (this._p + 1);
|
||||
this._k1m = 1.0 - this._k;
|
||||
if (_data.Count > 0) { base.Add(_data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) d, bool update = false)
|
||||
{
|
||||
|
||||
if (update)
|
||||
{
|
||||
this._lastema1 = this._lastlastema1;
|
||||
this._lastema2 = this._lastlastema2;
|
||||
this._lastema3 = this._lastlastema3;
|
||||
}
|
||||
|
||||
double _ema1, _ema2, _ema3;
|
||||
|
||||
if (this.Count < this._p)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else
|
||||
{
|
||||
_buffer.Add(d.v);
|
||||
}
|
||||
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
_ema1 = _ema2 = _ema3 = _sma;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ema1 = d.v * this._k + this._lastema1 * this._k1m;
|
||||
_ema2 = _ema1 * this._k + this._lastema2 * this._k1m;
|
||||
_ema3 = _ema2 * this._k + this._lastema3 * this._k1m;
|
||||
}
|
||||
|
||||
double _tema = 3 * (_ema1 - _ema2) + _ema3;
|
||||
|
||||
this._lastlastema1 = this._lastema1;
|
||||
this._lastlastema2 = this._lastema2;
|
||||
this._lastlastema3 = this._lastema3;
|
||||
this._lastema1 = _ema1;
|
||||
this._lastema2 = _ema2;
|
||||
this._lastema3 = _ema3;
|
||||
|
||||
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _tema);
|
||||
base.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
WMA: (linearly) Weighted Moving Average
|
||||
The weights are linearly decreasing over the period and the most recent data has
|
||||
the heaviest weight.
|
||||
|
||||
Sources:
|
||||
https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/weighted-moving-average-wma/
|
||||
https://www.technicalindicators.net/indicators-technical-analysis/83-moving-averages-simple-exponential-weighted
|
||||
**/
|
||||
|
||||
public class WMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public WMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
for (int i = 0; i < this._p; i++) { this._weights.Add(i + 1); }
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly System.Collections.Generic.List<double> _weights = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _wma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _wma += _buffer[i] * this._weights[i]; }
|
||||
_wma /= (this._buffer.Count * (this._buffer.Count + 1)) * 0.5;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _wma);
|
||||
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
/**
|
||||
ZLEMA: Zero Lag Exponential Moving Average
|
||||
|
||||
The Zero lag exponential moving average (ZLEMA) indicator was created by John
|
||||
Ehlers and Ric Way.
|
||||
|
||||
The formula for a given N-Day period and for a given Data series is:
|
||||
Lag = (Period-1)/2
|
||||
Ema Data = {Data+(Data-Data(Lag days ago))
|
||||
ZLEMA = EMA (EmaData,Period)
|
||||
|
||||
The idea is do a regular exponential moving average (EMA) calculation but on a
|
||||
de-lagged data instead of doing it on the regular data. Data is de-lagged by
|
||||
removing the data from "lag" days ago thus removing (or attempting to remove)
|
||||
the cumulative lag effect of the moving average.
|
||||
**/
|
||||
|
||||
public class ZLEMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly double _k, _k1m;
|
||||
private double _lastema, _lastlastema;
|
||||
|
||||
public ZLEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._k = 2.0 / (double)(period + 1);
|
||||
this._k1m = 1.0 - this._k;
|
||||
this._lastema = this._lastlastema = double.NaN;
|
||||
|
||||
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
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 * d.v - this._data[_l].v;
|
||||
|
||||
double _ema = System.Double.IsNaN(this._lastema) ? _lagdata : _lagdata * this._k + this._lastema * this._k1m;
|
||||
this._lastlastema = this._lastema;
|
||||
this._lastema = _ema;
|
||||
|
||||
(System.DateTime t, double v) result =
|
||||
(d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _ema);
|
||||
base.Add(result, update);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Version>0.1.10-beta</Version>
|
||||
<releaseNotes></releaseNotes>
|
||||
<Title>QuanTAlib</Title>
|
||||
<Product>Library of Technical Indicators for .NET</Product>
|
||||
<Description>Quantitative Technical Analysis library for both real-time (streaming) and historical data analysis</Description>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/mihakralj/QuanTAlib</RepositoryUrl>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<Authors>Miha Kralj</Authors>
|
||||
<Copyright>Miha Kralj</Copyright>
|
||||
<PackageReadmeFile>readme.md</PackageReadmeFile>
|
||||
<TargetFrameworks>net7.0;net6.0;net48;netcoreapp3.1;netstandard2.1</TargetFrameworks>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<RootNamespace>QuanTAlib</RootNamespace>
|
||||
<AssemblyName>QuanTAlib</AssemblyName>
|
||||
<IsPublishable>True</IsPublishable>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
|
||||
<DebugType>embedded</DebugType>
|
||||
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageTags>
|
||||
Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo;
|
||||
AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex;
|
||||
Quantitative;Historical;Quotes;
|
||||
</PackageTags>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<PackageLicenseFile></PackageLicenseFile>
|
||||
<SynchReleaseVersion>false</SynchReleaseVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<Optimize>True</Optimize>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<PlatformTarget>anycpu</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType></DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<PlatformTarget>anycpu</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageIcon>images\icon.png</PackageIcon>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\.github\QuanTAlib2.png" Pack="true" Visible="false" PackagePath="images\icon.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="QuanTAlib.nuspec" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\Docs\readme.md">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
BIAS: Rate of change between the source and a moving average.
|
||||
|
||||
Bias is a statistical term which means a systematic deviation from the actual value.
|
||||
|
||||
BIAS = (close - SMA) / SMA
|
||||
= (close / SMA) - 1
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Bias_of_an_estimator
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class BIAS_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public BIAS_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = TValue.v; }
|
||||
else { this._buffer.Add(TValue.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _sma += this._buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
double _bias = (this._buffer[this._buffer.Count - 1] / ((_sma != 0) ? _sma : 1)) - 1;
|
||||
|
||||
var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _bias);
|
||||
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
ENTP: Entropy
|
||||
|
||||
Introduced by Claude Shannon in 1948, entropy measures the unpredictability
|
||||
of the data, or equivalently, of its average information.
|
||||
|
||||
Calculation:
|
||||
P = close / Σ(close)
|
||||
ENTP = Σ(-P * Log(P) / Log(base))
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Entropy_(information_theory)
|
||||
https://math.stackexchange.com/questions/3428693/how-to-calculate-entropy-from-a-set-of-correlated-samples
|
||||
|
||||
**/
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
public class ENTP_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public ENTP_Series(TSeries source, int period, double logbase = 2.0, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._logbase = logbase;
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly double _logbase = 2.0;
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly System.Collections.Generic.List<double> _buff2 = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = TValue.v; }
|
||||
else { this._buffer.Add(TValue.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
double _sum = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _sum += this._buffer[i]; }
|
||||
|
||||
double _pp = this._buffer[this._buffer.Count - 1] / _sum;
|
||||
double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase);
|
||||
|
||||
if (update) { this._buff2[this._buff2.Count - 1] = _ppp; }
|
||||
else { this._buff2.Add(_ppp); }
|
||||
if (this._buff2.Count > this._p && this._p != 0) { this._buff2.RemoveAt(0); }
|
||||
|
||||
double _entp = 0;
|
||||
for (int i = 0; i < this._buff2.Count; i++) { _entp += this._buff2[i]; }
|
||||
|
||||
var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _entp);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
KURT: Kurtosis of population
|
||||
|
||||
Kurtosis characterizes the relative peakedness or flatness of a distribution
|
||||
compared with the normal distribution. Positive kurtosis indicates a relatively
|
||||
peaked distribution. Negative kurtosis indicates a relatively flat distribution.
|
||||
|
||||
The normal curve is called Mesokurtic curve. If the curve of a distribution is
|
||||
more outlier prone (or heavier-tailed) than a normal or mesokurtic curve then
|
||||
it is referred to as a Leptokurtic curve. If a curve is less outlier prone (or
|
||||
lighter-tailed) than a normal curve, it is called as a platykurtic curve.
|
||||
|
||||
Calculation:
|
||||
sum4 = Σ(close-SMA)^4
|
||||
sum2 = (Σ(close-SMA)^2)^2
|
||||
KURT = length * (sum4/sum2)
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Kurtosis
|
||||
https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
// https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/
|
||||
|
||||
public class KURT_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public KURT_Series(TSeries source, int period, double logbase = 2.0, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
this._logbase = logbase;
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
protected double _logbase = 2.0;
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else { this._buffer.Add(d.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
double _n = this._buffer.Count;
|
||||
|
||||
double _avg = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _avg += this._buffer[i]; }
|
||||
_avg /= _n;
|
||||
|
||||
double _s2 = 0;
|
||||
double _s4 = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++)
|
||||
{
|
||||
_s2 += (this._buffer[i] - _avg) * (this._buffer[i] - _avg);
|
||||
_s4 += (this._buffer[i] - _avg) * (this._buffer[i] - _avg) * (this._buffer[i] - _avg) * (this._buffer[i] - _avg);
|
||||
}
|
||||
|
||||
double _Vx = _s2 / (_n - 1);
|
||||
double _kurt = (_n > 3) ? (((_n * (_n + 1)) / ((_n - 1) * (_n - 2) * (_n - 3))) * (_s4 / (_Vx * _Vx)) - (3 * ((_n - 1) * (_n - 1) / ((_n - 2) * (_n - 3))))) : Double.NaN;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? Double.NaN : _kurt);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
MAD: Mean Absolute Deviation
|
||||
|
||||
Also known as AAD - Average Absolute Deviation, to differentiate it from Median Absolute Deviation
|
||||
MAD defines the degree of variation across the series.
|
||||
|
||||
Calculation:
|
||||
MAD = Σ(|close-SMA|) / period
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Average_absolute_deviation
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MAD_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MAD_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _mad = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); }
|
||||
_mad /= this._buffer.Count;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _mad);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
MAPE: Mean Absolute Percentage Error
|
||||
|
||||
Measures the size of the error in percentage terms
|
||||
|
||||
Calculation:
|
||||
MAPE = Σ(|close – SMA| / |close|) / n
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
|
||||
|
||||
Remark: returns infinity if any of observations is 0.
|
||||
Use SMAPE or WMAPE instead to avoid division-by-zero in MAPE
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MAPE_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MAPE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _mape = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _mape += (_buffer[i] != 0) ? Math.Abs(_buffer[i] - _sma) / Math.Abs(_buffer[i]) : double.PositiveInfinity; }
|
||||
_mape /= this._buffer.Count;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _mape);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace QuanTAlib;
|
||||
/*
|
||||
MAX - Maximum value in the given period in the series.
|
||||
|
||||
If period = 0 => period = full length of the series
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
public class MAX_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MAX_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly List<double> _buffer = new();
|
||||
|
||||
public override void Add((DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else { this._buffer.Add(d.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
double _max = d.v;
|
||||
for (int i = 0; i < this._buffer.Count; i++)
|
||||
{ _max = this._buffer[i] > _max ? this._buffer[i] : _max; }
|
||||
|
||||
var result = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _max);
|
||||
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
MED - Median value
|
||||
|
||||
Median of numbers is the middlemost value of the given set of numbers.
|
||||
It separates the higher half and the lower half of a given data sample.
|
||||
At least half of the observations are smaller than or equal to median
|
||||
and at least half of the observations are greater than or equal to the median.
|
||||
|
||||
If the number of values is odd, the middlemost observation of the sorted
|
||||
list is the median of the given data. If the number of values is even,
|
||||
median is the average of (n/2)th and [(n/2) + 1]th values of the sorted list.
|
||||
|
||||
If period = 0 => period is max
|
||||
|
||||
Sources:
|
||||
https://corporatefinanceinstitute.com/resources/knowledge/other/median/
|
||||
https://en.wikipedia.org/wiki/Median
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MED_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MED_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else { this._buffer.Add(d.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
System.Collections.Generic.List<double> _s = new(this._buffer);
|
||||
_s.Sort();
|
||||
int _p1 = _s.Count / 2;
|
||||
int _p2 = Math.Max(0, _s.Count / 2 - 1);
|
||||
double _med = (_s.Count % 2 != 0) ? _s[_p1] : (_s[_p1] + _s[_p2]) / 2;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _med);
|
||||
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
MIN - Minimum value in the given period in the series.
|
||||
|
||||
If period = 0 => period = full length of the series
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MIN_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MIN_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else { this._buffer.Add(d.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
double _min = d.v;
|
||||
for (int i = 0; i < this._buffer.Count; i++)
|
||||
{ _min = (this._buffer[i] < _min) ? this._buffer[i] : _min; }
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _min);
|
||||
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
MSE: Mean Square Error
|
||||
|
||||
Defined as a Mean (Average) of the Square of the difference between actual and estimated values.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Mean_squared_error
|
||||
|
||||
Remark:
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class MSE_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MSE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _mse = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_mse /= this._buffer.Count;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _mse);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
PSDEV: Population Standard Deviation
|
||||
|
||||
Population Standard Deviation is the square root of the biased variance, also knons as
|
||||
Uncorrected Sample Standard Deviation
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Standard_deviation#Uncorrected_sample_standard_deviation
|
||||
|
||||
Remark:
|
||||
PSDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation.
|
||||
For unbiased version that uses Bessel's correction, use SDEV instead.
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class PSDEV_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public PSDEV_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _pvar = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_pvar /= this._buffer.Count;
|
||||
double _psdev = Math.Sqrt(_pvar);
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _psdev);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
PVAR: Population Variance
|
||||
|
||||
Population variance....
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Variance
|
||||
Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
|
||||
|
||||
Remark:
|
||||
PVAR (Population Variance) is also known as a biased Sample Variance. For unbiased
|
||||
sample variance use SVAR instead.
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class PVAR_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public PVAR_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _pvar = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_pvar /= this._buffer.Count;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _pvar);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
SDEV: (Corrected) Sample Standard Deviation
|
||||
|
||||
Sample Standard Deviaton uses Bessel's correction to correct the bias in the variance.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation
|
||||
Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
|
||||
|
||||
Remark:
|
||||
|
||||
SSDEV (Sample Standard Deviation) is also known as a unbiased/corrected Standard Deviation.
|
||||
For a population/biased/uncorrected Standard Deviation, use PSDEV instead
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class SDEV_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SDEV_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else { this._buffer.Add(d.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _sma += this._buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _svar = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); }
|
||||
_svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction
|
||||
double _ssdev = Math.Sqrt(_svar);
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _ssdev);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
SMAPE: Symmetric Mean Absolute Percentage Error
|
||||
|
||||
Measures the size of the error in percentage terms
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class SMAPE_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SMAPE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _smape = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); }
|
||||
_smape /= this._buffer.Count;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _smape);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
VAR: Sample Variance
|
||||
|
||||
Sample variance uses Bessel's correction to correct the bias in the estimation of population variance.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Variance
|
||||
Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
|
||||
|
||||
Remark:
|
||||
VAR is also known as the Unbiased Sample Variance, while PVAR (Population Variance) is known as
|
||||
the Biased Sample Variance.
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class VAR_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public VAR_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
|
||||
else { this._buffer.Add(d.v); }
|
||||
if (this._buffer.Count > this._p && this._p != 0) { this._buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _sma += this._buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _svar = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); }
|
||||
_svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _svar);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
WMAPE: Weighted Mean Absolute Percentage Error
|
||||
|
||||
Measures the size of the error in percentage terms
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/WMAPE
|
||||
|
||||
**/
|
||||
|
||||
using System;
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class WMAPE_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public WMAPE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) d, bool update)
|
||||
{
|
||||
if (update) { _buffer[_buffer.Count - 1] = d.v; }
|
||||
else { _buffer.Add(d.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sma = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
|
||||
_sma /= this._buffer.Count;
|
||||
|
||||
double _div = 0;
|
||||
double _wmape = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++)
|
||||
{
|
||||
_wmape += Math.Abs(_buffer[i] - _sma);
|
||||
_div += Math.Abs(_buffer[i]);
|
||||
}
|
||||
_wmape /= _div;
|
||||
|
||||
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _wmape);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user