mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 06:18:05 +00:00
Refactoring the structure, upgrading to .NET 6.0/7.0/8.0
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
ADD - adding TSeries+TSeries together, or TSeries+double, or double+TSeries
|
||||
|
||||
Remarks:
|
||||
Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator.
|
||||
|
||||
</summary> */
|
||||
|
||||
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,29 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
DIV - divide TSeries/TSeries , or TSeries/double, or double/TSeries
|
||||
|
||||
Remarks:
|
||||
Most of scaffolding is packaged in abstracty class Pair_TSeries_Indicator.
|
||||
</summary> */
|
||||
|
||||
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,25 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
MAX - Maximum value in the given period in the series.
|
||||
If period = 0 => period = full length of the series
|
||||
</summary> */
|
||||
|
||||
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 System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _max = _buffer.Max();
|
||||
|
||||
base.Add((TValue.t, _max), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
MIDPOINT: Midpoint value (max+min)/2 in the given period in the series.
|
||||
If period = 0 => period = full length of the series
|
||||
|
||||
Sources:
|
||||
https://thefaqblog.com/what-is-the-midpoint-in-statistics/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MIDPOINT_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MIDPOINT_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((DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
double _max = TValue.v;
|
||||
double _min = TValue.v;
|
||||
for (int i = 0; i < this._buffer.Count; i++)
|
||||
{
|
||||
_max = Math.Max(this._buffer[i], _max);
|
||||
_min = Math.Min(this._buffer[i], _min);
|
||||
}
|
||||
double _mid = (_max + _min) * 0.5;
|
||||
|
||||
base.Add((TValue.t, _mid), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
MIDPRICE: Midpoint price (highhest high + lowest low)/2 in the given period in the series.
|
||||
If period = 0 => period = full length of the series
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MIDPRICE_Series : Single_TBars_Indicator
|
||||
{
|
||||
public MIDPRICE_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
if (base._bars.Count > 0)
|
||||
{ base.Add(base._bars); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _bufferhi = new();
|
||||
private readonly System.Collections.Generic.List<double> _bufferlo = new();
|
||||
|
||||
public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_bufferhi, TBar.h, _p, update);
|
||||
Add_Replace_Trim(_bufferlo, TBar.l, _p, update);
|
||||
|
||||
double _max = _bufferhi.Max();
|
||||
double _min = _bufferlo.Min();
|
||||
double _mid = (_max + _min) * 0.5;
|
||||
|
||||
base.Add((TBar.t, _mid), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
MIN - Minimum value in the given period in the series.
|
||||
If period = 0 => period = full length of the series
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
double _min = _buffer.Min();
|
||||
base.Add((TValue.t, _min), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
MUL - multiply TSeries*TSeries together, or TSeries*double, or double*TSeries
|
||||
|
||||
</summary> */
|
||||
|
||||
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,112 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
Abstract classes with all scaffolding required to build indicators.
|
||||
All abstracts support period, NaN, and all permutations of Add() methods.
|
||||
Indicator classess need to implement:
|
||||
- Chaining constructor (Abstract's constructor executes first)
|
||||
- Default Add(value) class
|
||||
- optional Add(series) bulk insert class (for optimization of historical analysis)
|
||||
|
||||
Single_TSeries_Indicator - one single-value TSeries in, one TSeries out.
|
||||
Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring)
|
||||
Single_TBars_Indicator - One OHLCV TBars in, one TSeries out.
|
||||
|
||||
</summary> */
|
||||
|
||||
|
||||
public abstract class Pair_TSeries_Indicator : TSeries
|
||||
{
|
||||
protected readonly int _p;
|
||||
protected readonly bool _NaN;
|
||||
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, int period, bool useNaN)
|
||||
{
|
||||
this._p = period;
|
||||
this._NaN = useNaN;
|
||||
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, 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);
|
||||
|
||||
protected static void Add_Replace(List<double> l, double v, bool update)
|
||||
{
|
||||
if (update)
|
||||
{ l[l.Count - 1] = v; }
|
||||
else
|
||||
{ l.Add(v); }
|
||||
}
|
||||
protected static void Add_Replace_Trim(List<double> l, double v, int p, bool update)
|
||||
{
|
||||
Add_Replace(l, v, update);
|
||||
if (l.Count > p && p != 0)
|
||||
{ l.RemoveAt(0); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
SUB - subtracting TSeries-TSeries, or TSeries-double, or double-TSeries
|
||||
|
||||
</summary> */
|
||||
|
||||
|
||||
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,35 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
SUM: Cumulative Sum (aka Running Total)
|
||||
SUM across a period provides a rolling sum of all values across the period.
|
||||
If SUM values would be divided with period, the output would be SMA()
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/CUSUM
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SUM_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SUM_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) { _buffer[_buffer.Count - 1] = TValue.v; }
|
||||
else { _buffer.Add(TValue.v); }
|
||||
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _sum = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; }
|
||||
|
||||
var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _sum);
|
||||
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
Abstract classes with all scaffolding required to build indicators.
|
||||
All abstracts support period, NaN, and all permutations of Add() methods.
|
||||
Indicator classess need to implement:
|
||||
- Chaining constructor (Abstract's constructor executes first)
|
||||
- Default Add(value) class
|
||||
- optional Add(series) bulk insert class (for optimization of historical analysis)
|
||||
|
||||
Single_TSeries_Indicator - one single-value TSeries in, one TSeries out.
|
||||
Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring)
|
||||
Single_TBars_Indicator - One OHLCV TBars in, one TSeries out.
|
||||
|
||||
</summary> */
|
||||
|
||||
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, int period, bool useNaN)
|
||||
{
|
||||
this._p = period;
|
||||
this._bars = source;
|
||||
this._NaN = useNaN;
|
||||
this._bars.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.t, 0.0), update);
|
||||
public virtual void Add((System.DateTime t, double v) TValue, bool update, bool useNaN)
|
||||
{
|
||||
var res = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : TValue.v);
|
||||
base.Add(res, update);
|
||||
}
|
||||
|
||||
// potentially overridable Add() method for the whole bars or 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 virtual void Add(TSeries data) { for (int i = 0; i < data.Count; i++) { base.Add(TValue: data[i], update: false); } }
|
||||
public void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar) => this.Add(TBar: TBar, 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);
|
||||
|
||||
protected static void Add_Replace(List<double> l, double v, bool update)
|
||||
{
|
||||
if (update)
|
||||
{ l[l.Count - 1] = v; }
|
||||
else
|
||||
{ l.Add(v); }
|
||||
}
|
||||
protected static void Add_Replace_Trim(List<double> l, double v, int p, bool update)
|
||||
{
|
||||
Add_Replace(l, v, update);
|
||||
if (l.Count > p && p != 0)
|
||||
{ l.RemoveAt(0); }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
Abstract classes with all scaffolding required to build indicators.
|
||||
All abstracts support period, NaN, and all permutations of Add() methods.
|
||||
Indicator classess need to implement:
|
||||
- Chaining constructor (Abstract's constructor executes first)
|
||||
- Default Add(value) class
|
||||
- optional Add(series) bulk insert class (for optimization of historical analysis)
|
||||
|
||||
Single_TSeries_Indicator - one single-value TSeries in, one TSeries out.
|
||||
Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring)
|
||||
Single_TBars_Indicator - One OHLCV TBars in, one TSeries out.
|
||||
|
||||
</summary> */
|
||||
public abstract class Single_TSeries_Indicator : TSeries
|
||||
{
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
protected int _p;
|
||||
|
||||
// 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) {
|
||||
_data = source;
|
||||
_period = period;
|
||||
_p = _period;
|
||||
_NaN = useNaN;
|
||||
_data.Pub += Sub;
|
||||
}
|
||||
|
||||
// overridable Add() method to add/update a single item at the end of the list
|
||||
|
||||
public virtual void Add((DateTime t, double v) TValue, bool update, bool useNaN) {
|
||||
if (_period == 0) { _p = Length; }
|
||||
var res = (TValue.t, Count < _p - 1 && _NaN ? double.NaN : TValue.v);
|
||||
base.Add(res, update);
|
||||
}
|
||||
public new virtual void Add((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) {
|
||||
foreach (var item in data) { Add(TValue: item, 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);
|
||||
|
||||
protected static void Add_Replace(List<double> l, double v, bool update)
|
||||
{
|
||||
if (update)
|
||||
{ l[l.Count - 1] = v; }
|
||||
else
|
||||
{ l.Add(v); }
|
||||
}
|
||||
protected static double Add_Replace_Trim(List<double> l, double v, int p, bool update)
|
||||
{
|
||||
Add_Replace(l, v, update);
|
||||
double ret = (l.Count > 0) ? l.First() : 0;
|
||||
if (l.Count > p && p != 0)
|
||||
{
|
||||
l.RemoveAt(0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
TBars class - includes all series for common data used in indicators and other calculations.
|
||||
Has a bit limited overloading and casting (compared to TSeries)
|
||||
Includes Select(int) method to simplify choosing the most optimal data source for indicators
|
||||
Includes the most basic pricing calcs: HL2, OC2, OHL3, HLC3, OHLC4, HLCC4
|
||||
(it is 'cheaper' to calculate them once during data capture than each time during data analysis)
|
||||
|
||||
</summary> */
|
||||
|
||||
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 TBars Tail(int count = 10)
|
||||
{
|
||||
TBars outBars = new();
|
||||
if (count > this.Count) { count = this.Count; }
|
||||
for (int i = this.Count - count; i < this.Count; i++) { outBars.Add(this[i]); }
|
||||
return outBars;
|
||||
}
|
||||
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);
|
||||
}
|
||||
else {
|
||||
base.Add((t, o, h, l, c, v));
|
||||
}
|
||||
_open.Add((t, o),update);
|
||||
_high.Add((t, h), update);
|
||||
_low.Add((t, l), update);
|
||||
_close.Add((t, c), update);
|
||||
_volume.Add((t, v), update);
|
||||
_hl2.Add((t, (h + l) * 0.5), update);
|
||||
_oc2.Add((t, (o + c) * 0.5), update);
|
||||
_ohl3.Add((t, (o + h + l) * 0.333333333333333), update);
|
||||
_hlc3.Add((t, (h + l + c) * 0.333333333333333), update);
|
||||
_ohlc4.Add((t, (o + h + l + c) * 0.25), update);
|
||||
_hlcc4.Add((t, (h + l + c + c) * 0.25), update);
|
||||
|
||||
this.OnEvent(update);
|
||||
}
|
||||
|
||||
// delegate used by event handler + event handler (Pub == publisher)
|
||||
public delegate void NewDataEventHandler(object source, TSeriesEventArgs args);
|
||||
public event NewDataEventHandler Pub;
|
||||
|
||||
// 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 });
|
||||
}
|
||||
}
|
||||
|
||||
public void Sub(object source, TSeriesEventArgs e)
|
||||
{
|
||||
TBars ss = (TBars)source;
|
||||
if (ss.Count > 1)
|
||||
{
|
||||
for (int i = 0; i < ss.Count; i++)
|
||||
{
|
||||
this.Add(ss[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Add(ss[ss.Count - 1], e.update);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
TR: True Range
|
||||
True Range was introduced by J. Welles Wilder in his book New Concepts in Technical Trading Systems.
|
||||
It measures the daily range plus any gap from the closing price of the preceding day.
|
||||
|
||||
Calculation:
|
||||
d1 = ABS(High - Low)
|
||||
d2 = ABS(High - Previous close)
|
||||
d3 = ABS(Previous close - Low)
|
||||
TR = MAX(d1,d2,d3)
|
||||
|
||||
Sources:
|
||||
https://www.macroption.com/true-range/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class TR_Series : Single_TBars_Indicator
|
||||
{
|
||||
private double _cm1, _cm1_o;
|
||||
public TR_Series(TBars source, bool useNaN = false) : base(source, period:0, useNaN:useNaN) {
|
||||
_cm1 =_cm1_o = double.NaN;
|
||||
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)
|
||||
{
|
||||
if (update) {_cm1 = _cm1_o; } else { _cm1_o = _cm1; }
|
||||
if (_cm1 is double.NaN) { _cm1 = TBar.c; } //first bar
|
||||
|
||||
double d1 = Math.Abs(TBar.h - TBar.l);
|
||||
double d2 = Math.Abs(_cm1 - TBar.h);
|
||||
double d3 = Math.Abs(_cm1 - TBar.l);
|
||||
var ret = (TBar.t, (base.Count==0 && base._NaN) ? double.NaN : Math.Max(d1,Math.Max(d2,d3)) );
|
||||
base.Add(ret, update);
|
||||
_cm1 = TBar.c;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
TSeries is the cornerstone of all QuanTAlib classess.
|
||||
TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads
|
||||
and other helpers that simplify usage of library.
|
||||
Think of TSeries as an equivalent of Numpy array.
|
||||
|
||||
- includes Length property (to mimic array's method)
|
||||
- includes publishing and subscribing methods that attach to events
|
||||
|
||||
</summary> */
|
||||
public class TSeries : List<(DateTime t, double v)> {
|
||||
|
||||
public static implicit operator (DateTime t, double v)(TSeries l) => l[^1];
|
||||
public static implicit operator double(TSeries l) => l[^1].v;
|
||||
public static implicit operator DateTime(TSeries l) => l[^1].t;
|
||||
public List<DateTime> t => this.Select(item => item.t).ToList();
|
||||
public List<double> v => this.Select(item => item.v).ToList();
|
||||
public int Length => this.Count;
|
||||
|
||||
public TSeries Tail(int count = 10) {
|
||||
var tailSeries = new TSeries();
|
||||
tailSeries.AddRange(this.Skip(Math.Max(0, this.Count - count)).Take(count));
|
||||
return tailSeries;
|
||||
}
|
||||
public void Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (update) { this[^1] = TValue; }
|
||||
else { base.Add(TValue); }
|
||||
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);
|
||||
protected virtual void OnEvent(bool update = false) {
|
||||
Pub?.Invoke(this, new TSeriesEventArgs { update = update }); }
|
||||
|
||||
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) {
|
||||
this.AddRange(ss);
|
||||
} else {
|
||||
Add(ss[^1], e.update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TSeriesEventArgs : EventArgs{
|
||||
public bool update { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
ZL: Zero Lag
|
||||
Data is de-lagged by removing the data from “lag” days ago, thus removing
|
||||
(or attempting to) the cumulative effect of the moving average.
|
||||
|
||||
Calculation:
|
||||
Lag = (Period-1)/2
|
||||
ZL = Data + (Data - Data(Lag days ago) )
|
||||
|
||||
Sources:
|
||||
https://mudrex.com/blog/zero-lag-ema-trading-strategy/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ZL_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public ZL_Series(TSeries source, int period, bool useNaN = false) : base(source, period:period, useNaN:useNaN) {
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((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);
|
||||
|
||||
var ret = (TValue.t, (base.Count==0 && base._NaN) ? double.NaN : _zl );
|
||||
base.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Title>QuanTAlib</Title>
|
||||
<Version>0.1.31</Version>
|
||||
<Product>Library of TA Calculations, Charts and Strategies for Quantower</Product>
|
||||
<Description>Quantitative Technical Analysis Library in C# for Quantower</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>net8.0;net7.0;net6.0</TargetFrameworks>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<LangVersion>preview</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>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<WarningLevel>7</WarningLevel>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<PlatformTarget>anycpu</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType></DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<WarningLevel>7</WarningLevel>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<PlatformTarget>anycpu</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PackageIcon>QuanTAlib2.png</PackageIcon>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png</PackageIconUrl>
|
||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||
<CodeAnalysisRuleSet>..\.sonarlint\mihakralj_quantalibcsharp.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="..\.sonarlint\mihakralj_quantalib\CSharp\SonarLint.xml" Link="SonarLint.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\Docs\readme.md">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
<None Include="..\.github\QuanTAlib2.png">
|
||||
<Pack>True</Pack>
|
||||
<Visible>False</Visible>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
<PackageReference Include="System.Text.Json" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,56 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
/* <summary>
|
||||
Alphavantage - Free API to collect 100 recent daily quotes. It requires a (free) API key
|
||||
Get API key at https://www.alphavantage.co/support/#api-key
|
||||
Parameters:
|
||||
Symbol: stock ("AAPL"),
|
||||
APIkey: unique Alphavantage API key
|
||||
|
||||
</summary>
|
||||
|
||||
public class Alphavantage_Feed : TBars
|
||||
{
|
||||
public enum Interval { Month, Week, Day, Hour, Min30, Min15, Min5, Min1}
|
||||
public Alphavantage_Feed(string Symbol = "IBM", string APIkey = "demo")
|
||||
{
|
||||
System.Net.Http.HttpClient client = new();
|
||||
|
||||
string req = "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED" + "&symbol=" + Symbol + "&apikey=" + APIkey;
|
||||
var msg = client.GetStringAsync(req).Result;
|
||||
var jres = JsonSerializer.Deserialize<JsonDocument>(msg).RootElement;
|
||||
jres.TryGetProperty("Time Series (Daily)", out JsonElement json);
|
||||
|
||||
if (json.ValueKind == JsonValueKind.Undefined) {throw new InvalidOperationException("Stock symbol "+Symbol+" not found"); }
|
||||
foreach (var val in json.EnumerateObject()) { base.Add(GetOHLC(val)); }
|
||||
base.Reverse();
|
||||
}
|
||||
private static (DateTime t, double o, double h, double l, double c, double v) GetOHLC(JsonProperty json)
|
||||
{
|
||||
double o, h, l, c, v;
|
||||
o = h = l = c = v = 0;
|
||||
DateTime date = Convert.ToDateTime(json.Name);
|
||||
foreach (var val in json.Value.EnumerateObject())
|
||||
{
|
||||
switch (val.Name)
|
||||
{
|
||||
case "1. open": o = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "1b. open (USD)": o = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "2. high": h = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "2b. high (USD)": h = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "3. low": l = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "3b. low (USD)": l = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "4. close": c = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "4b. close (USD)": c = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "5. adjusted close": c = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "5. volume": v = Convert.ToDouble(val.Value.ToString()); break;
|
||||
case "6. volume": v = Convert.ToDouble(val.Value.ToString()); break;
|
||||
default: o = 0; h = 0; l = 0; c = 0; v = 0; break;
|
||||
}
|
||||
}
|
||||
return (date, o, h, l, c, v);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
GBM - Geometric Brownian Motion is a random simulator of market movement, returning List<Quote>
|
||||
GBM can be used for testing indicators, validation and Monte Carlo simulations of strategies.
|
||||
|
||||
Sample usage:
|
||||
GBM-Random data = new(); // generates 1 year (252) list of bars
|
||||
GBM-Random data = new(Bars: 1000); // generates 1,000 bars
|
||||
GBM-Random data = new(Bars: 252, Volatility: 0.05, Drift: 0.0005, Seed: 100.0)
|
||||
|
||||
Parameters
|
||||
Bars: number of bars (quotes) requested
|
||||
Volatility: how dymamic/volatile the series should be; default is 1
|
||||
Drift: incremental drift due to annual interest rate; default is 5%
|
||||
Seed: starting value of the random series; should not be 0
|
||||
|
||||
</summary> */
|
||||
|
||||
public class GBM_Feed : TBars
|
||||
{
|
||||
private double seed;
|
||||
readonly double drift, volatility;
|
||||
readonly int precision;
|
||||
public GBM_Feed(int Bars = 252, double Volatility = 1.0, double Drift = 0.05, double Seed = 100.0, int Precision = 2) {
|
||||
this.seed = Seed;
|
||||
volatility = Volatility*0.01;
|
||||
drift = Drift*0.01;
|
||||
precision = Precision;
|
||||
for (int i = 0; i <Bars; i++) {
|
||||
DateTime Timestamp = DateTime.Today.AddDays(i - Bars);
|
||||
this.Add(Timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(bool update = false) {this.Add(DateTime.Now, update);}
|
||||
public void Add(DateTime timestamp, bool update = false) {
|
||||
double Open = GBM_value(seed, volatility*volatility, drift, precision);
|
||||
double Close = GBM_value(Open, volatility, drift, precision);
|
||||
|
||||
double OCMax = Math.Max(Open,Close);
|
||||
double High = (GBM_value(seed, volatility*0.5, 0, precision));
|
||||
High = (High<OCMax)? (2 * OCMax) - High : High;
|
||||
|
||||
double OCMin = Math.Min(Open,Close);
|
||||
double Low = (GBM_value(seed, volatility*0.5, 0, precision));
|
||||
Low = (Low>OCMin)? (2 * OCMin) - Low : Low;
|
||||
|
||||
double Volume = GBM_value(seed*10, volatility*2, Drift:0, precision: 1);
|
||||
|
||||
base.Add((timestamp, Open, High, Low, Close, Volume), update);
|
||||
seed = Close;
|
||||
}
|
||||
|
||||
private static double GBM_value(double Seed, double Volatility, double Drift, int precision) {
|
||||
Random rnd = new();
|
||||
double U1 = 1.0-rnd.NextDouble();
|
||||
double U2 = 1.0-rnd.NextDouble();
|
||||
double Z = Math.Sqrt(-2.0 * Math.Log(U1)) * Math.Sin(2.0 * Math.PI * U2);
|
||||
return Math.Round(Seed * Math.Exp( Drift - (Volatility*Volatility*0.5) + (Volatility * Z)), digits: precision);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
Random Bars generator - used for testing, validation and fun
|
||||
Returns 'bars' number of candles that follow common market movement.
|
||||
volatility defines how 'jumpy' is the series of
|
||||
startvalue defines beginning closing price that then guides the rest of series
|
||||
|
||||
</summary> */
|
||||
|
||||
public class RND_Feed : TBars
|
||||
{
|
||||
public RND_Feed(int Bars, double Volatility = 0.05, double Startvalue = 100.0)
|
||||
{
|
||||
Random rnd = new();
|
||||
double c = Startvalue;
|
||||
for (int i = 0; i < Bars; 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 - Bars), o, h, l, c, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
/* <summary>
|
||||
Yahoo Finance - Free API feed to collect daily market quotes
|
||||
Parameters:
|
||||
Symbol: stock symbol (default: "IBM")
|
||||
Period: number of days of collected history (default: 252)
|
||||
Usage:
|
||||
Yahoo_Feed ticker = new("MSFT", 20)
|
||||
|
||||
</summary>
|
||||
|
||||
public class Yahoo_Feed : TBars
|
||||
{
|
||||
public Yahoo_Feed(string Symbol = "IBM", int Period = 252) {
|
||||
Period = (int)(Period*1.45);
|
||||
string requestUrl = "https://query1.finance.yahoo.com/v8/finance/chart/"+
|
||||
Symbol+"?interval=1d&period1="+
|
||||
(int)new DateTimeOffset(DateTime.UtcNow.AddDays(-Period+1)).ToUnixTimeSeconds()+"&period2="+
|
||||
(int)new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
|
||||
System.Net.Http.HttpClient client = new();
|
||||
var msg = client.GetStringAsync(requestUrl).Result;
|
||||
var jresult = JsonSerializer.Deserialize<JsonDocument>(msg).RootElement;
|
||||
|
||||
jresult.TryGetProperty("chart",out JsonElement json);
|
||||
json.TryGetProperty("result",out json);
|
||||
json[0].TryGetProperty("timestamp",out JsonElement datetime);
|
||||
json[0].TryGetProperty("indicators",out json);
|
||||
json.TryGetProperty("quote",out json);
|
||||
json[0].TryGetProperty("open",out JsonElement open);
|
||||
json[0].TryGetProperty("high",out JsonElement high);
|
||||
json[0].TryGetProperty("low",out JsonElement low);
|
||||
json[0].TryGetProperty("close",out JsonElement close);
|
||||
json[0].TryGetProperty("volume",out JsonElement volume);
|
||||
|
||||
for (int i=0; i<datetime.GetArrayLength(); i++) {
|
||||
DateTime d = DateTimeOffset.FromUnixTimeSeconds(long.Parse(datetime[i].GetRawText())).DateTime;
|
||||
double o = Math.Round(double.Parse(open[i].GetRawText()),3);
|
||||
double h = Math.Round(double.Parse(high[i].GetRawText()),3);
|
||||
double l = Math.Round(double.Parse(low[i].GetRawText()),3);
|
||||
double c = Math.Round(double.Parse(close[i].GetRawText()),3);
|
||||
double v = Math.Round(double.Parse(volume[i].GetRawText()),3);
|
||||
base.Add(d, o, h, l, c, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
/* <summary>
|
||||
CCI: Commodity Channel Index
|
||||
Commodity Channel Index is a momentum oscillator used to primarily identify overbought
|
||||
and oversold levels relative to a mean. CCI measures the current price level relative
|
||||
to an average price level over a given period of time:
|
||||
- CCI is relatively high when prices are far above their average.
|
||||
- CCI is relatively low when prices are far below their average.
|
||||
Using this method, CCI can be used to identify overbought and oversold levels.
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/c/commoditychannelindex.asp
|
||||
https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci
|
||||
|
||||
</summary> */
|
||||
|
||||
public class CCI_Series : Single_TBars_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _tp = new();
|
||||
|
||||
public CCI_Series(TBars source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN)
|
||||
{
|
||||
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)
|
||||
{
|
||||
double _tpItem = (TBar.h + TBar.l + TBar.c) / 3.0;
|
||||
if (update) { this._tp[this._tp.Count - 1] = _tpItem; } else { this._tp.Add(_tpItem); }
|
||||
if (this._tp.Count > this._p) { this._tp.RemoveAt(0); }
|
||||
|
||||
// average TP over _tp buffer
|
||||
double _avgTp = _tp.Average();
|
||||
|
||||
// average Deviation over _tp buffer
|
||||
double _avgDv = 0;
|
||||
for (int i = 0; i < this._tp.Count; i++) { _avgDv += Math.Abs(_avgTp - this._tp[i]); }
|
||||
_avgDv /= this._tp.Count;
|
||||
|
||||
|
||||
double _cci = (_avgDv == 0) ? double.NaN : (this._tp[this._tp.Count-1] - _avgTp) / (0.015 * _avgDv);
|
||||
|
||||
base.Add((TBar.t, _cci), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
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)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
double _bias = (_buffer[_buffer.Count - 1] / ((_sma != 0) ? _sma : 1)) - 1;
|
||||
|
||||
base.Add((TValue.t, _bias), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
CORR: Pearson's Correlation Coefficient
|
||||
PCC is a measure of linear correlation between two sets of data.
|
||||
It is the ratio between the covariance of two variables and the product of
|
||||
their standard deviations; it is essentially a normalized measurement of
|
||||
the covariance, such that the result always has a value between −1 and 1.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Pearson_correlation_coefficient
|
||||
|
||||
</summary> */
|
||||
|
||||
public class CORR_Series : Pair_TSeries_Indicator
|
||||
{
|
||||
public CORR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN)
|
||||
{
|
||||
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); } }
|
||||
}
|
||||
|
||||
private readonly System.Collections.Generic.List<double> _x = new();
|
||||
private readonly System.Collections.Generic.List<double> _xx = new();
|
||||
private readonly System.Collections.Generic.List<double> _y = new();
|
||||
private readonly System.Collections.Generic.List<double> _yy = new();
|
||||
private readonly System.Collections.Generic.List<double> _xy = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_x, TValue1.v, _p, update);
|
||||
Add_Replace_Trim(_xx, TValue1.v * TValue1.v, _p, update);
|
||||
Add_Replace_Trim(_y, TValue2.v, _p, update);
|
||||
Add_Replace_Trim(_yy, TValue2.v * TValue2.v, _p, update);
|
||||
Add_Replace_Trim(_xy, TValue1.v * TValue2.v, _p, update);
|
||||
|
||||
double _sumx = _x.Sum();
|
||||
double _sumxx = _xx.Sum();
|
||||
double _sumy = _y.Sum();
|
||||
double _sumyy = _yy.Sum();
|
||||
double _sumxy = _xy.Sum();
|
||||
|
||||
double _covar = (_sumxx - _sumx * _sumx / _p) * (_sumyy - _sumy * _sumy / _p);
|
||||
double _cor = (_covar != 0) ? (_sumxy - _sumx * _sumy / _p) / Math.Sqrt(_covar) : 0.0;
|
||||
|
||||
var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _cor);
|
||||
if (update) { base[base.Count - 1] = result; } else { base.Add(result); }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
COVAR: Covariance
|
||||
Covariance is defined as the expected value (or mean) of the product
|
||||
of their deviations from their individual expected values.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Covariance
|
||||
|
||||
</summary> */
|
||||
|
||||
public class COVAR_Series : Pair_TSeries_Indicator
|
||||
{
|
||||
public COVAR_Series(TSeries d1, TSeries d2, int period, bool useNaN = false) : base(d1, d2, period, useNaN)
|
||||
{
|
||||
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); } }
|
||||
}
|
||||
|
||||
private readonly System.Collections.Generic.List<double> _x = new();
|
||||
private readonly System.Collections.Generic.List<double> _y = new();
|
||||
private readonly System.Collections.Generic.List<double> _xy = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue1, (System.DateTime t, double v) TValue2, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_x, TValue1.v, _p, update);
|
||||
Add_Replace_Trim(_y, TValue2.v, _p, update);
|
||||
Add_Replace_Trim(_xy, TValue1.v * TValue2.v, _p, update);
|
||||
|
||||
double _avgx = _x.Average();
|
||||
double _avgy = _y.Average();
|
||||
double _avgxy = _xy.Average();
|
||||
double _covar = _avgxy - (_avgx * _avgy);
|
||||
|
||||
var result = (TValue1.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _covar);
|
||||
if (update) { base[base.Count - 1] = result; } else { base.Add(result); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
/* <summary>
|
||||
DECAY:
|
||||
Linear decay can be modeled by a straight line with a negative slope of 1/period.
|
||||
The value decreases in a straight line from the last maximum to 0.
|
||||
Decay = Last Max - distance/period
|
||||
|
||||
Exponential decay is modeled as an exponential curve with diminishing factor of
|
||||
1-1/p
|
||||
|
||||
</summary> */
|
||||
|
||||
public class DECAY_Series : Single_TSeries_Indicator {
|
||||
private bool _exp;
|
||||
private double _pdecay, _ppdecay;
|
||||
private readonly double _dfactor;
|
||||
|
||||
public DECAY_Series(TSeries source, int period = 10, bool exponential= false, bool useNaN = false) : base(source, period, false) {
|
||||
_exp = exponential;
|
||||
_dfactor = (_exp)? 1.0 - 1.0 / (double)_p : 1/(double)_p;
|
||||
_pdecay = _ppdecay = 0;
|
||||
if (source.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update) {
|
||||
if (update) { _pdecay = _ppdecay; }
|
||||
else { _ppdecay = _pdecay; }
|
||||
|
||||
if (this.Count == 0) { _pdecay = TValue.v; }
|
||||
double _decay = Math.Max(TValue.v, Math.Max((_exp)?_pdecay*_dfactor:_pdecay-_dfactor, 0));
|
||||
_pdecay = _decay;
|
||||
|
||||
base.Add((TValue.t, _decay), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ENTROPY_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public ENTROPY_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;
|
||||
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)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sum = _buffer.Sum();
|
||||
|
||||
double _pp = this._buffer[this._buffer.Count - 1] / _sum;
|
||||
double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase);
|
||||
|
||||
Add_Replace_Trim(_buff2, _ppp, _p, update);
|
||||
double _entp = _buff2.Sum();
|
||||
|
||||
base.Add((TValue.t, _entp), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class KURTOSIS_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public KURTOSIS_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;
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _n = this._buffer.Count;
|
||||
double _avg = _buffer.Average();
|
||||
|
||||
double _s2 = 0;
|
||||
double _s4 = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++)
|
||||
{
|
||||
_s2 += (_buffer[i] - _avg) * (_buffer[i] - _avg);
|
||||
_s4 += (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_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 = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? Double.NaN : _kurt);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
LINREG: Linear Regression (using Least Square Method)
|
||||
Linear Regression provides a slope of a straight line that is the best approximation of the given set of data.
|
||||
The method of least squares is a standard approach in linear regression analysis to approximate the solution
|
||||
by minimizing the sum of the squares of the residuals made in the results of each individual equation.
|
||||
|
||||
Additional outputs provided by LINREG:
|
||||
.Intercept - y-intercept point of the best fit line
|
||||
.RSquared - R-Squared (R²), Coefficient of Determination
|
||||
.StdDev - Standard Deviation of data over given periods
|
||||
|
||||
y = Slope * x + Intercept
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Least_squares
|
||||
|
||||
</summary> */
|
||||
|
||||
public class LINREG_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public readonly TSeries Intercept = new();
|
||||
public readonly TSeries RSquared = new();
|
||||
public readonly TSeries StdDev = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
public LINREG_Series(TSeries source, int period, bool useNaN = false)
|
||||
: base(source, period, useNaN)
|
||||
{
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
int _len = this._buffer.Count;
|
||||
|
||||
// get averages for period
|
||||
double sumX = 0;
|
||||
double sumY = 0;
|
||||
|
||||
for (int p = 0; p < _len; p++)
|
||||
{
|
||||
sumX += this.Count - _len + 2 + p;
|
||||
sumY += _buffer[p];
|
||||
}
|
||||
double avgX = sumX / _len;
|
||||
double avgY = sumY / _len;
|
||||
|
||||
// least squares method
|
||||
double sumSqX = 0;
|
||||
double sumSqY = 0;
|
||||
double sumSqXY = 0;
|
||||
|
||||
for (int p = 0; p < _len; p++)
|
||||
{
|
||||
double devX = this.Count - _len + 2 + p - avgX;
|
||||
double devY = _buffer[p] - avgY;
|
||||
|
||||
sumSqX += devX * devX;
|
||||
sumSqY += devY * devY;
|
||||
sumSqXY += devX * devY;
|
||||
}
|
||||
|
||||
double _slope = sumSqXY / sumSqX;
|
||||
double _intercept = avgY - (_slope * avgX);
|
||||
|
||||
// calculate Standard Deviation and R-Squared
|
||||
double stdDevX = Math.Sqrt(sumSqX / _len);
|
||||
double stdDevY = Math.Sqrt(sumSqY / _len);
|
||||
double _StdDev = stdDevY;
|
||||
|
||||
double arrr = (stdDevX * stdDevY != 0) ? sumSqXY / (stdDevX * stdDevY) / _len : 0;
|
||||
double _RSquared = arrr * arrr;
|
||||
|
||||
var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _slope);
|
||||
base.Add(ret, update, _NaN);
|
||||
|
||||
ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _intercept);
|
||||
Intercept.Add(ret, update);
|
||||
|
||||
ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _StdDev);
|
||||
StdDev.Add(ret, update);
|
||||
|
||||
ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _RSquared);
|
||||
RSquared.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _mad = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); }
|
||||
_mad /= this._buffer.Count;
|
||||
|
||||
base.Add((TValue.t, _mad), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
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 /= (_buffer.Count>0) ? _buffer.Count : 1;
|
||||
|
||||
base.Add((TValue.t, _mape), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MEDIAN_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MEDIAN_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)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
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;
|
||||
|
||||
base.Add((TValue.t, _med), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _mse = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_mse /= this._buffer.Count;
|
||||
|
||||
base.Add((TValue.t, _mse), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
SDEV: 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:
|
||||
SDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation.
|
||||
For unbiased version that uses Bessel's correction, use SDEV instead.
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
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);
|
||||
|
||||
base.Add((TValue.t, _psdev), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
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;
|
||||
|
||||
base.Add((TValue.t, _smape), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
SSDEV: (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
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SSDEV_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SSDEV_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)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _svar = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _svar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_svar /= (_buffer.Count > 1) ? _buffer.Count - 1 : 1; // Bessel's correction
|
||||
double _ssdev = Math.Sqrt(_svar);
|
||||
|
||||
base.Add((TValue.t, _ssdev), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
SVAR: 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:
|
||||
SVAR is also known as the Unbiased Sample Variance, while VAR (Population Variance) is known as
|
||||
the Biased Sample Variance.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SVAR_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SVAR_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)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
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
|
||||
|
||||
base.Add((TValue.t, _svar), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
VAR: Population Variance
|
||||
Population variance without Bessel's correction
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Variance
|
||||
Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
|
||||
|
||||
Remark:
|
||||
VAR (Population Variance) is also known as a biased Sample Variance. For unbiased
|
||||
sample variance use SVAR instead.
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _pvar = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_pvar /= this._buffer.Count;
|
||||
|
||||
base.Add((TValue.t, _pvar), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
WMAPE: Weighted Mean Absolute Percentage Error
|
||||
Measures the size of the error in percentage terms. Improves problems with MAPE
|
||||
when there are zero or close-to-zero values because there would be a division by zero
|
||||
or values of MAPE tending to infinity.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/WMAPE
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
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!=0) ? _wmape/_div : double.PositiveInfinity;
|
||||
|
||||
base.Add((TValue.t, _wmape), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
ZSCORE: number of standard deviations from SMA
|
||||
Z-score describes a value's relationship to the mean of a series, as measured in
|
||||
terms of standard deviations from the mean. If a Z-score is 0, it indicates that
|
||||
the data point's score is identical to the mean score. A Z-score of 1.0 would
|
||||
indicate a value that is one standard deviation from the mean. Z-scores may be
|
||||
positive or negative, with a positive value indicating the score is above the
|
||||
mean and a negative score indicating it is below the mean.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Z-score
|
||||
https://www.investopedia.com/terms/z/zscore.asp
|
||||
|
||||
Calculation:
|
||||
std = std * STDEV(close, length)
|
||||
mean = SMA(close, length)
|
||||
ZSCORE = (close - mean) / std
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ZSCORE_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public ZSCORE_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)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
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);
|
||||
double _zscore = (_psdev == 0) ? double.NaN : (TValue.v - _sma) / _psdev;
|
||||
|
||||
base.Add((TValue.t, _zscore), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
ALMA: Arnaud Legoux Moving Average
|
||||
The ALMA moving average uses the curve of the Normal (Gauss) distribution, which
|
||||
can be shifted from 0 to 1. This allows regulating the smoothness and high
|
||||
sensitivity of the indicator. Sigma is another parameter that is responsible for
|
||||
the shape of the curve coefficients. This moving average reduces lag of the data
|
||||
in conjunction with smoothing to reduce noise.
|
||||
|
||||
|
||||
Sources:
|
||||
https://phemex.com/academy/what-is-arnaud-legoux-moving-averages
|
||||
https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/
|
||||
|
||||
TODO: Discrepancy with Pandas-TA (but passes the validation with Skender.GetAlma)
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ALMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly double[] _weight;
|
||||
private double _norm;
|
||||
private readonly double _offset, _sigma;
|
||||
|
||||
public ALMA_Series(TSeries source, int period, double offset = 0.85, double sigma = 6.0, bool useNaN = false)
|
||||
: base(source, period, useNaN)
|
||||
{
|
||||
_offset = offset;
|
||||
_sigma = sigma;
|
||||
_weight = new double[period];
|
||||
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
if (this._buffer.Count <= _p)
|
||||
{
|
||||
int _len = this._buffer.Count;
|
||||
_norm = 0;
|
||||
double _m = _offset * (_len - 1);
|
||||
double _s = _len / _sigma;
|
||||
for (int i = 0; i < _len; i++)
|
||||
{
|
||||
double _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s));
|
||||
_weight[i] = _wt;
|
||||
_norm += _wt;
|
||||
}
|
||||
}
|
||||
|
||||
double _weightedSum = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++)
|
||||
{ _weightedSum += _weight[i] * _buffer[i]; }
|
||||
double _alma = _weightedSum / _norm;
|
||||
|
||||
base.Add((TValue.t, _alma), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
public class DEMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly double _k;
|
||||
private int _len;
|
||||
private readonly bool _useSMA;
|
||||
private double _sum, _lastsum, _lastlastsum;
|
||||
private double _lastema1, _lastlastema1;
|
||||
private double _lastema2, _lastlastema2;
|
||||
|
||||
public DEMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
|
||||
{
|
||||
_k = 2.0 / (_p + 1);
|
||||
_len = 0;
|
||||
_useSMA = useSMA;
|
||||
_sum = _lastema1 = _lastema2 =0;
|
||||
if (_data.Count > 0) { base.Add(_data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
if (update) {
|
||||
_lastsum = _lastlastsum;
|
||||
_lastema1 = _lastlastema1;
|
||||
_lastema2 = _lastlastema2;
|
||||
}
|
||||
else {
|
||||
_lastlastsum = _lastsum;
|
||||
_lastlastema1 = _lastema1;
|
||||
_lastlastema2 = _lastema2;
|
||||
_len++;
|
||||
}
|
||||
|
||||
double _ema1, _ema2, _dema;
|
||||
if (this.Count == 0) {
|
||||
_ema1 = _ema2 = _sum = TValue.v;
|
||||
}
|
||||
else if (_len <= _period && _useSMA && _period != 0) {
|
||||
_sum += TValue.v;
|
||||
if (_period != 0 && _len > _period) {
|
||||
_sum -= (_data[base.Count - _period - (update ? 1 : 0)].v);
|
||||
}
|
||||
_ema1 = _sum / Math.Min(_len, _period);
|
||||
_ema2 = _ema1;
|
||||
}
|
||||
else {
|
||||
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
|
||||
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
|
||||
}
|
||||
_dema = 2*_ema1 - _ema2;
|
||||
|
||||
_lastema1 = _ema1;
|
||||
_lastema2 = _ema2;
|
||||
|
||||
base.Add((TValue.t, _dema), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
DWMA: Double Weighted Moving Average
|
||||
The weights are decreasing over the period with p^2 decay
|
||||
and the most recent data has the heaviest weight.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class DWMA_Series : Single_TSeries_Indicator {
|
||||
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);
|
||||
this._weights.Add(_weight);
|
||||
}
|
||||
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _weights = new();
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update) {
|
||||
Add_Replace_Trim(_buffer1, TValue.v, _p, update);
|
||||
double _wma1 = 0;
|
||||
double _wsum = 0;
|
||||
for (int i = 0; i < _buffer1.Count; i++) {
|
||||
_wma1 += _buffer1[i] * this._weights[i];
|
||||
_wsum += this._weights[i];
|
||||
}
|
||||
_wma1 /= _wsum;
|
||||
|
||||
base.Add((TValue.t, _wma1), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class EMA_Series : Single_TSeries_Indicator {
|
||||
private double _k;
|
||||
private double _lastema, _lastlastema;
|
||||
private double _sum, _oldsum;
|
||||
private int _len;
|
||||
private readonly bool _useSMA;
|
||||
|
||||
public EMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN) {
|
||||
_k = 2.0 / (_p + 1);
|
||||
_sum = _oldsum = _lastema = _lastlastema = 0;
|
||||
_len = 0;
|
||||
_useSMA = useSMA;
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update) {
|
||||
|
||||
if (update) { _lastema = _lastlastema; _sum = _oldsum; }
|
||||
else { _lastlastema = _lastema; _oldsum = _sum; _len++; }
|
||||
|
||||
double _ema = 0;
|
||||
// when period = 0, create cumulative/additive series where _k is progressively larger
|
||||
if (_period == 0) { _k = 2.0 / (_len + 1); }
|
||||
|
||||
// the first value of the series
|
||||
if (this.Count == 0) {
|
||||
_ema = _sum = TValue.v;
|
||||
}
|
||||
// if SMA is used for seeding, calculate SMA within period
|
||||
else if (_len <= _period && _useSMA && _period != 0) {
|
||||
_sum += TValue.v;
|
||||
if (_period != 0 && _len > _period) {
|
||||
_sum -= (_data[base.Count - _period - (update ? 1 : 0)].v);
|
||||
}
|
||||
_ema = _sum / Math.Min(_len, _period);
|
||||
}
|
||||
// calculate EMA out from last EMA and factor k
|
||||
else {
|
||||
_ema = _k * (TValue.v - _lastema) + _lastema;
|
||||
}
|
||||
_lastema = _ema;
|
||||
|
||||
base.Add((TValue.t, _ema), update, _NaN);
|
||||
}
|
||||
public void Reset() {
|
||||
_sum = _oldsum = _lastema = _lastlastema = 0;
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
FMA: Fibonacci Moving Average
|
||||
FMA calculates the average across multiple EMAs with periods following Fibonacci sequence
|
||||
(skipping initial Fibonacci numbers of 1, 1, 2) 3, 5, 8, 13, 21, 34...
|
||||
|
||||
FMA(n) = Average(EMA(3), EMA(5), EMA(8), ema(13), ... EMA(n-th Fib))
|
||||
|
||||
Sources:
|
||||
https://kaabar-sofien.medium.com/the-fibonacci-moving-average-the-full-guide-60e718117595
|
||||
https://usethinkscript.com/threads/fibonacci-moving-average.8099/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class FMA_Series : Single_TSeries_Indicator {
|
||||
readonly double[,] fib;
|
||||
double _oldsum;
|
||||
readonly int _len;
|
||||
|
||||
public FMA_Series(TSeries source, int period) : base(source, period, false) {
|
||||
_len = period;
|
||||
fib = new double[_len, 4];
|
||||
int a = 3;
|
||||
int b = 5;
|
||||
int f = 0;
|
||||
fib[0, 0] = 2 / ((double)a - 1);
|
||||
if (_len > 1) { fib[1, 0] = 2 / ((double)b - 1); }
|
||||
if (_len > 2) {
|
||||
for (int i = 2; i < _len; i++) {
|
||||
f = a + b;
|
||||
a = b;
|
||||
b = f;
|
||||
fib[i, 0] = 2 / ((double)f - 1);
|
||||
}
|
||||
}
|
||||
_oldsum = 0;
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update) {
|
||||
double _sum = 0;
|
||||
for (int i = 0; i < _len; i++) {
|
||||
if (update) { fib[i, 1] = fib[i, 3]; _sum = _oldsum; }
|
||||
else { fib[i, 3] = fib[i, 1]; _oldsum = _sum; }
|
||||
|
||||
if (this.Count == 0) { fib[i, 1] = TValue.v; }
|
||||
else {
|
||||
fib[i, 2] = fib[i, 0] * (TValue.v - fib[i, 1]) + fib[i, 1];
|
||||
fib[i, 1] = fib[i, 2];
|
||||
}
|
||||
_sum += fib[i, 1];
|
||||
}
|
||||
|
||||
double _fma = _sum / _len;
|
||||
base.Add((TValue.t, _fma), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
HEMA: Hull-EMA Moving Average - a hybrid indicator
|
||||
Modified HUll Moving Average; instead of using WMA (Weighted MA) for 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)
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
this._lastema1 = this._lastlastema1;
|
||||
this._lastema2 = this._lastlastema2;
|
||||
this._lastema3 = this._lastlastema3;
|
||||
}
|
||||
double _ema1 = System.Double.IsNaN(this._lastema1) ? TValue.v : TValue.v * this._k1 + this._lastema1 * (1 - this._k1);
|
||||
double _ema2 = System.Double.IsNaN(this._lastema2) ? TValue.v : TValue.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;
|
||||
|
||||
base.Add((TValue.t, _ema3), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
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)((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,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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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.
|
||||
|
||||
</summary>
|
||||
*/
|
||||
public class JMA_Series : Single_TSeries_Indicator {
|
||||
private readonly System.Collections.Generic.List<double> volty_short = new();
|
||||
private readonly System.Collections.Generic.List<double> vsum_buff = new();
|
||||
private readonly double pr;
|
||||
public TSeries mma1 { get; }
|
||||
public TSeries mma2 { get; }
|
||||
|
||||
private double upperBand, lowerBand, vsum, Kv, del1, del2;
|
||||
private double prev_ma1, prev_det0, prev_det1, prev_vsum, prev_jma;
|
||||
private double p_upperBand, p_lowerBand, p_Kv, p_prev_ma1, p_prev_det0, p_prev_det1, p_prev_vsum, p_prev_jma;
|
||||
private readonly int _voltyS, _voltyL;
|
||||
|
||||
public JMA_Series(TSeries source, int period, double phase = 0.0, int vshort = 10, int vlong = 65, bool useNaN = false) : base(source, period, useNaN) {
|
||||
upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = del1 = del2 = 0.0;
|
||||
Kv = 0;
|
||||
|
||||
pr = (phase * 0.01) + 1.5;
|
||||
if (phase < -100) { pr = 0.5; }
|
||||
if (phase > 100) { pr = 2.5; }
|
||||
_voltyS = vshort;
|
||||
_voltyL = vlong;
|
||||
mma1 = new();
|
||||
mma2 = new();
|
||||
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update) {
|
||||
if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; }
|
||||
if (update) {
|
||||
upperBand = p_upperBand;
|
||||
lowerBand = p_lowerBand;
|
||||
Kv = p_Kv;
|
||||
prev_vsum = p_prev_vsum;
|
||||
prev_ma1 = p_prev_ma1;
|
||||
prev_det0 = p_prev_det0;
|
||||
prev_det1 = p_prev_det1;
|
||||
prev_jma = p_prev_jma;
|
||||
}
|
||||
else {
|
||||
p_upperBand = upperBand;
|
||||
p_lowerBand = lowerBand;
|
||||
p_Kv = Kv;
|
||||
p_prev_vsum = prev_vsum;
|
||||
p_prev_ma1 = prev_ma1;
|
||||
p_prev_det0 = prev_det0;
|
||||
p_prev_det1 = prev_det1;
|
||||
p_prev_jma = prev_jma;
|
||||
}
|
||||
|
||||
// from Tvalue to volty
|
||||
del1 = TValue.v - upperBand;
|
||||
del2 = TValue.v - lowerBand;
|
||||
upperBand = (del1 > 0) ? TValue.v : TValue.v - (Kv * del1);
|
||||
lowerBand = (del2 < 0) ? TValue.v : TValue.v - (Kv * del2);
|
||||
double volty = 0;
|
||||
if (Math.Abs(del1) > Math.Abs(del2)) { volty = Math.Abs(del1); }
|
||||
if (Math.Abs(del1) < Math.Abs(del2)) { volty = Math.Abs(del2); }
|
||||
|
||||
//// from volty to avolty
|
||||
if (update) { volty_short[volty_short.Count - 1] = volty; }
|
||||
else { volty_short.Add(volty); }
|
||||
if (volty_short.Count > _voltyS) { volty_short.RemoveAt(0); }
|
||||
vsum = prev_vsum + 0.1 * (volty - volty_short.First());
|
||||
prev_vsum = vsum;
|
||||
if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; }
|
||||
else { vsum_buff.Add(vsum); }
|
||||
if (vsum_buff.Count > _voltyL) { vsum_buff.RemoveAt(0); }
|
||||
double avolty = 0;
|
||||
for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; }
|
||||
avolty /= vsum_buff.Count;
|
||||
|
||||
/// from avolty to rolty
|
||||
double rvolty = (avolty != 0) ? volty / avolty : 0;
|
||||
double len1 = (Math.Log(Math.Sqrt(_p)) / Math.Log(2.0)) + 2;
|
||||
if (len1 < 0)
|
||||
len1 = 0;
|
||||
double pow1 = Math.Max(len1 - 2.0, 0.5);
|
||||
if (rvolty > Math.Pow(len1, 1.0 / pow1)) { rvolty = Math.Pow(len1, 1.0 / pow1); }
|
||||
if (rvolty < 1) { rvolty = 1; }
|
||||
|
||||
//// from rvolty to second smoothing
|
||||
double pow2 = Math.Pow(rvolty, pow1);
|
||||
double beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
|
||||
Kv = Math.Pow(beta, Math.Sqrt(pow2));
|
||||
double alpha = Math.Pow(beta, pow2);
|
||||
double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1;
|
||||
prev_ma1 = ma1;
|
||||
mma1.Add(ma1);
|
||||
|
||||
double det0 = (1 - beta) * (TValue.v - ma1) + beta * prev_det0;
|
||||
prev_det0 = det0;
|
||||
double ma2 = ma1 + pr * det0;
|
||||
mma2.Add(ma2);
|
||||
|
||||
double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - prev_jma)) + (alpha * alpha * prev_det1);
|
||||
prev_det1 = det1;
|
||||
double jma = prev_jma + det1;
|
||||
prev_jma = jma;
|
||||
|
||||
base.Add((TValue.t, jma), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
KAMA: Kaufman's Adaptive Moving Average
|
||||
Created in 1988 by American quantitative finance theorist Perry J. Kaufman and is known as
|
||||
Kaufman's Adaptive Moving Average (KAMA). Even though the method was developed as early as 1972,
|
||||
it was not until the popular book titled "Trading Systems and Methods" that it was made widely
|
||||
available to the public. Unlike other conventional moving averages systems, the Kaufman's Adaptive
|
||||
Moving Average, considers market volatility apart from price fluctuations.
|
||||
|
||||
KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 )
|
||||
|
||||
Sources:
|
||||
https://www.tutorialspoint.com/kaufman-s-adaptive-moving-average-kama-formula-and-how-does-it-work
|
||||
https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/
|
||||
https://www.technicalindicators.net/indicators-technical-analysis/152-kama-kaufman-adaptive-moving-average
|
||||
|
||||
Remark:
|
||||
If useNaN:true argument is provided, KAMA starts calculating values from [period] bar onwards.
|
||||
Without useNaN argument (default setting), KAMA starts calculating values from bar 1 - and yields
|
||||
slightly different results for the first 50 bars - and then converges with the other one.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class KAMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly double _scFast, _scSlow;
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private double _lastkama = double.NaN;
|
||||
private double _lastlastkama;
|
||||
|
||||
public KAMA_Series(TSeries source, int period, int fast = 2, int slow= 30, bool useNaN = false) : base(source, period, useNaN) {
|
||||
_scFast = 2.0 / (fast+1);
|
||||
_scSlow = 2.0 / (slow+1);
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
if (update){
|
||||
_buffer[_buffer.Count - 1] = TValue.v;
|
||||
_lastkama = _lastlastkama;
|
||||
}
|
||||
else {
|
||||
_buffer.Add(TValue.v);
|
||||
_lastlastkama = _lastkama;
|
||||
}
|
||||
if (_buffer.Count > _p + 1) { _buffer.RemoveAt(0); }
|
||||
|
||||
double _kama = 0;
|
||||
if (this.Count < this._p) { _kama = TValue.v; }
|
||||
else {
|
||||
double _change = Math.Abs(_buffer[_buffer.Count - 1] - _buffer[(_buffer.Count > _p + 1) ? 1 : 0]);
|
||||
double _sumpv = 0;
|
||||
for (int i = 1; i < _buffer.Count; i++)
|
||||
{ _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;
|
||||
_kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama)));
|
||||
}
|
||||
_lastkama = _kama;
|
||||
base.Add((TValue.t, _kama), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
MACD: Moving Average Convergence/Divergence
|
||||
Moving average convergence divergence (MACD) is a trend-following momentum
|
||||
indicator that shows the relationship between two moving averages of a series.
|
||||
The MACD is calculated by subtracting the 26-period exponential moving average (EMA)
|
||||
from the 12-period EMA. MACD Signal is 9-day EMA of MACD.
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/m/macd.asp
|
||||
https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/macd
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MACD_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly EMA_Series _TSslow;
|
||||
private readonly EMA_Series _TSfast;
|
||||
private readonly SUB_Series _TSmacd;
|
||||
public EMA_Series Signal { get; }
|
||||
|
||||
public MACD_Series(TSeries source, int slow = 26, int fast = 12, int signal = 9, bool useNaN = false)
|
||||
: base(source, period: 0, useNaN)
|
||||
{
|
||||
_TSslow = new(source: source, period: slow, useNaN: false);
|
||||
_TSfast = new(source: source, period: fast, useNaN: false);
|
||||
_TSmacd = new(_TSfast, _TSslow);
|
||||
this.Signal = new(source: _TSmacd, period: signal, useNaN: useNaN);
|
||||
|
||||
if (source.Count > 0) { base.Add(_TSmacd); }
|
||||
}
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
double _macd;
|
||||
if (update)
|
||||
{
|
||||
_TSslow.Add(TValue, true);
|
||||
_TSfast.Add(TValue, true);
|
||||
}
|
||||
_macd = this._TSmacd[(this.Count < this._TSmacd.Count) ? this.Count : this._TSmacd.Count - 1].v;
|
||||
base.Add((TValue.t, _macd), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
MAMA: MESA Adaptive Moving Average
|
||||
Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of
|
||||
high/low price that uses classic electrical radio-frequency signal processing algorithms
|
||||
to reduce noise.
|
||||
|
||||
KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 )
|
||||
|
||||
Sources:
|
||||
https://mesasoftware.com/papers/MAMA.pdf
|
||||
https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MAMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public MAMA_Series(TSeries source, double fastlimit = 0.5, double slowlimit = 0.05, bool useNaN = false) : base(source, period: 5, useNaN)
|
||||
{
|
||||
fastl = fastlimit;
|
||||
slowl = slowlimit;
|
||||
Fama = new();
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
|
||||
private double sumPr, jI, jQ, fastl, slowl;
|
||||
private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt;
|
||||
private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama;
|
||||
public TSeries Fama { get; }
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
|
||||
if (!update) {
|
||||
// roll forward (oldx = x)
|
||||
pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i;
|
||||
i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i;
|
||||
q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i;
|
||||
dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i;
|
||||
sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i;
|
||||
i2.io = i2.i1; i2.i1 = i2.i;
|
||||
q2.io = q2.i1; q2.i1 = q2.i;
|
||||
re.io = re.i1; re.i1 = re.i;
|
||||
im.io = im.i1; im.i1 = im.i;
|
||||
pd.io = pd.i1; pd.i1 = pd.i;
|
||||
ph.io = ph.i1; ph.i1 = ph.i;
|
||||
mama.io = mama.i1; mama.i1 = mama.i;
|
||||
fama.io = fama.i1; fama.i1 = fama.i;
|
||||
}
|
||||
int i = base.Count;
|
||||
pr.i = TValue.v;
|
||||
if (i > 5) {
|
||||
double adj = (0.075 * pd.i1) + 0.54;
|
||||
|
||||
// smooth and detrender
|
||||
sm.i = ((4 * pr.i) + (3 * pr.i1) + (2 * pr.i2) + pr.i3) / 10;
|
||||
dt.i = ((0.0962 * sm.i) + (0.5769 * sm.i2) - (0.5769 * sm.i4) - (0.0962 * sm.i6)) * adj;
|
||||
|
||||
// in-phase and quadrature
|
||||
q1.i = ((0.0962 * dt.i) + (0.5769 * dt.i2) - (0.5769 * dt.i4) - (0.0962 * dt.i6)) * adj;
|
||||
i1.i = dt.i3;
|
||||
|
||||
// advance the phases by 90 degrees
|
||||
jI = ((0.0962 * i1.i) + (0.5769 * i1.i2) - (0.5769 * i1.i4) - (0.0962 * i1.i6)) * adj;
|
||||
jQ = ((0.0962 * q1.i) + (0.5769 * q1.i2) - (0.5769 * q1.i4) - (0.0962 * q1.i6)) * adj;
|
||||
|
||||
// phasor addition for 3-bar averaging
|
||||
i2.i = i1.i - jQ;
|
||||
q2.i = q1.i + jI;
|
||||
|
||||
i2.i = (0.2 * i2.i) + (0.8 * i2.i1); // smoothing it
|
||||
q2.i = (0.2 * q2.i) + (0.8 * q2.i1);
|
||||
|
||||
// homodyne discriminator
|
||||
re.i = (i2.i * i2.i1) + (q2.i * q2.i1);
|
||||
im.i = (i2.i * q2.i1) - (q2.i * i2.i1);
|
||||
|
||||
re.i = (0.2 * re.i) + (0.8 * re.i1); // smoothing it
|
||||
im.i = (0.2 * im.i) + (0.8 * im.i1);
|
||||
|
||||
// calculate period
|
||||
pd.i = (im.i != 0 && re.i != 0) ? (6.283185307179586 / Math.Atan(im.i / re.i)) : 0d;
|
||||
|
||||
// adjust period to thresholds
|
||||
pd.i = (pd.i > 1.5 * pd.i1) ? 1.5 * pd.i1 : pd.i;
|
||||
pd.i = (pd.i < 0.67 * pd.i1) ? 0.67 * pd.i1 : pd.i;
|
||||
pd.i = (pd.i < 6d) ? 6d : pd.i;
|
||||
pd.i = (pd.i > 50d) ? 50d : pd.i;
|
||||
|
||||
// smooth the period
|
||||
pd.i = (0.2 * pd.i) + (0.8 * pd.i1);
|
||||
|
||||
// determine phase position
|
||||
ph.i = (i1.i != 0) ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
|
||||
|
||||
// change in phase
|
||||
double delta = Math.Max(ph.i1 - ph.i, 1d);
|
||||
|
||||
// adaptive alpha value
|
||||
double alpha = Math.Max(fastl / delta, slowl);
|
||||
|
||||
// final indicators
|
||||
mama.i = ((alpha * pr.i) + ((1d - alpha) * mama.i1));
|
||||
fama.i = ((0.5d * alpha * mama.i) + ((1d - (0.5d * alpha)) * fama.i1));
|
||||
}
|
||||
else {
|
||||
sumPr += pr.i;
|
||||
pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0;
|
||||
mama.i = fama.i = sumPr / (i+1);
|
||||
}
|
||||
|
||||
base.Add((TValue.t, mama.i), update, _NaN);
|
||||
var result = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : fama.i);
|
||||
Fama.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class RMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.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) TValue, bool update)
|
||||
{
|
||||
double _ema;
|
||||
if (update) { this._lastema = this._lastlastema; }
|
||||
|
||||
if (this.Count < this._p)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
_ema = _buffer.Average();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ema = (TValue.v * _k) + (_lastema * _k1m);
|
||||
}
|
||||
|
||||
this._lastlastema = this._lastema;
|
||||
this._lastema = _ema;
|
||||
|
||||
base.Add((TValue.t, _ema), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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 (slow) iterative methods. It is not as fast as TA-LIB
|
||||
implementation, but it does allow incremental additions of inputs and real-time calculations of SMA()
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SMA_Series : Single_TSeries_Indicator {
|
||||
private double _sum, _oldsum;
|
||||
private int _len;
|
||||
|
||||
public SMA_Series(TSeries source, int period = 0, bool useNaN = false) : base(source, period, false) {
|
||||
_sum = _oldsum = 0;
|
||||
_len = 0;
|
||||
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 = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <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)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
_smma = _buffer.Average();
|
||||
}
|
||||
else
|
||||
{
|
||||
_smma = ((_lastsmma * (_p-1)) + TValue.v) / _p ;
|
||||
}
|
||||
|
||||
this._lastlastsmma = this._lastsmma;
|
||||
this._lastsmma = _smma;
|
||||
|
||||
base.Add((TValue.t, _smma), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
/* <summary>
|
||||
T3: Tillson T3 Moving Average
|
||||
Tim Tillson described it in "Technical Analysis of Stocks and Commodities", January 1998 in the
|
||||
article "Better Moving Averages". Tillson’s moving average becomes a popular indicator of
|
||||
technical analysis as it gets less lag with the price chart and its curve is considerably smoother.
|
||||
|
||||
Sources:
|
||||
https://technicalindicators.net/indicators-technical-analysis/150-t3-moving-average
|
||||
http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/
|
||||
|
||||
Calculation:
|
||||
Volume Factor is typically 0.7 (but also 0.618);
|
||||
Ema1 = Ema (Close);
|
||||
Ema2 = Ema (Ema1);
|
||||
Ema3 = Ema (Ema2);
|
||||
Ema4 = Ema (Ema3);
|
||||
Ema5 = Ema (Ema4);
|
||||
Ema6 = Ema (Ema5);
|
||||
T3 = –(a*a*a) * Ema6 + (3*a*a + 3*a*a*a) * Ema5 + (–6*a*a – 3*a – 3*a*a*a) * Ema4 + (1 + 3*a + a*a*a + 3*a*a) * Ema3
|
||||
|
||||
</summary> */
|
||||
public class T3_Series : Single_TSeries_Indicator {
|
||||
private readonly double _k, _k1m, _c1, _c2, _c3, _c4;
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer2 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer3 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer4 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer5 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer6 = new();
|
||||
|
||||
private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6;
|
||||
private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6;
|
||||
private bool _useSMA;
|
||||
|
||||
public T3_Series(TSeries source, int period, double vfactor = 0.7, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN) {
|
||||
double _a = vfactor; //0.7; //0.618
|
||||
_c1 = -_a * _a * _a;
|
||||
_c2 = 3 * _a * _a + 3 * _a * _a * _a;
|
||||
_c3 = -6 * _a * _a - 3 * _a - 3 * _a * _a * _a;
|
||||
_c4 = 1 + 3 * _a + _a * _a * _a + 3 * _a * _a;
|
||||
|
||||
_k = 2.0 / (_p + 1);
|
||||
_k1m = 1.0 - _k;
|
||||
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
|
||||
_useSMA = useSMA;
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update) {
|
||||
double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6;
|
||||
if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; _lastema4 = _llastema4; _lastema5 = _llastema5; _lastema6 = _llastema6; }
|
||||
else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _llastema4 = _lastema4; _llastema5 = _lastema5; _llastema6 = _lastema6; }
|
||||
|
||||
if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; }
|
||||
|
||||
if ((this.Count < _p) && _useSMA) {
|
||||
Add_Replace(_buffer1, TValue.v, update);
|
||||
_ema1 = 0;
|
||||
for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
|
||||
_ema1 /= _buffer1.Count;
|
||||
|
||||
Add_Replace(_buffer2, _ema1, update);
|
||||
_ema2 = 0;
|
||||
for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
|
||||
_ema2 /= _buffer2.Count;
|
||||
|
||||
Add_Replace(_buffer3, _ema2, update);
|
||||
_ema3 = 0;
|
||||
for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
|
||||
_ema3 /= _buffer3.Count;
|
||||
|
||||
Add_Replace(_buffer4, _ema3, update);
|
||||
_ema4 = 0;
|
||||
for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; }
|
||||
_ema4 /= _buffer4.Count;
|
||||
|
||||
Add_Replace(_buffer5, _ema4, update);
|
||||
_ema5 = 0;
|
||||
for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; }
|
||||
_ema5 /= _buffer5.Count;
|
||||
|
||||
Add_Replace(_buffer6, _ema5, update);
|
||||
_ema6 = 0;
|
||||
for (int i = 0; i < _buffer6.Count; i++) { _ema6 += _buffer6[i]; }
|
||||
_ema6 /= _buffer6.Count;
|
||||
}
|
||||
else {
|
||||
_ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m);
|
||||
_ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m);
|
||||
_ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m);
|
||||
_ema4 = (_ema3 * this._k) + (this._lastema4 * this._k1m);
|
||||
_ema5 = (_ema4 * this._k) + (this._lastema5 * this._k1m);
|
||||
_ema6 = (_ema5 * this._k) + (this._lastema6 * this._k1m);
|
||||
}
|
||||
_lastema1 = _ema1;
|
||||
_lastema2 = _ema2;
|
||||
_lastema3 = _ema3;
|
||||
_lastema4 = _ema4;
|
||||
_lastema5 = _ema5;
|
||||
_lastema6 = _ema6;
|
||||
|
||||
double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3;
|
||||
base.Add((TValue.t, _T3), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
public class TEMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.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) TValue, bool update)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
this._lastema1 = this._lastlastema1;
|
||||
this._lastema2 = this._lastlastema2;
|
||||
this._lastema3 = this._lastlastema3;
|
||||
}
|
||||
|
||||
double _ema1, _ema2, _ema3;
|
||||
|
||||
if (this.Count < this._p)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
double _sma = _buffer.Average();
|
||||
_ema1 = _ema2 = _ema3 = _sma;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ema1 = (TValue.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;
|
||||
|
||||
base.Add((TValue.t, _tema), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
TRIMA: Triangular Moving Average
|
||||
A weighted moving average where the shape of the weights are triangular and the greatest
|
||||
weight is in the middle of the period,
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/
|
||||
|
||||
Remark:
|
||||
trima = sma(sma(signal, n/2), n/2)
|
||||
|
||||
</summary> */
|
||||
|
||||
public class TRIMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer2 = new();
|
||||
private readonly int _p1a, _p1b;
|
||||
|
||||
public TRIMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
|
||||
{
|
||||
_p1a = (int) Math.Floor((period * 0.5) + 1);
|
||||
_p1b = (int) Math.Ceiling(0.5 * period);
|
||||
if (base._data.Count > 0) { base.Add(base._data); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
if (update) { _buffer1[_buffer1.Count - 1] = TValue.v; } else { _buffer1.Add(TValue.v); }
|
||||
if (_buffer1.Count > this._p1b && this._p1b != 0) { _buffer1.RemoveAt(0); }
|
||||
double _sma1 = _buffer1.Average();
|
||||
|
||||
if (update) { _buffer2[_buffer2.Count - 1] = _sma1; } else { _buffer2.Add(_sma1); }
|
||||
if (_buffer2.Count > this._p1a && this._p1a != 0) { _buffer2.RemoveAt(0); }
|
||||
double _trima = _buffer2.Average();
|
||||
|
||||
base.Add((TValue.t, _trima), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
/* <summary>
|
||||
TRIX: Triple Exponential Average
|
||||
Developed by Jack Hutson in the early 1980s, the triple exponential average (TRIX)
|
||||
has become a popular technical analysis tool to aid chartists in spotting diversions
|
||||
and directional cues in stock trading patterns.
|
||||
|
||||
|
||||
Calculation:
|
||||
Ema1 = Ema (Close);
|
||||
Ema2 = Ema (Ema1);
|
||||
Ema3 = Ema (Ema2);
|
||||
TRIX = (Ema3-Ema3[1]) / Ema3[1]
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/t/trix.asp
|
||||
|
||||
</summary> */
|
||||
public class TRIX_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly double _k, _k1m;
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer2 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer3 = new();
|
||||
|
||||
private double _lastema1, _lastema2, _lastema3;
|
||||
private double _llastema1, _llastema2, _llastema3;
|
||||
private bool _useSMA;
|
||||
|
||||
public TRIX_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
|
||||
{
|
||||
|
||||
_k = 2.0 / (_p + 1);
|
||||
_k1m = 1.0 - _k;
|
||||
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0;
|
||||
_useSMA = useSMA;
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
double _ema1, _ema2, _ema3;
|
||||
if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; }
|
||||
|
||||
if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; }
|
||||
else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; }
|
||||
|
||||
if ((this.Count < _p) && _useSMA)
|
||||
{
|
||||
Add_Replace(_buffer1, TValue.v, update);
|
||||
_ema1 = 0;
|
||||
for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
|
||||
_ema1 /= _buffer1.Count;
|
||||
|
||||
Add_Replace(_buffer2, _ema1, update);
|
||||
_ema2 = 0;
|
||||
for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
|
||||
_ema2 /= _buffer2.Count;
|
||||
|
||||
Add_Replace(_buffer3, _ema2, update);
|
||||
_ema3 = 0;
|
||||
for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
|
||||
_ema3 /= _buffer3.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m);
|
||||
_ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m);
|
||||
_ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m);
|
||||
}
|
||||
double _trix = 100 * (_ema3 - _lastema3) / _lastema3;
|
||||
_lastema1 = _ema1;
|
||||
_lastema2 = _ema2;
|
||||
_lastema3 = _ema3;
|
||||
|
||||
base.Add((TValue.t, _trix), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
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) TValue, bool update)
|
||||
{
|
||||
Add_Replace_Trim(_buffer, TValue.v, _p, update);
|
||||
|
||||
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;
|
||||
|
||||
base.Add((TValue.t, _wma), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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)
|
||||
|
||||
Remark:
|
||||
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.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ZLEMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly double _k, _k1m;
|
||||
private double _lastema, _lastema_o;
|
||||
private int _llag;
|
||||
private readonly bool _useSMA;
|
||||
|
||||
public ZLEMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
|
||||
{
|
||||
this._k = 2.0 / (this._p + 1);
|
||||
this._k1m = 1.0 - this._k;
|
||||
this._lastema = this._lastema_o = double.NaN;
|
||||
_llag = (int)((_p-1) * 0.5);
|
||||
_useSMA = useSMA;
|
||||
if (_data.Count > 0) { base.Add(_data); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
int _lag = Math.Max(this.Count-_llag, 0);
|
||||
if (update) {
|
||||
_lastema = _lastema_o; _lag--;
|
||||
} else {
|
||||
_lastema_o = _lastema;
|
||||
}
|
||||
double _zl = TValue.v + (TValue.v - _data[_lag].v);
|
||||
double _ema = 0;
|
||||
|
||||
if (this.Count < this._p && _useSMA) {
|
||||
Add_Replace_Trim(_buffer, _zl, _p, update);
|
||||
_ema = _buffer.Average();
|
||||
} else {
|
||||
_ema = (_zl * _k) + (_lastema * _k1m);
|
||||
}
|
||||
_lastema = _ema;
|
||||
|
||||
base.Add((TValue.t, _ema), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
ADL: Chaikin Accumulation/Distribution Line
|
||||
ADL is a volume-based indicator that measures the cumulative Money Flow Volume:
|
||||
|
||||
1. Money Flow Multiplier = [(Close - Low) - (High - Close)] /(High - Low)
|
||||
2. Money Flow Volume = Money Flow Multiplier x Volume for the Period
|
||||
3. ADL = Previous ADL + Current Period's Money Flow Volume
|
||||
|
||||
Sources:
|
||||
https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ADL_Series : Single_TBars_Indicator
|
||||
{
|
||||
private double _lastadl, _lastlastadl;
|
||||
|
||||
public ADL_Series(TBars source, bool useNaN = false) : base(source, 0, useNaN)
|
||||
{
|
||||
_lastadl = _lastlastadl = 0;
|
||||
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)
|
||||
{
|
||||
if (update) { this._lastadl = this._lastlastadl; }
|
||||
|
||||
double _adl = 0;
|
||||
double tmp = TBar.h - TBar.l;
|
||||
if (tmp > 0.0 ) { _adl = _lastadl + ((2*TBar.c - TBar.l - TBar.h) / tmp * TBar.v); }
|
||||
|
||||
this._lastlastadl = this._lastadl;
|
||||
this._lastadl = _adl;
|
||||
|
||||
base.Add((TBar.t, _adl), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
ADO: Chaikin Accumulation/Distribution Oscillator
|
||||
ADO measures the momentum of ADL using the difference between slow (10-day) EMA(ADL)
|
||||
and fast (3-day) EMA(ADL):
|
||||
|
||||
Chaikin A/D Oscillator = (3-day EMA of ADL) - (10-day EMA of ADL)
|
||||
|
||||
Sources:
|
||||
https://school.stockcharts.com/doku.php?id=technical_indicators:chaikin_oscillator
|
||||
|
||||
</summary> */
|
||||
|
||||
|
||||
public class ADOSC_Series : Single_TBars_Indicator
|
||||
{
|
||||
private readonly double _k1, _k2;
|
||||
private double _lastema1, _lastlastema1, _lastema2, _lastlastema2;
|
||||
private double _lastadl, _lastlastadl;
|
||||
|
||||
public ADOSC_Series(TBars source, int shortPeriod = 3, int longPeriod =10, bool useNaN = false) : base(source, period: 0, useNaN)
|
||||
{
|
||||
_k1 = 2.0 / (shortPeriod + 1);
|
||||
_k2 = 2.0 / (longPeriod + 1);
|
||||
_lastadl = _lastlastadl = _lastema1 = _lastlastema1 = _lastema2 = _lastlastema2 = 0;
|
||||
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)
|
||||
{
|
||||
if (update) {
|
||||
_lastadl = _lastlastadl;
|
||||
_lastema1 = _lastlastema1;
|
||||
_lastema2 = _lastlastema2;
|
||||
}
|
||||
|
||||
double _adl = 0;
|
||||
double tmp = TBar.h - TBar.l;
|
||||
if (tmp > 0.0) { _adl = _lastadl + ((2 * TBar.c - TBar.l - TBar.h) / tmp * TBar.v); }
|
||||
if (this.Count == 0) { _lastema1 = _lastema2 = _adl; }
|
||||
|
||||
double _ema1 = (_adl - _lastema1) * _k1 + _lastema1;
|
||||
double _ema2 = (_adl - _lastema2) * _k2 + _lastema2;
|
||||
|
||||
_lastlastadl = _lastadl; _lastadl = _adl;
|
||||
_lastlastema1 = _lastema1; _lastema1 = _ema1;
|
||||
_lastlastema2 = _lastema2; _lastema2 = _ema2;
|
||||
|
||||
double _adosc = _ema1 - _ema2;
|
||||
base.Add((TBar.t, _adosc), update, _NaN);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
public class ADOSC_Series : Single_TBars_Indicator
|
||||
{
|
||||
private readonly ADL_Series _TSadl;
|
||||
|
||||
private readonly EMA_Series _TSslow;
|
||||
private readonly EMA_Series _TSfast;
|
||||
private readonly SUB_Series _TSado;
|
||||
|
||||
public ADOSC_Series(TBars source, bool useNaN = false) : base(source, period: 0, useNaN)
|
||||
{
|
||||
_TSadl = new(source: source, useNaN: false);
|
||||
_TSslow = new(source: _TSadl, period: 10, useNaN: false);
|
||||
_TSfast = new(source: _TSadl, period: 3, useNaN: false);
|
||||
_TSado = new(_TSfast, _TSslow);
|
||||
|
||||
if (source.Count > 0)
|
||||
{ base.Add(_TSado); }
|
||||
Console.WriteLine(base.Count);
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update)
|
||||
{
|
||||
if (update)
|
||||
{ _TSadl.Add(TBar, true); }
|
||||
|
||||
double _ado = this._TSado[(this.Count < this._TSado.Count) ? this.Count : this._TSado.Count - 1].v;
|
||||
var result = (TBar.t, _ado);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
ATRP: Average True Range Percent
|
||||
Average True Range Percent is (ATR/Close Price)*100.
|
||||
This normalizes so it can be compared to other stocks.
|
||||
|
||||
Sources:
|
||||
https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atrp
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ATRP_Series : Single_TBars_Indicator {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly double _k;
|
||||
private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum;
|
||||
private readonly int _period;
|
||||
|
||||
public ATRP_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) {
|
||||
_period = period;
|
||||
_k = 1.0 / (double)(_p);
|
||||
_lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0;
|
||||
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) {
|
||||
if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; }
|
||||
else { _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; }
|
||||
|
||||
if (this.Count == 0) { _cm1 = TBar.c; }
|
||||
double d1 = Math.Abs(TBar.h - TBar.l);
|
||||
double d2 = Math.Abs(_cm1 - TBar.h);
|
||||
double d3 = Math.Abs(_cm1 - TBar.l);
|
||||
(DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3)));
|
||||
_cm1 = TBar.c;
|
||||
|
||||
double _atr = 0;
|
||||
if (this.Count == 0) { _atr = d.v; }
|
||||
else if (this.Count < _p + 1) { _sum += d.v; _atr = _sum / (this.Count); }
|
||||
else { _atr = _k * (d.v - _lastatr) + _lastatr; }
|
||||
_lastatr = _atr;
|
||||
|
||||
double _atrp = 100 * (_atr / TBar.c);
|
||||
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atrp);
|
||||
base.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
ATR: wildeR Moving Average
|
||||
The average true range (ATR) is a price volatility indicator
|
||||
showing the average price variation of assets within a given time period.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Average_true_range
|
||||
https://www.tradingview.com/wiki/Average_True_Range_(ATR)
|
||||
https://www.investopedia.com/terms/a/atr.asp
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ATR_Series : Single_TBars_Indicator {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly double _k;
|
||||
private double _lastatr, _lastlastatr, _cm1, _lastcm1, _sum, _oldsum;
|
||||
private readonly int _period;
|
||||
|
||||
public ATR_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) {
|
||||
_period = period;
|
||||
_k = 1.0 / (double)(_p);
|
||||
_lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0;
|
||||
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) {
|
||||
if (update) { _lastatr = _lastlastatr; _cm1 = _lastcm1; _sum = _oldsum; }
|
||||
else { _lastlastatr = _lastatr; _lastcm1 = _cm1; _oldsum = _sum; }
|
||||
|
||||
if (this.Count == 0) { _cm1 = TBar.c; }
|
||||
double d1 = Math.Abs(TBar.h - TBar.l);
|
||||
double d2 = Math.Abs(_cm1 - TBar.h);
|
||||
double d3 = Math.Abs(_cm1 - TBar.l);
|
||||
(DateTime t, double v) d = (TBar.t, Math.Max(d1, Math.Max(d2, d3)));
|
||||
_cm1 = TBar.c;
|
||||
|
||||
double _atr = 0;
|
||||
if (this.Count == 0) { _atr = d.v; }
|
||||
else if (this.Count < _p + 1) { _sum += d.v; _atr = _sum / (this.Count); }
|
||||
else { _atr = _k * (d.v - _lastatr) + _lastatr; }
|
||||
_lastatr = _atr;
|
||||
|
||||
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atr);
|
||||
base.Add(ret, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
BBANDS: Bollinger Bands®
|
||||
Price channels created by John Bollinger, depict volatility as standard deviation boundary
|
||||
line range from a moving average of price. The bands automatically widen when volatility
|
||||
increases and contract when volatility decreases. Their dynamic nature allows them to be
|
||||
used on different securities with the standard settings.
|
||||
|
||||
Mid Band = simple moving average (SMA)
|
||||
Upper Band = SMA + (standard deviation of price x multiplier)
|
||||
Lower Band = SMA - (standard deviation of price x multiplier)
|
||||
Bandwidth = Width of the channel: (Upper-Lower)/SMA
|
||||
%B = The location of the data point within the channel: (Price-Lower)/(Upper/Lower)
|
||||
Z-Score = number of standard deviations of the data point from SMA
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/b/bollingerbands.asp
|
||||
https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands
|
||||
|
||||
Note:
|
||||
Bollinger Bands® is a registered trademark of John A. Bollinger.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class BBANDS_Series : Single_TSeries_Indicator
|
||||
{
|
||||
public SMA_Series Mid { get; }
|
||||
public ADD_Series Upper { get; }
|
||||
public SUB_Series Lower { get; }
|
||||
public DIV_Series PercentB { get; }
|
||||
public DIV_Series Bandwidth { get; }
|
||||
public DIV_Series Zscore { get; }
|
||||
|
||||
private readonly SDEV_Series _sdev;
|
||||
private readonly MUL_Series _mulsdev;
|
||||
private readonly SUB_Series _pbdnd;
|
||||
private readonly SUB_Series _pbdvr;
|
||||
private readonly SUB_Series _zdnd;
|
||||
|
||||
public BBANDS_Series(TSeries source, int period = 26, double multiplier = 2.0, bool useNaN = false)
|
||||
: base(source, period: 0, useNaN)
|
||||
{
|
||||
this.Mid = new(source: source, period: period, useNaN: useNaN);
|
||||
|
||||
_sdev = new(source, period, useNaN: useNaN);
|
||||
_mulsdev = new(_sdev, multiplier);
|
||||
this.Upper = new(Mid, _mulsdev);
|
||||
this.Lower = new(Mid, _mulsdev);
|
||||
|
||||
_pbdnd = new(source, Lower);
|
||||
_pbdvr = new(Upper, Lower);
|
||||
|
||||
this.PercentB = new(_pbdnd, _pbdvr);
|
||||
this.Bandwidth = new(_pbdvr, Mid);
|
||||
|
||||
_zdnd = new(source, Mid);
|
||||
this.Zscore = new(_zdnd, _sdev);
|
||||
|
||||
if (source.Count > 0)
|
||||
{ base.Add(this.Bandwidth); }
|
||||
}
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update)
|
||||
{
|
||||
double _bbandwidth;
|
||||
if (update)
|
||||
{ _sdev.Add(TValue, true); }
|
||||
_bbandwidth = this.Bandwidth[(this.Count < this.Bandwidth.Count) ? this.Count : this.Bandwidth.Count - 1].v;
|
||||
var result = (TValue.t, _bbandwidth);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
CMO: Chande Momentum Oscillator
|
||||
Chande Momentum Oscillator (also known as CMO indicator) was developed by Tushar S. Chande
|
||||
CMO is similar to other momentum oscillators (e.g. RSI or Stochastics). Alike RSI oscillator,
|
||||
the CMO values move in the range from -100 to +100 points and its aim is to detect the
|
||||
overbought and oversold market conditions. CMO calculates the price momentum on both the up
|
||||
days as well as the down days. The CMO calculation is based on non-smoothed price values
|
||||
meaning that it can reach its extremes more frequently and the short-time swings are more visible.
|
||||
|
||||
Sources:
|
||||
https://www.technicalindicators.net/indicators-technical-analysis/144-cmo-chande-momentum-oscillator
|
||||
|
||||
</summary> */
|
||||
|
||||
public class CMO_Series : Single_TSeries_Indicator {
|
||||
private readonly System.Collections.Generic.List<double> _buff_up = new();
|
||||
private readonly System.Collections.Generic.List<double> _buff_dn = new();
|
||||
private double _plast_value, _last_value;
|
||||
|
||||
public CMO_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN) {
|
||||
if (this._data.Count > 0) { base.Add(this._data); }
|
||||
}
|
||||
|
||||
public override void Add((DateTime t, double v) TValue, bool update) {
|
||||
if (this.Count == 0) { _plast_value = _last_value = TValue.v; }
|
||||
if (update) _last_value = _plast_value; else _plast_value = _last_value;
|
||||
|
||||
Add_Replace_Trim(_buff_up, (TValue.v > _last_value) ? TValue.v-_last_value : 0, _p, update);
|
||||
Add_Replace_Trim(_buff_dn, (TValue.v < _last_value) ? _last_value-TValue.v : 0, _p, update);
|
||||
_last_value = TValue.v;
|
||||
|
||||
double _cmo_up = 0;
|
||||
double _cmo_dn = 0;
|
||||
for (int i = 0; i < Math.Min(_buff_up.Count, _buff_dn.Count); i++) {
|
||||
_cmo_up += _buff_up[i];
|
||||
_cmo_dn += _buff_dn[i];
|
||||
}
|
||||
|
||||
double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn);
|
||||
if (_cmo_up + _cmo_dn == 0)
|
||||
_cmo = 0;
|
||||
base.Add((TValue.t, _cmo), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
RSI: Relative Strength Index
|
||||
Created by J. Welles Wilder, the Relative Strength Index measures strength
|
||||
of the winning/losing streak over N lookback periods on a scale of 0 to 100,
|
||||
to depict overbought and oversold conditions.
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/r/rsi.asp
|
||||
|
||||
</summary> */
|
||||
|
||||
public class RSI_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _gain = new();
|
||||
private readonly System.Collections.Generic.List<double> _loss = new();
|
||||
private double _avgGain, _avgLoss, _lastValue;
|
||||
private double _avgGain_o, _avgLoss_o, _lastValue_o;
|
||||
private int i;
|
||||
|
||||
public RSI_Series(TSeries source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN) {
|
||||
i = 0;
|
||||
if (source.Count > 0) { base.Add(source); }
|
||||
}
|
||||
|
||||
public override void Add((System.DateTime t, double v) TValue, bool update) {
|
||||
double _rsi = 0;
|
||||
if (update) {
|
||||
_lastValue = _lastValue_o;
|
||||
_avgGain = _avgGain_o;
|
||||
_avgLoss = _avgLoss_o;
|
||||
}
|
||||
else {
|
||||
_lastValue_o = _lastValue;
|
||||
_avgGain_o = _avgGain;
|
||||
_avgLoss_o = _avgLoss;
|
||||
}
|
||||
|
||||
if (i == 0) { _lastValue = TValue.v; }
|
||||
|
||||
double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0;
|
||||
Add_Replace_Trim(_gain, _gainval, _p, update);
|
||||
double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0;
|
||||
Add_Replace_Trim(_loss, _lossval, _p, update);
|
||||
_lastValue = TValue.v;
|
||||
|
||||
// calculate RSI
|
||||
if (i > _p)
|
||||
{
|
||||
_avgGain = ((_avgGain * (_p - 1)) + _gain[_gain.Count - 1]) / _p;
|
||||
_avgLoss = ((_avgLoss * (_p - 1)) + _loss[_loss.Count - 1]) / _p;
|
||||
if (_avgLoss > 0) {
|
||||
double rs = _avgGain / _avgLoss;
|
||||
_rsi = 100 - (100 / (1 + rs));
|
||||
}
|
||||
else { _rsi = 100; }
|
||||
}
|
||||
// initialize average gain
|
||||
else
|
||||
{
|
||||
double _sumGain = 0;
|
||||
for (int p = 0; p < _gain.Count; p++) { _sumGain += _gain[p]; }
|
||||
double _sumLoss = 0;
|
||||
for (int p = 0; p < _loss.Count; p++) { _sumLoss += _loss[p]; }
|
||||
|
||||
_avgGain = _sumGain / _gain.Count;
|
||||
_avgLoss = _sumLoss / _loss.Count;
|
||||
|
||||
_rsi = (_avgLoss > 0) ? 100 - (100 / (1 + (_avgGain / _avgLoss))) : 100;
|
||||
}
|
||||
|
||||
if (!update) { i++; }
|
||||
var result = (TValue.t, (this.Count < this._p && this._NaN) ? double.NaN : _rsi);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
OBV: On-Balance Volume
|
||||
On-balance volume (OBV) is a technical trading momentum indicator that uses volume flow to predict
|
||||
changes in stock price. Joseph Granville first developed the OBV metric in the 1963 book
|
||||
Granville's New Key to Stock Market Profits.
|
||||
|
||||
| +volume; if close > close[previous]
|
||||
OBV = OBV[previous] + | 0; if close = close[previous]
|
||||
| -volume; if close < close[previous]
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/o/onbalancevolume.asp
|
||||
https://www.tradingview.com/wiki/On_Balance_Volume_(OBV)
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/on-balance-volume-obv/
|
||||
https://www.motivewave.com/studies/on_balance_volume.htm
|
||||
|
||||
Note:
|
||||
There is no consensus on what is the first OBV value in the series:
|
||||
- TA-LIB uses the first volume: OBV[0] = volume[0]
|
||||
- Skender stock library uses 0: OBV[0] = 0
|
||||
|
||||
</summary> */
|
||||
|
||||
public class OBV_Series : Single_TBars_Indicator
|
||||
{
|
||||
private double _lastobv, _lastlastobv;
|
||||
private double _lastclose, _lastlastclose;
|
||||
public OBV_Series(TBars source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN)
|
||||
{
|
||||
this._lastobv = this._lastlastobv = 0;
|
||||
this._lastclose = this._lastlastclose = 0;
|
||||
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)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
this._lastobv = this._lastlastobv;
|
||||
this._lastclose = this._lastlastclose;
|
||||
}
|
||||
|
||||
double _obv = this._lastobv;
|
||||
if (TBar.c > this._lastclose) { _obv += TBar.v; }
|
||||
if (TBar.c < this._lastclose) { _obv -= TBar.v; }
|
||||
|
||||
this._lastlastobv = this._lastobv;
|
||||
this._lastobv = _obv;
|
||||
|
||||
this._lastlastclose = this._lastclose;
|
||||
this._lastclose = TBar.c;
|
||||
|
||||
var result = (TBar.t, (this.Count < this._p && this._NaN) ? double.NaN : _obv);
|
||||
base.Add(result, update);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user