Testing data processing

This commit is contained in:
Michael Schimmel
2025-07-27 08:39:16 +02:00
parent aa53a88953
commit 468adcf203
8 changed files with 261 additions and 173 deletions
+24 -111
View File
@@ -86,25 +86,7 @@ type
class function CreateMean: TConvertFunc<TArray<Double>, Double>; static;
end;
TIndicatorFactory = class
type
TFunc = TConvertFunc<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)
TEMA = class
type
TParam = record
Period: Integer;
@@ -119,35 +101,9 @@ type
end;
public
constructor Create;
function CreateIndicator(const Params: TDataRecord): TIndicatorFactory.TFunc; override;
class function CreateEMA(const Param: TParam): TConvertFunc<TInput, TResult>; static;
end;
TMACD = class
type
TParam = record
Fast: TConvertFunc<Double, Double>;
Slow: TConvertFunc<Double, Double>;
Signal: TConvertFunc<Double, Double>;
end;
TInput = record
Price: Double;
end;
TResult = record
MacdLine: Double;
SignalLine: Double;
Histogram: Double;
end;
public
class function CreateMACD(const Param: TParam): TConvertFunc<TInput, TResult>; static;
end;
var
Registry: TList<TIndicatorFactory>;
implementation
{ TIndicators }
@@ -548,79 +504,36 @@ begin
end;
end;
// Creates a MACD indicator from three provided moving average functions.
class function TMACD.CreateMACD(const Param: TParam): TConvertFunc<TInput, TResult>;
class function TEMA.CreateEMA(const Param: TParam): TConvertFunc<TInput, TResult>;
begin
Result :=
function(const Input: TInput): TResult
var
fastVal, slowVal: Double;
begin
fastVal := Param.Fast(Input.Price);
slowVal := Param.Slow(Input.Price);
var Period := Param.Period;
var lastEma: Double := Double.NaN;
var sourceData: TSeries<Double>;
var multiplier := 2 / (Period + 1);
if IsNan(slowVal) then // slowVal will be the last one to become non-NaN
Result :=
function(const Value: TInput): TResult
begin
sourceData.Add(Value.Price, Period);
if (sourceData.Count < Period) then
begin
Result.MacdLine := Double.NaN;
Result.SignalLine := Double.NaN;
Result.Histogram := Double.NaN;
Result.MA := Double.NaN;
Exit;
end;
if not IsNan(lastEma) then
begin
// Subsequent EMA calculation
lastEma := (Value.Price - lastEma) * multiplier + lastEma;
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;
// First EMA is a SMA of the initial period
lastEma := TIndicators.CalculateSMA(sourceData, Period);
end;
Result.MA := lastEma;
end;
end;
constructor TEMA.Create;
begin
inherited
Create(TDataRecord.TLayout.FromRecord<TParam>, TDataRecord.TLayout.FromRecord<TInput>, TDataRecord.TLayout.FromRecord<TResult>)
end;
function TEMA.CreateIndicator(const Params: TDataRecord): TIndicatorFactory.TFunc;
begin
var Period := Params.GetValue<Integer>('Period');
var Input := TDataRecord.TLayout.FromRecord<TInput>;
var inPrice := Input.IndexOf('Price');
var Output := TDataRecord.TLayout.FromRecord<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.FromRecord<TResult>;
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.