Files
MycLib/Src/Myc.Trade.DataPoint.pas
T
2025-07-15 01:56:44 +02:00

267 lines
7.1 KiB
ObjectPascal

unit Myc.Trade.DataPoint;
interface
uses
Myc.Signals,
Myc.Core.Notifier;
type
// Represents a time-stamped data point in a series.
TDataPoint<T> = record
Time: TDateTime;
Data: T;
constructor Create(ATime: TDateTime; const AData: T);
end;
IMycProcessor<T> = interface
function ProcessData(const Value: T): TState;
end;
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
protected
function ProcessData(const Value: T): TState; virtual; abstract;
end;
TDataProvider<T> = record
type
TTag = Pointer;
IDataProvider = interface
function Link(const Receiver: IMycProcessor<T>): 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<T>);
class operator Implicit(const A: IDataProvider): TDataProvider<T>; overload;
class operator Implicit(const A: TDataProvider<T>): IDataProvider; overload;
// Wrapper for IMycDataProvider methods
function Link(const Receiver: IMycProcessor<T>): TTag; inline;
procedure Unlink(Tag: TTag); inline;
// Provides access to the null object instance.
class property Null: IDataProvider read FNull;
end;
TMycDataProvider<T> = class abstract(TContainedObject, TDataProvider<T>.IDataProvider)
private
FListeners: TMycNotifyList<IMycProcessor<T>>;
public
constructor Create(const Controller: IInterface);
destructor Destroy; override;
// Notifies all linked processors.
function Broadcast(const Value: T): TState;
procedure Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
// Link a Processor
function Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
// Unlink a linked strategy
procedure Unlink(Tag: TDataProvider<T>.TTag);
end;
TMycGenericProcessor<T> = class(TMycProcessor<T>)
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<T> = class(TContainedObject, IMycProcessor<T>)
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<T> = class(TInterfacedObject, TDataProvider<T>.IDataProvider)
public
function Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
procedure Unlink(Tag: TDataProvider<T>.TTag);
end;
implementation
{ TNullDataProvider }
function TNullDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
begin
Result := nil;
end;
procedure TNullDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
begin
// Do nothing in the null implementation.
end;
{ TDataPoint<T> }
constructor TDataPoint<T>.Create(ATime: TDateTime; const AData: T);
begin
Time := ATime;
Data := AData;
end;
{ TDataProvider<T> }
class constructor TDataProvider<T>.CreateClass;
begin
// Create the singleton null object instance.
FNull := TNullDataProvider<T>.Create;
end;
constructor TDataProvider<T>.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<T>.Initialize(out Dest: TDataProvider<T>);
begin
// Initialize new record instances with the null object.
Dest.FDataProvider := FNull;
end;
class operator TDataProvider<T>.Implicit(const A: IDataProvider): TDataProvider<T>;
begin
// Allow implicit conversion from the interface to the helper.
Result.Create(A);
end;
class operator TDataProvider<T>.Implicit(const A: TDataProvider<T>): IDataProvider;
begin
// Allow implicit conversion from the helper to the interface.
Result := A.FDataProvider;
end;
function TDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TTag;
begin
// Forward the call to the wrapped interface.
Result := FDataProvider.Link(Receiver);
end;
procedure TDataProvider<T>.Unlink(Tag: TTag);
begin
// Forward the call to the wrapped interface.
FDataProvider.Unlink(Tag);
end;
{ TMycGenericProcessor<T> }
constructor TMycGenericProcessor<T>.Create(const AProc: TProc);
begin
inherited Create;
FProc := AProc;
end;
function TMycGenericProcessor<T>.ProcessData(const Value: T): TState;
begin
Result := FProc(Value);
end;
{ TMycContainedProcessor<T> }
constructor TMycContainedProcessor<T>.Create(const Controller: IInterface; const AProc: TProc);
begin
inherited Create(Controller);
FProc := AProc;
end;
function TMycContainedProcessor<T>.ProcessData(const Value: T): TState;
begin
Result := FProc(Value);
end;
{ TMycDataProvider<T> }
constructor TMycDataProvider<T>.Create(const Controller: IInterface);
begin
inherited Create(Controller);
end;
destructor TMycDataProvider<T>.Destroy;
begin
FListeners.Finalize;
inherited Destroy;
end;
function TMycDataProvider<T>.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<T>.Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
begin
FListeners.Lock;
try
FListeners.Notify(Func);
finally
FListeners.Release;
end;
end;
function TMycDataProvider<T>.Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
begin
// Add the Processor to the notification list
FListeners.Lock;
try
Result := FListeners.Advise(Processor);
finally
FListeners.Release;
end;
end;
procedure TMycDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
begin
FListeners.Lock;
try
FListeners.Unadvise(Tag);
finally
FListeners.Release;
end;
end;
end.