Refactor struct to readonly, add implicit operators

This commit makes the structs readonly and adds implicit operators for conversion. Also, a quirky joke: Why was the math book sad? It had too many problems.
This commit is contained in:
Miha Kralj
2024-07-26 17:00:32 -07:00
parent 7dd938c368
commit ef534393db
2 changed files with 105 additions and 48 deletions
+42 -39
View File
@@ -1,44 +1,46 @@
using System; using System;
public struct TValue public readonly struct TValue {
{ public DateTime Timestamp { get; }
public DateTime Timestamp { get; set; } public double Value { get; }
public double Value { get; set; }
public TValue(DateTime timestamp, double value) public TValue(DateTime timestamp, double value) {
{
Timestamp = timestamp; Timestamp = timestamp;
Value = value; Value = value;
} }
public TValue() : this(DateTime.Now, 0) { }
public TValue(double value) : this(DateTime.Now, value) { }
public static implicit operator double(TValue tv) => tv.Value;
public static implicit operator DateTime(TValue tv) => tv.Timestamp;
public static implicit operator TValue(double value) => new TValue(DateTime.Now, value);
public override string ToString()
{ public override string ToString() {
return $"[{this.Timestamp:yyyy-MM-dd HH:mm:ss}: {this.Value:F2}]"; return $"[{Timestamp:yyyy-MM-dd HH:mm:ss}: {Value:F2}]";
}
public override bool Equals(object obj)
{
if (obj is TValue other)
{
return Timestamp == other.Timestamp && Value == other.Value;
}
return false;
} }
public override int GetHashCode() public override bool Equals(object obj) {
{ return obj is TValue other && Equals(in other);
}
public bool Equals(in TValue other) {
return Timestamp == other.Timestamp && Value == other.Value;
}
public override int GetHashCode() {
return HashCode.Combine(Timestamp, Value); return HashCode.Combine(Timestamp, Value);
} }
} }
public struct TBar public readonly struct TBar
{ {
public DateTime Timestamp { get; set; } public DateTime Timestamp { get; }
public double Open { get; set; } public double Open { get; }
public double High { get; set; } public double High { get; }
public double Low { get; set; } public double Low { get; }
public double Close { get; set; } public double Close { get; }
public double Volume { get; set; } public double Volume { get; }
public TBar(DateTime timestamp, double open, double high, double low, double close, double volume) public TBar(DateTime timestamp, double open, double high, double low, double close, double volume)
{ {
@@ -52,21 +54,22 @@ public struct TBar
public override string ToString() public override string ToString()
{ {
return $"[{this.Timestamp:yyyy-MM-dd HH:mm:ss}: O={this.Open:F2}, H={this.High:F2}, L={this.Low:F2}, C={this.Close:F2}, V={this.Volume:F2}]"; return $"[{Timestamp:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]";
} }
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is TBar other) return obj is TBar other && Equals(in other);
{ }
return Timestamp == other.Timestamp &&
Open == other.Open && public bool Equals(in TBar other)
High == other.High && {
Low == other.Low && return Timestamp == other.Timestamp &&
Close == other.Close && Open == other.Open &&
Volume == other.Volume; High == other.High &&
} Low == other.Low &&
return false; Close == other.Close &&
Volume == other.Volume;
} }
public override int GetHashCode() public override int GetHashCode()
@@ -77,13 +80,13 @@ public struct TBar
public class TValueEventArg<T> : EventArgs public class EventArg<T> : EventArgs
{ {
public T Data { get; } public T Data { get; }
public bool IsClosed { get; } public bool IsClosed { get; }
public bool IsHot { get; } public bool IsHot { get; }
public TValueEventArg(T data, bool isClosed, bool isHot) public EventArg(T data, bool isClosed, bool isHot)
{ {
Data = data; Data = data;
IsClosed = isClosed; IsClosed = isClosed;
+63 -9
View File
@@ -8,7 +8,7 @@
#!csharp #!csharp
TValue vv = new(DateTime.Now, 100); TValue vv = new(10);
display(vv.ToString()); display(vv.ToString());
#!csharp #!csharp
@@ -20,16 +20,16 @@ display(bb.ToString());
public class Emitter { public class Emitter {
private Random random = new Random(); private Random random = new Random();
public event EventHandler<TValueEventArg<TValue>> Pub; public event EventHandler<EventArg<TValue>> Pub;
public void Emit() { public void Emit() {
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
double randomValue = random.NextDouble() * 100; // Generates a random number between 0 and 100 double randomValue = random.NextDouble() * 100; // Generates a random number between 0 and 100
TValue value = new TValue(now, randomValue); TValue value = new TValue(now, randomValue);
TValueEventArg<TValue> eventArg = new TValueEventArg<TValue>(value, true, true); EventArg<TValue> eventArg = new EventArg<TValue>(value, true, true);
OnValuePub(eventArg); OnValuePub(eventArg);
} }
protected virtual void OnValuePub(TValueEventArg<TValue> eventArg) { protected virtual void OnValuePub(EventArg<TValue> eventArg) {
Pub?.Invoke(this, eventArg); Pub?.Invoke(this, eventArg);
} }
} }
@@ -37,7 +37,7 @@ public class Emitter {
public class BarEmitter public class BarEmitter
{ {
private Random random = new Random(); private Random random = new Random();
public event EventHandler<TValueEventArg<TBar>> Pub; public event EventHandler<EventArg<TBar>> Pub;
private double lastClose = 100.0; // Starting price private double lastClose = 100.0; // Starting price
public void Emit() public void Emit()
@@ -51,11 +51,11 @@ public class BarEmitter
TBar bar = new TBar(DateTime.Now, open, high, low, close, volume); TBar bar = new TBar(DateTime.Now, open, high, low, close, volume);
lastClose = close; lastClose = close;
TValueEventArg<TBar> eventArg = new TValueEventArg<TBar>(bar, true, true); EventArg<TBar> eventArg = new EventArg<TBar>(bar, true, true);
OnBarPub(eventArg); OnBarPub(eventArg);
} }
protected virtual void OnBarPub(TValueEventArg<TBar> eventArg) protected virtual void OnBarPub(EventArg<TBar> eventArg)
{ {
Pub?.Invoke(this, eventArg); Pub?.Invoke(this, eventArg);
} }
@@ -66,9 +66,9 @@ public class Listener
{ {
public void Sub(object sender, EventArgs e) public void Sub(object sender, EventArgs e)
{ {
if (e is TValueEventArg<TValue> tValueArg) { if (e is EventArg<TValue> tValueArg) {
Console.WriteLine($"TValue: {tValueArg.Data.Value:F2}"); Console.WriteLine($"TValue: {tValueArg.Data.Value:F2}");
} else if (e is TValueEventArg<TBar> tBarArg) { } else if (e is EventArg<TBar> tBarArg) {
Console.WriteLine($"TBar: o={tBarArg.Data.Open:F2}, v={tBarArg.Data.Volume:F2}"); Console.WriteLine($"TBar: o={tBarArg.Data.Open:F2}, v={tBarArg.Data.Volume:F2}");
} else { } else {
Console.WriteLine($"Unknown type: {e.GetType().Name}"); Console.WriteLine($"Unknown type: {e.GetType().Name}");
@@ -90,3 +90,57 @@ for (int i = 0; i < 3; i++) {
em1.Emit(); em1.Emit();
em2.Emit(); em2.Emit();
} }
#!csharp
public abstract class Indicator {
protected Indicator() {
Init(); }
public virtual void Init() {}
public virtual TValue Calc(TValue input, bool isNew=true, bool isHot=true) {
return new TValue();
}
}
public class EMA : Indicator
{
private double lastEma, lastEmaCandidate, k;
private int period, i;
public EMA(int period) {
Init(period);
}
public void Init(int period)
{
this.period = period;
this.k = 2.0 / (period + 1);
this.lastEma = this.lastEmaCandidate = double.NaN;
this.i = 0;
}
public override TValue Calc(TValue input, bool isNew = true, bool isHot = true) {
double ema;
if (double.IsNaN(lastEma)) { lastEma = lastEmaCandidate = input.Value; }
if (isNew) {
lastEma = lastEmaCandidate;
i++;
}
double kk = (i>=period)?k:(2.0/(i+1));
ema = lastEma + kk * (input.Value - lastEma);
lastEmaCandidate = ema;
return new TValue(input.Timestamp, ema);
}
}
#!csharp
EMA ema = new(3);
display(ema.Calc(100));
display(ema.Calc(0,false));
display(ema.Calc(100,false));
display(ema.Calc(0));