unit Myc.Trade.DataPoint; interface uses Myc.Signals, Myc.Core.Notifier; type // Represents a time-stamped data point in a series. TDataPoint = record Time: TDateTime; Data: T; constructor Create(ATime: TDateTime; const AData: T); end; IMycProcessor = interface function ProcessData(const Value: T): TState; end; TMycProcessor = class abstract(TInterfacedObject, IMycProcessor) protected function ProcessData(const Value: T): TState; virtual; abstract; end; TDataProvider = record type TTag = Pointer; IDataProvider = interface function Link(const Receiver: IMycProcessor): TTag; procedure Unlink(Tag: TTag); end; {$REGION 'private'} strict private class var FNull: IDataProvider; class constructor CreateClass; private FDataProvider: IDataProvider; {$ENDREGION} public constructor Create(const ADataProvider: IDataProvider); // Managed record operators class operator Initialize(out Dest: TDataProvider); class operator Implicit(const A: IDataProvider): TDataProvider; overload; class operator Implicit(const A: TDataProvider): IDataProvider; overload; // Wrapper for IMycDataProvider methods function Link(const Receiver: IMycProcessor): TTag; inline; procedure Unlink(Tag: TTag); inline; // Provides access to the null object instance. class property Null: IDataProvider read FNull; end; TMycDataProvider = class abstract(TContainedObject, TDataProvider.IDataProvider) private FListeners: TMycNotifyList>; public constructor Create(const Controller: IInterface); destructor Destroy; override; // Notifies all linked processors. function Broadcast(const Value: T): TState; procedure Notify(const Func: TMycNotifyList>.TNotifyProc); // Link a Processor function Link(const Processor: IMycProcessor): TDataProvider.TTag; // Unlink a linked strategy procedure Unlink(Tag: TDataProvider.TTag); end; TMycGenericProcessor = class(TMycProcessor) type TProc = reference to function(const Value: T): TState; private FProc: TProc; protected function ProcessData(const Value: T): TState; override; final; public constructor Create(const AProc: TProc); end; TMycContainedProcessor = class(TContainedObject, IMycProcessor) type TProc = function(const Value: T): TState of object; private FProc: TProc; function ProcessData(const Value: T): TState; public constructor Create(const Controller: IInterface; const AProc: TProc); end; TNullDataProvider = class(TInterfacedObject, TDataProvider.IDataProvider) public function Link(const Receiver: IMycProcessor): TDataProvider.TTag; procedure Unlink(Tag: TDataProvider.TTag); end; implementation { TNullDataProvider } function TNullDataProvider.Link(const Receiver: IMycProcessor): TDataProvider.TTag; begin Result := nil; end; procedure TNullDataProvider.Unlink(Tag: TDataProvider.TTag); begin // Do nothing in the null implementation. end; { TDataPoint } constructor TDataPoint.Create(ATime: TDateTime; const AData: T); begin Time := ATime; Data := AData; end; { TDataProvider } class constructor TDataProvider.CreateClass; begin // Create the singleton null object instance. FNull := TNullDataProvider.Create; end; constructor TDataProvider.Create(const ADataProvider: IDataProvider); begin FDataProvider := ADataProvider; // Ensure that the internal interface is never nil. if not Assigned(FDataProvider) then FDataProvider := FNull; end; class operator TDataProvider.Initialize(out Dest: TDataProvider); begin // Initialize new record instances with the null object. Dest.FDataProvider := FNull; end; class operator TDataProvider.Implicit(const A: IDataProvider): TDataProvider; begin // Allow implicit conversion from the interface to the helper. Result.Create(A); end; class operator TDataProvider.Implicit(const A: TDataProvider): IDataProvider; begin // Allow implicit conversion from the helper to the interface. Result := A.FDataProvider; end; function TDataProvider.Link(const Receiver: IMycProcessor): TTag; begin // Forward the call to the wrapped interface. Result := FDataProvider.Link(Receiver); end; procedure TDataProvider.Unlink(Tag: TTag); begin // Forward the call to the wrapped interface. FDataProvider.Unlink(Tag); end; { TMycGenericProcessor } constructor TMycGenericProcessor.Create(const AProc: TProc); begin inherited Create; FProc := AProc; end; function TMycGenericProcessor.ProcessData(const Value: T): TState; begin Result := FProc(Value); end; { TMycContainedProcessor } constructor TMycContainedProcessor.Create(const Controller: IInterface; const AProc: TProc); begin inherited Create(Controller); FProc := AProc; end; function TMycContainedProcessor.ProcessData(const Value: T): TState; begin Result := FProc(Value); end; { TMycDataProvider } constructor TMycDataProvider.Create(const Controller: IInterface); begin inherited Create(Controller); end; destructor TMycDataProvider.Destroy; begin FListeners.Finalize; inherited Destroy; end; function TMycDataProvider.Broadcast(const Value: T): TState; begin FListeners.Lock; try var item := FListeners.First; if item = nil then exit(TState.Null); var i := 1; while item.Next <> nil do begin inc(i); item := item.Next; end; var done := TLatch.CreateLatch(i); while item <> nil do begin item.Receiver.ProcessData(Value).Signal.Subscribe(done); item := item.Prev; end; Result := done.State; finally FListeners.Release; end; end; procedure TMycDataProvider.Notify(const Func: TMycNotifyList>.TNotifyProc); begin FListeners.Lock; try FListeners.Notify(Func); finally FListeners.Release; end; end; function TMycDataProvider.Link(const Processor: IMycProcessor): TDataProvider.TTag; begin // Add the Processor to the notification list FListeners.Lock; try Result := FListeners.Advise(Processor); finally FListeners.Release; end; end; procedure TMycDataProvider.Unlink(Tag: TDataProvider.TTag); begin FListeners.Lock; try FListeners.Unadvise(Tag); finally FListeners.Release; end; end; end.