TSeries + DataEndpoint
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
* Unterstütze mich bei der Entwicklung unter Embarcadero Delphi.
|
||||
* Ich bin ein sehr erfahrener Softwareentwickler. Fasse dich kurz und nutze Fachsprache.
|
||||
* Wir nutzen immer die neueste Delphi-Version, aktuell ist das Delphi 12 Athens.
|
||||
* Wir nutzen immer die neueste Delphi-Version, aktuell ist das Delphi 12.3 Athens.
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
- Einrückung mit 4 Leerzeichen anstelle von 2. Auch bei Kommentaren.
|
||||
- Das Code-Format ist UTF-8. Nur ASCII, keine Sonderzeichen erlaubt (insb. kein No-Break-Space!)
|
||||
- Folgende Schlüsselwörter müssen klein geschrieben werden:
|
||||
and, or, not, mod, div, in, as, is, array of, sizeof(), inc(), dec()
|
||||
and, or, not, mod, div, in, as, is, array of, sizeof(), inc(), dec(), exit, inc, dec, shl, shr
|
||||
- Compiler-Direktiven (z.B. $region) sollen immer klein geschrieben werden.
|
||||
|
||||
* Ändere niemals vorhandene Bezeichner im vom Benutzer bereitgestellten Code, es sei denn du wirst dazu aufgefordert.
|
||||
|
||||
@@ -63,9 +64,9 @@
|
||||
|
||||
* Achte beim Erstellen von Format-Strings (z. B. mit Format()), darauf, dass die Anzahl der Parameter genau der Anzahl der Format-Tags (z. B. %s, %d) entspricht. Überprüfe die Typkompatibilität.
|
||||
|
||||
* Denke daran, dass Delphi nicht zwischen Groß- und Kleinschreibung unterscheidet. Bezeichner müssen sich von Schlüsselwörtern unterscheiden.
|
||||
* Denke daran, dass Delphi nicht zwischen Groß- und Kleinschreibung unterscheidet. Bezeichner müssen sich immer von Schlüsselwörtern unterscheiden.
|
||||
|
||||
* Interfaces benötigen keine GUIDs. Füge keine GUIDs in Interfaces ein und schlagen Sie dies auch nicht vor. Wenn ein gegebenes Interface keine GUID hat, ist das so gewollt. GUIDs werden ausschließlich von mir vergeben. Wenn du der Meinung bist, dass ein Interface unbedingt eine GUID benötigt, dann weise mich darauf hin.
|
||||
* Interfaces benötigen keine GUIDs. Füge keine GUIDs in Interfaces ein und schlagen Sie dies auch nicht vor. Wenn ein gegebenes Interface keine GUID hat, ist das so gewollt. GUIDs werden ausschließlich von mir vergeben. Füge niemals selbst eine GUID hinzu.
|
||||
|
||||
* TThread ist in System.Classes definiert.
|
||||
* TInterlocked ist in System.SyncObjs definiert.
|
||||
@@ -74,24 +75,40 @@
|
||||
* begin und end stehen am Anfang einer neuen Zeile. then steht nie am Anfang einer neuen Zeile.
|
||||
* RECORDs, die einen Initialize-Operator haben, sind Managed Records. Sie benötigen also kein explizites Create.
|
||||
|
||||
# Unit-Tests
|
||||
# In Unit-Tests
|
||||
|
||||
* Verwende nur statische Strings für Log-Einträge und Asserts. Keine Format(), ToString usw. (Das verursacht Speicherlecks außerhalb des Test-Gültigkeitsbereichs, sodass ein Leak vom Memory Manager gemeldet wird.)
|
||||
|
||||
* Verwende das Attribut für parametrische Tests, z. B. [TestCase('TestName', 'Parameter1,Parameter2,...')]
|
||||
* Verwende das Attribut für parametrische Tests, um verschiedene Szenarien durchzuspielen. Z. B. [TestCase('TestName', 'Parameter1,Parameter2,...')]
|
||||
|
||||
|
||||
# Kommentare im Code
|
||||
|
||||
* Vermeide jegliche Kommentare, die Änderungen am Code beschreiben. Z.B. `// hier wurde was geändert`. Das mag ich gar nicht.
|
||||
|
||||
* Kommentare sind immer englisch.
|
||||
|
||||
* Benutze keine HTML-Tags (`<summary>`, etc.)!
|
||||
* Benutze `//` oder `(* *)` und fasse dich extrem kurz. Meistens genügen Einzeiler vor den Deklarationen.
|
||||
|
||||
* Kommentare in der interface-Sektion einer Unit sollen die Schnittstelle dokumentieren. Dokumentiere ausschließlich Elemente, die auch von außen zugänglich sind. Und beziehe dich nur auf Elemente, die von außen zugänglich sind. Im Interface wird beschrieben, WAS eine Funktion macht. Es wird nicht beschrieben WIE sie es macht!
|
||||
* Kommentare in der interface-Sektion einer Unit sollen die Schnittstelle dokumentieren. Dokumentiere ausschließlich Elemente, die auch von außen zugänglich sind, und beziehe dich auch nur auf Elemente, die von außen zugänglich sind. Im Interface wird beschrieben, **was** eine Funktion macht. Es wird nicht beschrieben **wie** sie es macht!
|
||||
|
||||
* Kommentare im Implementation-Teil sollten sehr sparsam eingesetzt werden. Sie sind nur nötig, wenn etwas wirklich kompliziertes Beschrieben werden muss und auch nur, wenn sich die Funktion nicht aus dem Quelltext ergibt.
|
||||
|
||||
* Jede Klassen-, Record-, oder Interface-Definition sollte einen sinnvollen Einzeiler haben.
|
||||
|
||||
# Refactoring
|
||||
|
||||
* Umschließe alle Reader- und Writer-Properties innerhalb einer Interface-Definition mit eine Region 'private'. So zum Beispiel:
|
||||
|
||||
```
|
||||
IConverter = interface(IMycProcessor<S>)
|
||||
{$region 'private'}
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
{$endregion}
|
||||
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
||||
end;
|
||||
```
|
||||
|
||||
# Projektplan
|
||||
|
||||
@@ -109,6 +126,8 @@
|
||||
**Interface helper** sind ein Konzept, dass *nicht* explizit in Delphi/Pascal verankert ist. Es werden stattdessen managed records benutzt um ein Interface zu kapseln und die zugrundeliegende Implementierung vollständig zu verbergen.
|
||||
|
||||
- Ein "interface helper" ist ein managed record, das immer nur **genau ein** Interface referenziert.
|
||||
|
||||
- Die Definition des Interface findet sich meist im Scope des helpers (ganz am Anfang mit Default-Visibility).
|
||||
|
||||
- Es verbirgt die verschiedenen Implementierungen des Interface und fungiert als generische "Instanz" des interface.
|
||||
|
||||
@@ -122,79 +141,79 @@
|
||||
- Wrapper für die Methoden und Properties des Interface.
|
||||
- ein class property "Null".
|
||||
|
||||
* Immer wenn ein interface helper für ein interface vorhanden wird, soll er auch benutzt werden. Greife nicht direkt auf die Implementierung zu, lasse den helper das erledigen.
|
||||
* Direkte Wrapper auf Interface-Methoden sind *inline*.
|
||||
|
||||
## Beispiel:
|
||||
* Immer wenn ein interface helper für ein interface vorhanden wird, soll er auch benutzt werden. Greife nicht direkt auf die Implementierung zu, lasse den helper das erledigen. Beispiel:
|
||||
|
||||
```
|
||||
type
|
||||
IFuture = interface
|
||||
function GetValue: T;
|
||||
function GetDone: TState;
|
||||
end;
|
||||
```
|
||||
type
|
||||
TFuture<T> = record
|
||||
type
|
||||
IFuture = interface
|
||||
function GetValue: T;
|
||||
function GetDone: TState;
|
||||
end;
|
||||
|
||||
TFuture<T> = record
|
||||
{$REGION 'private'}
|
||||
strict private
|
||||
class var
|
||||
FNull: IFuture;
|
||||
strict private
|
||||
class var
|
||||
FNull: IFuture;
|
||||
|
||||
class constructor CreateClass;
|
||||
class constructor CreateClass;
|
||||
|
||||
private
|
||||
FFuture: IFuture;
|
||||
function GetDone: TState; inline;
|
||||
function GetValue: T; inline;
|
||||
{$ENDREGION}
|
||||
public
|
||||
constructor Create(const AFuture: IFuture);
|
||||
private
|
||||
FFuture: IFuture;
|
||||
function GetDone: TState; inline;
|
||||
function GetValue: T; inline;
|
||||
|
||||
class operator Initialize(out Dest: TFuture<T>);
|
||||
class operator Implicit(const A: IFuture): TFuture<T>; overload;
|
||||
class operator Implicit(const A: TFuture<T>): IFuture; overload;
|
||||
public
|
||||
constructor Create(const AFuture: IFuture);
|
||||
|
||||
class property Null: IFuture read FNull;
|
||||
class operator Initialize(out Dest: TFuture<T>);
|
||||
class operator Implicit(const A: IFuture): TFuture<T>; overload;
|
||||
class operator Implicit(const A: TFuture<T>): IFuture; overload;
|
||||
|
||||
// Wrapper methods for IFuture
|
||||
property Done: TState read GetDone;
|
||||
property Value: T read GetValue;
|
||||
end;
|
||||
class property Null: IFuture read FNull;
|
||||
|
||||
constructor TFuture<T>.Create(const AFuture: IFuture);
|
||||
begin
|
||||
FFuture := AFuture;
|
||||
if not Assigned(FFuture) then
|
||||
FFuture := FNull;
|
||||
end;
|
||||
// Wrapper methods for IFuture
|
||||
property Done: TState read GetDone;
|
||||
property Value: T read GetValue;
|
||||
end;
|
||||
|
||||
class constructor TFuture<T>.CreateClass;
|
||||
begin
|
||||
// init FNull
|
||||
end;
|
||||
constructor TFuture<T>.Create(const AFuture: IFuture);
|
||||
begin
|
||||
FFuture := AFuture;
|
||||
if not Assigned(FFuture) then
|
||||
FFuture := FNull;
|
||||
end;
|
||||
|
||||
class operator TFuture<T>.Implicit(const A: IFuture): TFuture<T>;
|
||||
begin
|
||||
Result.Create(A);
|
||||
end;
|
||||
class constructor TFuture<T>.CreateClass;
|
||||
begin
|
||||
FNull := TNullFuture.Create;
|
||||
end;
|
||||
|
||||
class operator TFuture<T>.Implicit(const A: TFuture<T>): IFuture;
|
||||
begin
|
||||
Result := A.FFuture;
|
||||
end;
|
||||
class operator TFuture<T>.Implicit(const A: IFuture): TFuture<T>;
|
||||
begin
|
||||
Result.FFuture := A;
|
||||
end;
|
||||
|
||||
class operator TFuture<T>.Initialize(out Dest: TFuture<T>);
|
||||
begin
|
||||
Dest.FFuture := FNull;
|
||||
end;
|
||||
class operator TFuture<T>.Implicit(const A: TFuture<T>): IFuture;
|
||||
begin
|
||||
Result := A.FFuture;
|
||||
end;
|
||||
|
||||
function TFuture<T>.GetDone: TState;
|
||||
begin
|
||||
Result := FFuture.Done;
|
||||
end;
|
||||
class operator TFuture<T>.Initialize(out Dest: TFuture<T>);
|
||||
begin
|
||||
Dest.FFuture := FNull;
|
||||
end;
|
||||
|
||||
function TFuture<T>.GetValue: T;
|
||||
begin
|
||||
Result := FFuture.Value;
|
||||
end;
|
||||
```
|
||||
function TFuture<T>.GetDone: TState;
|
||||
begin
|
||||
Result := FFuture.Done;
|
||||
end;
|
||||
|
||||
function TFuture<T>.GetValue: T;
|
||||
begin
|
||||
Result := FFuture.Value;
|
||||
end;
|
||||
```
|
||||
|
||||
|
||||
@@ -17,22 +17,22 @@ uses
|
||||
type
|
||||
TChartSeriesReceiver<T> = class(TMycProcessor<T>)
|
||||
strict private
|
||||
FCurrData: TMycDataArray<T>;
|
||||
FCurrData: TSeries<T>;
|
||||
FLookback: TMutable<Int64>;
|
||||
private
|
||||
FData: TWriteable<TMycDataArray<T>>;
|
||||
FData: TWriteable<TSeries<T>>;
|
||||
protected
|
||||
function ProcessData(const Value: T): TState; override;
|
||||
public
|
||||
constructor Create(const ALookback: TMutable<Int64>);
|
||||
property Data: TWriteable<TMycDataArray<T>> read FData;
|
||||
property Data: TWriteable<TSeries<T>> read FData;
|
||||
end;
|
||||
|
||||
TChartSeriesProcessor<T> = class(TMycChart.TSeries)
|
||||
strict private
|
||||
FDataSeries: TMycDataArray<T>;
|
||||
FDataSeries: TSeries<T>;
|
||||
private
|
||||
FData: TMycDataArray<T>;
|
||||
FData: TSeries<T>;
|
||||
FDataProvider: TDataProvider<T>;
|
||||
FReceiver: TChartSeriesReceiver<T>;
|
||||
FReceiverTag: TDataProvider<T>.TTag;
|
||||
@@ -44,7 +44,7 @@ type
|
||||
public
|
||||
constructor Create(const ADataProvider: TDataProvider<T>; const ALookback: TMutable<Int64>);
|
||||
destructor Destroy; override;
|
||||
property Data: TMycDataArray<T> read FData;
|
||||
property Data: TSeries<T> read FData;
|
||||
end;
|
||||
|
||||
{ TChartCustomLayer }
|
||||
@@ -146,8 +146,7 @@ constructor TChartSeriesReceiver<T>.Create(const ALookback: TMutable<Int64>);
|
||||
begin
|
||||
inherited Create;
|
||||
FLookback := ALookback;
|
||||
FCurrData := TMycDataArray<T>.CreateEmpty;
|
||||
FData := TWriteable<TMycDataArray<T>>.CreateWriteable(FCurrData).Protect;
|
||||
FData := TWriteable<TSeries<T>>.CreateWriteable(FCurrData).Protect;
|
||||
end;
|
||||
|
||||
function TChartSeriesReceiver<T>.ProcessData(const Value: T): TState;
|
||||
@@ -163,7 +162,6 @@ constructor TChartSeriesProcessor<T>.Create(const ADataProvider: TDataProvider<T
|
||||
begin
|
||||
inherited Create;
|
||||
FDataProvider := ADataProvider;
|
||||
FDataSeries := TMycDataArray<T>.CreateEmpty;
|
||||
FData := FDataSeries;
|
||||
|
||||
FReceiver := TChartSeriesReceiver<T>.Create(ALookback);
|
||||
|
||||
+21
-20
@@ -6,7 +6,7 @@ type
|
||||
// A series is an array of values with the newest ite at index=0. Each series counts the total of added items since creation, but
|
||||
// it actually may contain less items, because the array size is limited by the lookback parameter, when adding items.
|
||||
// Series are immutable.
|
||||
TMycDataArray<T> = record
|
||||
TSeries<T> = record
|
||||
private
|
||||
const
|
||||
ChunkSize = 1024;
|
||||
@@ -21,13 +21,14 @@ type
|
||||
function GetItems(Idx: Int64): T; inline;
|
||||
public
|
||||
constructor Create(const AChunks: TArray<TChunk>; ACount, ATotalCount: Int64);
|
||||
|
||||
class operator Initialize(out Dest: TSeries<T>);
|
||||
// Add a singe item
|
||||
function Add(const Data: T; Lookback: Int64): TMycDataArray<T>; overload;
|
||||
function Add(const Data: T; Lookback: Int64): TSeries<T>; overload;
|
||||
// Add a ranmge of items
|
||||
function Add(const Data: array of T; First, Count, Lookback: Int64): TMycDataArray<T>; overload;
|
||||
class function CreateEmpty: TMycDataArray<T>; static;
|
||||
function Add(const Data: array of T; First, Count, Lookback: Int64): TSeries<T>; overload;
|
||||
// Helper to create a data array from a raw TArray.
|
||||
class function CreateFromArray(const AData: TArray<T>; First, Count: Integer): TMycDataArray<T>; static;
|
||||
class function CreateFromArray(const AData: TArray<T>; First, Count: Integer): TSeries<T>; static;
|
||||
property Count: Int64 read FCount;
|
||||
property TotalCount: Int64 read FTotalCount;
|
||||
property Items[Idx: Int64]: T read GetItems; default;
|
||||
@@ -35,28 +36,22 @@ type
|
||||
|
||||
implementation
|
||||
|
||||
{ TMycDataArray<T> }
|
||||
{ TSeries<T> }
|
||||
|
||||
constructor TMycDataArray<T>.Create(const AChunks: TArray<TChunk>; ACount, ATotalCount: Int64);
|
||||
constructor TSeries<T>.Create(const AChunks: TArray<TChunk>; ACount, ATotalCount: Int64);
|
||||
begin
|
||||
FChunks := AChunks;
|
||||
FCount := ACount;
|
||||
FTotalCount := ATotalCount;
|
||||
end;
|
||||
|
||||
class function TMycDataArray<T>.CreateEmpty: TMycDataArray<T>;
|
||||
begin
|
||||
Result.FChunks := nil;
|
||||
Result.FCount := 0;
|
||||
end;
|
||||
|
||||
class function TMycDataArray<T>.CreateFromArray(const AData: TArray<T>; First, Count: Integer): TMycDataArray<T>;
|
||||
class function TSeries<T>.CreateFromArray(const AData: TArray<T>; First, Count: Integer): TSeries<T>;
|
||||
begin
|
||||
// Use the Add method on an empty array to perform the chunking logic.
|
||||
Result := CreateEmpty.Add(AData, First, Count, Count);
|
||||
Result.Add(AData, First, Count, Count);
|
||||
end;
|
||||
|
||||
function TMycDataArray<T>.Add(const Data: array of T; First, Count, Lookback: Int64): TMycDataArray<T>;
|
||||
function TSeries<T>.Add(const Data: array of T; First, Count, Lookback: Int64): TSeries<T>;
|
||||
var
|
||||
destPhysicalIdx, sourcePhysicalIdx: Int64;
|
||||
itemsToSkip: Int64;
|
||||
@@ -103,15 +98,15 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := TMycDataArray<T>.Create(newChunks, newCount, FTotalCount + Count);
|
||||
Result := TSeries<T>.Create(newChunks, newCount, FTotalCount + Count);
|
||||
end;
|
||||
|
||||
function TMycDataArray<T>.Add(const Data: T; Lookback: Int64): TMycDataArray<T>;
|
||||
function TSeries<T>.Add(const Data: T; Lookback: Int64): TSeries<T>;
|
||||
begin
|
||||
Result := Add([Data], 0, 1, Lookback);
|
||||
end;
|
||||
|
||||
function TMycDataArray<T>.GetItems(Idx: Int64): T;
|
||||
function TSeries<T>.GetItems(Idx: Int64): T;
|
||||
var
|
||||
physicalIndex: Int64;
|
||||
begin
|
||||
@@ -120,9 +115,15 @@ begin
|
||||
Result := FChunks[physicalIndex div ChunkSize][physicalIndex mod ChunkSize];
|
||||
end;
|
||||
|
||||
function TMycDataArray<T>.LogicalToPhysicalIndex(LogicalIndex: Int64): Int64;
|
||||
function TSeries<T>.LogicalToPhysicalIndex(LogicalIndex: Int64): Int64;
|
||||
begin
|
||||
Result := FCount - LogicalIndex - 1;
|
||||
end;
|
||||
|
||||
class operator TSeries<T>.Initialize(out Dest: TSeries<T>);
|
||||
begin
|
||||
Dest.FCount := 0;
|
||||
Dest.FTotalCount := 0;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -5,9 +5,11 @@ interface
|
||||
uses
|
||||
System.SysUtils,
|
||||
Myc.Signals,
|
||||
Myc.Mutable,
|
||||
Myc.Core.Notifier,
|
||||
Myc.Trade.Types,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Core.Notifier;
|
||||
Myc.Trade.DataArray,
|
||||
Myc.Trade.DataPoint;
|
||||
|
||||
type
|
||||
// Abstract base class for data consumers.
|
||||
@@ -126,6 +128,23 @@ type
|
||||
constructor Create(const Controller: IInterface; const AProc: TProc);
|
||||
end;
|
||||
|
||||
// Endpoint that collects data into a series.
|
||||
TMycDataEndpoint<T> = class(TInterfacedObject, TMutable<TSeries<T>>.IMutable)
|
||||
private
|
||||
FProcessor: TMycContainedProcessor<T>;
|
||||
FTag: TDataProvider<T>.TTag;
|
||||
FDataProvider: TDataProvider<T>;
|
||||
FLookback: Int64;
|
||||
FData: TSeries<T>;
|
||||
FChanged: TEvent;
|
||||
function GetChanged: TSignal;
|
||||
function GetValue: TSeries<T>;
|
||||
function ProcessData(const Value: T): TState;
|
||||
public
|
||||
constructor Create(const ADataProvider: TDataProvider<T>; ALookback: Int64);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@@ -354,4 +373,42 @@ begin
|
||||
Result := FProc(Value);
|
||||
end;
|
||||
|
||||
{ TMycDataEndpoint<T> }
|
||||
|
||||
constructor TMycDataEndpoint<T>.Create(const ADataProvider: TDataProvider<T>; ALookback: Int64);
|
||||
begin
|
||||
inherited Create;
|
||||
FDataProvider := ADataProvider;
|
||||
FLookback := ALookback;
|
||||
|
||||
FProcessor := TMycContainedProcessor<T>.Create(Self, ProcessData);
|
||||
FTag := FDataProvider.Link(FProcessor);
|
||||
end;
|
||||
|
||||
destructor TMycDataEndpoint<T>.Destroy;
|
||||
begin
|
||||
FDataProvider.Unlink(FTag);
|
||||
FProcessor.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TMycDataEndpoint<T>.GetChanged: TSignal;
|
||||
begin
|
||||
Result := FChanged.Signal;
|
||||
end;
|
||||
|
||||
function TMycDataEndpoint<T>.GetValue: TSeries<T>;
|
||||
begin
|
||||
Result := FData;
|
||||
end;
|
||||
|
||||
function TMycDataEndpoint<T>.ProcessData(const Value: T): TState;
|
||||
begin
|
||||
Result := TState.Null;
|
||||
|
||||
// Add new data point, respecting the lookback period.
|
||||
FData := FData.Add(Value, FLookback);
|
||||
FChanged.Notify;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+26
-36
@@ -4,21 +4,19 @@ interface
|
||||
|
||||
uses
|
||||
Myc.Signals,
|
||||
Myc.Trade.Types;
|
||||
Myc.Mutable,
|
||||
Myc.Trade.Types,
|
||||
Myc.Trade.DataArray;
|
||||
|
||||
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;
|
||||
|
||||
// A generic interface for components that process data of type T.
|
||||
IMycProcessor<T> = interface
|
||||
function ProcessData(const Value: T): TState;
|
||||
end;
|
||||
|
||||
// Interface helper for IDataProvider providing the null object pattern.
|
||||
TDataProvider<T> = record
|
||||
public
|
||||
type
|
||||
TTag = Pointer;
|
||||
IDataProvider = interface
|
||||
@@ -26,14 +24,14 @@ type
|
||||
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);
|
||||
|
||||
@@ -50,15 +48,18 @@ type
|
||||
class property Null: IDataProvider read FNull;
|
||||
end;
|
||||
|
||||
// Interface helper for IMycConverter<S,T> providing the null object pattern.
|
||||
// Interface helper for IConverter<S,T> providing the null object pattern.
|
||||
TConverter<S, T> = record
|
||||
public
|
||||
type
|
||||
IConverter = interface(IMycProcessor<S>)
|
||||
{$region 'private'}
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
{$endregion}
|
||||
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
||||
end;
|
||||
|
||||
{$REGION 'private'}
|
||||
{$region 'private'}
|
||||
strict private
|
||||
class var
|
||||
FNull: IConverter;
|
||||
@@ -66,7 +67,7 @@ type
|
||||
private
|
||||
FConverter: IConverter;
|
||||
function GetSender: TDataProvider<T>; inline;
|
||||
{$ENDREGION}
|
||||
{$endregion}
|
||||
public
|
||||
constructor Create(const AConverter: IConverter);
|
||||
|
||||
@@ -83,18 +84,21 @@ type
|
||||
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;
|
||||
|
||||
// Extracts the field of a record by it's name (using RTTI).
|
||||
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
|
||||
// Wrapper for IConverter.Sender
|
||||
property Sender: TDataProvider<T> read GetSender;
|
||||
end;
|
||||
|
||||
// Factory for creating specific converter instances.
|
||||
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;
|
||||
class function CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TMutable<TSeries<T>>; static;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -102,14 +106,6 @@ implementation
|
||||
uses
|
||||
Myc.Trade.DataPoint.Impl;
|
||||
|
||||
{ TDataPoint<T> }
|
||||
|
||||
constructor TDataPoint<T>.Create(ATime: TDateTime; const AData: T);
|
||||
begin
|
||||
Time := ATime;
|
||||
Data := AData;
|
||||
end;
|
||||
|
||||
{ TDataProvider<T> }
|
||||
|
||||
class constructor TDataProvider<T>.CreateClass;
|
||||
@@ -121,38 +117,32 @@ 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;
|
||||
|
||||
@@ -160,14 +150,12 @@ end;
|
||||
|
||||
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;
|
||||
@@ -190,44 +178,46 @@ end;
|
||||
|
||||
function TConverter<S, T>.Field<R>(const FieldName: String): TConverter<T, R>;
|
||||
begin
|
||||
Result := Chain<R>(TMycRecordFieldReader<T, R>.Create(FieldName));
|
||||
Result := Chain<R>(TConverter.CreateRecordField<T, R>(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;
|
||||
|
||||
{ TConverter }
|
||||
|
||||
class function TConverter.CreateCounter<T>: TConverter<T, Int64>;
|
||||
begin
|
||||
Result := TMycDataCounter<T>.Create;
|
||||
end;
|
||||
|
||||
class function TConverter.CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TMutable<TSeries<T>>;
|
||||
begin
|
||||
Result := TMycDataEndpoint<T>.Create(DataProvider, Lookback);
|
||||
end;
|
||||
|
||||
class function TConverter.CreateRecordField<S, T>(const FieldName: String): TConverter<S, T>;
|
||||
begin
|
||||
Result := TMycRecordFieldReader<S, T>.Create(FieldName);
|
||||
|
||||
@@ -11,6 +11,7 @@ uses
|
||||
Myc.Futures,
|
||||
Myc.Signals,
|
||||
Myc.Mutable,
|
||||
Myc.Trade.Types,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Core.FileCache;
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ type
|
||||
|
||||
TIndicators = record
|
||||
private
|
||||
class function CalculateSMA(const Series: TMycDataArray<Double>; const Period: Integer): Double; static;
|
||||
class function CalculateStdDev(const Series: TMycDataArray<Double>; const Period: Integer): Double; static;
|
||||
class function CalculateWMA(const Series: TMycDataArray<Double>; const Period: Integer): Double; static;
|
||||
class function CalculateSMA(const Series: TSeries<Double>; const Period: Integer): Double; static;
|
||||
class function CalculateStdDev(const Series: TSeries<Double>; const Period: Integer): Double; static;
|
||||
class function CalculateWMA(const Series: TSeries<Double>; const Period: Integer): Double; static;
|
||||
public
|
||||
// Simple Moving Average
|
||||
class function CreateSMA(Period: Integer): TConstFunc<Double, Double>; static;
|
||||
@@ -55,7 +55,7 @@ implementation
|
||||
|
||||
{ TIndicators }
|
||||
|
||||
class function TIndicators.CalculateSMA(const Series: TMycDataArray<Double>; const Period: Integer): Double;
|
||||
class function TIndicators.CalculateSMA(const Series: TSeries<Double>; const Period: Integer): Double;
|
||||
var
|
||||
i: Integer;
|
||||
sum: Double;
|
||||
@@ -70,7 +70,7 @@ begin
|
||||
Result := sum / Period;
|
||||
end;
|
||||
|
||||
class function TIndicators.CalculateStdDev(const Series: TMycDataArray<Double>; const Period: Integer): Double;
|
||||
class function TIndicators.CalculateStdDev(const Series: TSeries<Double>; const Period: Integer): Double;
|
||||
var
|
||||
i: Integer;
|
||||
mean, sumOfSquares: Double;
|
||||
@@ -86,7 +86,7 @@ begin
|
||||
Result := Sqrt(sumOfSquares / Period);
|
||||
end;
|
||||
|
||||
class function TIndicators.CalculateWMA(const Series: TMycDataArray<Double>; const Period: Integer): Double;
|
||||
class function TIndicators.CalculateWMA(const Series: TSeries<Double>; const Period: Integer): Double;
|
||||
var
|
||||
i: Integer;
|
||||
numerator: Double;
|
||||
@@ -114,7 +114,7 @@ end;
|
||||
|
||||
class function TIndicators.CreateBollingerBands(Period: Integer; Multiplier: Double): TConstFunc<Double, TBollingerBandsResult>;
|
||||
begin
|
||||
var sourceData := TMycDataArray<Double>.CreateEmpty;
|
||||
var sourceData: TSeries<Double>;
|
||||
Result :=
|
||||
function(const Value: Double): TBollingerBandsResult
|
||||
var
|
||||
@@ -138,7 +138,7 @@ end;
|
||||
class function TIndicators.CreateEMA(Period: Integer): TConstFunc<Double, Double>;
|
||||
begin
|
||||
var lastEma: Double := Double.NaN;
|
||||
var sourceData := TMycDataArray<Double>.CreateEmpty;
|
||||
var sourceData: TSeries<Double>;
|
||||
var multiplier := 2 / (Period + 1);
|
||||
|
||||
Result :=
|
||||
@@ -170,8 +170,8 @@ class function TIndicators.CreateHMA(Period: Integer): TConstFunc<Double, Double
|
||||
begin
|
||||
var periodHalf := Period div 2;
|
||||
var periodSqrt := Round(Sqrt(Period));
|
||||
var sourceData := TMycDataArray<Double>.CreateEmpty;
|
||||
var diffSeries := TMycDataArray<Double>.CreateEmpty;
|
||||
var sourceData: TSeries<Double>;
|
||||
var diffSeries: TSeries<Double>;
|
||||
|
||||
Result :=
|
||||
function(const Value: Double): Double
|
||||
@@ -244,7 +244,7 @@ class function TIndicators.CreateRSI(Period: Integer): TConstFunc<Double, Double
|
||||
begin
|
||||
var avgGain: Double := Double.NaN;
|
||||
var avgLoss: Double := Double.NaN;
|
||||
var sourceData := TMycDataArray<Double>.CreateEmpty;
|
||||
var sourceData: TSeries<Double>;
|
||||
|
||||
Result :=
|
||||
function(const Value: Double): Double
|
||||
@@ -301,7 +301,7 @@ end;
|
||||
|
||||
class function TIndicators.CreateSMA(Period: Integer): TConstFunc<Double, Double>;
|
||||
begin
|
||||
var sourceData := TMycDataArray<Double>.CreateEmpty;
|
||||
var sourceData: TSeries<Double>;
|
||||
Result :=
|
||||
function(const Value: Double): Double
|
||||
begin
|
||||
@@ -315,7 +315,7 @@ end;
|
||||
|
||||
class function TIndicators.CreateStochastic(KPeriod, DPeriod: Integer): TConstFunc<TOhlcItem, TStochasticResult>;
|
||||
begin
|
||||
var sourceData := TMycDataArray<TOhlcItem>.CreateEmpty;
|
||||
var sourceData: TSeries<TOhlcItem>;
|
||||
var smaD := CreateSMA(DPeriod);
|
||||
|
||||
Result :=
|
||||
|
||||
@@ -21,6 +21,13 @@ type
|
||||
constructor Create(AOpen, AHigh, ALow, AClose, AVolume: Double);
|
||||
end;
|
||||
|
||||
// Represents a time-stamped data point in a series.
|
||||
TDataPoint<T> = record
|
||||
Time: TDateTime;
|
||||
Data: T;
|
||||
constructor Create(ATime: TDateTime; const AData: T);
|
||||
end;
|
||||
|
||||
TConstFunc<S, T> = reference to function(const Value: S): T;
|
||||
|
||||
implementation
|
||||
@@ -44,4 +51,12 @@ begin
|
||||
Volume := AVolume;
|
||||
end;
|
||||
|
||||
{ TDataPoint<T> }
|
||||
|
||||
constructor TDataPoint<T>.Create(ATime: TDateTime; const AData: T);
|
||||
begin
|
||||
Time := ATime;
|
||||
Data := AData;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user