diff --git a/AuraTrader/AuraTrader.dpr b/AuraTrader/AuraTrader.dpr
index 31edf55..19e7cf9 100644
--- a/AuraTrader/AuraTrader.dpr
+++ b/AuraTrader/AuraTrader.dpr
@@ -6,7 +6,8 @@ uses
FMX.Forms,
MainForm in 'MainForm.pas' {Form1},
TestModule in 'TestModule.pas',
- DynamicFMXControl in 'DynamicFMXControl.pas';
+ DynamicFMXControl in 'DynamicFMXControl.pas',
+ Myc.Trade.Pipeline.Impl in '..\Src\Myc.Trade.Pipeline.Impl.pas';
{$R *.res}
diff --git a/AuraTrader/AuraTrader.dproj b/AuraTrader/AuraTrader.dproj
index 3808279..15ce6c0 100644
--- a/AuraTrader/AuraTrader.dproj
+++ b/AuraTrader/AuraTrader.dproj
@@ -137,6 +137,7 @@
+
Base
diff --git a/AuraTrader/MainForm.pas b/AuraTrader/MainForm.pas
index 0f6102e..9a33a0b 100644
--- a/AuraTrader/MainForm.pas
+++ b/AuraTrader/MainForm.pas
@@ -32,6 +32,7 @@ uses
Myc.Futures,
Myc.Trade.Types,
Myc.Trade.DataStream,
+ Myc.Trade.Pipeline,
Myc.Data.Series,
Myc.Data.Pipeline,
Myc.Signals,
@@ -298,7 +299,7 @@ begin
var ticker := TConverter.CreateIdentity>;
Result := ticker.Consumer;
- var OhlcPoint := ticker.Producer.Chain>(TConverter.CreateOhlcAggregation(Timeframe));
+ var OhlcPoint := ticker.Producer.Chain>(TTradeConverter.CreateOhlcAggregation(Timeframe));
var Ohlc := OhlcPoint.Field('Data');
@@ -423,7 +424,7 @@ begin
var equity :=
TConverter.CreateAggregation(
- function(const Value: Double; const Broadcast: TConverter.TBroadcastProc): TState
+ function(const Value: Double; const Broadcast: TBroadcastFunc): TState
begin
if not FInit then
begin
@@ -599,7 +600,7 @@ begin
var ticker := TConverter.CreateIdentity>;
- var OhlcPoint := ticker.Producer.Chain>(TConverter.CreateOhlcAggregation(timeframe));
+ var OhlcPoint := ticker.Producer.Chain>(TTradeConverter.CreateOhlcAggregation(timeframe));
// var OhlcTicker := TConverter.CreateIdentity>;
// var OhlcPoint := OhlcTicker.Sender;
diff --git a/AuraTrader/StrategyTest.pas b/AuraTrader/StrategyTest.pas
index 0d5a999..14e72d9 100644
--- a/AuraTrader/StrategyTest.pas
+++ b/AuraTrader/StrategyTest.pas
@@ -6,6 +6,7 @@ uses
Myc.Signals,
Myc.Data.Pipeline,
Myc.Trade.Types,
+ Myc.Trade.Pipeline,
Myc.Trade.Indicators;
function CreateStrategy1(Timeframe: TTimeframe): TConverter, Double>; overload;
@@ -28,7 +29,7 @@ type
begin
var ticker := TConverter.CreateIdentity>;
- var OhlcPoint := ticker.Producer.Chain>(TConverter.CreateOhlcAggregation(Timeframe));
+ var OhlcPoint := ticker.Producer.Chain>(TTradeConverter.CreateOhlcAggregation(Timeframe));
var Ohlc := OhlcPoint.Field('Data');
@@ -97,7 +98,7 @@ begin
var positionManager :=
signalGenerator.Chain(
TConverter.CreateAggregation(
- function(const Value: TSignalEvent; const Broadcast: TConverter.TBroadcastProc): TState
+ function(const Value: TSignalEvent; const Broadcast: TBroadcastFunc): TState
var
pnl: Double;
begin
@@ -164,7 +165,7 @@ begin
var equity :=
positionManager.Chain(
TConverter.CreateAggregation(
- function(const Value: Double; const Broadcast: TConverter.TBroadcastProc): TState
+ function(const Value: Double; const Broadcast: TBroadcastFunc): TState
begin
if not FInit then
begin
diff --git a/Src/Myc.Data.Pipeline.Impl.pas b/Src/Myc.Data.Pipeline.Impl.pas
index 0747a15..71cc27c 100644
--- a/Src/Myc.Data.Pipeline.Impl.pas
+++ b/Src/Myc.Data.Pipeline.Impl.pas
@@ -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 read FNull;
end;
- // A consumer implementation that is owned by a controller.
- TMycGenericConsumer = class(TMycConsumer)
- private
- FProc: TConstFunc;
- protected
- function Consume(const Value: T): TState; override; final;
- public
- constructor Create(const Controller: IInterface; const AProc: TConstFunc);
- end;
-
TMycProducer = class(TInterfacedObject, IProducer)
strict private
type
@@ -63,16 +52,14 @@ type
class property Null: IProducer read FNull;
end;
- // Concrete producer that manages a list of consumers (listeners).
- TMycContainedProducer = class(TContainedObject, IProducer)
+ // A consumer implementation that is owned by a controller.
+ TMycGenericConsumer = class(TMycConsumer)
private
- FListeners: TMycNotifyList>;
+ FProc: TConvertFunc;
+ 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): TTag;
- procedure Unlink(Tag: TTag);
+ constructor Create(const Controller: IInterface; const AProc: TConvertFunc);
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 read FNull;
end;
+ // Concrete producer that manages a list of consumers (listeners).
+ TMycContainedProducer = class(TContainedObject, IProducer)
+ private
+ FListeners: TMycNotifyList>;
+ public
+ constructor Create(const Controller: IInterface);
+ destructor Destroy; override;
+ function Broadcast(const Value: T): TState;
+ function Link(const Consumer: IConsumer): TTag;
+ procedure Unlink(Tag: TTag);
+ end;
+
// A generic converter that uses a function reference for the conversion logic.
TMycGenericConverter = class(TMycConverter)
private
- FFunc: TConstFunc;
+ FFunc: TConvertFunc;
protected
function Consume(const Value: S): TState; override;
public
- constructor Create(const AFunc: TConstFunc);
+ constructor Create(const AFunc: TConvertFunc);
end;
TMycGenericAggregator = class(TMycConverter)
private
- FFunc: TConstFunc.TBroadcastProc, TState>;
+ FFunc: TAggregateFunc;
protected
function Consume(const Value: S): TState; override;
public
- constructor Create(const AFunc: TConstFunc.TBroadcastProc, TState>);
+ constructor Create(const AFunc: TAggregateFunc);
end;
TMycGenericParallelConverter = class(TMycConverter)
private
- FFunc: TConstFunc;
+ FFunc: TConvertFunc;
FQueue: TState;
protected
function Consume(const Value: S): TState; override;
public
- constructor Create(const AFunc: TConstFunc);
+ constructor Create(const AFunc: TConvertFunc);
end;
TMycIdentityConverter = class(TMycConverter)
@@ -187,21 +186,6 @@ type
class function CreateDataEndpoint(Lookback: Integer; out Series: TLazy>): IConsumer;
end;
- TTickAggregation = class(TMycConverter, TDataPoint>)
- private
- FTimeframe: TTimeframe;
- FCurrentBar: TDataPoint;
- function GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
- function GetCurrentBar: TDataPoint;
- function GetTimeframe: TTimeframe;
- protected
- function Consume(const Value: TDataPoint): TState; override;
- public
- constructor Create(const ATimeframe: TTimeframe);
- property CurrentBar: TDataPoint read GetCurrentBar;
- property Timeframe: TTimeframe read GetTimeframe;
- end;
-
TMycParallelConverter = class(TMycConverter)
private
FQueue: TState;
@@ -209,21 +193,6 @@ type
function Consume(const Value: T): TState; override; final;
end;
- TOhlcAggregation = class(TMycConverter, TDataPoint>)
- private
- FTimeframe: TTimeframe;
- FCurrentBar: TDataPoint;
- function GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
- function GetCurrentBar: TDataPoint;
- function GetTimeframe: TTimeframe;
- protected
- function Consume(const Value: TDataPoint): TState; override;
- public
- constructor Create(const ATimeframe: TTimeframe);
- property CurrentBar: TDataPoint read GetCurrentBar;
- property Timeframe: TTimeframe read GetTimeframe;
- end;
-
// Endpoint that collects data into a series.
TMycDataJoin = class(TInterfacedObject, IProducer>)
private
@@ -273,11 +242,7 @@ type
implementation
uses
- System.TypInfo,
System.RTTI,
- System.DateUtils,
- System.Math,
- Winapi.Windows,
Myc.TaskManager;
class constructor TMycConsumer.CreateClass;
@@ -392,7 +357,7 @@ end;
{ TMycGenericConverter }
-constructor TMycGenericConverter.Create(const AFunc: TConstFunc);
+constructor TMycGenericConverter.Create(const AFunc: TConvertFunc);
begin
inherited Create;
FFunc := AFunc;
@@ -462,7 +427,7 @@ end;
{ TMycGenericConsumer }
-constructor TMycGenericConsumer.Create(const Controller: IInterface; const AProc: TConstFunc);
+constructor TMycGenericConsumer.Create(const Controller: IInterface; const AProc: TConvertFunc);
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;
-begin
- Result := FCurrentBar;
-end;
-
-function TTickAggregation.GetTimeframe: TTimeframe;
-begin
- Result := FTimeframe;
-end;
-
-function TTickAggregation.Consume(const Value: TDataPoint): 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 }
constructor TMycSequence.Create(ACount: Integer);
@@ -688,7 +565,7 @@ end;
{ TMycGenericParallelConverter }
-constructor TMycGenericParallelConverter.Create(const AFunc: TConstFunc);
+constructor TMycGenericParallelConverter.Create(const AFunc: TConvertFunc);
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;
-begin
- Result := FCurrentBar;
-end;
-
-function TOhlcAggregation.GetTimeframe: TTimeframe;
-begin
- Result := FTimeframe;
-end;
-
-function TOhlcAggregation.Consume(const Value: TDataPoint): 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 }
constructor TMycDataJoin.Create(ACount: Integer);
@@ -807,7 +602,7 @@ begin
FContainedProvider := TMycContainedProducer>.Create(Self);
var cFunc :=
- function(Idx: Integer): TConstFunc
+ function(Idx: Integer): TConvertFunc
begin
Result := function(const Value: T): TState begin Result := Consume(Idx, Value); end
end;
@@ -882,7 +677,7 @@ end;
{ TMycGenericAggregator }
-constructor TMycGenericAggregator.Create(const AFunc: TConstFunc.TBroadcastProc, TState>);
+constructor TMycGenericAggregator.Create(const AFunc: TAggregateFunc);
begin
inherited Create;
FFunc := AFunc;
diff --git a/Src/Myc.Data.Pipeline.pas b/Src/Myc.Data.Pipeline.pas
index 160133e..8e113d4 100644
--- a/Src/Myc.Data.Pipeline.pas
+++ b/Src/Myc.Data.Pipeline.pas
@@ -5,7 +5,6 @@ interface
uses
Myc.Signals,
Myc.Mutable,
- Myc.Trade.Types,
Myc.Data.Series,
Myc.Data.Records;
@@ -31,6 +30,10 @@ type
property Consumer: IConsumer read GetConsumer;
end;
+ TConvertFunc = reference to function(const Value: S): T;
+ TBroadcastFunc = reference to function(const Value: T): TState;
+ TAggregateFunc = reference to function(const Value: S; const Broadcast: TBroadcastFunc): TState;
+
// Interface helper for IProducer providing the null object pattern nad subscriptions for linked consumers.
TProducer = record
public
@@ -64,7 +67,7 @@ type
// Chain consumers
function Chain(const Next: IConsumer): IConsumer; overload; inline;
function Chain(const Next: IConverter): TProducer; overload; inline;
- function Chain(const Func: TConstFunc): TProducer; overload; inline;
+ function Chain(const Func: TConvertFunc): TProducer; overload; inline;
// Extracts the field of a record by it's name (using RTTI).
function Field(const FieldName: String): TProducer; inline;
@@ -76,9 +79,6 @@ type
// Interface helper for IConverter providing the null object pattern.
TConverter = record
- public
- type
- TBroadcastProc = reference to function(const Value: T): TState;
private
FConverter: IConverter;
function GetConsumer: IConsumer; inline;
@@ -94,8 +94,8 @@ type
class function Construct(const Consumer: IConsumer; const Producer: TProducer): TConverter; static;
- class function CreateConverter(const Func: TConstFunc): TConverter; static;
- class function CreateAggregation(const Func: TConstFunc): TConverter; static;
+ class function CreateConverter(const Func: TConvertFunc): TConverter; static;
+ class function CreateAggregation(const Func: TAggregateFunc): TConverter; static;
property Consumer: IConsumer read GetConsumer;
property Producer: TProducer read GetProducer;
@@ -114,9 +114,6 @@ type
class function CreateTicker: TConverter, T>; static;
class function CreateIdentity: TConverter; static;
- class function CreateTickAggregation(Timeframe: TTimeframe): TConverter, TDataPoint>; static;
- class function CreateOhlcAggregation(Timeframe: TTimeframe): TConverter, TDataPoint>; static;
-
class function CreateEndpoint(Lookback: Int64; out Series: TLazy>): IConsumer; static;
class function Join(const Producers: TArray>): TProducer>; static;
@@ -188,7 +185,7 @@ begin
Result := Next;
end;
-function TProducer.Chain(const Func: TConstFunc): TProducer;
+function TProducer.Chain(const Func: TConvertFunc): TProducer;
begin
Result := Chain(TMycGenericConverter.Create(Func));
end;
@@ -251,12 +248,12 @@ begin
Result := TMycComposedConverter.Create(Consumer, Producer);
end;
-class function TConverter.CreateAggregation(const Func: TConstFunc): TConverter;
+class function TConverter.CreateAggregation(const Func: TAggregateFunc): TConverter;
begin
Result := TMycGenericAggregator.Create(Func);
end;
-class function TConverter.CreateConverter(const Func: TConstFunc): TConverter;
+class function TConverter.CreateConverter(const Func: TConvertFunc): TConverter;
begin
Result := TMycGenericConverter.Create(Func);
end;
@@ -291,11 +288,6 @@ begin
Result := A.FConverter;
end;
-class function TConverter.CreateTickAggregation(Timeframe: TTimeframe): TConverter, TDataPoint>;
-begin
- Result := TTickAggregation.Create(Timeframe);
-end;
-
{ TConverter }
class function TConverter.CreateCounter: TConverter;
@@ -318,11 +310,6 @@ begin
Result := TMycTicker.Create;
end;
-class function TConverter.CreateOhlcAggregation(Timeframe: TTimeframe): TConverter, TDataPoint>;
-begin
- Result := TOhlcAggregation.Create(Timeframe);
-end;
-
class function TConverter.DataMapping(
const Inputs: TArray;
const Output: TDataRecord.TLayout
@@ -350,7 +337,7 @@ begin
Result := TDataRecord.Create(Output);
for var i := 0 to High(Idxs) do
for var j := 0 to High(Idxs[i]) do
- Inputs[i].CopyField(Idxs[i][j].FromIdx, Result, Idxs[i][j].ToIdx);
+ Result.CopyValue(Inputs[i], Idxs[i][j].FromIdx, Idxs[i][j].ToIdx);
end
);
end;
diff --git a/Src/Myc.Data.Records.pas b/Src/Myc.Data.Records.pas
index 701c3c8..fa35036 100644
--- a/Src/Myc.Data.Records.pas
+++ b/Src/Myc.Data.Records.pas
@@ -4,8 +4,6 @@ interface
uses
System.SysUtils,
- System.Generics.Collections,
- System.Rtti,
System.TypInfo;
type
@@ -23,8 +21,11 @@ type
procedure FromType(const [ref] Buffer: TBytes; SrcType: PTypeInfo; const Src);
procedure ToType(const [ref] Buffer: TBytes; DstType: PTypeInfo; var Dst);
- procedure Assign(const [ref] Buffer: TBytes; const Src); overload;
- procedure Finalize(const [ref] Buffer: TBytes);
+ procedure InitField(const [ref] Buffer: TBytes);
+ procedure AssignField(const [ref] Dest: TBytes; const [ref] Source: TBytes);
+ procedure FinalizeField(const [ref] Buffer: TBytes);
+
+ procedure CopyField(Src, Dst: Pointer);
property Offset: Integer read FOffset;
@@ -51,6 +52,7 @@ type
FFields: TArray;
constructor Create(const AFields: TArray);
+
public
class function FromRecord: TLayout; static;
class function Construct(const Def: TArray): TLayout; static;
@@ -60,15 +62,17 @@ type
end;
const
- Align = 8;
+ Align = sizeof(Pointer);
private
FLayout: TLayout;
FBuffer: TBytes;
public
- constructor Create(const ALayout: TLayout; const ABuffer: TBytes = nil);
+ constructor Create(const ALayout: TLayout);
+
class operator Finalize(var Dest: TDataRecord);
+ class operator Assign(var Dest: TDataRecord; const [ref] Src: TDataRecord);
class function FromRecord: TDataRecord; overload; static;
class function FromRecord(const Src: T): TDataRecord; overload; static;
@@ -79,52 +83,54 @@ type
function GetValue(const Name: String): T; overload;
procedure GetValue(Idx: Integer; out Value); overload;
- procedure CopyField(Idx: Integer; Dst: TDataRecord; DstIdx: Integer);
+ procedure CopyValue(const SrcRec: TDataRecord; SrcIdx, DstIdx: Integer);
property Layout: TLayout read FLayout;
end;
+implementation
+
+uses
+ System.Rtti;
+
const
DataSize: array[TDataRecord.TFieldType] of Integer =
(sizeof(Double), sizeof(Int64), sizeof(String), sizeof(TDateTime), sizeof(TDataRecord));
-implementation
+{ TDataRecord }
-uses
- System.Classes;
-
-constructor TDataRecord.Create(const ALayout: TLayout; const ABuffer: TBytes = nil);
+constructor TDataRecord.Create(const ALayout: TLayout);
begin
FLayout := ALayout;
- FBuffer := ABuffer;
var bufSize := 0;
if Length(FLayout.Fields) > 0 then
with FLayout.Fields[High(FLayout.Fields)] do
bufSize := Offset + AlignedSize;
- if FBuffer = nil then
- SetLength(FBuffer, bufSize)
- else
- Assert(Length(FBuffer) >= bufSize);
+ SetLength(FBuffer, bufSize);
+ for var i := 0 to High(FLayout.Fields) do
+ FLayout.Fields[i].InitField(FBuffer);
end;
-procedure TDataRecord.CopyField(Idx: Integer; Dst: TDataRecord; DstIdx: Integer);
+procedure TDataRecord.CopyValue(const SrcRec: TDataRecord; SrcIdx, DstIdx: Integer);
begin
- Assert(Dst.Layout.Fields[DstIdx].FieldType = FLayout.Fields[Idx].FieldType);
- Dst.Layout.Fields[DstIdx].Assign(Dst.FBuffer, FBuffer[FLayout.Fields[DstIdx].Offset])
-end;
+ Assert(SrcRec.Layout.Fields[SrcIdx].FieldType = FLayout.Fields[SrcIdx].FieldType);
-{ TDataRecord }
+ var Src := @SrcRec.FBuffer[SrcRec.Layout.Fields[SrcIdx].Offset];
+ var Dst := @FBuffer[FLayout.Fields[DstIdx].Offset];
+
+ FLayout.Fields[SrcIdx].CopyField(Src, Dst);
+end;
class function TDataRecord.FromRecord: TDataRecord;
begin
- Result := TDataRecord.Create(TLayout.FromRecord);
+ Result.Create(TLayout.FromRecord);
end;
class function TDataRecord.FromRecord(const Src: T): TDataRecord;
begin
- Result := FromRecord;
+ Result.Create(TLayout.FromRecord);
var ctx := TRttiContext.Create;
var rttiType := ctx.GetType(TypeInfo(T));
@@ -187,10 +193,22 @@ begin
FLayout.Fields[idx].FromType(FBuffer, TypeInfo(T), Value);
end;
+class operator TDataRecord.Assign(var Dest: TDataRecord; const [ref] Src: TDataRecord);
+begin
+ if Dest.FLayout.FFields <> Src.FLayout.FFields then
+ begin
+ Finalize(Dest);
+ Dest.Create(Src.Layout);
+ end;
+
+ for var i := 0 to High(Dest.Layout.Fields) do
+ Dest.Layout.Fields[i].AssignField(Dest.FBuffer, Src.FBuffer);
+end;
+
class operator TDataRecord.Finalize(var Dest: TDataRecord);
begin
- for var i := 0 to High(Dest.FLayout.Fields) do
- Dest.FLayout.Fields[i].Finalize(Dest.FBuffer);
+ for var i := High(Dest.FLayout.Fields) downto 0 do
+ Dest.Layout.Fields[i].FinalizeField(Dest.FBuffer);
end;
constructor TDataRecord.TField.Create(const AName: string; AFieldType: TFieldType; AOffset: Integer);
@@ -200,7 +218,7 @@ begin
FOffset := AOffset;
end;
-procedure TDataRecord.TField.Finalize(const [ref] Buffer: TBytes);
+procedure TDataRecord.TField.InitField(const [ref] Buffer: TBytes);
begin
if not (FFieldType in [dfString, dfRecord]) then
exit;
@@ -209,8 +227,22 @@ begin
var P := @Buffer[FOffset];
case FFieldType of
- dfString: PString(P)^ := '';
- dfRecord: TDataRecord(P^) := Default(TDataRecord);
+ dfString: Initialize(PString(P)^);
+ dfRecord: Initialize(TDataRecord(P^));
+ end;
+end;
+
+procedure TDataRecord.TField.FinalizeField(const [ref] Buffer: TBytes);
+begin
+ if not (FFieldType in [dfString, dfRecord]) then
+ exit;
+
+ Assert(FOffset + Size <= Length(Buffer));
+
+ var P := @Buffer[FOffset];
+ case FFieldType of
+ dfString: Finalize(PString(P)^);
+ dfRecord: Finalize(TDataRecord(P^));
end;
end;
@@ -218,8 +250,6 @@ procedure TDataRecord.TField.FromType(const [ref] Buffer: TBytes; SrcType: PType
begin
Assert(FOffset + Size <= Length(Buffer));
- Finalize(Buffer);
-
var Dst := @Buffer[FOffset];
case FFieldType of
dfFloat:
@@ -317,19 +347,24 @@ begin
end;
end;
-procedure TDataRecord.TField.Assign(const [ref] Buffer: TBytes; const Src);
+procedure TDataRecord.TField.AssignField(const [ref] Dest: TBytes; const [ref] Source: TBytes);
begin
- Assert(FOffset + Size <= Length(Buffer));
+ Assert(FOffset + Size <= Length(Dest));
- Finalize(Buffer);
+ var Dst := @Dest[FOffset];
+ var Src := @Source[FOffset];
- var Dst := @Buffer[FOffset];
+ CopyField(Src, Dst);
+end;
+
+procedure TDataRecord.TField.CopyField(Src, Dst: Pointer);
+begin
case FFieldType of
- dfFloat: PDouble(Dst)^ := PDouble(@Src)^;
- dfInteger: PInt64(Dst)^ := PInt64(@Src)^;
- dfString: PString(Dst)^ := PString(@Src)^;
- dfTimestamp: PDateTime(Dst)^ := PDateTime(@Src)^;
- dfRecord: TDataRecord(Dst^) := TDataRecord(Src);
+ dfFloat: PDouble(Dst)^ := PDouble(Src)^;
+ dfInteger: PInt64(Dst)^ := PInt64(Src)^;
+ dfString: PString(Dst)^ := PString(Src)^;
+ dfTimestamp: PDateTime(Dst)^ := PDateTime(Src)^;
+ dfRecord: TDataRecord(Dst^) := TDataRecord(Src^);
else
Assert(false);
end;
diff --git a/Src/Myc.Trade.Indicators.pas b/Src/Myc.Trade.Indicators.pas
index 04feb51..e4e6140 100644
--- a/Src/Myc.Trade.Indicators.pas
+++ b/Src/Myc.Trade.Indicators.pas
@@ -8,8 +8,9 @@ uses
System.Generics.Collections,
System.Rtti,
Myc.Data.Records,
- Myc.Trade.Types,
- Myc.Data.Series;
+ Myc.Data.Pipeline,
+ Myc.Data.Series,
+ Myc.Trade.Types;
type
// Result for the Moving Average Convergence Divergence (MACD) indicator.
@@ -46,48 +47,48 @@ type
class function CalculateWMA(const Series: TSeries; const Period: Integer): Double; static;
public
// Simple Moving Average
- class function CreateSMA(Period: Integer): TConstFunc; static;
+ class function CreateSMA(Period: Integer): TConvertFunc; static;
// Exponential Moving Average
- class function CreateEMA(Period: Integer): TConstFunc; static;
+ class function CreateEMA(Period: Integer): TConvertFunc; static;
// Hull Moving Average
- class function CreateHMA(Period: Integer): TConstFunc; static;
+ class function CreateHMA(Period: Integer): TConvertFunc; static;
// Relative Strength Index
- class function CreateRSI(Period: Integer): TConstFunc; static;
+ class function CreateRSI(Period: Integer): TConvertFunc; static;
// Moving Average Convergence Divergence
- class function CreateMACD(FastPeriod, SlowPeriod, SignalPeriod: Integer): TConstFunc; overload; static;
+ class function CreateMACD(FastPeriod, SlowPeriod, SignalPeriod: Integer): TConvertFunc; overload; static;
class function CreateMACD(
const EmaFast,
EmaSlow,
- EmaSignal: TConstFunc
- ): TConstFunc; overload; static;
+ EmaSignal: TConvertFunc
+ ): TConvertFunc; overload; static;
// Stochastic Oscillator
- class function CreateStochastic(KPeriod, DPeriod: Integer): TConstFunc; overload; static;
+ class function CreateStochastic(KPeriod, DPeriod: Integer): TConvertFunc; overload; static;
class function CreateStochastic(
KPeriod: Integer;
- const SmaD: TConstFunc
- ): TConstFunc; overload; static;
+ const SmaD: TConvertFunc
+ ): TConvertFunc; overload; static;
// Bollinger Bands
- class function CreateBollingerBands(Period: Integer; Multiplier: Double): TConstFunc; static;
+ class function CreateBollingerBands(Period: Integer; Multiplier: Double): TConvertFunc; static;
// Average True Range
- class function CreateATR(Period: Integer): TConstFunc; overload; static;
- class function CreateATR(const MovAvgTR: TConstFunc): TConstFunc; overload; static;
+ class function CreateATR(Period: Integer): TConvertFunc; overload; static;
+ class function CreateATR(const MovAvgTR: TConvertFunc): TConvertFunc; overload; static;
// Keltner Channels
class function CreateKeltnerChannels(
Period: Integer;
Multiplier: Double
- ): TConstFunc; overload; static;
+ ): TConvertFunc; overload; static;
class function CreateKeltnerChannels(
- const MovAvgMiddle: TConstFunc;
- const AtrFunc: TConstFunc;
+ const MovAvgMiddle: TConvertFunc;
+ const AtrFunc: TConvertFunc;
Multiplier: Double
- ): TConstFunc; overload; static;
+ ): TConvertFunc; overload; static;
- class function CreateMean: TConstFunc, Double>; static;
+ class function CreateMean: TConvertFunc, Double>; static;
end;
TIndicatorFactory = class
type
- TFunc = TConstFunc;
+ TFunc = TConvertFunc;
private
FParams: TDataRecord.TLayout;
FInput: TDataRecord.TLayout;
@@ -125,9 +126,9 @@ type
TMACD = class
type
TParam = record
- Fast: TConstFunc;
- Slow: TConstFunc;
- Signal: TConstFunc