refinement

This commit is contained in:
Miha
2024-07-31 17:57:52 -07:00
parent 60c2331cc0
commit f1378e82ba
6 changed files with 382 additions and 179 deletions
+2 -2
View File
@@ -3,10 +3,10 @@ on:
workflow_dispatch:
push:
branches:
- '*'
- main
pull_request:
branches:
- '*'
- main
jobs:
build_test:
+19 -69
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -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
View File
@@ -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