Refactor (#20)

This commit is contained in:
Miha Kralj
2023-04-27 22:37:49 -07:00
committed by GitHub
146 changed files with 4841 additions and 4215 deletions
-25
View File
@@ -1,25 +0,0 @@
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);
}
}
-37
View File
@@ -1,37 +0,0 @@
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);
}
}
-25
View File
@@ -1,25 +0,0 @@
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);
}
}
-35
View File
@@ -1,35 +0,0 @@
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);
}
}
-34
View File
@@ -1,34 +0,0 @@
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);
}
}
+78 -70
View File
@@ -1,74 +1,82 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Title>QuanTAlib</Title>
<Version>0.2.0</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>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Title>QuanTAlib</Title>
<Version>0.2.0</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>
<AssemblyVersion>0.2.1.0</AssemblyVersion>
<FileVersion>0.2.1.0</FileVersion>
<InformationalVersion>0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d</InformationalVersion>
<SuppressNETSdkWarningProperty>NETSDK1057</SuppressNETSdkWarningProperty>
</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>
<Version>0.2.1-dev.2</Version>
</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>
</ItemGroup>
</PackageTags>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageLicenseFile>
</PackageLicenseFile>
<AssemblyVersion>0.2.1.0</AssemblyVersion>
<FileVersion>0.2.1.0</FileVersion>
<InformationalVersion>0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d</InformationalVersion>
<SuppressNETSdkWarningProperty>NETSDK1057</SuppressNETSdkWarningProperty>
<SuppressNETSdkWarningProperty>IDE1006</SuppressNETSdkWarningProperty>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<NoWarn>$(NoWarn);NETSDK1057</NoWarn>
</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>
<Version>0.2.1-dev.2</Version>
</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>
</ItemGroup>
</Project>
@@ -44,7 +44,7 @@ public abstract class Single_TBars_Indicator : TSeries
// 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 virtual new 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);
@@ -23,29 +23,32 @@ public abstract class Single_TSeries_Indicator : TSeries
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;
}
// 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
// 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);
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);
// potentially overridable Add() method for the whole series (could be replaced with faster bulk algo)
public virtual new 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);
-136
View File
@@ -1,136 +0,0 @@
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 => "HLC3",
8 => "OHLC4",
_ => "HLCC4",
};
}
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);
}
}
}
-63
View File
@@ -1,63 +0,0 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
/* <summary>
TSeries is the cornerstone of all QuanTAlib classes.
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 TSeriesEventArgs : EventArgs{
public bool update { get; set; }
}
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 (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (update) { this[^1] = TValue; }
else { base.Add(TValue); }
OnEvent(update);
return TValue;
}
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 {
this.Add(ss[^1], e.update);
}
}
}
+1 -1
View File
@@ -81,7 +81,7 @@ public class EQUITY_Series : Single_TSeries_Indicator {
//Console.WriteLine($"{TValue.v,3}\t {(_inmarket)} : {_cash,10:f2} + {_units*_price[this.Count-1].v,7:f2} = {_equity-_capital:f2}");
}
inmarket.Add(TValue.t, (double)_inmarket);
inmarket.Add((TValue.t, (double)_inmarket));
base.Add((TValue.t, _equity), update, _NaN);
}
}
-34
View File
@@ -1,34 +0,0 @@
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);
}
}
-39
View File
@@ -1,39 +0,0 @@
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 readonly 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);
}
}
-44
View File
@@ -1,44 +0,0 @@
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);
}
}
@@ -1,57 +0,0 @@
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);
}
}
-38
View File
@@ -1,38 +0,0 @@
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);
}
}
-42
View File
@@ -1,42 +0,0 @@
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);
}
}
-44
View File
@@ -1,44 +0,0 @@
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);
}
}
-33
View File
@@ -1,33 +0,0 @@
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);
}
}
-39
View File
@@ -1,39 +0,0 @@
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=0, 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);
}
}
-33
View File
@@ -1,33 +0,0 @@
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);
}
}
-39
View File
@@ -1,39 +0,0 @@
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);
}
}
-38
View File
@@ -1,38 +0,0 @@
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);
}
}
-38
View File
@@ -1,38 +0,0 @@
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);
}
}
-40
View File
@@ -1,40 +0,0 @@
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);
}
}
-46
View File
@@ -1,46 +0,0 @@
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);
}
}
-63
View File
@@ -1,63 +0,0 @@
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/
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);
}
}
-72
View File
@@ -1,72 +0,0 @@
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;
_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 = Double.IsNaN(_ema1)?_lastema1:_ema1;
_lastema2 = Double.IsNaN(_ema2)?_lastema2:_ema2;
base.Add((TValue.t, _dema), update, _NaN);
}
}
-33
View File
@@ -1,33 +0,0 @@
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 {
private readonly System.Collections.Generic.List<double> _buffer1 = new();
private readonly System.Collections.Generic.List<double> _weights = new();
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); }
}
public override void Add((System.DateTime t, double v) TValue, bool update) {
Add_Replace_Trim(_buffer1, TValue.v, _p, update);
double _wma1 = 0, _wsum = 0;
for (int i = 0; i < _buffer1.Count; i++) {
_wma1 += _buffer1[i] * _weights[i];
_wsum += _weights[i];
}
_wma1 /= _wsum;
base.Add((TValue.t, _wma1), update, _NaN);
}
}
-71
View File
@@ -1,71 +0,0 @@
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 = Double.IsNaN(_ema)?_lastema:_ema;
base.Add((TValue.t, _ema), update, _NaN);
}
public void Reset() {
_sum = _oldsum = _lastema = _lastlastema = 0;
_len = 0;
}
}
-59
View File
@@ -1,59 +0,0 @@
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);
}
}
-57
View File
@@ -1,57 +0,0 @@
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);
}
}
-119
View File
@@ -1,119 +0,0 @@
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);
}
}
-64
View File
@@ -1,64 +0,0 @@
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);
}
}
+122 -83
View File
@@ -15,105 +15,144 @@ Sources:
</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;
readonly double 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)
{
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, 5, useNaN) {
fastl = fastlimit;
slowl = slowlimit;
Fama = new TSeries();
if (_data.Count > 0) {
base.Add(_data);
}
}
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;
private double sumPr, jI, jQ;
private readonly double 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; }
// 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;
public override void Add((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;
}
// 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;
var i = Count;
pr.i = TValue.v;
if (i > 5) {
var adj = 0.075 * pd.i1 + 0.54;
// 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;
// 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;
// phasor addition for 3-bar averaging
i2.i = i1.i - jQ;
q2.i = q1.i + jI;
// 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;
i2.i = (0.2 * i2.i) + (0.8 * i2.i1); // smoothing it
q2.i = (0.2 * q2.i) + (0.8 * q2.i1);
// 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;
// homodyne discriminator
re.i = (i2.i * i2.i1) + (q2.i * q2.i1);
im.i = (i2.i * q2.i1) - (q2.i * i2.i1);
// phasor addition for 3-bar averaging
i2.i = i1.i - jQ;
q2.i = q1.i + jI;
re.i = (0.2 * re.i) + (0.8 * re.i1); // smoothing it
im.i = (0.2 * im.i) + (0.8 * im.i1);
i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it
q2.i = 0.2 * q2.i + 0.8 * q2.i1;
// calculate period
pd.i = (im.i != 0 && re.i != 0) ? (6.283185307179586 / Math.Atan(im.i / re.i)) : 0d;
// homodyne discriminator
re.i = i2.i * i2.i1 + q2.i * q2.i1;
im.i = i2.i * q2.i1 - q2.i * i2.i1;
// 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;
re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it
im.i = 0.2 * im.i + 0.8 * im.i1;
// smooth the period
pd.i = (0.2 * pd.i) + (0.8 * pd.i1);
// calculate period
pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d;
// determine phase position
ph.i = (i1.i != 0) ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
// 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;
// change in phase
double delta = Math.Max(ph.i1 - ph.i, 1d);
// smooth the period
pd.i = 0.2 * pd.i + 0.8 * pd.i1;
// adaptive alpha value
double alpha = Math.Max(fastl / delta, slowl);
// determine phase position
ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
// 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);
}
// change in phase
var delta = Math.Max(ph.i1 - ph.i, 1d);
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);
}
// adaptive alpha value
var 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, Count < _p - 1 && _NaN ? double.NaN : fama.i);
Fama.Add(result, update);
}
}
-56
View File
@@ -1,56 +0,0 @@
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);
}
}
-44
View File
@@ -1,44 +0,0 @@
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;
}
}
-51
View File
@@ -1,51 +0,0 @@
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);
}
}
-100
View File
@@ -1,100 +0,0 @@
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". Tillsons 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/
</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 readonly 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);
}
}
-70
View File
@@ -1,70 +0,0 @@
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);
}
}
-43
View File
@@ -1,43 +0,0 @@
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);
}
}
-75
View File
@@ -1,75 +0,0 @@
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.
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 readonly 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);
}
}
-35
View File
@@ -1,35 +0,0 @@
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);
}
}
-62
View File
@@ -1,62 +0,0 @@
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);
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ public class ATRP_Series : Single_TBars_Indicator {
public ATRP_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) {
_period = period;
_k = 1.0 / (double)(_p);
_k = 1.0 / (double)(_period);
_lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0;
if (this._bars.Count > 0) { base.Add(this._bars); }
}
-46
View File
@@ -1,46 +0,0 @@
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);
}
}
-78
View File
@@ -1,78 +0,0 @@
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);
}
}
+107
View File
@@ -0,0 +1,107 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Linq;
/* <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/
Discrepancy with Pandas-TA (but passes the validation with Skender.GetAlma)
</summary> */
public class ALMA_Series : TSeries {
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly System.Collections.Generic.List<double> _weight = new();
private double _norm;
private readonly double _offset, _sigma;
//core constructors
public ALMA_Series(int period, double offset, double sigma, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"ALMA({period})";
_offset = offset;
_sigma = sigma;
_weight = new();
}
public ALMA_Series(TSeries source, int period, double offset, double sigma, bool useNaN) : this(period, offset, sigma, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public ALMA_Series() : this(period:0, offset:0.85, sigma:6.0, useNaN: false) { }
public ALMA_Series(int period) : this(period: period, offset:0.85, sigma:6.0, useNaN:false) { }
public ALMA_Series(TBars source) : this(source:source.Close, period:0, offset:0.85, sigma:6.0, useNaN:false) { }
public ALMA_Series(TBars source, int period) : this(source:source.Close, period:period, offset: 0.85, sigma: 6.0, useNaN: false) { }
public ALMA_Series(TBars source, int period, double offset, double sigma, bool useNaN) : this(source.Close, period:period, offset: offset, sigma: sigma, useNaN: false) { }
public ALMA_Series(TSeries source) : this(source, period:0, offset:0.85, sigma:6.0, useNaN:false) { }
public ALMA_Series(TSeries source, int period) : this(source:source, period:period, offset:0.85, sigma:6.0, useNaN:false) { }
public ALMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: useNaN) { }
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) {
BufferTrim(_buffer, TValue.v, _period, update);
if (_weight.Count < _buffer.Count) {
for (int i = 0; i < (_buffer.Count - _weight.Count); i++) { _weight.Add(0.0); }
}
if (this._buffer.Count <= _period || _period ==0) {
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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma);
return base.Add(res, update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
_weight.Clear();
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
}
+75
View File
@@ -0,0 +1,75 @@
namespace QuanTAlib;
using System;
/* <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 : TSeries {
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private readonly SMA_Series _sma;
//core constructors
public BIAS_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"BIAS({period})";
_sma = new(period, false);
}
public BIAS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public BIAS_Series() : this(period: 0, useNaN: false) { }
public BIAS_Series(int period) : this(period: period, useNaN: false) { }
public BIAS_Series(TBars source) : this(source.Close, 0, false) { }
public BIAS_Series(TBars source, int period) : this(source.Close, period, false) { }
public BIAS_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public BIAS_Series(TSeries source) : this(source, 0, false) { }
public BIAS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
var _s = _sma.Add(TValue,update);
double _bias = (TValue.v / ((_s.v!=0)?_s.v:1)) - 1;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _bias);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_sma.Reset();
}
}
+92
View File
@@ -0,0 +1,92 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buff_up = new();
private readonly System.Collections.Generic.List<double> _buff_dn = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private double _plast_value, _last_value;
//core constructors
public CMO_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"CMO({period})";
}
public CMO_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public CMO_Series() : this(period: 0, useNaN: false) { }
public CMO_Series(int period) : this(period: period, useNaN: false) { }
public CMO_Series(TBars source) : this(source.Close, 0, false) { }
public CMO_Series(TBars source, int period) : this(source.Close, period, false) { }
public CMO_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public CMO_Series(TSeries source) : this(source, 0, false) { }
public CMO_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (update) { _last_value = _plast_value; } else { _plast_value = _last_value; }
BufferTrim(buffer:_buff_up, (TValue.v > _last_value) ? TValue.v - _last_value : 0, period:_period, update: update);
BufferTrim(buffer: _buff_dn, (TValue.v < _last_value) ? _last_value - TValue.v : 0, period: _period, update: 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; }
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _cmo);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buff_up.Clear();
_buff_dn.Clear();
}
}
+74
View File
@@ -0,0 +1,74 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <summary>
CUSUM: 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 CUSUM_Series : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public CUSUM_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"CUSUM({period})";
}
public CUSUM_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public CUSUM_Series() : this(period: 0, useNaN: false) { }
public CUSUM_Series(int period) : this(period: period, useNaN: false) { }
public CUSUM_Series(TBars source) : this(source.Close, 0, false) { }
public CUSUM_Series(TBars source, int period) : this(source.Close, period, false) { }
public CUSUM_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public CUSUM_Series(TSeries source) : this(source, 0, false) { }
public CUSUM_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
double _sum = 0;
for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; }
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sum);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+86
View File
@@ -0,0 +1,86 @@
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 : TSeries {
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private readonly bool _exp;
private double _pdecay, _ppdecay;
private readonly double _dfactor;
//core constructors
public DECAY_Series(int period, bool exponential, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"DECAY({period})";
_exp = exponential;
_dfactor = (_exp) ? 1.0 - 1.0 / (double)_period : 1 / (double)_period;
_pdecay = _ppdecay = 0;
}
public DECAY_Series(TSeries source, int period, bool exponential, bool useNaN) : this(period, exponential, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public DECAY_Series() : this(period: 0, exponential: false, useNaN: false) { }
public DECAY_Series(int period) : this(period: period, exponential: false, useNaN: false) { }
public DECAY_Series(TBars source) : this(source.Close, period: 0, exponential: false, useNaN: false) { }
public DECAY_Series(TBars source, int period) : this(source.Close, period: period, exponential:false, useNaN:false) { }
public DECAY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, exponential: false, useNaN) { }
public DECAY_Series(TSeries source) : this(source, period: 0, exponential: false, useNaN:false) { }
public DECAY_Series(TSeries source, int period) : this(source: source, period: period, exponential: false, useNaN: false) { }
public DECAY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, exponential: false, useNaN: useNaN) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN), 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _decay);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_pdecay = _ppdecay = 0;
}
}
+131
View File
@@ -0,0 +1,131 @@
namespace QuanTAlib;
using System;
using System.Linq;
/* <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 : TSeries {
private double _k;
private double _sum, _oldsum;
private double _lastema1, _oldema1, _lastema2, _oldema2;
private int _len;
private readonly bool _useSMA;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructor
public DEMA_Series(int period, bool useNaN, bool useSMA) : base() {
_period = period;
_NaN = useNaN;
_useSMA = useSMA;
Name = $"DEMA({period})";
_k = 2.0 / (_period + 1);
_len = 0;
_sum = _oldsum = _lastema1 = _lastema2 = 0;
}
//generic constructors (source)
public DEMA_Series() : this(0, false, true) {}
public DEMA_Series(int period) : this(period, false, true) {}
public DEMA_Series(TBars source) : this(source.Close, 0, false) {}
public DEMA_Series(TBars source, int period) : this(source.Close, period, false) {}
public DEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
public DEMA_Series(TSeries source, int period) : this(source, period, false, true) {}
public DEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
public DEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (update) {
_lastema1 = _oldema1;
_lastema2 = _oldema2;
_sum = _oldsum;
}
else {
_oldema1 = _lastema1;
_oldema2 = _lastema2;
_oldsum = _sum;
_len++;
}
if (_period == 0) {
_k = 2.0 / (_len + 1);
}
double _ema1, _ema2, _dema;
if (Count == 0) {
_ema1 = _ema2 = _sum = TValue.v;
}
else if (_len <= _period && _useSMA && _period != 0) {
_sum += TValue.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 = double.IsNaN(_ema1) ? _lastema1 : _ema1;
_lastema2 = double.IsNaN(_ema2) ? _lastema2 : _ema2;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dema);
return base.Add(res, update);
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) {
return (DateTime.Today, double.NaN);
}
foreach (var item in data) {
Add(item, false);
}
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return Add(_data.Last, update);
}
public (DateTime t, double v) Add() {
return Add(_data.Last, false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(_data.Last, e.update);
}
//reset calculation
public override void Reset() {
_sum = _oldsum = _lastema1 = _lastema2 = 0;
_len = 0;
}
}
+126
View File
@@ -0,0 +1,126 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/* <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 : TSeries {
private readonly List<double> _buffer = new();
private List<double> _weights = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
protected int _len;
//core constructors
public DWMA_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"DWMA({period})";
_len = 0;
_weights = CalculateWeights(_period);
}
public DWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public DWMA_Series() : this(0, false) {
}
public DWMA_Series(int period) : this(period, false) {
}
public DWMA_Series(TBars source) : this(source.Close, 0, false) {
}
public DWMA_Series(TBars source, int period) : this(source.Close, period, false) {
}
public DWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {
}
public DWMA_Series(TSeries source, int period) : this(source, period, false) {
}
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(_buffer, TValue.v, _period, update);
if (_period == 0) {
_len++;
_weights = CalculateWeights(_len);
}
double _dwma = 0, _wsum = 0;
var bufferCount = _buffer.Count;
var lockObj = new object();
Parallel.For(0, bufferCount, i =>
{
var temp = _buffer[i] * _weights[i];
lock (lockObj) {
_dwma += temp;
_wsum += _weights[i];
}
});
_dwma /= _wsum;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dwma);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) {
return (DateTime.Today, double.NaN);
}
foreach (var item in data) {
Add(item, false);
}
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return Add(_data.Last, update);
}
public (DateTime t, double v) Add() {
return Add(_data.Last, false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(_data.Last, e.update);
}
//calculating weights
private static List<double> CalculateWeights(int period) {
var weights = new List<double>(period);
for (var i = 0; i < period; i++) {
weights.Add((i + 1) * (i + 1));
}
return weights;
}
//reset calculation
public override void Reset() {
_len = 0;
_buffer.Clear();
_weights = CalculateWeights(_period);
}
}
+123
View File
@@ -0,0 +1,123 @@
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 : TSeries {
private double _k;
private double _lastema, _oldema;
private double _sum, _oldsum;
private int _len;
private readonly bool _useSMA;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public EMA_Series(int period, bool useNaN, bool useSMA) : base() {
_period = period;
_NaN = useNaN;
_useSMA = useSMA;
Name = $"EMA({period})";
_k = 2.0 / (_period + 1);
_len = 0;
_sum = _oldsum = _lastema = _oldema = 0;
}
public EMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public EMA_Series() : this(0, false, true) {}
public EMA_Series(int period) : this(period, false, true) {}
public EMA_Series(TBars source) : this(source.Close, 0, false) {}
public EMA_Series(TBars source, int period) : this(source.Close, period, false) {}
public EMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
public EMA_Series(TSeries source, int period) : this(source, period, false, true) {}
public EMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
if (update) {
_lastema = _oldema;
_sum = _oldsum;
}
else {
_oldema = _lastema;
_oldsum = _sum;
_len++;
}
double _ema = 0;
if (_period == 0) {
_k = 2.0 / (_len + 1);
}
if (Count == 0) {
_ema = _sum = TValue.v;
}
else if (_len <= _period && _useSMA && _period != 0) {
_sum += TValue.v;
if (_period != 0 && _len > _period) {
_sum -= _data[Count - _period - (update ? 1 : 0)].v;
}
_ema = _sum / Math.Min(_len, _period);
}
else {
_ema = _k * (TValue.v - _lastema) + _lastema;
}
_lastema = double.IsNaN(_ema) ? _lastema : _ema;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ema);
return base.Add(res, update);
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_sum = _oldsum = _lastema = _oldema = 0;
_len = 0;
}
}
+90
View File
@@ -0,0 +1,90 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Linq;
/* <summary>
ENTROPY:
Introduced by Claude Shannon in 1948, entropy measures the unpredictability
of the data, or equivalently, of its average information.
Calculation:
P = close / Σ(close)
ENTROPY = Σ(-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 : TSeries {
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private readonly double _logbase;
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly System.Collections.Generic.List<double> _buff2 = new();
//core constructors
public ENTROPY_Series(int period, double logbase, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
_logbase = logbase;
Name = $"ENTROPY({period})";
}
public ENTROPY_Series(TSeries source, int period, double logbase, bool useNaN) : this(period, logbase, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public ENTROPY_Series() : this(period: 0, logbase: 2.0, useNaN: false) { }
public ENTROPY_Series(int period) : this(period: period, logbase: 2.0, useNaN: false) { }
public ENTROPY_Series(TBars source) : this(source.Close, period: 0, logbase: 2.0, useNaN: false) { }
public ENTROPY_Series(TBars source, int period) : this(source.Close, period, logbase: 2.0, useNaN: false) { }
public ENTROPY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, logbase: 2.0, useNaN: useNaN) { }
public ENTROPY_Series(TSeries source) : this(source, period: 0, logbase: 2.0, useNaN: false) { }
public ENTROPY_Series(TSeries source, int period) : this(source: source, period: period, logbase: 2.0, useNaN: false) { }
public ENTROPY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, logbase: 2.0, useNaN: useNaN) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN), update);
}
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
double _sum = _buffer.Sum();
double _pp = this._buffer[^1] / _sum;
double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase);
BufferTrim(_buff2, _ppp, _period, update);
double _entp = _buff2.Sum();
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _entp);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
_buff2.Clear();
}
}
+100
View File
@@ -0,0 +1,100 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Numerics;
using System.Linq;
/* <summary>
FWMA: Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average
(WMA) where the weights are based on the Fibonacci Sequence.
</summary> */
public class FWMA_Series : TSeries {
private readonly List<double> _buffer = new();
private List<double> _weights = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
protected int _len;
public FWMA_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"FWMA({period})";
_len = 0;
_weights = CalculateWeights(_period);
}
public FWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public FWMA_Series() : this(period: 0, useNaN: false) { }
public FWMA_Series(int period) : this(period: period, useNaN: false) { }
public FWMA_Series(TBars source) : this(source.Close, 0, false) { }
public FWMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public FWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public FWMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
if (_period == 0) {
_len++;
_weights = CalculateWeights(_len);
}
double _fwma = 0;
double totalWeights = _weights.Sum();
object lockObj = new object();
Parallel.For(0, _buffer.Count, i =>
{
double temp = _buffer[i] * _weights[i];
lock (lockObj) { _fwma += temp; }
});
_fwma /= totalWeights;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fwma);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
private static List<double> CalculateWeights(int period) {
//to prevent overflow, max period can be no more than 1476
period = (period > 1476) ? 1476 : period;
List<double> weights = new List<double>(period);
BigInteger a = 0;
BigInteger b = 1;
for (int i = 0; i < period; i++) {
BigInteger temp = a;
a = b;
b = temp + b;
weights.Add((double)Decimal.Parse(a.ToString()));
}
return weights;
}
public override void Reset() {
_weights = CalculateWeights(_period);
_buffer.Clear();
}
}
+116
View File
@@ -0,0 +1,116 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private double _k1, _k2, _k3;
private int _len;
private double _lastema1, _oldema1;
private double _lastema2, _oldema2;
private double _lasthema, _oldhema;
//core constructors
public HEMA_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"HEMA({period})";
CalculateK(_period, out _k1, out _k2, out _k3);
_len = 0;
_lastema1 = _oldema1 = _lastema2 = _oldema2 = _lasthema = _oldhema = 0;
}
public HEMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public HEMA_Series() : this(period: 0, useNaN: false) { }
public HEMA_Series(int period) : this(period: period, useNaN: false) { }
public HEMA_Series(TBars source) : this(source.Close, 0, false) { }
public HEMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public HEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public HEMA_Series(TSeries source) : this(source, 0, false) { }
public HEMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (update) {
_lastema1 = _oldema1;
_lastema2 = _oldema2;
_lasthema = _oldhema;
}
else {
_oldema1 = _lastema1;
_oldema2 = _lastema2;
_oldhema = _lasthema;
}
double _ema1, _ema2, _hema;
if (_period == 0) {
_len++;
CalculateK(_len, out _k1, out _k2, out _k3);
}
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, double.NaN), update);
} else if (this.Count == 0) {
_ema1 = _ema2 = _hema = TValue.v;
}
else {
_ema1 = _k1 * (TValue.v - _lastema1) + _lastema1;
_ema2 = _k2 * (TValue.v - _lastema2) + _lastema2;
_hema = _k3 * (((2 * _ema1) - _ema2) - _lasthema) + _lasthema;
}
_lastema1 = _ema1;
_lastema2 = _ema2;
_lasthema = _hema;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hema);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_lastema1 = _lastema2 = _lasthema = 0;
_oldema1 = _oldema2 = _oldhema = 0;
_len = 0;
}
public static void CalculateK(int len, out double k1, out double k2, out double k3) {
k1 = 8 / (double)(len + 7);
k2 = 3 / (double)(len + 2);
k3 = 2 / Math.Sqrt(len + 3);
}
}
+91
View File
@@ -0,0 +1,91 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 {
protected int _period, _period2, _psqrt;
protected readonly bool _NaN;
protected readonly TSeries _data;
protected WMA_Series _wma1, _wma2, _wma3;
//core constructors
public HMA_Series(int period, bool useNaN) : base() {
_period = period;
_period2 = period /2;
_psqrt = (int)Math.Sqrt(period);
_NaN = useNaN;
_wma1 = new(Math.Max(_period2,1), false);
_wma2 = new(Math.Max(_period,1), false);
_wma3 = new(Math.Max(_psqrt,1), useNaN);
Name = $"HMA({period})";
}
public HMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public HMA_Series() : this(period: 0, useNaN: false) { }
public HMA_Series(int period) : this(period: period, useNaN: false) { }
public HMA_Series(TBars source) : this(source.Close, 0, false) { }
public HMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public HMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public HMA_Series(TSeries source) : this(source, 0, false) { }
public HMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (_period == 0) {
_wma1.Len = this.Count / 2;
_wma2.Len = this.Count;
_wma1.Len = (int)Math.Sqrt(this.Count);
}
double _w1 = _wma1.Add(TValue, update).v;
double _w2 = _wma2.Add(TValue, update).v;
double _hma = _wma3.Add((2 * _w1) - _w2, update).v;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hma);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_wma1.Reset();
_wma2.Reset();
_wma3.Reset();
}
}
@@ -1,5 +1,6 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Linq;
/* <summary>
@@ -18,38 +19,51 @@ Issues:
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 {
</summary> */
public class JMA_Series : TSeries {
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
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;
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) {
//core constructors
public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"JMA({period})";
upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 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) {
double del1 = 0.0, del2 = 0.0;
public JMA_Series(TSeries source, int period, double phase, int vshort, int vlong, bool useNaN) : this(period, phase, vshort, vlong, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public JMA_Series() : this(period: 0, phase: 0, vshort:10, vlong:65, useNaN: false) { }
public JMA_Series(int period) : this(period: period, phase: 0, vshort: 10, vlong: 65, useNaN: false) { }
public JMA_Series(TBars source) : this(source.Close, period:0, phase:0.0, vshort:10, vlong:65, useNaN:false) { }
public JMA_Series(TBars source, int period) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
public JMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { }
public JMA_Series(TSeries source) : this(source, period:0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
public JMA_Series(TSeries source, int period) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
public JMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; }
if (update) {
upperBand = p_upperBand;
@@ -72,9 +86,13 @@ public class JMA_Series : Single_TSeries_Indicator {
p_prev_jma = prev_jma;
}
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, double.NaN),update);
}
// from Tvalue to volty
del1 = TValue.v - upperBand;
del2 = TValue.v - lowerBand;
double del1 = TValue.v - upperBand;
double 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;
@@ -96,7 +114,7 @@ public class JMA_Series : Single_TSeries_Indicator {
/// from avolty to rolty
double rvolty = (avolty != 0) ? volty / avolty : 0;
double len1 = (Math.Log(Math.Sqrt(_p)) / Math.Log(2.0)) + 2;
double len1 = (Math.Log(Math.Sqrt(_period)) / Math.Log(2.0)) + 2;
if (len1 < 0) { len1 = 0; }
double pow1 = Math.Max(len1 - 2.0, 0.5);
@@ -105,23 +123,45 @@ public class JMA_Series : Single_TSeries_Indicator {
//// from rvolty to second smoothing
double pow2 = Math.Pow(rvolty, pow1);
double beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
double beta = 0.45 * (_period - 1) / (0.45 * (_period - 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : jma);
return base.Add(res, update);
}
base.Add((TValue.t, jma), update, _NaN);
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0;
}
}
+114
View File
@@ -0,0 +1,114 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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.
KAMA[i] = KAMA[i-1] + SC * ( price - KAMA[i-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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
private double _lastkama, _lastlastkama;
private int _len;
private readonly double _scFast, _scSlow;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public KAMA_Series(int period, int fast, int slow, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
_len = 0;
_scFast = 2.0 / (((period < fast) ? period : fast) + 1);
_scSlow = 2.0 / (slow + 1);
_lastkama = _lastlastkama = 0;
Name = $"KAMA({period})";
}
public KAMA_Series(TSeries source, int period, int fast, int slow, bool useNaN) : this(period, fast, slow, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public KAMA_Series() : this(period: 0, fast: 2, slow: 30, useNaN: false) { }
public KAMA_Series(int period) : this(period: period, fast: 2, slow: 30, useNaN: false) { }
public KAMA_Series(TBars source) : this(source.Close, period: 0, fast: 2, slow: 30, useNaN: false) { }
public KAMA_Series(TBars source, int period) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: false) { }
public KAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: useNaN) { }
public KAMA_Series(TSeries source) : this(source, period: 0, fast: 2, slow: 30, useNaN: false) { }
public KAMA_Series(TSeries source, int period) : this(source: source, period: period, fast: 2, slow: 30, useNaN: false) { }
public KAMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, fast: 2, slow: 30, useNaN: useNaN) { }
public KAMA_Series(TSeries source, int period, int fast, int slow) : this(source: source, period: period, fast: fast, slow: slow, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN), update);
}
if (update) { _lastkama = _lastlastkama; }
else { _lastlastkama = _lastkama; }
BufferTrim(buffer: _buffer, value: TValue.v, period: _period + 1, update: update);
double _kama = 0;
if (this.Count < _period) { _kama = TValue.v; }
else {
double _change = Math.Abs(_buffer[^1] - _buffer[(_buffer.Count > _period + 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)));
}
_len++;
_lastkama = _kama;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kama);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
_len = 0;
_lastkama = _lastlastkama = 0;
}
}
+99
View File
@@ -0,0 +1,99 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Linq;
/* <summary>
KURTOSIS: 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
KURTOSIS = 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 : TSeries {
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private readonly System.Collections.Generic.List<double> _buffer = new();
//core constructors
public KURTOSIS_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"KURTOSIS({period})";
}
public KURTOSIS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public KURTOSIS_Series() : this(period: 0, useNaN: false) { }
public KURTOSIS_Series(int period) : this(period: period, useNaN: false) { }
public KURTOSIS_Series(TSeries source) : this(source, period: 0, useNaN: false) { }
public KURTOSIS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN), update);
}
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
double _n = _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) * _s4) / (_Vx * _Vx * (_n - 3) * (_n - 1) * (_n - 2)) - (3 * (_n - 1) * (_n - 1) / ((_n - 2) * (_n - 3))) //using Sheskin Algo
: (_s2 * _s2) / _n - 3; //using Snedecor and Cochran (1967) algo
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kurt);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+82
View File
@@ -0,0 +1,82 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public MAD_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"MAD({period})";
}
public MAD_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public MAD_Series() : this(period: 0, useNaN: false) { }
public MAD_Series(int period) : this(period: period, useNaN: false) { }
public MAD_Series(TBars source) : this(source.Close, 0, false) { }
public MAD_Series(TBars source, int period) : this(source.Close, period, false) { }
public MAD_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public MAD_Series(TSeries source) : this(source, 0, false) { }
public MAD_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mad);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+88
View File
@@ -0,0 +1,88 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public MAPE_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"MAPE({period})";
}
public MAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public MAPE_Series() : this(period: 0, useNaN: false) { }
public MAPE_Series(int period) : this(period: period, useNaN: false) { }
public MAPE_Series(TBars source) : this(source.Close, 0, false) { }
public MAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
public MAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public MAPE_Series(TSeries source) : this(source, 0, false) { }
public MAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mape);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+71
View File
@@ -0,0 +1,71 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public MAX_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"MAX({period})";
}
public MAX_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public MAX_Series() : this(period: 0, useNaN: false) { }
public MAX_Series(int period) : this(period: period, useNaN: false) { }
public MAX_Series(TBars source) : this(source.Close, 0, false) { }
public MAX_Series(TBars source, int period) : this(source.Close, period, false) { }
public MAX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public MAX_Series(TSeries source) : this(source, 0, false) { }
public MAX_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
double _max= _buffer.Max();
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+89
View File
@@ -0,0 +1,89 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public MEDIAN_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"MEDIAN({period})";
}
public MEDIAN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public MEDIAN_Series() : this(period: 0, useNaN: false) { }
public MEDIAN_Series(int period) : this(period: period, useNaN: false) { }
public MEDIAN_Series(TBars source) : this(source.Close, 0, false) { }
public MEDIAN_Series(TBars source, int period) : this(source.Close, period, false) { }
public MEDIAN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public MEDIAN_Series(TSeries source) : this(source, 0, false) { }
public MEDIAN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _med);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+75
View File
@@ -0,0 +1,75 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public MIDPOINT_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"MIDPOINT({period})";
}
public MIDPOINT_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public MIDPOINT_Series() : this(period: 0, useNaN: false) { }
public MIDPOINT_Series(int period) : this(period: period, useNaN: false) { }
public MIDPOINT_Series(TBars source) : this(source.Close, 0, false) { }
public MIDPOINT_Series(TBars source, int period) : this(source.Close, period, false) { }
public MIDPOINT_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public MIDPOINT_Series(TSeries source) : this(source, 0, false) { }
public MIDPOINT_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
double _max= _buffer.Max();
double _min = _buffer.Min();
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : (_max+_min)*0.5);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+71
View File
@@ -0,0 +1,71 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public MIN_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"MAX({period})";
}
public MIN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public MIN_Series() : this(period: 0, useNaN: false) { }
public MIN_Series(int period) : this(period: period, useNaN: false) { }
public MIN_Series(TBars source) : this(source.Close, 0, false) { }
public MIN_Series(TBars source, int period) : this(source.Close, period, false) { }
public MIN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public MIN_Series(TSeries source) : this(source, 0, false) { }
public MIN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
double _max= _buffer.Min();
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+79
View File
@@ -0,0 +1,79 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public MSE_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"MSE({period})";
}
public MSE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public MSE_Series() : this(period: 0, useNaN: false) { }
public MSE_Series(int period) : this(period: period, useNaN: false) { }
public MSE_Series(TBars source) : this(source.Close, 0, false) { }
public MSE_Series(TBars source, int period) : this(source.Close, period, false) { }
public MSE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public MSE_Series(TSeries source) : this(source, 0, false) { }
public MSE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mse);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+119
View File
@@ -0,0 +1,119 @@
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 : TSeries {
private double _k;
private double _lastrma, _oldrma;
private double _sum, _oldsum;
private readonly bool _useSMA;
private int _len;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructor
public RMA_Series(int period, bool useNaN, bool useSMA) : base() {
_period = period;
_NaN = useNaN;
_useSMA = useSMA;
Name = $"RMA({period})";
_k = 1.0 / (double)(this._period);
_len = 0;
_sum = _oldsum = _lastrma = _oldrma = 0;
}
//generic constructors (source)
public RMA_Series() : this(0, false, true) {}
public RMA_Series(int period) : this(period, false, true) {}
public RMA_Series(TBars source) : this(source.Close, 0, false) {}
public RMA_Series(TBars source, int period) : this(source.Close, period, false) {}
public RMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
public RMA_Series(TSeries source, int period) : this(source, period, false, true) {}
public RMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
public RMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
if (update) {
_lastrma = _oldrma;
_sum = _oldsum;
}
else {
_oldrma = _lastrma;
_oldsum = _sum;
_len++;
}
double _rma = 0;
if (_period == 0) {
_k = 1.0 / (double)(this._len);
}
if (Count == 0) {
_rma = _sum = TValue.v;
} else if (_len <= _period && _useSMA && _period != 0) {
_sum += TValue.v;
if (_period != 0 && _len > _period) {
_sum -= _data[Count - _period - (update ? 1 : 0)].v;
}
_rma = _sum / Math.Min(_len, _period);
}
else {
_rma = _k * (TValue.v - _lastrma) + _lastrma;
}
_lastrma = double.IsNaN(_rma) ? _lastrma : _rma;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rma);
return base.Add(res, update);
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_sum = _oldsum = _lastrma = _oldrma = 0;
_len = 0;
}
}
+123
View File
@@ -0,0 +1,123 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _gain = new();
private readonly System.Collections.Generic.List<double> _loss = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private double _avgGain, _avgLoss, _lastValue;
private double _avgGain_o, _avgLoss_o, _lastValue_o;
private int i;
//core constructors
public RSI_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"RSI({period})";
i = 0;
}
public RSI_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public RSI_Series() : this(period: 0, useNaN: false) { }
public RSI_Series(int period) : this(period: period, useNaN: false) { }
public RSI_Series(TBars source) : this(source.Close, 0, false) { }
public RSI_Series(TBars source, int period) : this(source.Close, period, false) { }
public RSI_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public RSI_Series(TSeries source) : this(source, 0, false) { }
public RSI_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
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;
BufferTrim(_gain, _gainval, _period, update);
double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0;
BufferTrim(_loss, _lossval, _period, update);
_lastValue = TValue.v;
// calculate RSI
if (i > _period && _period != 0) {
_avgGain = ((_avgGain * (_period - 1)) + _gain[^1]) / _period;
_avgLoss = ((_avgLoss * (_period - 1)) + _loss[^1]) / _period;
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 res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rsi);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
i = 0;
}
}
+85
View File
@@ -0,0 +1,85 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public SDEV_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"SDEV({period})";
}
public SDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public SDEV_Series() : this(period: 0, useNaN: false) { }
public SDEV_Series(int period) : this(period: period, useNaN: false) { }
public SDEV_Series(TBars source) : this(source.Close, 0, false) { }
public SDEV_Series(TBars source, int period) : this(source.Close, period, false) { }
public SDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public SDEV_Series(TSeries source) : this(source, 0, false) { }
public SDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
double _sma = _buffer.Average();
double _var = 0;
for (int i = 0; i < _buffer.Count; i++) { _var += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
_var /= this._buffer.Count;
double _sdev = Math.Sqrt(_var);
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sdev);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+80
View File
@@ -0,0 +1,80 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public SMAPE_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"SMAPE({period})";
}
public SMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public SMAPE_Series() : this(period: 0, useNaN: false) { }
public SMAPE_Series(int period) : this(period: period, useNaN: false) { }
public SMAPE_Series(TBars source) : this(source.Close, 0, false) { }
public SMAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
public SMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public SMAPE_Series(TSeries source) : this(source, 0, false) { }
public SMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smape);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+99
View File
@@ -0,0 +1,99 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
private double _sum, _oldsum;
private readonly int _period;
private readonly TSeries _data;
protected readonly bool _NaN;
//core constructor
public SMA_Series(int period, bool useNaN) : base() {
_period = Math.Max(0, period);
_NaN = useNaN;
Name = $"SMA({period})";
_sum = _oldsum = 0;
}
public SMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public SMA_Series() : this(0, false) {}
public SMA_Series(int period) : this(period, false) {}
public SMA_Series(TBars source) : this(source.Close, 0, false) {}
public SMA_Series(TBars source, int period) : this(source.Close, period, false) {}
public SMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
public SMA_Series(TSeries source) : this(source, 0, false) {}
public SMA_Series(TSeries source, int period) : this(source, period, false) {}
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
if (double.IsNaN(TValue.v)) { return (TValue.t, double.NaN);
} else {
if (update && _buffer.Count > 0) {
_sum -= _buffer[^1];
_buffer[^1] = TValue.v;
_oldsum = _sum;
}
else {
_buffer.Add(TValue.v);
_oldsum = _sum;
}
_sum += TValue.v;
if (_period != 0 && _buffer.Count > _period) {
_sum -= _buffer[0];
_buffer.RemoveAt(0);
}
}
double _div = _period == 0 ? _buffer.Count : Math.Min(_buffer.Count, _period);
var _sma = _sum / _div;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sma);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_sum = _oldsum = 0;
_buffer.Clear();
}
}
+96
View File
@@ -0,0 +1,96 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private double _lastsmma, _lastlastsmma;
//core constructors
public SMMA_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"SMMA({period})";
}
public SMMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public SMMA_Series() : this(period: 0, useNaN: false) { }
public SMMA_Series(int period) : this(period: period, useNaN: false) { }
public SMMA_Series(TBars source) : this(source.Close, 0, false) { }
public SMMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public SMMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public SMMA_Series(TSeries source) : this(source, 0, false) { }
public SMMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, double.NaN),update);
}
double _smma = 0;
if (update) { this._lastsmma = this._lastlastsmma; }
if (this.Count < this._period) {
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
_smma = _buffer.Average();
}
else {
_smma = ((_lastsmma * (_period - 1)) + TValue.v) / _period;
}
this._lastlastsmma = this._lastsmma;
this._lastsmma = _smma;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smma);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
this._lastsmma = this._lastlastsmma = 0;
}
}
+85
View File
@@ -0,0 +1,85 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public SSDEV_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"SSDEV({period})";
}
public SSDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public SSDEV_Series() : this(period: 0, useNaN: false) { }
public SSDEV_Series(int period) : this(period: period, useNaN: false) { }
public SSDEV_Series(TBars source) : this(source.Close, 0, false) { }
public SSDEV_Series(TBars source, int period) : this(source.Close, period, false) { }
public SSDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public SSDEV_Series(TSeries source) : this(source, 0, false) { }
public SSDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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);
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ssdev);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+84
View File
@@ -0,0 +1,84 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 SVAR_Series : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public SVAR_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"SVAR({period})";
}
public SVAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public SVAR_Series() : this(period: 0, useNaN: false) { }
public SVAR_Series(int period) : this(period: period, useNaN: false) { }
public SVAR_Series(TBars source) : this(source.Close, 0, false) { }
public SVAR_Series(TBars source, int period) : this(source.Close, period, false) { }
public SVAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public SVAR_Series(TSeries source) : this(source, 0, false) { }
public SVAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _svar);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+164
View File
@@ -0,0 +1,164 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
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". Tillsons 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/
</summary> */
public class T3_Series : TSeries {
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 readonly bool _useSMA;
private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6;
private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6;
protected int _len;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) : base() {
_period = period;
_len = 0;
_NaN = useNaN;
Name = $"T3({period})";
_useSMA = useSMA;
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 / (_period + 1);
_k1m = 1.0 - _k;
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
}
public T3_Series(TSeries source, int period, double vfactor, bool useSMA, bool useNaN) : this(period, vfactor, useSMA, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public T3_Series() : this(period: 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
public T3_Series(int period) : this(period: period, vfactor: 0.7, useSMA: true, useNaN: false) { }
public T3_Series(TBars source) : this(source.Close, 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
public T3_Series(TBars source, int period) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: false) { }
public T3_Series(TBars source, int period, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { }
public T3_Series(TBars source, int period, double vfactor, bool useNaN) : this(source.Close, period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { }
public T3_Series(TBars source, int period, bool useSMA, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: useSMA, useNaN: useNaN) { }
public T3_Series(TSeries source) : this(source, 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
public T3_Series(TSeries source, int period) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: false) { }
public T3_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { }
public T3_Series(TSeries source, int period, double vfactor) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: false) { }
public T3_Series(TSeries source, int period, double vfactor, bool useNaN) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6;
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN),update);
}
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 (_len == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; }
if ((_len < _period) && _useSMA) {
BufferTrim(_buffer1, TValue.v, _period, update);
_ema1 = 0;
for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
_ema1 /= _buffer1.Count;
BufferTrim(_buffer2, _ema1, _period, update);
_ema2 = 0;
for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
_ema2 /= _buffer2.Count;
BufferTrim(_buffer3, _ema2, _period, update);
_ema3 = 0;
for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
_ema3 /= _buffer3.Count;
BufferTrim(_buffer4, _ema3, _period, update);
_ema4 = 0;
for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; }
_ema4 /= _buffer4.Count;
BufferTrim(_buffer5, _ema4, _period, update);
_ema5 = 0;
for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; }
_ema5 /= _buffer5.Count;
BufferTrim(_buffer6, _ema5, _period, 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);
}
_len++;
_lastema1 = _ema1;
_lastema2 = _ema2;
_lastema3 = _ema3;
_lastema4 = _ema4;
_lastema5 = _ema5;
_lastema6 = _ema6;
double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _T3);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
_buffer1.Clear();
_buffer2.Clear();
_buffer3.Clear();
_buffer4.Clear();
_buffer5.Clear();
_buffer6.Clear();
_len = 0;
}
}
+126
View File
@@ -0,0 +1,126 @@
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)>
{
public string Name { get; set; }
private readonly TSeries _open = new("open");
private readonly TSeries _high = new("high");
private readonly TSeries _low = new("low");
private readonly TSeries _close = new("close");
private readonly TSeries _volume = new("volume");
private readonly TSeries _hl2 = new("HL2");
private readonly TSeries _oc2 = new("OC2");
private readonly TSeries _ohl3 = new("OHL3");
private readonly TSeries _hlc3 = new("HLC3");
private readonly TSeries _ohlc4 = new("OHLC4");
private readonly TSeries _hlcc4 = new("HLCC4");
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() { }
public TBars(string Name) {
this.Name = Name;
}
public (DateTime t, double o, double h, double l, double c, double v) Last => this[^1];
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 => "HLC3",
8 => "OHLC4",
_ => "HLCC4",
};
}
public virtual (DateTime t, double o, double h, double l, double c, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) =>
Add((t: (this.Count == 0) ? DateTime.Today : this[^1].t.AddDays(1),p.o,p.h,p.l,p.c,p.v),update);
public virtual (DateTime t, double o, double h, double l, double c, double v) Add(double o, double h, double l, double c, double v, bool update = false) =>
Add((o,h,l,c,v),update);
public virtual (DateTime t, double o, double h, double l, double c, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) =>
this.Add((t, o, h, l, c, v), update);
public virtual (DateTime t, double o, double h, double l, double c, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) {
if (update) { this[^1] = TBar; } else { base.Add(TBar); }
_open.Add((TBar.t, TBar.o), update);
_high.Add((TBar.t, TBar.h), update);
_low.Add((TBar.t, TBar.l), update);
_close.Add((TBar.t, TBar.c), update);
_volume.Add((TBar.t, TBar.v), update);
_hl2.Add((TBar.t, (TBar.h + TBar.l) * 0.5), update);
_oc2.Add((TBar.t, (TBar.o + TBar.c) * 0.5), update);
_ohl3.Add((TBar.t, (TBar.o + TBar.h + TBar.l) * 0.333333333333333), update);
_hlc3.Add((TBar.t, (TBar.h + TBar.l + TBar.c) * 0.333333333333333), update);
_ohlc4.Add((TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25), update);
_hlcc4.Add((TBar.t, (TBar.h + TBar.l + TBar.c + TBar.c) * 0.25), update);
this.OnEvent(update);
return TBar;
}
public delegate void NewDataEventHandler(object source, TSeriesEventArgs args);
public event NewDataEventHandler Pub;
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);
}
}
}
+123
View File
@@ -0,0 +1,123 @@
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 : TSeries {
private double _k;
private double _sum, _oldsum;
private double _lastema1, _oldema1, _lastema2, _oldema2, _lastema3, _oldema3;
private int _len;
private readonly bool _useSMA;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructor
public TEMA_Series(int period, bool useNaN, bool useSMA) : base() {
_period = period;
_NaN = useNaN;
_useSMA = useSMA;
Name = $"TEMA({period})";
_k = 2.0 / (_period + 1);
_len = 0;
_sum = _oldsum = _lastema1 = _lastema2 = _lastema3 = 0;
}
public TEMA_Series() : this(0, false, true) {}
public TEMA_Series(int period) : this(period, false, true) {}
public TEMA_Series(TBars source) : this(source.Close, 0, false) {}
public TEMA_Series(TBars source, int period) : this(source.Close, period, false) {}
public TEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
public TEMA_Series(TSeries source, int period) : this(source, period, false, true) {}
public TEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
public TEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (update) {
_lastema1 = _oldema1;
_lastema2 = _oldema2;
_lastema3 = _oldema3;
_sum = _oldsum;
}
else {
_oldema1 = _lastema1;
_oldema2 = _lastema2;
_oldema3 = _lastema3;
_oldsum = _sum;
_len++;
}
if (_period == 0) { _k = 2.0 / (_len + 1); }
double _ema1, _ema2, _ema3, _tema;
if (this.Count == 0) {
_ema1 = _ema2 = _ema3 =_sum = TValue.v;
}
else if (_len <= _period && _useSMA && _period != 0) {
_sum += TValue.v;
_ema1 = _sum / Math.Min(_len, _period);
_ema2 = _ema1;
_ema3 = _ema2;
}
else {
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
_ema3 = (_ema2 - _lastema3) * _k + _lastema3;
}
_tema = (3 * (_ema1 - _ema2)) + _ema3;
_lastema1 = Double.IsNaN(_ema1)?_lastema1:_ema1;
_lastema2 = Double.IsNaN(_ema2)?_lastema2:_ema2;
_lastema3 = Double.IsNaN(_ema3) ? _lastema3 : _ema3;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _tema);
return base.Add(res, update);
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_sum = _oldsum = _lastema1 = _lastema2 = 0;
_len = 0;
}
}
+87
View File
@@ -0,0 +1,87 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly int _p1a, _p1b;
private SMA_Series sma, trima;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public TRIMA_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"xMA({period})";
_p1a = (int)Math.Floor((period * 0.5) + 1);
_p1b = (int)Math.Ceiling(0.5 * period);
sma = new(_p1a);
trima = new(_p1b);
}
public TRIMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public TRIMA_Series() : this(period: 0, useNaN: false) { }
public TRIMA_Series(int period) : this(period: period, useNaN: false) { }
public TRIMA_Series(TBars source) : this(source.Close, 0, false) { }
public TRIMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public TRIMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public TRIMA_Series(TSeries source) : this(source, 0, false) { }
public TRIMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN), update);
}
var _sma = sma.Add(TValue, update);
var _trima = trima.Add(_sma, update);
var res = (_trima.t, Count < _period - 1 && _NaN ? double.NaN : _trima.v);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
sma.Reset();
trima.Reset();
}
}
+119
View File
@@ -0,0 +1,119 @@
namespace QuanTAlib;
using System;
using System.Linq;
/* <summary>
TRIX: Triple Exponential Average Oscillator
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.
Sources:
https://www.investopedia.com/terms/t/trix.asp
</summary> */
public class TRIX_Series : TSeries {
private readonly double _k;
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 readonly bool _useSMA;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public TRIX_Series(int period, bool useNaN, bool useSMA) : base() {
_period = period;
_NaN = useNaN;
_useSMA = useSMA;
Name = $"TRIX({period})";
_k = 2.0 / (_period + 1);
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0;
}
public TRIX_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public TRIX_Series() : this(0, false, true) {}
public TRIX_Series(int period) : this(period, false, true) {}
public TRIX_Series(TBars source) : this(source.Close, 0, false) {}
public TRIX_Series(TBars source, int period) : this(source.Close, period, false) {}
public TRIX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
public TRIX_Series(TSeries source, int period) : this(source, period, false, true) {}
public TRIX_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN), update);
}
if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; }
if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; }
else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; }
double _ema1, _ema2, _ema3;
if ((this.Count < _period) && _useSMA) {
BufferTrim(_buffer1, TValue.v, _period, update);
_ema1 = 0;
for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
_ema1 /= _buffer1.Count;
BufferTrim(_buffer2, _ema1, _period, update);
_ema2 = 0;
for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
_ema2 /= _buffer2.Count;
BufferTrim(_buffer3, _ema2, _period, update);
_ema3 = 0;
for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
_ema3 /= _buffer3.Count;
}
else {
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
_ema3 = (_ema2 - _lastema3) * _k + _lastema3;
}
double _trix = 100 * (_ema3 - _lastema3) / _lastema3;
_lastema1 = _ema1;
_lastema2 = _ema2;
_lastema3 = _ema3;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _trix);
return base.Add(res, update);
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
}
}
+85
View File
@@ -0,0 +1,85 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
/* <summary>
TSeries is the cornerstone of all QuanTAlib classes.
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 TSeriesEventArgs : EventArgs {
public bool update { get; set; }
}
public class TSeries : List<(DateTime t, double v)> {
public List<DateTime> t => this.Select(item => item.t).ToList();
public List<double> v => this.Select(item => item.v).ToList();
public (DateTime t, double v) Last => this[^1];
public int Length => Count;
public string Name { get; set; }
public TSeries() {
this.Name = "data";
}
public TSeries(string Name) {
this.Name = Name;
}
public virtual (DateTime t, double v) Add(double v, bool update = false) {
var Value = (t: Count == 0 ? DateTime.Today : this[^1].t.AddDays(1), v);
return Add(Value, update);
}
public virtual (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (update) {
this[^1] = TValue;
}
else {
base.Add(TValue);
}
OnEvent(update);
return TValue;
}
public virtual (DateTime t, double v) Add(TSeries data) {
foreach (var item in data) { Add(item, false); }
return data.Last;
}
public void Sub(object source, TSeriesEventArgs e) {
var data = (TSeries) source;
if (data == null) { return; }
foreach (var item in data) { Add(item, update: false); }
}
public delegate void NewEventHandler(object source, TSeriesEventArgs args);
public event NewEventHandler Pub;
protected virtual void OnEvent(bool update = false)
{
Pub?.Invoke(this, new TSeriesEventArgs {update = update});
}
/// common helpers
public static void BufferTrim(List<double> buffer, double value, int period, bool update) {
if (!update) {
buffer.Add(value);
if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); }
return;
}
buffer[^1] = value;
}
public virtual void Reset() {
}
}
+84
View File
@@ -0,0 +1,84 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public VAR_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"VAR({period})";
}
public VAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public VAR_Series() : this(period: 0, useNaN: false) { }
public VAR_Series(int period) : this(period: period, useNaN: false) { }
public VAR_Series(TBars source) : this(source.Close, 0, false) { }
public VAR_Series(TBars source, int period) : this(source.Close, period, false) { }
public VAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public VAR_Series(TSeries source) : this(source, 0, false) { }
public VAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pvar);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+85
View File
@@ -0,0 +1,85 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public WMAPE_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"WMAPE({period})";
}
public WMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public WMAPE_Series() : this(period: 0, useNaN: false) { }
public WMAPE_Series(int period) : this(period: period, useNaN: false) { }
public WMAPE_Series(TBars source) : this(source.Close, 0, false) { }
public WMAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
public WMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public WMAPE_Series(TSeries source) : this(source, 0, false) { }
public WMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wmape);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+107
View File
@@ -0,0 +1,107 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
private System.Collections.Generic.List<double> _weights = new();
protected int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
protected int _len;
public int Len {
get { return _len; }
set { _len = value; }
}
//core constructors
public WMA_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"WMA({period})";
_len = 1;
_weights = CalculateWeights(_period);
}
public WMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public WMA_Series() : this(period: 0, useNaN: false) { }
public WMA_Series(int period) : this(period: period, useNaN: false) { }
public WMA_Series(TBars source) : this(source.Close, 0, false) { }
public WMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public WMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public WMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) {
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
if (_period == 0) {
_weights = CalculateWeights(_len);
_len++;
}
double _wma = 0;
double totalWeights = (_buffer.Count * (_buffer.Count + 1)) * 0.5;
object lockObj = new object();
Parallel.For(0, _buffer.Count, i =>
{
double temp = _buffer[i] * this._weights[i];
lock (lockObj) { _wma += temp; }
});
_wma /= totalWeights;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wma);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//calculating weights
private static List<double> CalculateWeights(int period) {
List<double> weights = new List<double>(period);
for (int i = 0; i < period; i++) {
weights.Add(i + 1);
}
return weights;
}
//reset calculation
public override void Reset() {
_len = 0;
_weights = CalculateWeights(_period);
_buffer.Clear();
}
}
+97
View File
@@ -0,0 +1,97 @@
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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
private int _len;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private readonly EMA_Series _ema;
//core constructor
public ZLEMA_Series(int period, bool useNaN, bool useSMA) : base() {
_period = period;
_NaN = useNaN;
Name = $"ZLEMA({period})";
_len = 1;
_ema = new(period);
}
//generic constructors (source)
public ZLEMA_Series() : this(0, false, true) { }
public ZLEMA_Series(int period) : this(period, false, true) { }
public ZLEMA_Series(TBars source) : this(source.Close, 0, false) { }
public ZLEMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public ZLEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public ZLEMA_Series(TSeries source, int period) : this(source, period, false, true) { }
public ZLEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { }
public ZLEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
int _lag;
if (_period == 0) {
_lag = (int)((_len - 1) * 0.5);
_len++;
}
else { _lag = (int)((_period - 1) * 0.5); }
_lag = Math.Min(_lag, _buffer.Count - 1);
_lag = Math.Max(_lag, 0) + 1;
double _zlValue = 2 * TValue.v - _buffer[^_lag];
double _zlema = _ema.Add((TValue.t, _zlValue), update).v;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlema);
return base.Add(res, update);
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
_ema.Reset();
}
}
+93
View File
@@ -0,0 +1,93 @@
namespace QuanTAlib;
using System;
using System.Linq;
/* <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: TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
private int _len;
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
private readonly EMA_Series _ema;
//core constructor
public ZL_Series(int period, bool useNaN, bool useSMA) : base() {
_period = period;
_NaN = useNaN;
Name = $"ZL({period})";
_len = 1;
_ema = new(period);
}
//generic constructors (source)
public ZL_Series() : this(0, false, true) { }
public ZL_Series(int period) : this(period, false, true) { }
public ZL_Series(TBars source) : this(source.Close, 0, false) { }
public ZL_Series(TBars source, int period) : this(source.Close, period, false) { }
public ZL_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public ZL_Series(TSeries source, int period) : this(source, period, false, true) { }
public ZL_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { }
public ZL_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
int _lag;
if (_period == 0) {
_lag = (int)((_len - 1) * 0.5);
_len++;
}
else { _lag = (int)((_period - 1) * 0.5); }
_lag = Math.Min(_lag, _buffer.Count - 1);
_lag = Math.Max(_lag, 0) + 1;
double _zlValue = 2 * TValue.v - _buffer[^_lag];
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlValue);
return base.Add(res, update);
}
//variation of Add()
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
_ema.Reset();
}
}
+91
View File
@@ -0,0 +1,91 @@
using System.Linq;
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <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 : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public ZSCORE_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"ZSCORE({period})";
}
public ZSCORE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public ZSCORE_Series() : this(period: 0, useNaN: false) { }
public ZSCORE_Series(int period) : this(period: period, useNaN: false) { }
public ZSCORE_Series(TBars source) : this(source.Close, 0, false) { }
public ZSCORE_Series(TBars source, int period) : this(source.Close, period, false) { }
public ZSCORE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public ZSCORE_Series(TSeries source) : this(source, 0, false) { }
public ZSCORE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: 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) ? 1 : (TValue.v - _sma) / _psdev;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zscore);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+72
View File
@@ -0,0 +1,72 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
/* <summary>
</summary> */
public class xMA_Series : TSeries {
private readonly System.Collections.Generic.List<double> _buffer = new();
protected readonly int _period;
protected readonly bool _NaN;
protected readonly TSeries _data;
//core constructors
public xMA_Series(int period, bool useNaN) : base() {
_period = period;
_NaN = useNaN;
Name = $"xMA({period})";
}
public xMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
_data = source;
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
_data.Pub += Sub;
Add(_data);
}
public xMA_Series() : this(period: 0, useNaN: false) { }
public xMA_Series(int period) : this(period: period, useNaN: false) { }
public xMA_Series(TBars source) : this(source.Close, 0, false) { }
public xMA_Series(TBars source, int period) : this(source.Close, period, false) { }
public xMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
public xMA_Series(TSeries source) : this(source, 0, false) { }
public xMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
//////////////////
// core Add() algo
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (double.IsNaN(TValue.v)) {
return base.Add((TValue.t, Double.NaN), update);
}
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
double _xma = 0;
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _xma);
return base.Add(res, update);
}
public override (DateTime t, double v) Add(TSeries data) {
if (data == null) { return (DateTime.Today, Double.NaN); }
foreach (var item in data) { Add(item, false); }
return _data.Last;
}
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
return Add(TValue, false);
}
public (DateTime t, double v) Add(bool update) {
return this.Add(TValue: _data.Last, update: update);
}
public (DateTime t, double v) Add() {
return Add(TValue: _data.Last, update: false);
}
private new void Sub(object source, TSeriesEventArgs e) {
Add(TValue: _data.Last, update: e.update);
}
//reset calculation
public override void Reset() {
_buffer.Clear();
}
}
+6 -6
View File
@@ -7,7 +7,7 @@ namespace QuanTAlib;
public class MovingAverage_chart : Indicator {
#region Parameters
[InputParameter("MA1: Type:", 0, variants: new object[]
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA1type = 15;
@@ -20,7 +20,7 @@ public class MovingAverage_chart : Indicator {
private int MA1DataSource = 3;
[InputParameter("MA2: Type:", 3, variants: new object[]
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA2type = 16;
@@ -97,8 +97,8 @@ public class MovingAverage_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
MA1 = new FMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
this.Name += $"FMA";
MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
this.Name += $"FWMA";
break;
case 8:
MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false);
@@ -171,8 +171,8 @@ public class MovingAverage_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
MA2 = new FMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
this.Name += $"FMA";
MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
this.Name += $"FWMA";
break;
case 8:
MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false);
+22 -18
View File
@@ -7,7 +7,7 @@ namespace QuanTAlib;
public class MovingAverageSlope_chart : Indicator {
#region Parameters
[InputParameter("MA1: Type:", 0, variants: new object[]
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA1type = 16;
@@ -20,7 +20,7 @@ public class MovingAverageSlope_chart : Indicator {
private int MA1DataSource = 3;
[InputParameter("MA2: Type:", 3, variants: new object[]
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
{ "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA2type = 6;
@@ -50,6 +50,8 @@ public class MovingAverageSlope_chart : Indicator {
private TSeries MA1, MA2;
private LINREG_Series sMA1, sMA2;
private CROSS_Series sig1, sig2;
private bool inLong, inShort;
///////
public MovingAverageSlope_chart() {
@@ -99,8 +101,8 @@ public class MovingAverageSlope_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
MA1 = new FMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
this.Name += $"FMA";
MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
this.Name += $"FWMA";
break;
case 8:
MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false);
@@ -173,8 +175,8 @@ public class MovingAverageSlope_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
MA2 = new FMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
this.Name += $"FMA";
MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
this.Name += $"FWMA";
break;
case 8:
MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false);
@@ -225,11 +227,7 @@ public class MovingAverageSlope_chart : Indicator {
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);
this.bars.Add(this.Time(),this.Open(), this.High(), this.Low(), this.Close(), this.Volume(), update);
this.SetValue(this.MA1[^1].v, lineIndex: 0);
this.SetValue(this.MA2[^1].v, lineIndex: 1);
@@ -240,31 +238,37 @@ public class MovingAverageSlope_chart : Indicator {
this.LinesSeries[1].SetMarker(0,s2Color);
if (sig1[^1].v > 0 || sig2[^1].v > 0) {
if (sMA1[^1].v >= 0 && sMA2[^1].v >= 0)
if (sMA1[^1].v >= 0 && sMA2[^1].v >= 0 && LongTrades)
{
inLong = true;
this.BeginCloud(0, 1, Color.FromArgb(127, Color.DarkGreen));
this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v)? 0 : 1 ].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow));
}
else {
this.EndCloud(0, 1, Color.Empty);
this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow));
if (inShort)
{
this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow));
inShort = false;
}
}
}
if (sig1[^1].v < 0 || sig2[^1].v < 0) {
if (sMA1[^1].v <= 0 && sMA2[^1].v <= 0)
if (sMA1[^1].v <= 0 && sMA2[^1].v <= 0 && ShortTrades)
{
inShort = true;
this.BeginCloud(0, 1, Color.FromArgb(100, Color.Red));
this.LinesSeries[(this.MA1[^1].v > this.MA2[^1].v) ? 0 : 1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow));
}
else {
this.EndCloud(0, 1, Color.Empty);
this.LinesSeries[(this.MA1[^1].v > this.MA2[^1].v)?1:0].SetMarker(1, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow));
if (inLong) {
LinesSeries[(this.MA1[^1].v > this.MA2[^1].v)?1:0].SetMarker(1, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow));
inLong = false;
}
}
}
}
public override void OnPaintChart(PaintChartEventArgs args) {
base.OnPaintChart(args);
+1 -3
View File
@@ -64,9 +64,7 @@ public class JMA_chart : Indicator {
rec[PriceType.Close], rec[PriceType.Volume]);
}
indicator = new(source: bars.Select(DataSource), period: Period,
phase: Jphase, vshort: Vshort, vlong: Vlong,
useNaN: true);
indicator = new(source: bars.Select(DataSource), period: Period, phase: Jphase, vshort: Vshort, vlong: Vlong, useNaN: true);
}
protected override void OnUpdate(UpdateArgs args) {
-1
View File
@@ -90,7 +90,6 @@ public class TrailingStop_chart : Indicator {
this.SetValue(_ratchetL, lineIndex: 1);
this.SetValue(_tslineS, lineIndex: 2);
this.SetValue(_ratchetS, lineIndex: 3);
}
public override void OnPaintChart(PaintChartEventArgs args) {
+2
View File
@@ -17,6 +17,8 @@
<InformationalVersion>0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d</InformationalVersion>
<Version>0.2.1-dev.2</Version>
<SuppressNETSdkWarningProperty>NETSDK1057</SuppressNETSdkWarningProperty>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<NoWarn>NETSDK1057</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>True</Optimize>
+1 -1
View File
@@ -64,7 +64,7 @@ namespace SimpleMACross {
bars.Add(hdm.Last().TimeLeft, hdm.Last()[PriceType.Open], hdm.Last()[PriceType.High],
hdm.Last()[PriceType.Low], hdm.Last()[PriceType.Close], hdm.Last()[PriceType.Volume], update);
if (!update) {this.LogInfo($"{bars.Close.Last().t} OHLC4:{(double)bars.OHLC4}");}
if (!update) {this.LogInfo($"{bars.Close.Last().t} OHLC4:{(double)bars.OHLC4.Last.v}");}
}
+2
View File
@@ -17,6 +17,8 @@
<InformationalVersion>0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d</InformationalVersion>
<Version>0.2.1-dev.2</Version>
<SuppressNETSdkWarningProperty>NETSDK1057</SuppressNETSdkWarningProperty>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<NoWarn>NETSDK1057</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>True</Optimize>
+153
View File
@@ -0,0 +1,153 @@
using Xunit;
using System;
using QuanTAlib;
namespace Basics;
#nullable disable
public class Indicators
{
private static Type[] maSeriesTypes = new Type[]
{
typeof(SMA_Series),
typeof(EMA_Series),
typeof(DEMA_Series),
typeof(TEMA_Series),
typeof(WMA_Series),
typeof(ALMA_Series),
typeof(DWMA_Series),
typeof(FWMA_Series),
typeof(HMA_Series),
typeof(ZLEMA_Series),
typeof(RMA_Series),
typeof(HEMA_Series),
typeof(JMA_Series),
typeof(CUSUM_Series),
typeof(SMMA_Series),
typeof(T3_Series),
typeof(KAMA_Series),
typeof(TRIMA_Series),
};
[Theory]
[MemberData(nameof(MASeriesData))]
public void Name_exists(Type classType)
{
TSeries data = new("Data") {1,2,3};
var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
Assert.NotEmpty(MA_Series.Name);
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void Series_Length(Type classType)
{
GBM_Feed feed = new(1000);
TSeries data = feed.OHLC4;
var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
Assert.Equal(1000, MA_Series.Count);
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void Return_data(Type classType)
{
TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
var result = MA_Series.Add(20);
Assert.Equal(result.v, MA_Series.Last.v);
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void Update(Type classType)
{
TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
var pre_update = MA_Series.Last.v;
double pre_data = data.Last.v;
data.Add(20, true);
data.Add(pre_data, true);
Assert.Equal(pre_update, MA_Series.Last.v);
Assert.Equal(data.Count, MA_Series.Count);
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void Period_zero(Type classType)
{
GBM_Feed feed = new(100);
TSeries data = feed.OHLC4;
var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries;
Assert.Equal(data.Count, MA_Series.Count);
Assert.False(double.IsNaN(MA_Series.Last.v));
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void Reset(Type classType)
{
GBM_Feed feed = new(10);
TSeries data = feed.OHLC4;
var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries;
MA_Series.Reset();
data.Add(0);
Assert.Equal(data.Last.v, MA_Series.Last.v);
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void Period_one(Type classType)
{
GBM_Feed feed = new(100);
TSeries data = feed.OHLC4;
var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries;
Assert.InRange(MA_Series.Last.v - data.Last.v, -10e-6, 10e-6);
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void NaN_test(Type classType)
{
GBM_Feed feed = new(100);
TSeries data = feed.OHLC4;
var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries;
Assert.True(double.IsNaN(MA_Series[0].v));
Assert.True(double.IsNaN(MA_Series[8].v));
Assert.False(double.IsNaN(MA_Series[9].v));
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void Edge_numbers(Type classType)
{
TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity };
var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries;
Assert.Equal(4, MA_Series.Count);
}
[Theory]
[MemberData(nameof(MASeriesData))]
public void handling_NaN(Type classType) {
TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries;
Assert.False(double.IsNaN(MA_Series.Last.v));
}
public static IEnumerable<object[]> MASeriesData()
{
foreach (var type in maSeriesTypes)
{
yield return new object[] { type };
}
}
}
#nullable restore

Some files were not shown because too many files have changed in this diff Show More