400 lines
11 KiB
ObjectPascal
400 lines
11 KiB
ObjectPascal
unit Myc.Trade.DataPoint;
|
|
|
|
interface
|
|
|
|
uses
|
|
Myc.Signals,
|
|
Myc.Trade.Types,
|
|
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;
|
|
|
|
// Interface helper for IMycConverter<S,T> providing the null object pattern.
|
|
TConverter<S, T> = record
|
|
type
|
|
IConverter = interface(IMycProcessor<S>)
|
|
function GetSender: TDataProvider<T>.IDataProvider;
|
|
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
|
end;
|
|
|
|
{$REGION 'private'}
|
|
strict private
|
|
class var
|
|
FNull: IConverter;
|
|
class constructor CreateClass;
|
|
private
|
|
FConverter: IConverter;
|
|
function GetSender: TDataProvider<T>; inline;
|
|
{$ENDREGION}
|
|
public
|
|
constructor Create(const AConverter: IConverter);
|
|
|
|
// Managed record operators
|
|
class operator Initialize(out Dest: TConverter<S, T>);
|
|
class operator Implicit(const A: IConverter): TConverter<S, T>; overload;
|
|
class operator Implicit(const A: TConverter<S, T>): IConverter; overload;
|
|
|
|
class function CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
|
|
|
|
// Wrapper for IMycProcessor.ProcessData
|
|
function ProcessData(const Value: S): TState; inline;
|
|
|
|
function Chain<R>(const Next: TConverter<T, R>): TConverter<T, R>; overload; inline;
|
|
function Chain<R>(const Func: TConstFunc<T, R>): TConverter<T, R>; overload; inline;
|
|
|
|
function Field<R>(const FieldName: String): TConverter<T, R>; overload; inline;
|
|
|
|
// Provides access to the null object instance.
|
|
class property Null: IConverter read FNull;
|
|
// Wrapper for IMycConverter.Sender
|
|
property Sender: TDataProvider<T> read GetSender;
|
|
end;
|
|
|
|
TConverter = record
|
|
class function CreateCounter<T>: TConverter<T, Int64>; static;
|
|
class function CreateTicker<T>: TConverter<TArray<T>, T>; static;
|
|
class function CreateRecordField<S, T>(const FieldName: String): TConverter<S, T>; static;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Myc.Trade.DataPoint.Impl;
|
|
|
|
{ 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;
|
|
|
|
{ TConverter<S, T> }
|
|
|
|
class constructor TConverter<S, T>.CreateClass;
|
|
begin
|
|
// Create the singleton null object instance.
|
|
FNull := TNullConverter<S, T>.Create;
|
|
end;
|
|
|
|
constructor TConverter<S, T>.Create(const AConverter: IConverter);
|
|
begin
|
|
FConverter := AConverter;
|
|
// Ensure that the internal interface is never nil.
|
|
if not Assigned(FConverter) then
|
|
FConverter := FNull;
|
|
end;
|
|
|
|
function TConverter<S, T>.Chain<R>(const Next: TConverter<T, R>): TConverter<T, R>;
|
|
begin
|
|
FConverter.Sender.Link(Next);
|
|
Result := Next;
|
|
end;
|
|
|
|
function TConverter<S, T>.Chain<R>(const Func: TConstFunc<T, R>): TConverter<T, R>;
|
|
begin
|
|
Result := Chain<R>(TMycGenericConverter<T, R>.Create(Func));
|
|
end;
|
|
|
|
class function TConverter<S, T>.CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>;
|
|
begin
|
|
Result := TMycGenericConverter<S, T>.Create(Func);
|
|
end;
|
|
|
|
function TConverter<S, T>.Field<R>(const FieldName: String): TConverter<T, R>;
|
|
begin
|
|
Result := Chain<R>(TMycRecordFieldReader<T, R>.Create(FieldName));
|
|
end;
|
|
|
|
function TConverter<S, T>.GetSender: TDataProvider<T>;
|
|
begin
|
|
// Forward the call to the wrapped interface.
|
|
Result := FConverter.Sender;
|
|
end;
|
|
|
|
class operator TConverter<S, T>.Initialize(out Dest: TConverter<S, T>);
|
|
begin
|
|
// Initialize new record instances with the null object.
|
|
Dest.FConverter := FNull;
|
|
end;
|
|
|
|
class operator TConverter<S, T>.Implicit(const A: IConverter): TConverter<S, T>;
|
|
begin
|
|
// Allow implicit conversion from the interface to the helper.
|
|
Result.Create(A);
|
|
end;
|
|
|
|
class operator TConverter<S, T>.Implicit(const A: TConverter<S, T>): IConverter;
|
|
begin
|
|
// Allow implicit conversion from the helper to the interface.
|
|
Result := A.FConverter;
|
|
end;
|
|
|
|
function TConverter<S, T>.ProcessData(const Value: S): TState;
|
|
begin
|
|
// Forward the call to the wrapped interface.
|
|
Result := FConverter.ProcessData(Value);
|
|
end;
|
|
|
|
class function TConverter.CreateCounter<T>: TConverter<T, Int64>;
|
|
begin
|
|
Result := TMycDataCounter<T>.Create;
|
|
end;
|
|
|
|
class function TConverter.CreateRecordField<S, T>(const FieldName: String): TConverter<S, T>;
|
|
begin
|
|
Result := TMycRecordFieldReader<S, T>.Create(FieldName);
|
|
end;
|
|
|
|
class function TConverter.CreateTicker<T>: TConverter<TArray<T>, T>;
|
|
begin
|
|
Result := TMycTicker<T>.Create;
|
|
end;
|
|
|
|
end.
|