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
+2 -1
View File
@@ -7,7 +7,8 @@ uses
MainForm in 'MainForm.pas' {Form1},
TestModule in 'TestModule.pas',
DynamicFMXControl in 'DynamicFMXControl.pas',
Myc.Trade.Pipeline.Impl in '..\Src\Myc.Trade.Pipeline.Impl.pas';
Myc.Trade.Pipeline.Impl in '..\Src\Myc.Trade.Pipeline.Impl.pas',
TestMethodCallFromRecordParams in 'TestMethodCallFromRecordParams.pas';
{$R *.res}
+1
View File
@@ -138,6 +138,7 @@
<DCCReference Include="TestModule.pas"/>
<DCCReference Include="DynamicFMXControl.pas"/>
<DCCReference Include="..\Src\Myc.Trade.Pipeline.Impl.pas"/>
<DCCReference Include="TestMethodCallFromRecordParams.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
+1
View File
@@ -205,6 +205,7 @@ object Form1: TForm1
TabOrder = 0
Text = 'Button1'
TextSettings.Trimming = None
OnClick = Button1Click
end
end
end
+16 -10
View File
@@ -52,7 +52,8 @@ uses
DynamicFMXControl,
Myc.FMX.Chart,
Myc.Trade.Indicators,
StrategyTest;
StrategyTest,
TestMethodCallFromRecordParams;
type
TForm1 = class(TForm)
@@ -87,6 +88,7 @@ type
procedure StopButtonClick(Sender: TObject);
procedure TreeViewDblClick(Sender: TObject);
procedure AddWorkspaceActionExecute(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Strat2ButtonClick(Sender: TObject);
procedure TestActionExecute(Sender: TObject);
procedure StrategyButtonClick(Sender: TObject);
@@ -285,6 +287,11 @@ begin
Control.Align := TAlignLayout.Top;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Test1(LogMemo.Lines);
end;
function TForm1.CreateStrategy2(Timeframe: TTimeframe): IConsumer<TDataPoint<TOhlcItem>>;
type
TSignal = record
@@ -478,21 +485,20 @@ begin
pnlChart.SetXAxisCounter<Double>(equity.Producer);
////////////
var EMAFactory := TEMA.Create;
var Params := TDataRecord.Create(EMAFactory.Params);
Params.SetValue<Integer>('Period', 20);
var Params: TEMA.TParam;
Params.Period := 20;
var indi := EMAFActory.CreateIndicator(Params);
var indi := TEMA.CreateEMA(Params);
var EMAConv := TConverter<TDataRecord, TDataRecord>.CreateConverter(indi);
var EMAConv := TConverter<TEMA.TInput, TEMA.TResult>.CreateConverter(indi);
var equityEMA :=
equity
.Producer
.Chain<TDataRecord>(EMAFactory.Input.FieldAsRecord<Double>('Price'))
.Chain<TDataRecord>(EMAConv)
.Chain<Double>(EMAFactory.Output.FieldOfRecord<Double>('MA'));
.Chain<TEMA.TInput>(function(const Value: Double): TEMA.TInput begin Result.Price := Value end)
.Chain<TEMA.TResult>(EMAConv)
.Chain<Double>(function(const Value: TEMA.TResult): Double begin Result := Value.MA end);
//////////////
@@ -675,7 +681,7 @@ begin
exit;
var timeframe := TTimeframe.M15;
//ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
var tstStrat := StrategyTest.CreateStrategy1(timeframe);
ExecuteStrategy(Symbol, timeframe, tstStrat.Consumer);
@@ -0,0 +1,134 @@
unit TestMethodCallFromRecordParams;
interface
// We need RTTI here!
{$M+}
uses
System.Rtti,
System.SysUtils,
System.TypInfo,
System.Classes,
Myc.Data.Records;
type
InvokeAttribute = class(TCustomAttribute);
FactoryAttribute = class(TCustomAttribute);
TMyProc<TValue, TResult> = reference to function(const [ref] Value: TValue): TResult;
TFactoryProc<TParams, TValue, TResult> = reference to function(const [ref] Params: TParams): TMyProc<TValue, TResult>;
TMyWorker = record
type
TParams = record
Log: Int64;
text: String;
end;
TValue = record
xyz: Double;
end;
TResult = record
val: Int64;
desc: String;
end;
[Factory]
class function CreateFactory: TFactoryProc<TParams, TValue, TResult>; static;
end;
procedure Test1(const Log: TStrings);
implementation
class function TMyWorker.CreateFactory: TFactoryProc<TParams, TValue, TResult>;
begin
Result :=
function(const [ref] Params: TParams): TMyProc<TValue, TResult>
begin
var Log := TStrings(Params.Log);
var text := Params.text;
Result :=
function(const [ref] Value: TValue): TResult
begin
// Use Format to avoid locale issues with float conversion
Log.Add(Format('Val=%f (...%s)', [Value.xyz, text]));
Result.desc := 'done';
end;
end;
end;
type
TInvokeProc = reference to procedure(const Value, Res: TDataRecord);
function CreateInvoker(Ctx: TRttiContext; CallFunc: TValue): TInvokeProc;
begin
var rttiMyProc := Ctx.GetType(CallFunc.TypeInfo);
var workerInvokeMethod := rttiMyProc.GetMethod('Invoke');
var workerParamTypeInfo := workerInvokeMethod.GetParameters[0].ParamType.Handle;
var args: array[0..0] of TValue;
Result :=
procedure(const Value, Res: TDataRecord)
begin
TValue.MakeWithoutCopy(Value.RawData, workerParamTypeInfo, args[0], true);
workerInvokeMethod.Invoke(CallFunc, args).ExtractRawData(Res.RawData);
end;
end;
procedure Test1(const Log: TStrings);
begin
var params := TDataRecord.FromRecord<TMyWorker.TParams>;
params.SetValue<Int64>('Log', Int64(Log));
params.SetValue<String>('text', 'The quick brown fox jumps...');
var workerValueParam := TDataRecord.FromRecord<TMyWorker.TValue>;
workerValueParam.SetValue<Double>('xyz', 3.14159);
var finalResultRec := TDataRecord.FromRecord<TMyWorker.TResult>;
var invoke: TInvokeProc;
////////////
var Ctx := TRttiContext.Create;
var rttiType := Ctx.GetType(TypeInfo(TMyWorker));
// Find and invoke the method marked with [Factory] attribute.
for var method in rttiType.GetMethods do
begin
if method.HasAttribute<FactoryAttribute> then
begin
// First invocation returns the factory procedure itself (an interface).
var createFuncValue := method.Invoke(TValue.Empty, []);
// Anonymous methods are interfaces, so we get the TRttiInterfaceType.
var rttiCreateFunc := Ctx.GetType(createFuncValue.TypeInfo) as TRttiInterfaceType;
// The actual code is in the 'Invoke' method of that interface.
var factoryInvokeMethod := rttiCreateFunc.GetMethod('Invoke');
// Prepare the argument for the factory proc: a pointer to 'params'
var factoryArg: array[0..0] of TValue;
var factoryParamTypeInfo := factoryInvokeMethod.GetParameters[0].ParamType.Handle;
TValue.Make(params.RawData, factoryParamTypeInfo, factoryArg[0]);
var myProcValue := factoryInvokeMethod.Invoke(createFuncValue, factoryArg);
invoke := CreateInvoker(Ctx, myProcValue);
break;
end;
end;
//////////
Assert(Assigned(invoke));
invoke(workerValueParam, finalResultRec);
var desc := finalResultRec.GetValue<String>('desc');
Log.Add('Final result description: ' + desc);
Assert(desc = 'done');
end;
end.