Implementing first "strategy" for proof of concept

This commit is contained in:
Michael Schimmel
2025-07-11 11:06:18 +02:00
parent 644b6074d6
commit 487169afe0
20 changed files with 1761 additions and 274 deletions
+105 -19
View File
@@ -8,18 +8,18 @@ uses
type
// A data record for an Ask/Bid price pair.
TAskBidItem = packed record
Ask: Single;
Bid: Single;
constructor Create(AAsk, ABid: Single);
Ask: Double;
Bid: Double;
constructor Create(AAsk, ABid: Double);
end;
TOhlcItem = record
Open: Single;
High: Single;
Low: Single;
Close: Single;
Volume: Single;
constructor Create(AOpen, AHigh, ALow, AClose, AVolume: Single);
Open: Double;
High: Double;
Low: Double;
Close: Double;
Volume: Double;
constructor Create(AOpen, AHigh, ALow, AClose, AVolume: Double);
end;
// Represents a time-stamped data point in a series.
@@ -29,13 +29,38 @@ type
constructor Create(ATime: TDateTime; const AData: T);
end;
// A time-ordered series of data points, optimized for chronological additions.
// The most recently added element has the logical index 0.
IMycProcessor<T> = interface
procedure ProcessData(const Value: T);
end;
TMycGenericProcessor<T> = class(TInterfacedObject, IMycProcessor<T>)
type
TProc = reference to procedure(const Value: T);
private
FProc: TProc;
procedure ProcessData(const Value: T);
public
constructor Create(const AProc: TProc);
end;
TMycContainedProcessor<T> = class(TContainedObject, IMycProcessor<T>)
type
TProc = procedure(const Value: T) of object;
private
FProc: TProc;
procedure ProcessData(const Value: T);
public
constructor Create(const Controller: IInterface; const AProc: TProc);
end;
// An immutable time-ordered series of data points.
// The most recent element has the logical index 0.
IDataSeries<T> = interface
function GetCount: Int64;
function GetItems(Idx: Int64): TDataPoint<T>;
function GetTotalCount: Int64;
function GetLookback: Int64;
// Add data and result the new series.
function Add(const Data: TArray<TDataPoint<T>>; First, Count: Integer): IDataSeries<T>;
property Count: Int64 read GetCount;
// Accesses data points by their logical index.
@@ -74,7 +99,6 @@ type
function Add(const Data: TArray<TDataPoint<T>>): TDataSeries<T>; overload;
function Add(const Data: TArray<TDataPoint<T>>; First, Count: Integer): TDataSeries<T>; overload;
function Convert<S>(const Func: TConvertFunc<S>): TDataSeries<S>;
// Searches for a data point by its timestamp.
@@ -99,12 +123,21 @@ type
function ToOhlc(TimeFrame: TTimeSpan): TDataSeries<TOhlcItem>;
end;
TDataSeriesOhlcHelper = record helper for TDataSeries<TOhlcItem>
function ToOpen: TDataSeries<Single>;
function ToClose: TDataSeries<Single>;
function ToHigh: TDataSeries<Single>;
function ToLow: TDataSeries<Single>;
function ToVolume: TDataSeries<Single>;
end;
implementation
uses
System.SysUtils,
System.Math,
System.Generics.Collections,
Myc.Trade.DataArray,
Myc.Trade.Core.DataPoint;
// Optimized helper function using direct TTimeSpan features and integer arithmetic.
@@ -180,17 +213,40 @@ begin
if not firstPointInBar then
ohlcPoints.Add(TDataPoint<TOhlcItem>.Create(windowEndTime, currentBar));
var dataArray := TMycDataArray<TOhlcItem>.CreateFromArray(ohlcPoints.ToArray, 0, ohlcPoints.Count);
var seriesImpl := TMycDataSeries<TOhlcItem>.Create(ohlcPoints.Count, dataArray, ohlcPoints.Count);
Result := TDataSeries<TOhlcItem>.Create(seriesImpl);
Result := TDataSeries<TOhlcItem>.CreateDataSeries(ohlcPoints.Count, ohlcPoints.ToArray);
finally
ohlcPoints.Free;
end;
end;
function TDataSeriesOhlcHelper.ToClose: TDataSeries<Single>;
begin
Result := Self.Convert<Single>(function(const Ohlc: TDataPoint<TOhlcItem>): Single begin Result := Ohlc.Data.Close; end);
end;
function TDataSeriesOhlcHelper.ToHigh: TDataSeries<Single>;
begin
Result := Self.Convert<Single>(function(const Ohlc: TDataPoint<TOhlcItem>): Single begin Result := Ohlc.Data.High; end);
end;
function TDataSeriesOhlcHelper.ToLow: TDataSeries<Single>;
begin
Result := Self.Convert<Single>(function(const Ohlc: TDataPoint<TOhlcItem>): Single begin Result := Ohlc.Data.Low; end);
end;
function TDataSeriesOhlcHelper.ToOpen: TDataSeries<Single>;
begin
Result := Self.Convert<Single>(function(const Ohlc: TDataPoint<TOhlcItem>): Single begin Result := Ohlc.Data.Open; end);
end;
function TDataSeriesOhlcHelper.ToVolume: TDataSeries<Single>;
begin
Result := Self.Convert<Single>(function(const Ohlc: TDataPoint<TOhlcItem>): Single begin Result := Ohlc.Data.Volume; end);
end;
{ TAskBidItem }
constructor TAskBidItem.Create(AAsk, ABid: Single);
constructor TAskBidItem.Create(AAsk, ABid: Double);
begin
Ask := AAsk;
Bid := ABid;
@@ -198,7 +254,7 @@ end;
{ TOhlcItem }
constructor TOhlcItem.Create(AOpen, AHigh, ALow, AClose, AVolume: Single);
constructor TOhlcItem.Create(AOpen, AHigh, ALow, AClose, AVolume: Double);
begin
Open := AOpen;
High := AHigh;
@@ -225,12 +281,19 @@ end;
function TDataSeries<T>.Add(const Data: TArray<TDataPoint<T>>; First, Count: Integer): TDataSeries<T>;
begin
{$ifdef DEBUG}
for var i := First + 1 to First + Count - 1 do
Assert(Data[i].Time >= Data[i - 1].Time, 'Input array for Add is not chronologically sorted');
if FDataSeries.Count > 0 then
Assert(Data[First].Time >= FDataSeries[0].Time, 'First new item is older than last existing item');
{$endif}
Result := FDataSeries.Add(Data, First, Count);
end;
function TDataSeries<T>.Add(const Data: TArray<TDataPoint<T>>): TDataSeries<T>;
begin
Result := FDataSeries.Add(Data, 0, Length(Data));
Result := Add(Data, 0, Length(Data));
end;
function TDataSeries<T>.Convert<S>(const Func: TConvertFunc<S>): TDataSeries<S>;
@@ -240,7 +303,8 @@ end;
class function TDataSeries<T>.CreateDataSeries(Lookback: Int64; const Data: TArray<TDataPoint<T>> = nil): TDataSeries<T>;
begin
Result := TMycDataSeries<T>.CreateDataSeries(Lookback, TMycDataArray<T>.CreateFromArray(Data, 0, Length(Data)), Length(Data));
Result :=
TMycDataSeries<T>.CreateDataSeries(Lookback, TMycDataArray<TDataPoint<T>>.CreateFromArray(Data, 0, Length(Data)), Length(Data));
end;
class operator TDataSeries<T>.Finalize(var Dest: TDataSeries<T>);
@@ -350,4 +414,26 @@ begin
Result[i] := FDataSeries[n - i].Data;
end;
constructor TMycGenericProcessor<T>.Create(const AProc: TProc);
begin
inherited Create;
FProc := AProc;
end;
procedure TMycGenericProcessor<T>.ProcessData(const Value: T);
begin
FProc(Value);
end;
constructor TMycContainedProcessor<T>.Create(const Controller: IInterface; const AProc: TProc);
begin
inherited Create(Controller);
FProc := AProc;
end;
procedure TMycContainedProcessor<T>.ProcessData(const Value: T);
begin
FProc(Value);
end;
end.