Unit refactoring
Fixed massive heap corruption bug in TDataRecord
This commit is contained in:
+31
-236
@@ -9,9 +9,8 @@ uses
|
||||
Myc.Signals,
|
||||
Myc.Mutable,
|
||||
Myc.Core.Notifier,
|
||||
Myc.Data.Pipeline,
|
||||
Myc.Trade.Types,
|
||||
Myc.Data.Series;
|
||||
Myc.Data.Series,
|
||||
Myc.Data.Pipeline;
|
||||
|
||||
type
|
||||
// Abstract base class for data consumers.
|
||||
@@ -31,16 +30,6 @@ type
|
||||
class property Null: IConsumer<T> read FNull;
|
||||
end;
|
||||
|
||||
// A consumer implementation that is owned by a controller.
|
||||
TMycGenericConsumer<T> = class(TMycConsumer<T>)
|
||||
private
|
||||
FProc: TConstFunc<T, TState>;
|
||||
protected
|
||||
function Consume(const Value: T): TState; override; final;
|
||||
public
|
||||
constructor Create(const Controller: IInterface; const AProc: TConstFunc<T, TState>);
|
||||
end;
|
||||
|
||||
TMycProducer<T> = class(TInterfacedObject, IProducer<T>)
|
||||
strict private
|
||||
type
|
||||
@@ -63,16 +52,14 @@ type
|
||||
class property Null: IProducer<T> read FNull;
|
||||
end;
|
||||
|
||||
// Concrete producer that manages a list of consumers (listeners).
|
||||
TMycContainedProducer<T> = class(TContainedObject, IProducer<T>)
|
||||
// A consumer implementation that is owned by a controller.
|
||||
TMycGenericConsumer<T> = class(TMycConsumer<T>)
|
||||
private
|
||||
FListeners: TMycNotifyList<IConsumer<T>>;
|
||||
FProc: TConvertFunc<T, TState>;
|
||||
protected
|
||||
function Consume(const Value: T): TState; override; final;
|
||||
public
|
||||
constructor Create(const Controller: IInterface);
|
||||
destructor Destroy; override;
|
||||
function Broadcast(const Value: T): TState;
|
||||
function Link(const Consumer: IConsumer<T>): TTag;
|
||||
procedure Unlink(Tag: TTag);
|
||||
constructor Create(const Controller: IInterface; const AProc: TConvertFunc<T, TState>);
|
||||
end;
|
||||
|
||||
// Abstract base class for components that now act as a producer and contain a consumer.
|
||||
@@ -101,33 +88,45 @@ type
|
||||
class property Null: IConverter<S, T> read FNull;
|
||||
end;
|
||||
|
||||
// Concrete producer that manages a list of consumers (listeners).
|
||||
TMycContainedProducer<T> = class(TContainedObject, IProducer<T>)
|
||||
private
|
||||
FListeners: TMycNotifyList<IConsumer<T>>;
|
||||
public
|
||||
constructor Create(const Controller: IInterface);
|
||||
destructor Destroy; override;
|
||||
function Broadcast(const Value: T): TState;
|
||||
function Link(const Consumer: IConsumer<T>): TTag;
|
||||
procedure Unlink(Tag: TTag);
|
||||
end;
|
||||
|
||||
// A generic converter that uses a function reference for the conversion logic.
|
||||
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
||||
private
|
||||
FFunc: TConstFunc<S, T>;
|
||||
FFunc: TConvertFunc<S, T>;
|
||||
protected
|
||||
function Consume(const Value: S): TState; override;
|
||||
public
|
||||
constructor Create(const AFunc: TConstFunc<S, T>);
|
||||
constructor Create(const AFunc: TConvertFunc<S, T>);
|
||||
end;
|
||||
|
||||
TMycGenericAggregator<S, T> = class(TMycConverter<S, T>)
|
||||
private
|
||||
FFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>;
|
||||
FFunc: TAggregateFunc<S, T>;
|
||||
protected
|
||||
function Consume(const Value: S): TState; override;
|
||||
public
|
||||
constructor Create(const AFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>);
|
||||
constructor Create(const AFunc: TAggregateFunc<S, T>);
|
||||
end;
|
||||
|
||||
TMycGenericParallelConverter<S, T> = class(TMycConverter<S, T>)
|
||||
private
|
||||
FFunc: TConstFunc<S, T>;
|
||||
FFunc: TConvertFunc<S, T>;
|
||||
FQueue: TState;
|
||||
protected
|
||||
function Consume(const Value: S): TState; override;
|
||||
public
|
||||
constructor Create(const AFunc: TConstFunc<S, T>);
|
||||
constructor Create(const AFunc: TConvertFunc<S, T>);
|
||||
end;
|
||||
|
||||
TMycIdentityConverter<T> = class(TMycConverter<T, T>)
|
||||
@@ -187,21 +186,6 @@ type
|
||||
class function CreateDataEndpoint(Lookback: Integer; out Series: TLazy<TSeries<T>>): IConsumer<T>;
|
||||
end;
|
||||
|
||||
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;
|
||||
|
||||
TMycParallelConverter<T> = class(TMycConverter<T, T>)
|
||||
private
|
||||
FQueue: TState;
|
||||
@@ -209,21 +193,6 @@ type
|
||||
function Consume(const Value: T): TState; override; final;
|
||||
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;
|
||||
|
||||
// Endpoint that collects data into a series.
|
||||
TMycDataJoin<T> = class(TInterfacedObject, IProducer<TArray<T>>)
|
||||
private
|
||||
@@ -273,11 +242,7 @@ type
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.TypInfo,
|
||||
System.RTTI,
|
||||
System.DateUtils,
|
||||
System.Math,
|
||||
Winapi.Windows,
|
||||
Myc.TaskManager;
|
||||
|
||||
class constructor TMycConsumer<T>.CreateClass;
|
||||
@@ -392,7 +357,7 @@ end;
|
||||
|
||||
{ TMycGenericConverter<S, T> }
|
||||
|
||||
constructor TMycGenericConverter<S, T>.Create(const AFunc: TConstFunc<S, T>);
|
||||
constructor TMycGenericConverter<S, T>.Create(const AFunc: TConvertFunc<S, T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
@@ -462,7 +427,7 @@ end;
|
||||
|
||||
{ TMycGenericConsumer<T> }
|
||||
|
||||
constructor TMycGenericConsumer<T>.Create(const Controller: IInterface; const AProc: TConstFunc<T, TState>);
|
||||
constructor TMycGenericConsumer<T>.Create(const Controller: IInterface; const AProc: TConvertFunc<T, TState>);
|
||||
begin
|
||||
inherited Create(Controller);
|
||||
FProc := AProc;
|
||||
@@ -550,94 +515,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ 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;
|
||||
|
||||
{ TMycSequence<T> }
|
||||
|
||||
constructor TMycSequence<T>.Create(ACount: Integer);
|
||||
@@ -688,7 +565,7 @@ end;
|
||||
|
||||
{ TMycGenericParallelConverter<S, T> }
|
||||
|
||||
constructor TMycGenericParallelConverter<S, T>.Create(const AFunc: TConstFunc<S, T>);
|
||||
constructor TMycGenericParallelConverter<S, T>.Create(const AFunc: TConvertFunc<S, T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
@@ -711,88 +588,6 @@ begin
|
||||
FQueue := Result;
|
||||
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;
|
||||
|
||||
{ TMycDataJoin<T> }
|
||||
|
||||
constructor TMycDataJoin<T>.Create(ACount: Integer);
|
||||
@@ -807,7 +602,7 @@ begin
|
||||
FContainedProvider := TMycContainedProducer<TArray<T>>.Create(Self);
|
||||
|
||||
var cFunc :=
|
||||
function(Idx: Integer): TConstFunc<T, TState>
|
||||
function(Idx: Integer): TConvertFunc<T, TState>
|
||||
begin
|
||||
Result := function(const Value: T): TState begin Result := Consume(Idx, Value); end
|
||||
end;
|
||||
@@ -882,7 +677,7 @@ end;
|
||||
|
||||
{ TMycGenericAggregator<S, T> }
|
||||
|
||||
constructor TMycGenericAggregator<S, T>.Create(const AFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>);
|
||||
constructor TMycGenericAggregator<S, T>.Create(const AFunc: TAggregateFunc<S, T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
|
||||
Reference in New Issue
Block a user