Files
MycLib/Src/Myc.Trade.Pipeline.Impl.pas
T
Michael Schimmel aa53a88953 Unit refactoring
Fixed massive heap corruption bug in TDataRecord
2025-07-25 11:54:53 +02:00

219 lines
9.3 KiB
ObjectPascal

unit Myc.Trade.Pipeline.Impl;
interface
uses
Myc.Signals,
Myc.Data.Pipeline,
Myc.Data.Pipeline.Impl,
Myc.Trade.Types;
type
TTickAggregation = class(TMycConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>)
private
FTimeframe: TTimeframe;
FCurrentBar: TDataPoint<TOhlcItem>;
function GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
function GetCurrentBar: TDataPoint<TOhlcItem>;
function GetTimeframe: TTimeframe;
protected
function Consume(const Value: TDataPoint<Double>): TState; override;
public
constructor Create(const ATimeframe: TTimeframe);
property CurrentBar: TDataPoint<TOhlcItem> read GetCurrentBar;
property Timeframe: TTimeframe read GetTimeframe;
end;
TOhlcAggregation = class(TMycConverter<TDataPoint<TOhlcItem>, TDataPoint<TOhlcItem>>)
private
FTimeframe: TTimeframe;
FCurrentBar: TDataPoint<TOhlcItem>;
function GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
function GetCurrentBar: TDataPoint<TOhlcItem>;
function GetTimeframe: TTimeframe;
protected
function Consume(const Value: TDataPoint<TOhlcItem>): TState; override;
public
constructor Create(const ATimeframe: TTimeframe);
property CurrentBar: TDataPoint<TOhlcItem> read GetCurrentBar;
property Timeframe: TTimeframe read GetTimeframe;
end;
implementation
uses
System.Math,
System.DateUtils;
{ TTickAggregation }
constructor TTickAggregation.Create(const ATimeframe: TTimeframe);
begin
inherited Create;
FTimeframe := ATimeframe;
end;
function TTickAggregation.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
var
baseTime: TDateTime;
begin
// Implementation is unchanged
baseTime := RecodeMilliSecond(TimeStamp, 0);
case Timeframe of
S: Result := baseTime;
S5: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 5));
S15: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 15));
S30: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 30));
M: Result := RecodeSecond(baseTime, 0);
M2: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 2));
M3: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 3));
M5: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 5));
M10: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 10));
M15: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 15));
M30: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 30));
H: Result := RecodeMinute(RecodeSecond(baseTime, 0), 0);
H2: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 2));
H3: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 3));
H4: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 4));
H8: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 8));
H12: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 12));
D: Result := StartOfTheDay(TimeStamp);
D2: Result := Floor(TimeStamp) - (Floor(TimeStamp) mod 2);
D3: Result := Floor(TimeStamp) - (Floor(TimeStamp) mod 3);
W: Result := TimeStamp.StartOfTheWeek;
MN: Result := TimeStamp.StartOfTheMonth;
MN3: Result := RecodeMonth(TimeStamp.StartOfTheMonth, (MonthOf(TimeStamp) - 1) div 3 * 3 + 1);
MN6: Result := RecodeMonth(TimeStamp.StartOfTheMonth, (MonthOf(TimeStamp) - 1) div 6 * 6 + 1);
Y: Result := TimeStamp.StartOfTheYear;
else
Result := 0;
end;
end;
function TTickAggregation.GetCurrentBar: TDataPoint<TOhlcItem>;
begin
Result := FCurrentBar;
end;
function TTickAggregation.GetTimeframe: TTimeframe;
begin
Result := FTimeframe;
end;
function TTickAggregation.Consume(const Value: TDataPoint<Double>): TState;
var
barStartTime: TDateTime;
lastBarTime: TDateTime;
begin
barStartTime := GetBarStartTime(Value.Time, FTimeframe);
lastBarTime := FCurrentBar.Time;
if (barStartTime > lastBarTime) then
begin
if (lastBarTime > 0) then
begin
Result := Broadcast(FCurrentBar);
end;
FCurrentBar.Data.Open := Value.Data;
FCurrentBar.Data.High := Value.Data;
FCurrentBar.Data.Low := Value.Data;
FCurrentBar.Data.Close := Value.Data;
FCurrentBar.Data.Volume := 1;
FCurrentBar.Time := barStartTime;
end
else
begin
if Value.Data > FCurrentBar.Data.High then
FCurrentBar.Data.High := Value.Data;
if Value.Data < FCurrentBar.Data.Low then
FCurrentBar.Data.Low := Value.Data;
FCurrentBar.Data.Close := Value.Data;
FCurrentBar.Data.Volume := FCurrentBar.Data.Volume + 1;
end;
end;
{ TOhlcAggregation }
constructor TOhlcAggregation.Create(const ATimeframe: TTimeframe);
begin
inherited Create;
FTimeframe := ATimeframe;
end;
function TOhlcAggregation.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
begin
// Same implementation as TTickAggregation
var baseTime := RecodeMilliSecond(TimeStamp, 0);
case Timeframe of
S: Result := baseTime;
S5: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 5));
S15: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 15));
S30: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 30));
M: Result := RecodeSecond(baseTime, 0);
M2: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 2));
M3: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 3));
M5: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 5));
M10: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 10));
M15: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 15));
M30: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 30));
H: Result := RecodeMinute(RecodeSecond(baseTime, 0), 0);
H2: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 2));
H3: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 3));
H4: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 4));
H8: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 8));
H12: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 12));
D: Result := StartOfTheDay(TimeStamp);
D2: Result := Floor(TimeStamp) - (Floor(TimeStamp) mod 2);
D3: Result := Floor(TimeStamp) - (Floor(TimeStamp) mod 3);
W: Result := TimeStamp.StartOfTheWeek;
MN: Result := TimeStamp.StartOfTheMonth;
MN3: Result := RecodeMonth(TimeStamp.StartOfTheMonth, (MonthOf(TimeStamp) - 1) div 3 * 3 + 1);
MN6: Result := RecodeMonth(TimeStamp.StartOfTheMonth, (MonthOf(TimeStamp) - 1) div 6 * 6 + 1);
Y: Result := TimeStamp.StartOfTheYear;
else
Result := 0;
end;
end;
function TOhlcAggregation.GetCurrentBar: TDataPoint<TOhlcItem>;
begin
Result := FCurrentBar;
end;
function TOhlcAggregation.GetTimeframe: TTimeframe;
begin
Result := FTimeframe;
end;
function TOhlcAggregation.Consume(const Value: TDataPoint<TOhlcItem>): TState;
var
barStartTime: TDateTime;
lastBarTime: TDateTime;
begin
barStartTime := GetBarStartTime(Value.Time, FTimeframe);
lastBarTime := FCurrentBar.Time;
if (barStartTime > lastBarTime) then
begin
if (lastBarTime > 0) then
begin
Result := Broadcast(FCurrentBar);
end;
FCurrentBar.Data := Value.Data;
FCurrentBar.Time := barStartTime;
end
else
begin
if Value.Data.High > FCurrentBar.Data.High then
FCurrentBar.Data.High := Value.Data.High;
if Value.Data.Low < FCurrentBar.Data.Low then
FCurrentBar.Data.Low := Value.Data.Low;
FCurrentBar.Data.Close := Value.Data.Close;
FCurrentBar.Data.Volume := FCurrentBar.Data.Volume + Value.Data.Volume;
end;
end;
end.