Files
MycLib/AuraTrader/TestMethodCallFromRecordParams.pas
T
2025-07-27 16:41:54 +02:00

210 lines
6.3 KiB
ObjectPascal

unit TestMethodCallFromRecordParams;
interface
uses
System.SysUtils,
System.Classes,
Myc.Data.Records,
Myc.Data.Pipeline,
Myc.Trade.Indicators;
type
TMyWorker = class
public
type
TParams = record
Log: Int64;
text: String;
end;
TArgs = record
xyz: Double;
end;
TResult = record
val: Int64;
desc: String;
end;
[IndicatorFactory]
class function CreateFactory: TIndicatorFactoryProc<TParams, TArgs, TResult>; static;
end;
// SMA indicator for demonstration purposes
TSmaIndicator = class
public
type
TParams = record
Period: Integer;
end;
TArgs = record
Value: Double;
end;
TResult = record
Sma: Double;
end;
[IndicatorFactory]
class function CreateFactory: TIndicatorFactoryProc<TParams, TArgs, TResult>; static;
end;
procedure Test1(const Log: TStrings);
procedure TestSma(const Log: TStrings);
implementation
uses
System.Math;
class function TMyWorker.CreateFactory: TIndicatorFactoryProc<TParams, TArgs, TResult>;
begin
Result :=
function(const Params: TParams): TConvertFunc<TArgs, TResult>
begin
var Log := TStrings(Params.Log);
var text := Params.text;
Result :=
function(const Args: TArgs): TResult
begin
// Use Format to avoid locale issues with float conversion
Log.Add(Format('Val=%f (...%s)', [Args.xyz, text]));
Result.desc := 'done';
end;
end;
end;
procedure Test1(const Log: TStrings);
begin
var fact := TGenericIndicatorFactory.CreateFromTemplate<TMyWorker>;
var params := TDataRecord.FromRecord<TMyWorker.TParams>;
params.SetValue<Int64>('Log', Int64(Log));
params.SetValue<String>('text', 'The quick brown fox jumps...');
var indi := fact.CreateIndicator(params);
var args := TDataRecord.FromRecord<TMyWorker.TArgs>;
args.SetValue<Double>('xyz', 3.14159);
var res := indi(args);
var desc := res.GetValue<String>('desc');
Log.Add('Final result description: ' + desc);
Assert(desc = 'done');
end;
{ TSmaIndicator }
class function TSmaIndicator.CreateFactory: TIndicatorFactoryProc<TParams, TArgs, TResult>;
begin
Result :=
function(const Params: TParams): TConvertFunc<TArgs, TResult>
var
// State for the indicator closure
period: Integer;
buffer: TArray<Double>;
sum: Double;
count: Integer;
idx: Integer;
begin
period := Params.Period;
if period <= 0 then
raise Exception.Create('Period must be positive');
SetLength(buffer, period);
sum := 0.0;
count := 0;
idx := 0;
Result :=
function(const Args: TArgs): TResult
begin
// If the buffer is full, subtract the oldest value that is about to be overwritten.
if count >= period then
sum := sum - buffer[idx];
// Add the new value to the buffer and the sum.
buffer[idx] := Args.Value;
sum := sum + Args.Value;
// Advance the index for the circular buffer.
idx := (idx + 1) mod period;
// Increment the fill count until the buffer is full for the first time.
if count < period then
inc(count);
// Calculate the SMA. The result is the average of the values currently in the buffer.
if count > 0 then
Result.Sma := sum / count
else
Result.Sma := 0.0;
end;
end;
end;
procedure TestSma(const Log: TStrings);
var
fact: TGenericIndicatorFactory;
params: TDataRecord;
indi: TConvertFunc<TDataRecord, TDataRecord>;
args: TDataRecord;
res: TDataRecord;
smaValue: Double;
begin
Log.Add('');
Log.Add('--- Testing SMA ---');
fact := TGenericIndicatorFactory.CreateFromTemplate<TSmaIndicator>;
// 1. Setup indicator parameters.
params := TDataRecord.FromRecord<TSmaIndicator.TParams>;
params.SetValue<Integer>('Period', 3);
Log.Add(Format('Creating SMA with Period=%d', [params.GetValue<Integer>('Period')]));
// 2. Create the indicator instance.
indi := fact.CreateIndicator(params);
// 3. Create the arguments record (can be reused).
args := TDataRecord.FromRecord<TSmaIndicator.TArgs>;
// 4. Feed values and check results.
args.SetValue<Double>('Value', 10.0);
res := indi(args);
smaValue := res.GetValue<Double>('Sma');
Log.Add(Format('Input: 10.0 -> SMA: %f', [smaValue]));
Assert(SameValue(smaValue, 10.0)); // Avg of [10] is 10
args.SetValue<Double>('Value', 11.0);
res := indi(args);
smaValue := res.GetValue<Double>('Sma');
Log.Add(Format('Input: 11.0 -> SMA: %f', [smaValue]));
Assert(SameValue(smaValue, 10.5)); // Avg of [10, 11] is 10.5
args.SetValue<Double>('Value', 12.0);
res := indi(args);
smaValue := res.GetValue<Double>('Sma');
Log.Add(Format('Input: 12.0 -> SMA: %f', [smaValue]));
Assert(SameValue(smaValue, 11.0)); // Avg of [10, 11, 12] is 11
args.SetValue<Double>('Value', 13.0);
res := indi(args);
smaValue := res.GetValue<Double>('Sma');
Log.Add(Format('Input: 13.0 -> SMA: %f', [smaValue]));
Assert(SameValue(smaValue, 12.0)); // Avg of [11, 12, 13] is 12
args.SetValue<Double>('Value', 14.0);
res := indi(args);
smaValue := res.GetValue<Double>('Sma');
Log.Add(Format('Input: 14.0 -> SMA: %f', [smaValue]));
Assert(SameValue(smaValue, 13.0)); // Avg of [12, 13, 14] is 13
Log.Add('SMA test successful.');
end;
end.