Merge branch 'dev' into main

This commit is contained in:
Miha Kralj
2022-04-29 20:39:27 -07:00
27 changed files with 867 additions and 227 deletions
+1 -1
View File
@@ -99,4 +99,4 @@ jobs:
run: dotnet nuget push '.\Source\bin\Release\QuanTAlib.*.nupkg'
--api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }}
--source https://api.nuget.org/v3/index.json
--skip-duplicate
# --skip-duplicate
+6 -7
View File
@@ -9,6 +9,7 @@
| OHL3 - (Open+High+Low)/3 |✔️|||✔️|
| OHLC4 - (O+H+L+C)/4 |✔️|||✔️|
| HLCC4 - Weighted Price |✔️||✔️|✔️|
| ZL - Zero Lag - De-lagged price |✔️|||✔️|
| ADD - Addition |✔️|✔️|||
| SUB - Subtraction |✔️|✔️|||
| MUL - Multiplication |✔️|✔️|||
@@ -19,7 +20,7 @@
| BIAS - Bias |✔️|||✔️|
| ENTR - Entropy |✔️|||✔️|
| KUR - Kurtosis |✔️|||✔️|
| LINREG - Linear Regression ||✔️|✔️||
| LINREG - Linear Regression |✔️|✔️|✔️||
| MAD - Mean Absolute Deviation |✔️||✔️|✔️|
| MAPE - Mean Absolute Percent Error |✔️||✔️||
| MAX - Max value |✔️|✔️|||
@@ -29,9 +30,7 @@
| PSDEV - Population Standard Deviation |✔️||||
| PVAR - Population Variance |✔️||||
| QUANTILE ||||✔️|
| RS - R-Squared Coefficient |||✔️||
| SKEW - Skewness ||||✔️|
| SLOPE - Slope |||✔️||
| SMAPE - Symmetric Mean Absolute Percent Error |✔️||||
| SDEV - Sample Standard Deviation |✔️|✔️|✔️|✔️|
| VAR - Sample Variance |✔️|||✔️|
@@ -40,7 +39,7 @@
||||||
| **Moving Averages** |||||
| AFIRMA - Autoregressive Finite Impulse Response Moving Average |||||
| ALMA - Arnaud Legoux Moving Average |||✔️|✔️|
| ALMA - Arnaud Legoux Moving Average |✔️||✔️|✔️|
| ARIMA - Autoregressive Integrated Moving Average |||||
| ATR - Average True Range |✔️|✔️|✔️|✔️|
| ATRP - Average True Range Percent |✔️||✔️||
@@ -54,7 +53,7 @@
| JMA - Jurik Moving Average |✔️|||✔️|
| KAMA - Kaufman's Adaptive Moving Average |✔️|✔️|✔️|✔️|
| LSMA - Least Squares Moving Average |||✔️||
| MACD - Moving Average Convergence/Divergence ||✔️|✔️|✔️|
| MACD - Moving Average Convergence/Divergence |✔️|✔️|✔️|✔️|
| MAMA - MESA Adaptive Moving Average ||✔️|✔️||
| MMA - Modified Moving Average |||✔️||
| NATR - Normalized Average True Range ||✔️|✔️|✔️|
@@ -88,7 +87,7 @@
| AROON - Aroon oscillator ||✔️|✔️|✔️|
| BBANDS - Bollinger Bands ||✔️|✔️|✔️|
| BOP - Balance of Power ||✔️|✔️|✔️|
| CCI - Commodity Channel Index ||✔️|✔️|✔️|
| CCI - Commodity Channel Index |✔️|✔️|✔️|✔️|
| CFO - Chande Forcast Oscillator ||||✔️|
| CMF - Chaikin Money Flow |||✔️|✔️|
| CMO - Chande Momentum Oscillator ||✔️||✔️|
@@ -106,7 +105,7 @@
| PO - Price Oscillator ||||✔️|
| PPO - Percentage Price Oscillator ||✔️||✔️|
| PVI - Positive Volume Index ||||✔️|
| RSI - Relative Strength Index ||✔️|✔️|✔️|
| RSI - Relative Strength Index |✔️|✔️|✔️|✔️|
| RVGI - Relative Vigor Index ||||✔️|
| SRSI - Stochastic RSI |||✔️|✔️|
| TRIX - 1-day ROC of TEMA ||✔️|✔️|✔️|
+43
View File
@@ -0,0 +1,43 @@
using System.Diagnostics;
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class CCI_chart : Indicator
{
#region Parameters
[InputParameter("Smoothing period", 0, 1, 999, 1, 1)]
private readonly int Period = 10;
#endregion Parameters
private TBars bars;
///////
private CCI_Series indicator;
///////
public CCI_chart()
{
this.SeparateWindow = true;
this.Name = "CCI - Commodity Channel Index";
this.Description = "CCI description";
this.AddLineSeries("CCI", Color.RoyalBlue, 3, LineStyle.Solid);
}
protected override void OnInit()
{
this.ShortName = "CCI (" + this.Period + ")";
this.bars = new();
this.indicator = new(source: bars, period: this.Period, useNaN: false);
}
protected override void OnUpdate(UpdateArgs args)
{
bool update = (args.Reason != UpdateReason.NewBar && args.Reason != UpdateReason.HistoricalBar);
this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low),
this.GetPrice(PriceType.Close), this.GetPrice(PriceType.Volume), update);
double result = this.indicator[this.indicator.Count - 1].v;
this.SetValue(result);
}
}
+53
View File
@@ -0,0 +1,53 @@
using System.Drawing;
using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
public class RSI_chart : Indicator
{
#region Parameters
[InputParameter("Smoothing period", 0, 1, 999, 1, 1)]
private int Period = 10;
[InputParameter("Data source", 1, variants: new object[]
{ "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5,
"OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })]
private int DataSource = 8;
#endregion Parameters
private TBars bars;
///////
private RSI_Series indicator;
///////
public RSI_chart()
{
this.SeparateWindow = true;
this.Name = "RSI - Relative Strength Index";
this.Description = "RSI description";
this.AddLineSeries("RSI", Color.RoyalBlue, 3, LineStyle.Solid);
}
protected override void OnInit()
{
this.bars = new();
this.ShortName =
"RSI (" + TBars.SelectStr(this.DataSource) + ", " + this.Period + ")";
this.indicator = new(source: bars.Select(this.DataSource),
period: this.Period, useNaN: true);
}
protected override void OnUpdate(UpdateArgs args)
{
bool update = !(args.Reason == UpdateReason.NewBar ||
args.Reason == UpdateReason.HistoricalBar);
this.bars.Add(this.Time(), this.GetPrice(PriceType.Open),
this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low),
this.GetPrice(PriceType.Close),
this.GetPrice(PriceType.Volume), update);
double result = this.indicator[this.indicator.Count - 1].v;
this.SetValue(result, 0);
}
}
+9 -10
View File
@@ -9,12 +9,12 @@ public class ZLMA_chart : Indicator
#region Parameters
[InputParameter("Smoothing period", 0, 1, 999, 1, 1)]
private int Period = 10;
private readonly int Period = 10;
[InputParameter("Data source", 1, variants: new object[]
{ "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5,
"OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })]
private int DataSource = 3;
private readonly int DataSource = 3;
[InputParameter("MA algorithm", 2, variants: new object[]
{ "SMA", 0,
@@ -27,16 +27,15 @@ public class ZLMA_chart : Indicator
"JMA", 7,
"SMMA", 8
})]
private int matype = 2;
private readonly int matype = 2;
#endregion Parameters
private TBars bars;
///////
private ZL_Series zerolag;
///////
private TSeries indicator;
///////
///////
public ZLMA_chart()
{
this.SeparateWindow = false;
@@ -63,7 +62,7 @@ public class ZLMA_chart : Indicator
};
this.ShortName = "ZLMA (" + maname + ", " + TBars.SelectStr(this.DataSource) + ", " + this.Period + ")";
this.zerolag = new(source: bars.Select(this.DataSource), period: this.Period, useNaN: false);
ZL_Series zerolag = new(source: bars.Select(this.DataSource), period: this.Period, useNaN: false);
this.indicator = matype switch
{
0 => new SMA_Series(source: zerolag, period: this.Period, useNaN: false),
@@ -88,7 +87,7 @@ public class ZLMA_chart : Indicator
this.GetPrice(PriceType.Close),
this.GetPrice(PriceType.Volume), update);
double result = this.indicator[this.indicator.Count - 1].v;
double result = this.indicator[this.indicator.Count-1].v;
this.SetValue(result);
}
}
+4 -9
View File
@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>latest</LangVersion>
<LangVersion>preview</LangVersion>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<Platforms>AnyCPU</Platforms>
<AlgoType>Indicator</AlgoType>
@@ -14,7 +14,6 @@
<Nullable>disable</Nullable>
<SignAssembly>False</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>True</Optimize>
<WarningLevel>3</WarningLevel>
@@ -22,7 +21,6 @@
<PlatformTarget>anycpu</PlatformTarget>
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>embedded</DebugType>
<Optimize>True</Optimize>
@@ -30,20 +28,17 @@
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<PlatformTarget>anycpu</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Source\**\*.cs" Exclude="..\Source\obj\**" >
<Compile Include="..\Source\**\*.cs" Exclude="..\Source\obj\**">
<Link>QuanTAlib\%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
</ItemGroup>
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
<Copy SourceFiles=".\bin\$(Configuration)\net48\Quantower_QTAlib.dll" DestinationFolder="\Quantower\Settings\Scripts\Indicators\QuanTAlib" />
</Target>
<ItemGroup>
<Reference Include="TradingPlatform.BusinessLayer">
<HintPath>.\dll\TradingPlatform.BusinessLayer.dll</HintPath>
<HintPath>C:\Quantower\TradingPlatform\v1.124.6\bin\TradingPlatform.BusinessLayer.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
</Project>
+2 -2
View File
@@ -141,11 +141,11 @@ public abstract class Single_TBars_Indicator : TSeries
this._p = period;
this._bars = source;
this._NaN = useNaN;
this._bars.Close.Pub += this.Sub;
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.c, update);
public virtual void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar, bool update) => base.Add((TBar.t, TBar.c), update);
// potentially overridable Add() method for the whole series (could be replaced with faster bulk algo)
public virtual void Add(TBars bars)
+17
View File
@@ -108,5 +108,22 @@ public class TBars : System.Collections.Generic.List<(DateTime t, double o, doub
_ohlc4.Add((t, (o + h + l + c) * 0.25));
_hlcc4.Add((t, (h + l + c + c) * 0.25));
}
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 });
}
}
}
+66
View File
@@ -0,0 +1,66 @@
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/
</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)
{
if (update) { this._buffer[this._buffer.Count - 1] = TValue.v; }
else { this._buffer.Add(TValue.v); }
if (this._buffer.Count > this._p) { this._buffer.RemoveAt(0); }
if (this._buffer.Count <= _p) { calc_weights(); }
double _weightedSum = 0;
for (int i = 0; i < this._buffer.Count; i++) { _weightedSum += _weight[i] * _buffer[i]; }
double _alma = _weightedSum / _norm;
var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _alma);
base.Add(ret, update);
}
private void calc_weights()
{
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;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
namespace QuanTAlib;
using System;
/* <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 = 0;
for (int i = 0; i < this._tp.Count; i++) { _avgTp+=this._tp[i]; }
_avgTp /= this._tp.Count;
// 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);
var result = (TBar.t, (this.Count < this._p && this._NaN) ? double.NaN : _cci);
base.Add(result, update);
}
}
+1 -3
View File
@@ -27,7 +27,7 @@ public class JMA_Series : Single_TSeries_Indicator
private double prev_ma1, prev_det0, prev_det1, prev_jma, bsmax, bsmin;
private double o_prev_ma1, o_prev_det0, o_prev_det1, o_prev_jma, o_bsmax, o_bsmin;
private readonly double pr, pow1, len2, beta, rvolty, _l;
private readonly double pr, pow1, len2, beta, rvolty;
public JMA_Series(TSeries source, int period, double phase = 0.0, bool useNaN = false) : base(source, period, useNaN)
{
@@ -41,8 +41,6 @@ public class JMA_Series : Single_TSeries_Indicator
this.rvolty = Math.Exp((1 / this.pow1) * Math.Log(len1));
this.len2 = Math.Sqrt(0.5 * (_p - 1)) * len1;
this.beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
this._l = (int)Math.Round(this._p - 1 * 0.5);
if (base._data.Count > 0) { base.Add(base._data); }
}
+1 -1
View File
@@ -25,7 +25,7 @@ Remark:
public class KAMA_Series : Single_TSeries_Indicator
{
private static double _scFast, _scSlow;
private readonly double _scFast, _scSlow;
private readonly System.Collections.Generic.List<double> _buffer = new();
private double _lastkama = double.NaN;
private double _lastlastkama;
+46
View File
@@ -0,0 +1,46 @@
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;
var result = (TValue.t, _macd);
base.Add(result, update);
}
}
+73
View File
@@ -0,0 +1,73 @@
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;
private double _avgLoss;
private double _lastValue;
private double _lastlastValue;
public RSI_Series(TSeries source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN)
{ if (source.Count > 0) { base.Add(source); } }
public override void Add((System.DateTime t, double v) TValue, bool update)
{
int i = this.Count;
double _rsi = 0;
if (update) { _lastValue = _lastlastValue; }
if (i == 0) { _lastValue = TValue.v; }
double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0;
if (update) { _gain[_gain.Count - 1] = _gainval; } else { _gain.Add(_gainval); }
if (_gain.Count > this._p) { _gain.RemoveAt(0); }
double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0;
if (update) { _loss[_loss.Count - 1] = _lossval; } else { _loss.Add(_lossval); }
if (_loss.Count > this._p) { _loss.RemoveAt(0); }
_lastlastValue = _lastValue;
_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;
}
var result = (TValue.t, (this.Count < this._p && this._NaN) ? double.NaN : _rsi);
base.Add(result, update);
}
}
+70 -70
View File
@@ -1,71 +1,71 @@
namespace QuanTAlib;
using System;
/* <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, _lastlastema;
public ZLEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
{
this._k = 2.0 / (this._p + 1);
this._k1m = 1.0 - this._k;
this._lastema = this._lastlastema = double.NaN;
if (base._data.Count > 0)
{ base.Add(base._data); }
}
public override void Add((System.DateTime t, double v) TValue, bool update)
{
int _lag = (int)((_p - 1) * 0.5);
_lag = (this.Count - _lag < 0) ? 0 : this.Count - _lag;
double _zl = TValue.v + (TValue.v - _data[_lag].v);
double _ema = 0;
if (update)
{ this._lastema = this._lastlastema; }
if (this.Count < this._p)
{
if (update)
{ this._buffer[this._buffer.Count - 1] = _zl; }
else
{
this._buffer.Add(_zl);
}
if (this._buffer.Count > this._p)
{ this._buffer.RemoveAt(0); }
for (int i = 0; i < this._buffer.Count; i++)
{ _ema += this._buffer[i]; }
_ema /= this._buffer.Count;
}
else
{
_ema = TValue.v * this._k + this._lastema * this._k1m;
}
this._lastlastema = this._lastema;
this._lastema = _ema;
var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
base.Add(ret, update);
}
namespace QuanTAlib;
using System;
/* <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, _lastlastema;
public ZLEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
{
this._k = 2.0 / (this._p + 1);
this._k1m = 1.0 - this._k;
this._lastema = this._lastlastema = double.NaN;
if (base._data.Count > 0)
{ base.Add(base._data); }
}
public override void Add((System.DateTime t, double v) TValue, bool update)
{
int _lag = (int)((_p - 1) * 0.5);
_lag = (this.Count - _lag < 0) ? 0 : this.Count - _lag;
double _zl = TValue.v + (TValue.v - _data[_lag].v);
double _ema = 0;
if (update)
{ this._lastema = this._lastlastema; }
if (this.Count < this._p)
{
if (update)
{ this._buffer[this._buffer.Count - 1] = _zl; }
else
{
this._buffer.Add(_zl);
}
if (this._buffer.Count > this._p)
{ this._buffer.RemoveAt(0); }
for (int i = 0; i < this._buffer.Count; i++)
{ _ema += this._buffer[i]; }
_ema /= this._buffer.Count;
}
else
{
_ema = TValue.v * this._k + this._lastema * this._k1m;
}
this._lastlastema = this._lastema;
this._lastema = _ema;
var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
base.Add(ret, update);
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>0.1.12</Version>
<releaseNotes></releaseNotes>
<Version>0.1.13</Version>
<releaseNotes>Added MACD, RSI, CCI, ALMA, LINREG</releaseNotes>
<Title>QuanTAlib</Title>
<Product>Library of Technical Indicators for .NET</Product>
<Description>Quantitative Technical Analysis library for both real-time (streaming) and historical data analysis</Description>
+1 -1
View File
@@ -23,7 +23,7 @@ public class ENTP_Series : Single_TSeries_Indicator
this._logbase = logbase;
if (base._data.Count > 0) { base.Add(base._data); }
}
private readonly double _logbase = 2.0;
private readonly double _logbase;
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly System.Collections.Generic.List<double> _buff2 = new();
+2 -2
View File
@@ -20,7 +20,7 @@ Calculation:
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 KURT_Series : Single_TSeries_Indicator
@@ -30,7 +30,7 @@ public class KURT_Series : Single_TSeries_Indicator
this._logbase = logbase;
if (base._data.Count > 0) { base.Add(base._data); }
}
protected double _logbase = 2.0;
protected double _logbase;
private readonly System.Collections.Generic.List<double> _buffer = new();
public override void Add((System.DateTime t, double v) TValue, bool update)
+93
View File
@@ -0,0 +1,93 @@
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)
{
if (update) { this._buffer[this._buffer.Count - 1] = TValue.v; }
else { this._buffer.Add(TValue.v); }
if (this._buffer.Count > this._p) { this._buffer.RemoveAt(0); }
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((double)sumSqX / _len);
double stdDevY = Math.Sqrt((double)sumSqY / _len);
double _StdDev = stdDevY;
double arrr = (stdDevX * stdDevY != 0) ? (double)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);
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);
}
}
+33
View File
@@ -0,0 +1,33 @@
using Xunit;
using System;
using QuanTAlib;
namespace MovingAvg;
public class ALMA_Test
{
[Fact]
public void Add_Test()
{
TSeries a = new() { 0, 1, 2, 3, 4, 5 };
ALMA_Series c = new(a, 4);
Assert.Equal(6, c.Count);
a.Add(5);
Assert.Equal(a.Count, c.Count);
a.Add(10, update: true);
Assert.Equal(a.Count, c.Count);
}
[Fact]
public void Edge_Test()
{
TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
ALMA_Series c = new(a, 3);
Assert.Equal(a.Count, c.Count);
a.Add(double.NaN);
Assert.Equal(a.Count, c.Count);
a.Add(double.PositiveInfinity);
Assert.Equal(a.Count, c.Count);
}
}
+33
View File
@@ -0,0 +1,33 @@
using Xunit;
using System;
using QuanTAlib;
namespace MovingAvg;
public class MACD_Test
{
[Fact]
public void Add_Test()
{
TSeries a = new() { 0, 1, 2, 3, 4, 5 };
MACD_Series c = new(a, 26,12,9);
Assert.Equal(6, c.Count);
a.Add(5);
Assert.Equal(a.Count, c.Count);
a.Add(0, update: true);
Assert.Equal(a.Count, c.Count);
}
[Fact]
public void Edge_Test()
{
TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
MACD_Series c = new(a, 26,12,9);
Assert.Equal(a.Count, c.Count);
a.Add(double.NaN);
Assert.Equal(a.Count, c.Count);
a.Add(double.PositiveInfinity);
Assert.Equal(a.Count, c.Count);
}
}
+33
View File
@@ -0,0 +1,33 @@
using Xunit;
using System;
using QuanTAlib;
namespace MovingAvg;
public class RSI_Test
{
[Fact]
public void Add_Test()
{
TSeries a = new() { 0, 1, 2, 3, 4, 5 };
RSI_Series c = new(a, 3);
Assert.Equal(6, c.Count);
a.Add(5);
Assert.Equal(a.Count, c.Count);
a.Add(0, update: true);
Assert.Equal(a.Count, c.Count);
}
[Fact]
public void Edge_Test()
{
TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
RSI_Series c = new(a, 3);
Assert.Equal(a.Count, c.Count);
a.Add(double.NaN);
Assert.Equal(a.Count, c.Count);
a.Add(double.PositiveInfinity);
Assert.Equal(a.Count, c.Count);
}
}
+33
View File
@@ -0,0 +1,33 @@
using Xunit;
using System;
using QuanTAlib;
namespace Statistics;
public class LINREG_Test
{
[Fact]
public void Add_Test()
{
TSeries a = new() { 0, 1, 2, 3, 4, 5 };
LINREG_Series c = new(a, 3);
Assert.Equal(6, c.Count);
a.Add(5);
Assert.Equal(a.Count, c.Count);
a.Add(0, update: true);
Assert.Equal(a.Count, c.Count);
}
[Fact]
public void Edge_Test()
{
TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
LINREG_Series c = new(a, 3);
Assert.Equal(a.Count, c.Count);
a.Add(double.NaN);
Assert.Equal(a.Count, c.Count);
a.Add(double.PositiveInfinity);
Assert.Equal(a.Count, c.Count);
}
}
+1
View File
@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
+119 -119
View File
@@ -1,120 +1,120 @@
using Xunit;
using System;
using QuanTAlib;
using Python.Runtime;
using Python.Included;
namespace Validation;
public class PandasTA
{
private readonly RND_Feed bars;
private readonly Random rnd = new();
private readonly int period;
private readonly dynamic ta;
private readonly dynamic df;
public PandasTA()
{
this.bars = new(1000);
this.period = this.rnd.Next(28) + 3;
Installer.SetupPython().Wait();
Installer.TryInstallPip();
Installer.PipInstallModule("numpy");
Installer.PipInstallModule("pandas");
Installer.PipInstallModule("pandas-ta");
PythonEngine.Initialize();
this.ta = Py.Import("pandas_ta");
this.df = this.ta.DataFrame(this.bars.Close.v);
}
~PandasTA()
{
PythonEngine.Shutdown();
}
[Fact]
void SMA()
{
SMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.sma(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
/*
[Fact]
void EMA()
{
EMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.ema(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void TEMA()
{
TEMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.tema(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void ENTP()
{
ENTP_Series QL = new(this.bars.Close, this.period, useNaN:false);
var pta = this.ta.entropy(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void WMA()
{
WMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.wma(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void DEMA()
{
DEMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.dema(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void BIAS()
{
BIAS_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.bias(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void KURT()
{
KURT_Series QL = new(this.bars.Close, this.period, useNaN: false);
var pta = this.ta.kurtosis(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 4), Math.Round(QL.Last().v, 4));
}
[Fact]
void MAD()
{
MAD_Series QL = new(this.bars.Close, this.period, useNaN: false);
var pta = this.ta.mad(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
*/
using Xunit;
using System;
using QuanTAlib;
using Python.Runtime;
using Python.Included;
namespace Validation;
public class PandasTA
{
private readonly RND_Feed bars;
private readonly Random rnd = new();
private readonly int period;
private readonly dynamic ta;
private readonly dynamic df;
public PandasTA()
{
this.bars = new(1000);
this.period = this.rnd.Next(28) + 3;
Installer.SetupPython().Wait();
Installer.TryInstallPip();
Installer.PipInstallModule("numpy");
Installer.PipInstallModule("pandas");
Installer.PipInstallModule("pandas-ta");
PythonEngine.Initialize();
this.ta = Py.Import("pandas_ta");
this.df = this.ta.DataFrame(this.bars.Close.v);
}
~PandasTA()
{
PythonEngine.Shutdown();
}
[Fact]
void SMA()
{
SMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.sma(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
/*
[Fact]
void EMA()
{
EMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.ema(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void TEMA()
{
TEMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.tema(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void ENTP()
{
ENTP_Series QL = new(this.bars.Close, this.period, useNaN:false);
var pta = this.ta.entropy(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void WMA()
{
WMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.wma(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void DEMA()
{
DEMA_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.dema(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void BIAS()
{
BIAS_Series QL = new(this.bars.Close, this.period, false);
var pta = this.ta.bias(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
[Fact]
void KURT()
{
KURT_Series QL = new(this.bars.Close, this.period, useNaN: false);
var pta = this.ta.kurtosis(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 4), Math.Round(QL.Last().v, 4));
}
[Fact]
void MAD()
{
MAD_Series QL = new(this.bars.Close, this.period, useNaN: false);
var pta = this.ta.mad(close: this.df[0], length: this.period);
Assert.Equal(System.Math.Round((double)pta.tail(1), 7), Math.Round(QL.Last().v, 7));
}
*/
}
+47
View File
@@ -99,6 +99,14 @@ public class Skender_Stock
Assert.Equal(Math.Round((double)SK.Last().Atr!, 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void CCI()
{
CCI_Series QL = new(this.bars, this.period, false);
var SK = this.quotes.GetCci(this.period);
Assert.Equal(Math.Round((double)SK.Last().Cci!, 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void ATRP()
@@ -126,4 +134,43 @@ public class Skender_Stock
Assert.Equal(Math.Round((double)SK.Last().Smma!, 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void MACD()
{
MACD_Series QL = new(this.bars.Close, 26,12,9, useNaN: false);
var SK = this.quotes.GetMacd(12,26,9);
Assert.Equal(Math.Round((double)SK.Last().Macd!, 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void RSI()
{
RSI_Series QL = new(this.bars.Close, this.period, useNaN: false);
var SK = this.quotes.GetRsi(this.period);
Assert.Equal(Math.Round((double)SK.Last().Rsi!, 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void ALMA()
{
ALMA_Series QL = new(this.bars.Close, this.period, useNaN: false);
var SK = this.quotes.GetAlma(this.period);
Assert.Equal(Math.Round((double)SK.Last().Alma!, 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void LINREG()
{
LINREG_Series QL = new(this.bars.Close, this.period, useNaN: false);
var SK = this.quotes.GetSlope(this.period);
Assert.Equal(Math.Round((double)SK.Last().Slope!, 8), Math.Round(QL.Last().v, 8));
Assert.Equal(Math.Round((double)SK.Last().Intercept!, 8), Math.Round(QL.Intercept.Last().v, 8));
Assert.Equal(Math.Round((double)SK.Last().RSquared!, 8), Math.Round(QL.RSquared.Last().v, 8));
Assert.Equal(Math.Round((double)SK.Last().StdDev!, 8), Math.Round(QL.StdDev.Last().v, 8));
}
}
+28
View File
@@ -101,4 +101,32 @@ public class TA_LIB
Assert.Equal(Math.Round(this.TALIB[this.TALIB.Length - outBegIdx - 1], 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void CCI()
{
CCI_Series QL = new(this.bars, this.period, false);
Core.Cci(this.inhigh, this.inlow, this.inclose, 0, this.bars.Count - 1, this.TALIB, out int outBegIdx, out _, this.period);
Assert.Equal(Math.Round(this.TALIB[this.TALIB.Length - outBegIdx - 1], 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void RSI()
{
RSI_Series QL = new(this.bars.Close, this.period, false);
Core.Rsi(this.inclose, 0, this.bars.Count - 1, this.TALIB, out int outBegIdx, out _, this.period);
Assert.Equal(Math.Round(this.TALIB[this.TALIB.Length - outBegIdx - 1], 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void MACD()
{
double[] macdSignal = new double[this.bars.Count];
double[] macdHist = new double[this.bars.Count];
MACD_Series QL = new(this.bars.Close, slow: 26, fast: 12, signal: 9, false);
Core.Macd(this.inclose, 0, this.bars.Count - 1, outMacd: this.TALIB, outMacdSignal: macdSignal, outMacdHist: macdHist, out int outBegIdx, out _);
Assert.Equal(Math.Round(this.TALIB[this.TALIB.Length - outBegIdx - 1], 8), Math.Round(QL.Last().v, 8));
}
}