Testing data processing
This commit is contained in:
@@ -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}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -205,6 +205,7 @@ object Form1: TForm1
|
||||
TabOrder = 0
|
||||
Text = 'Button1'
|
||||
TextSettings.Trimming = None
|
||||
OnClick = Button1Click
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+16
-10
@@ -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.
|
||||
Reference in New Issue
Block a user