unit Myc.Trade.Core.DataConverter; interface uses Myc.Signals, Myc.Trade.Types, Myc.Trade.DataPoint, Myc.Trade.DataConverter; type // Null object implementation for IMycConverter TNullConverter = class(TInterfacedObject, TConverter.IConverter) private function GetSender: TDataProvider.IDataProvider; public function ProcessData(const Value: S): TState; end; TMycConverter = class abstract(TMycProcessor, TConverter.IConverter) private FSender: TMycDataProvider; function GetSender: TDataProvider.IDataProvider; protected function ProcessData(const Value: S): TState; override; abstract; // Broadcasts the given data to all linked processors. function Broadcast(const Value: T): TState; public constructor Create; destructor Destroy; override; property Sender: TDataProvider.IDataProvider read GetSender; end; TMycGenericConverter = class(TMycConverter) private FFunc: TConstFunc; protected function ProcessData(const Value: S): TState; override; public constructor Create(const AFunc: TConstFunc); end; TMycIndicator = class(TMycConverter) protected function ProcessData(const Value: S): TState; override; final; function Calculate(const Value: S): T; virtual; abstract; end; TMycDataCounter = class(TMycConverter) private FCount: Int64; protected function ProcessData(const Value: T): TState; override; public constructor Create; end; TMycTicker = class(TMycConverter, T>) public function ProcessData(const Values: TArray): TState; override; end; TMycRecordFieldReader = class(TMycConverter) private FOffset: Integer; public constructor Create(const AFieldName: String); function ProcessData(const Values: S): TState; override; end; implementation uses System.TypInfo, System.SysUtils, System.RTTI; { TMycConverter } constructor TMycConverter.Create; begin inherited Create; FSender := TMycDataProvider.Create(Self); end; destructor TMycConverter.Destroy; begin FSender.Free; inherited Destroy; end; function TMycConverter.Broadcast(const Value: T): TState; begin Result := FSender.Broadcast(Value); end; function TMycConverter.GetSender: TDataProvider.IDataProvider; begin Result := FSender; end; { TNullConverter } function TNullConverter.GetSender: TDataProvider.IDataProvider; begin Result := TDataProvider.Null; end; function TNullConverter.ProcessData(const Value: S): TState; begin Result := TState.Null; end; { TMycGenericConverter } constructor TMycGenericConverter.Create(const AFunc: TConstFunc); begin inherited Create; FFunc := AFunc; end; function TMycGenericConverter.ProcessData(const Value: S): TState; begin Result := Broadcast(FFunc(Value)); end; { TMycIndicator } function TMycIndicator.ProcessData(const Value: S): TState; begin Result := Broadcast(Calculate(Value)); end; constructor TMycDataCounter.Create; begin inherited Create; FCount := 0; end; function TMycDataCounter.ProcessData(const Value: T): TState; begin Result := Broadcast(FCount); inc(FCount); end; function TMycTicker.ProcessData(const Values: TArray): TState; begin var done := TLatch.CreateLatch(Length(Values)); // Process each incoming data point for var i := 0 to High(Values) do Broadcast(Values[i]).Signal.Subscribe(done); Result := done.State; end; constructor TMycRecordFieldReader.Create(const AFieldName: String); begin inherited Create; var Context := TRttiContext.Create; var Field := Context.GetType(TypeInfo(S)).GetField(AFieldName); var TypeT := Context.GetType(TypeInfo(T)); var Fields := Context.GetType(TypeInfo(S)).GetFields; var name := ''; if AFieldName = 'Time' then for var i := 0 to High(Fields) do begin name := name + ' ' + Fields[i].Name; end; Assert(Assigned(Field), 'Field ' + AFieldName + ' not found'); Assert(Field.FieldType.TypeKind = TypeT.TypeKind, 'Incorrect type'); if Assigned(Field) and (Field.FieldType.TypeKind = TypeT.TypeKind) then FOffset := Field.Offset else FOffset := -1; end; function TMycRecordFieldReader.ProcessData(const Values: S): TState; type PT = ^T; begin if FOffset < 0 then exit(TState.Null); var fieldPtr := PByte(@Values); inc(fieldPtr, FOffset); Result := Broadcast(PT(fieldPtr)^); end; end.