mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-06 13:07:44 +00:00
refinement
This commit is contained in:
@@ -3,10 +3,10 @@ on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build_test:
|
||||
|
||||
+19
-69
@@ -1,74 +1,24 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.2.32210.308
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calculations", "Calculations\Calculations.csproj", "{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Indicators", "Indicators\Indicators.csproj", "{43AD2D78-024C-4D96-A70B-915CF519965A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Strategies", "Strategies\Strategies.csproj", "{FA526AF6-95BC-4AC0-8B46-A304FD06689D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{47B6ACDB-F535-4FEB-9A0A-C427CAE8C28E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
docs\.nojekyll = docs\.nojekyll
|
||||
docs\ALMA.md = docs\ALMA.md
|
||||
docs\DEMA.md = docs\DEMA.md
|
||||
docs\DWMA.md = docs\DWMA.md
|
||||
docs\EMA.md = docs\EMA.md
|
||||
docs\FMA.md = docs\FMA.md
|
||||
docs\getting_started.ipynb = docs\getting_started.ipynb
|
||||
docs\HEMA.md = docs\HEMA.md
|
||||
docs\HMA.md = docs\HMA.md
|
||||
docs\HWMA.md = docs\HWMA.md
|
||||
docs\index.html = docs\index.html
|
||||
docs\indicators.md = docs\indicators.md
|
||||
docs\JMA.md = docs\JMA.md
|
||||
docs\KAMA.md = docs\KAMA.md
|
||||
docs\LICENSE = docs\LICENSE
|
||||
docs\MAMA.md = docs\MAMA.md
|
||||
docs\QA.md = docs\QA.md
|
||||
docs\readme.md = docs\readme.md
|
||||
docs\RMA.md = docs\RMA.md
|
||||
docs\SMA.md = docs\SMA.md
|
||||
docs\SMMA.md = docs\SMMA.md
|
||||
docs\T3.md = docs\T3.md
|
||||
docs\TEMA.md = docs\TEMA.md
|
||||
docs\TRIMA.md = docs\TRIMA.md
|
||||
docs\WMA.md = docs\WMA.md
|
||||
docs\ZLEMA.md = docs\ZLEMA.md
|
||||
docs\_sidebar.md = docs\_sidebar.md
|
||||
EndProjectSection
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calculations", "v2\calculations.csproj", "{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{283EACC9-3AF6-4DAE-9C1C-0F7F8C8CD70D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{43AD2D78-024C-4D96-A70B-915CF519965A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{43AD2D78-024C-4D96-A70B-915CF519965A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{43AD2D78-024C-4D96-A70B-915CF519965A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{43AD2D78-024C-4D96-A70B-915CF519965A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FA526AF6-95BC-4AC0-8B46-A304FD06689D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E5592DC2-0542-45B2-A0CF-C6B1EDC72B87}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AAE21F8A-9BC2-4647-A9EB-4DC86C569080}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E5592DC2-0542-45B2-A0CF-C6B1EDC72B87}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+58
-32
@@ -1,45 +1,71 @@
|
||||
public class EMA
|
||||
{
|
||||
private readonly int period;
|
||||
private int index;
|
||||
private readonly int _period;
|
||||
private int _index, _hotIndex;
|
||||
public TValue Value { get; private set; }
|
||||
public bool IsHot => index > period;
|
||||
public int Period => Math.Min(index, period);
|
||||
private double k;
|
||||
private double lastEMA, lastEMAcandidate;
|
||||
public bool IsHot => _index > _period;
|
||||
public int Period => Math.Min(_index, _period);
|
||||
private double _k;
|
||||
private double _lastEMA, _lastEMACandidate;
|
||||
|
||||
public EMA(int Period) {
|
||||
this.period = Period;
|
||||
public EMA(int period) {
|
||||
_period = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public void Init() {
|
||||
this.Value = default;
|
||||
this.index = 0;
|
||||
this.k = 2.0 / (period + 1);
|
||||
this.lastEMA = 0;
|
||||
this.lastEMAcandidate = 0;
|
||||
}
|
||||
|
||||
public TValue Update(TValue Input, bool IsNew = true) {
|
||||
double ma;
|
||||
if (double.IsNaN(Input.Value) || double.IsInfinity(Input.Value)) {
|
||||
return new TValue(Input.Time, lastEMA, IsNew, index > period);
|
||||
}
|
||||
if (IsNew) {
|
||||
if (index<1) { lastEMA = Input.Value; }
|
||||
lastEMAcandidate = lastEMA;
|
||||
index++;
|
||||
public EMA(object source, int period) : this(period) {
|
||||
var sourceType = source.GetType();
|
||||
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
|
||||
if (updateMethod != null) {
|
||||
var pubEvent = sourceType.GetEvent("Pub");
|
||||
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
|
||||
pubEvent.AddEventHandler(source, new Signal(Sub));
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
|
||||
}
|
||||
} else {
|
||||
if (index<=1) { lastEMAcandidate = Input.Value; }
|
||||
lastEMA = lastEMAcandidate;
|
||||
throw new ArgumentException("Source object must have an Update(TValue) method.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Init() {
|
||||
Value = default;
|
||||
_index = _hotIndex = 0;
|
||||
_k = 2.0 / (_period + 1);
|
||||
_lastEMA = 0;
|
||||
_lastEMACandidate = 0;
|
||||
}
|
||||
|
||||
public TValue Update(TValue input) {
|
||||
if (!input.IsHot && input.IsNew) { _hotIndex++; }
|
||||
|
||||
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
|
||||
Value = new TValue(input.Time, _lastEMA, input.IsNew, _index > _period);
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
if (input.IsNew) {
|
||||
if (_index < 1) { _lastEMA = input.Value; }
|
||||
_lastEMACandidate = _lastEMA;
|
||||
_index++;
|
||||
} else {
|
||||
if (_index <= 1) { _lastEMACandidate = input.Value; }
|
||||
_lastEMA = _lastEMACandidate;
|
||||
}
|
||||
|
||||
double kk = (index <= period) ? (2.0 / (index+1)) : k;
|
||||
ma = (Input.Value - lastEMA) * kk + lastEMA;
|
||||
lastEMA = ma;
|
||||
double kk = (_index <= _period) ? (2.0 / (_index + 1)) : _k;
|
||||
|
||||
this.Value = new TValue(Input.Time, ma, IsNew, index > period);
|
||||
return this.Value;
|
||||
double ema = (input.Value - _lastEMA) * kk + _lastEMA;
|
||||
_lastEMA = ema;
|
||||
|
||||
Value = new TValue(input.Time, ema, input.IsNew, _index > (_period + _hotIndex));
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Sub(object source, TValue arg) {
|
||||
Update(arg);
|
||||
}
|
||||
|
||||
public event Signal Pub;
|
||||
}
|
||||
+66
-41
@@ -1,55 +1,80 @@
|
||||
|
||||
public class SMA
|
||||
{
|
||||
public CircularBuffer buffer = null!;
|
||||
private readonly int period;
|
||||
public double sum;
|
||||
private readonly int _period;
|
||||
private int _index, _hotIndex;
|
||||
public TValue Value { get; private set; }
|
||||
public bool IsHot { get; private set; }
|
||||
public bool IsHot => _index >= _period;
|
||||
public int Period => Math.Min(_index, _period);
|
||||
private double _sum;
|
||||
private double _lastValidSMA;
|
||||
private CircularBuffer _buffer;
|
||||
private double _lastAddedValue;
|
||||
|
||||
public SMA(int period)
|
||||
{
|
||||
this.period = period;
|
||||
public SMA(int period) {
|
||||
_period = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
this.buffer = new CircularBuffer(period);
|
||||
this.sum = 0;
|
||||
this.IsHot = false;
|
||||
this.Value = default;
|
||||
public SMA(object source, int period) : this(period) {
|
||||
var sourceType = source.GetType();
|
||||
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
|
||||
|
||||
if (updateMethod != null) {
|
||||
var pubEvent = sourceType.GetEvent("Pub");
|
||||
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
|
||||
pubEvent.AddEventHandler(source, new Signal(Sub));
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
|
||||
}
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have an Update(TValue) method.");
|
||||
}
|
||||
}
|
||||
|
||||
public TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
if (buffer.Count == 0)
|
||||
{
|
||||
// If buffer is empty, always add the value regardless of isNew
|
||||
buffer.Add(input.Value, true);
|
||||
sum = input.Value;
|
||||
}
|
||||
else if (isNew && buffer.Count == buffer.Capacity)
|
||||
{
|
||||
// If buffer is full and it's a new value, remove oldest
|
||||
sum -= buffer[0];
|
||||
buffer.Add(input.Value, true);
|
||||
sum += input.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's not new, or if buffer isn't full yet
|
||||
if (!isNew)
|
||||
{
|
||||
// Remove the last value if we're updating
|
||||
sum -= buffer[buffer.Count - 1];
|
||||
}
|
||||
buffer.Add(input.Value, isNew);
|
||||
sum += input.Value;
|
||||
public void Init() {
|
||||
_buffer = new CircularBuffer(_period);
|
||||
_sum = 0;
|
||||
_lastValidSMA = 0;
|
||||
Value = default;
|
||||
_index = _hotIndex = 0;
|
||||
_lastAddedValue = 0;
|
||||
}
|
||||
|
||||
public TValue Update(TValue input) {
|
||||
if (!input.IsHot && input.IsNew) { _hotIndex++; }
|
||||
|
||||
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
|
||||
Value = new TValue(input.Time, _lastValidSMA, input.IsNew, _index > _period);
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
double sma = sum / buffer.Count;
|
||||
IsHot = buffer.Count >= period;
|
||||
Value = new TValue(input.Time, sma, isNew, IsHot);
|
||||
if (input.IsNew) {
|
||||
if (_buffer.Count == _buffer.Capacity) {
|
||||
_sum -= _buffer[0];
|
||||
}
|
||||
_buffer.Add(input.Value, true);
|
||||
_sum += input.Value;
|
||||
_lastAddedValue = input.Value;
|
||||
_index++;
|
||||
} else {
|
||||
_sum = _sum - _lastAddedValue + input.Value;
|
||||
_buffer[_buffer.Count - 1] = input.Value;
|
||||
_lastAddedValue = input.Value;
|
||||
}
|
||||
|
||||
double sma = _sum / _buffer.Count;
|
||||
_lastValidSMA = sma;
|
||||
|
||||
Value = new TValue(input.Time, sma, input.IsNew, _index > (_period + _hotIndex));
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Sub(object source, TValue arg) {
|
||||
Update(arg);
|
||||
}
|
||||
|
||||
public event Signal Pub;
|
||||
}
|
||||
@@ -18,3 +18,4 @@ public readonly record struct TValue(DateTime Time, double Value, bool IsNew = t
|
||||
public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: {Value:F2}]";
|
||||
}
|
||||
|
||||
public delegate void Signal(object source, TValue args);
|
||||
+236
-35
@@ -15,66 +15,267 @@ using System.Runtime.CompilerServices;
|
||||
#load "./basics/CircularBuffer.cs"
|
||||
#load "./basics/TValue.cs"
|
||||
#load "./basics/TBar.cs"
|
||||
#load "./Indicators/SMA.cs"
|
||||
#load "./Indicators/EMA.cs"
|
||||
//#load "./Indicators/SMA.cs"
|
||||
//#load "./Indicators/EMA.cs"
|
||||
#load "./GBM_Feed.cs"
|
||||
|
||||
#!csharp
|
||||
|
||||
public delegate void Signal(object source, TValue args);
|
||||
|
||||
#!csharp
|
||||
|
||||
public class Template
|
||||
{
|
||||
private CircularBuffer buffer = null!;
|
||||
private readonly int period;
|
||||
private int index;
|
||||
private CircularBuffer _buffer = null!;
|
||||
private readonly int _period;
|
||||
private int _index, _hotIndex;
|
||||
public TValue Value { get; private set; }
|
||||
public bool IsHot { get; private set; }
|
||||
public bool IsHot => _index >= _period;
|
||||
public int Period => Math.Min(_index, _period);
|
||||
private double _sum;
|
||||
private double _lastValidValue;
|
||||
private double _lastAddedValue;
|
||||
|
||||
public Template(int Period) {
|
||||
this.period = Period;
|
||||
public Template(int period) {
|
||||
_period = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public void Init() {
|
||||
this.buffer = new CircularBuffer(period);
|
||||
this.IsHot = false;
|
||||
this.Value = default;
|
||||
this.index = 0;
|
||||
public Template(object source, int period) : this(period) {
|
||||
var sourceType = source.GetType();
|
||||
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue), typeof(bool) });
|
||||
|
||||
if (updateMethod != null) {
|
||||
var pubEvent = sourceType.GetEvent("Pub");
|
||||
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
|
||||
pubEvent.AddEventHandler(source, new Signal(Sub));
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
|
||||
}
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have an Update(TValue, bool) method.");
|
||||
}
|
||||
}
|
||||
|
||||
public TValue Update(TValue Input, bool IsNew = true) {
|
||||
this.buffer.Add(Input,IsNew);
|
||||
//first value
|
||||
if (this.index == 0) {
|
||||
if (IsNew) { this.index++; }
|
||||
this.Value = new TValue(Input.Time, Input.Value, IsNew, true);
|
||||
return this.Value;
|
||||
public void Init() {
|
||||
_buffer = new CircularBuffer(_period);
|
||||
_sum = 0;
|
||||
_lastValidValue = 0;
|
||||
Value = default;
|
||||
_index = _hotIndex = 0;
|
||||
_lastAddedValue = 0;
|
||||
}
|
||||
|
||||
public TValue Update(TValue input, bool isNew = true) {
|
||||
if (!input.IsHot && isNew) { _hotIndex++; }
|
||||
|
||||
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
|
||||
Value = new TValue(input.Time, _lastValidValue, isNew, _index > _period);
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
if (IsNew) {
|
||||
// starting a new bar, fresh calc
|
||||
index++;
|
||||
} else {
|
||||
// updating existing bar, recalc
|
||||
}
|
||||
double ma = Input.Value;
|
||||
if (isNew) {
|
||||
if (_buffer.Count == _buffer.Capacity) {
|
||||
_sum -= _buffer[0];
|
||||
}
|
||||
_buffer.Add(input.Value, true);
|
||||
_sum += input.Value;
|
||||
_lastAddedValue = input.Value;
|
||||
_index++;
|
||||
} else {
|
||||
_sum = _sum - _lastAddedValue + input.Value;
|
||||
_buffer[_buffer.Count - 1] = input.Value;
|
||||
_lastAddedValue = input.Value;
|
||||
}
|
||||
|
||||
this.Value = new TValue(Input.Time, ma, IsNew, index > period);
|
||||
return this.Value;
|
||||
double result = _sum / _buffer.Count;
|
||||
_lastValidValue = result;
|
||||
|
||||
Value = new TValue(input.Time, result, isNew, _index > (_period + _hotIndex));
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Sub(object source, TValue arg) {
|
||||
Update(arg);
|
||||
}
|
||||
|
||||
public event Signal Pub;
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
//public delegate void Signal(object source, TValue args);
|
||||
|
||||
public class SMA
|
||||
{
|
||||
private readonly int _period;
|
||||
private int _index, _hotIndex;
|
||||
public TValue Value { get; private set; }
|
||||
public bool IsHot => _index >= _period;
|
||||
public int Period => Math.Min(_index, _period);
|
||||
private double _sum;
|
||||
private double _lastValidSMA;
|
||||
private CircularBuffer _buffer;
|
||||
private double _lastAddedValue;
|
||||
|
||||
public SMA(int period) {
|
||||
_period = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public SMA(object source, int period) : this(period) {
|
||||
var sourceType = source.GetType();
|
||||
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
|
||||
|
||||
if (updateMethod != null) {
|
||||
var pubEvent = sourceType.GetEvent("Pub");
|
||||
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
|
||||
pubEvent.AddEventHandler(source, new Signal(Sub));
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
|
||||
}
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have an Update(TValue) method.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Init() {
|
||||
_buffer = new CircularBuffer(_period);
|
||||
_sum = 0;
|
||||
_lastValidSMA = 0;
|
||||
Value = default;
|
||||
_index = _hotIndex = 0;
|
||||
_lastAddedValue = 0;
|
||||
}
|
||||
|
||||
public TValue Update(TValue input) {
|
||||
if (!input.IsHot && input.IsNew) { _hotIndex++; }
|
||||
|
||||
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
|
||||
Value = new TValue(input.Time, _lastValidSMA, input.IsNew, _index > _period);
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
if (input.IsNew) {
|
||||
if (_buffer.Count == _buffer.Capacity) {
|
||||
_sum -= _buffer[0];
|
||||
}
|
||||
_buffer.Add(input.Value, true);
|
||||
_sum += input.Value;
|
||||
_lastAddedValue = input.Value;
|
||||
_index++;
|
||||
} else {
|
||||
_sum = _sum - _lastAddedValue + input.Value;
|
||||
_buffer[_buffer.Count - 1] = input.Value;
|
||||
_lastAddedValue = input.Value;
|
||||
}
|
||||
|
||||
double sma = _sum / _buffer.Count;
|
||||
_lastValidSMA = sma;
|
||||
|
||||
Value = new TValue(input.Time, sma, input.IsNew, _index > (_period + _hotIndex));
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Sub(object source, TValue arg) {
|
||||
Update(arg);
|
||||
}
|
||||
|
||||
public event Signal Pub;
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
public class EMA
|
||||
{
|
||||
private readonly int _period;
|
||||
private int _index, _hotIndex;
|
||||
public TValue Value { get; private set; }
|
||||
public bool IsHot => _index > _period;
|
||||
public int Period => Math.Min(_index, _period);
|
||||
private double _k;
|
||||
private double _lastEMA, _lastEMACandidate;
|
||||
|
||||
public EMA(int period) {
|
||||
_period = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public EMA(object source, int period) : this(period) {
|
||||
var sourceType = source.GetType();
|
||||
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
|
||||
if (updateMethod != null) {
|
||||
var pubEvent = sourceType.GetEvent("Pub");
|
||||
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
|
||||
pubEvent.AddEventHandler(source, new Signal(Sub));
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
|
||||
}
|
||||
} else {
|
||||
throw new ArgumentException("Source object must have an Update(TValue) method.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Init() {
|
||||
Value = default;
|
||||
_index = _hotIndex = 0;
|
||||
_k = 2.0 / (_period + 1);
|
||||
_lastEMA = 0;
|
||||
_lastEMACandidate = 0;
|
||||
}
|
||||
|
||||
public TValue Update(TValue input) {
|
||||
if (!input.IsHot && input.IsNew) { _hotIndex++; }
|
||||
|
||||
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
|
||||
Value = new TValue(input.Time, _lastEMA, input.IsNew, _index > _period);
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
if (input.IsNew) {
|
||||
if (_index < 1) { _lastEMA = input.Value; }
|
||||
_lastEMACandidate = _lastEMA;
|
||||
_index++;
|
||||
} else {
|
||||
if (_index <= 1) { _lastEMACandidate = input.Value; }
|
||||
_lastEMA = _lastEMACandidate;
|
||||
}
|
||||
|
||||
double kk = (_index <= _period) ? (2.0 / (_index + 1)) : _k;
|
||||
|
||||
double ema = (input.Value - _lastEMA) * kk + _lastEMA;
|
||||
_lastEMA = ema;
|
||||
|
||||
Value = new TValue(input.Time, ema, input.IsNew, _index > (_period + _hotIndex));
|
||||
Pub?.Invoke(this, Value);
|
||||
return Value;
|
||||
}
|
||||
|
||||
public void Sub(object source, TValue arg) {
|
||||
Update(arg);
|
||||
}
|
||||
|
||||
public event Signal Pub;
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
EMA ma = new(3);
|
||||
SMA ma1 = new(ma, 3);
|
||||
Console.WriteLine($"{"Close",5} {"MA()",10}");
|
||||
for (int i = 1; i < 10; i=i+1)
|
||||
{
|
||||
//ma.Update(100,true);
|
||||
ma.Update(10,true);
|
||||
ma.Update(i,false);
|
||||
|
||||
|
||||
Console.WriteLine($"{i} {(double)ma.Value,10:F2} {ma.Value.IsNew}");
|
||||
TValue tv = new(DateTime.Now, 1000, IsNew: true);
|
||||
ma.Update(tv);
|
||||
tv = new(DateTime.Now, i, IsNew: false);
|
||||
ma.Update(tv);
|
||||
|
||||
Console.WriteLine($"{i} {(double)ma.Value,10:F2} {(double)ma1.Value,10:F2} {ma1.Value.IsHot}");
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
Reference in New Issue
Block a user