Refactoring DataFlow, 1st Generic Data records working

This commit is contained in:
Michael Schimmel
2025-07-22 01:13:01 +02:00
parent dd50049b06
commit bf4ef71cba
11 changed files with 1074 additions and 907 deletions
+156
View File
@@ -5,6 +5,9 @@ interface
uses
System.SysUtils,
System.Math,
System.Generics.Collections,
System.Rtti,
Myc.DataRecord,
Myc.Trade.Types,
Myc.Trade.DataArray;
@@ -78,8 +81,72 @@ type
const AtrFunc: TConstFunc<TOhlcItem, Double>;
Multiplier: Double
): TConstFunc<TOhlcItem, TKeltnerChannelsResult>; overload; static;
class function CreateMean: TConstFunc<TArray<Double>, Double>; static;
end;
TIndicatorFactory = class
type
TFunc = TConstFunc<TDataRecord, TDataRecord>;
private
FParams: TDataRecord.TLayout;
FInput: TDataRecord.TLayout;
FOutput: TDataRecord.TLayout;
public
constructor Create(const AParams, AInput, AOutput: TDataRecord.TLayout);
function CreateIndicator(const Params: TDataRecord): TFunc; virtual; abstract;
property Params: TDataRecord.TLayout read FParams;
property Input: TDataRecord.TLayout read FInput;
property Output: TDataRecord.TLayout read FOutput;
end;
TEMA = class(TIndicatorFactory)
type
TParam = record
Period: Integer;
end;
TInput = record
Price: Double;
end;
TResult = record
MA: Double;
end;
public
constructor Create;
function CreateIndicator(const Params: TDataRecord): TIndicatorFactory.TFunc; override;
end;
TMACD = class
type
TParam = record
Fast: TConstFunc<Double, Double>;
Slow: TConstFunc<Double, Double>;
Signal: TConstFunc<Double, Double>;
end;
TInput = record
Price: Double;
end;
TResult = record
MacdLine: Double;
SignalLine: Double;
Histogram: Double;
end;
public
class function CreateMACD(const Param: TParam): TConstFunc<TInput, TResult>; static;
end;
var
Registry: TList<TIndicatorFactory>;
implementation
{ TIndicators }
@@ -466,4 +533,93 @@ begin
end;
end;
class function TIndicators.CreateMean: TConstFunc<TArray<Double>, Double>;
begin
Result :=
function(const Value: TArray<Double>): Double
begin
if Length(Value) = 0 then
exit(NaN);
Result := Value[0];
for var i := 1 to High(Value) do
Result := Result + Value[i];
Result := Result / Length(Value);
end;
end;
// Creates a MACD indicator from three provided moving average functions.
class function TMACD.CreateMACD(const Param: TParam): TConstFunc<TInput, TResult>;
begin
Result :=
function(const Input: TInput): TResult
var
fastVal, slowVal: Double;
begin
fastVal := Param.Fast(Input.Price);
slowVal := Param.Slow(Input.Price);
if IsNan(slowVal) then // slowVal will be the last one to become non-NaN
begin
Result.MacdLine := Double.NaN;
Result.SignalLine := Double.NaN;
Result.Histogram := Double.NaN;
end
else
begin
Result.MacdLine := fastVal - slowVal;
Result.SignalLine := Param.Signal(Result.MacdLine);
if not IsNan(Result.SignalLine) then
Result.Histogram := Result.MacdLine - Result.SignalLine
else
Result.Histogram := Double.NaN;
end;
end;
end;
constructor TEMA.Create;
begin
inherited
Create(TDataRecord.TLayout.CreateFrom<TParam>, TDataRecord.TLayout.CreateFrom<TInput>, TDataRecord.TLayout.CreateFrom<TResult>)
end;
function TEMA.CreateIndicator(const Params: TDataRecord): TIndicatorFactory.TFunc;
begin
var Period := Params.GetValue<Integer>('Period');
var Input := TDataRecord.TLayout.CreateFrom<TInput>;
var inPrice := Input.IndexOf('Price');
var Output := TDataRecord.TLayout.CreateFrom<TResult>;
var outMA := Output.IndexOf('MA');
var CalcEMA := TIndicators.CreateEMA(Period);
Result :=
function(const Input: TDataRecord): TDataRecord
begin
var Price: Double;
Input.GetValue(inPrice, Price);
var MA := CalcEMA(Price);
Result := TDataRecord.CreateFrom(Output);
Result.SetValue(outMA, MA);
end;
end;
constructor TIndicatorFactory.Create(const AParams, AInput, AOutput: TDataRecord.TLayout);
begin
inherited Create;
FParams := AParams;
FInput := AInput;
FOutput := AOutput;
end;
initialization
Registry := TList<TIndicatorFactory>.Create;
Registry.Add(TEMA.Create);
finalization
Registry.Free;
end.