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; 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; static; end; procedure Test1(const Log: TStrings); procedure TestSma(const Log: TStrings); implementation uses System.Math; class function TMyWorker.CreateFactory: TIndicatorFactoryProc; begin Result := function(const Params: TParams): TConvertFunc 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; var params := TDataRecord.FromRecord; params.SetValue('Log', Int64(Log)); params.SetValue('text', 'The quick brown fox jumps...'); var indi := fact.CreateIndicator(params); var args := TDataRecord.FromRecord; args.SetValue('xyz', 3.14159); var res := indi(args); var desc := res.GetValue('desc'); Log.Add('Final result description: ' + desc); Assert(desc = 'done'); end; { TSmaIndicator } class function TSmaIndicator.CreateFactory: TIndicatorFactoryProc; begin Result := function(const Params: TParams): TConvertFunc var // State for the indicator closure period: Integer; buffer: TArray; 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; args: TDataRecord; res: TDataRecord; smaValue: Double; begin Log.Add(''); Log.Add('--- Testing SMA ---'); fact := TGenericIndicatorFactory.CreateFromTemplate; // 1. Setup indicator parameters. params := TDataRecord.FromRecord; params.SetValue('Period', 3); Log.Add(Format('Creating SMA with Period=%d', [params.GetValue('Period')])); // 2. Create the indicator instance. indi := fact.CreateIndicator(params); // 3. Create the arguments record (can be reused). args := TDataRecord.FromRecord; // 4. Feed values and check results. args.SetValue('Value', 10.0); res := indi(args); smaValue := res.GetValue('Sma'); Log.Add(Format('Input: 10.0 -> SMA: %f', [smaValue])); Assert(SameValue(smaValue, 10.0)); // Avg of [10] is 10 args.SetValue('Value', 11.0); res := indi(args); smaValue := res.GetValue('Sma'); Log.Add(Format('Input: 11.0 -> SMA: %f', [smaValue])); Assert(SameValue(smaValue, 10.5)); // Avg of [10, 11] is 10.5 args.SetValue('Value', 12.0); res := indi(args); smaValue := res.GetValue('Sma'); Log.Add(Format('Input: 12.0 -> SMA: %f', [smaValue])); Assert(SameValue(smaValue, 11.0)); // Avg of [10, 11, 12] is 11 args.SetValue('Value', 13.0); res := indi(args); smaValue := res.GetValue('Sma'); Log.Add(Format('Input: 13.0 -> SMA: %f', [smaValue])); Assert(SameValue(smaValue, 12.0)); // Avg of [11, 12, 13] is 12 args.SetValue('Value', 14.0); res := indi(args); smaValue := res.GetValue('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.