TTicker
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<ProjectVersion>20.3</ProjectVersion>
|
||||
<FrameworkType>FMX</FrameworkType>
|
||||
<Base>True</Base>
|
||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
||||
<Config Condition="'$(Config)'==''">Release</Config>
|
||||
<Platform Condition="'$(Platform)'==''">Win64</Platform>
|
||||
<ProjectName Condition="'$(ProjectName)'==''">AuraTrader</ProjectName>
|
||||
<TargetedPlatforms>3</TargetedPlatforms>
|
||||
|
||||
@@ -24,24 +24,6 @@ type
|
||||
constructor Create(const AFunc: TConvertFunc);
|
||||
end;
|
||||
|
||||
TTicksToTimeframe = class(TMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>>)
|
||||
private
|
||||
FTimeframe: TTimeframe;
|
||||
// Stores the currently aggregating OHLC data.
|
||||
FCurrentBar: TDataPoint<TOhlcItem>;
|
||||
function GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||
function GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||
function GetTimeframe: TTimeframe;
|
||||
public
|
||||
constructor Create(const ATimeframe: TTimeframe);
|
||||
|
||||
// Process new data. This is called concurrently and must not have side effects out of the scope of this class!
|
||||
function ProcessData(const Values: TArray<TDataPoint<TAskBidItem>>): TState; override;
|
||||
|
||||
property CurrentBar: TDataPoint<TOhlcItem> read GetCurrentBar;
|
||||
property Timeframe: TTimeframe read GetTimeframe;
|
||||
end;
|
||||
|
||||
TIndicator<S, T> = class(TMycConverter<S, T>)
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override; final;
|
||||
@@ -57,21 +39,77 @@ type
|
||||
constructor Create(const AFunc: TIndicatorFunc<S, T>);
|
||||
end;
|
||||
|
||||
TTicksToBars = class(TMycConverter<TDataPoint<TAskBidItem>, TDataPoint<TOhlcItem>>)
|
||||
private
|
||||
FTimeframe: TTimeframe;
|
||||
// Stores the currently aggregating OHLC data.
|
||||
FCurrentBar: TDataPoint<TOhlcItem>;
|
||||
function GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||
function GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||
function GetTimeframe: TTimeframe;
|
||||
public
|
||||
constructor Create(const ATimeframe: TTimeframe);
|
||||
|
||||
// Process new data. This is called concurrently and must not have side effects out of the scope of this class!
|
||||
function ProcessData(const Value: TDataPoint<TAskBidItem>): TState; override;
|
||||
|
||||
property CurrentBar: TDataPoint<TOhlcItem> read GetCurrentBar;
|
||||
property Timeframe: TTimeframe read GetTimeframe;
|
||||
end;
|
||||
|
||||
TTicker<T> = class(TMycConverter<TArray<T>, T>)
|
||||
public
|
||||
function ProcessData(const Values: TArray<T>): TState; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.DateUtils,
|
||||
System.Math;
|
||||
|
||||
{ TTicksToTimeframe }
|
||||
{ TMycGenericConverter<S, T> }
|
||||
|
||||
constructor TTicksToTimeframe.Create(const ATimeframe: TTimeframe);
|
||||
constructor TMycGenericConverter<S, T>.Create(const AFunc: TConvertFunc);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
end;
|
||||
|
||||
function TMycGenericConverter<S, T>.ProcessData(const Value: S): TState;
|
||||
begin
|
||||
Result := Broadcast(FFunc(Value));
|
||||
end;
|
||||
|
||||
{ TIndicator<S,T> }
|
||||
|
||||
function TIndicator<S, T>.ProcessData(const Value: S): TState;
|
||||
begin
|
||||
Result := Broadcast(Calculate(Value));
|
||||
end;
|
||||
|
||||
{ TGenericIndicator<S,T> }
|
||||
|
||||
constructor TGenericIndicator<S, T>.Create(const AFunc: TIndicatorFunc<S, T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
end;
|
||||
|
||||
function TGenericIndicator<S, T>.Calculate(const Value: S): T;
|
||||
begin
|
||||
Result := FFunc(Value);
|
||||
end;
|
||||
|
||||
{ TTicksToBars }
|
||||
|
||||
constructor TTicksToBars.Create(const ATimeframe: TTimeframe);
|
||||
begin
|
||||
inherited Create;
|
||||
FTimeframe := ATimeframe;
|
||||
end;
|
||||
|
||||
function TTicksToTimeframe.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||
function TTicksToBars.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||
var
|
||||
baseTime: TDateTime;
|
||||
begin
|
||||
@@ -119,99 +157,64 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TTicksToTimeframe.GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||
function TTicksToBars.GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||
begin
|
||||
Result := FCurrentBar;
|
||||
end;
|
||||
|
||||
function TTicksToTimeframe.GetTimeframe: TTimeframe;
|
||||
function TTicksToBars.GetTimeframe: TTimeframe;
|
||||
begin
|
||||
Result := FTimeframe;
|
||||
end;
|
||||
|
||||
function TTicksToTimeframe.ProcessData(const Values: TArray<TDataPoint<TAskBidItem>>): TState;
|
||||
function TTicksToBars.ProcessData(const Value: TDataPoint<TAskBidItem>): TState;
|
||||
var
|
||||
point: TDataPoint<TAskBidItem>;
|
||||
midPrice: Single;
|
||||
barStartTime: TDateTime;
|
||||
lastBarTime: TDateTime;
|
||||
currentBar: TOhlcItem;
|
||||
states: TList<TState>;
|
||||
begin
|
||||
states := TList<TState>.Create;
|
||||
try
|
||||
// Process each incoming data point
|
||||
for point in Values do
|
||||
midPrice := (Value.Data.Ask + Value.Data.Bid) / 2;
|
||||
|
||||
// Update bar for the strategy's timeframe
|
||||
barStartTime := GetBarStartTime(Value.Time, FTimeframe);
|
||||
lastBarTime := FCurrentBar.Time;
|
||||
|
||||
if (barStartTime > lastBarTime) then
|
||||
begin
|
||||
// A new bar starts, so the previous one is now complete.
|
||||
if (lastBarTime > 0) then
|
||||
begin
|
||||
midPrice := (point.Data.Ask + point.Data.Bid) / 2;
|
||||
|
||||
// Update bar for the strategy's timeframe
|
||||
barStartTime := GetBarStartTime(point.Time, FTimeframe);
|
||||
lastBarTime := FCurrentBar.Time;
|
||||
|
||||
if (barStartTime > lastBarTime) then
|
||||
begin
|
||||
// A new bar starts, so the previous one is now complete.
|
||||
if (lastBarTime > 0) then
|
||||
begin
|
||||
states.Add(Broadcast(FCurrentBar));
|
||||
end;
|
||||
|
||||
// Start a new bar, Volume is 1 because this is the first tick.
|
||||
currentBar := TOhlcItem.Create(midPrice, midPrice, midPrice, midPrice, 1);
|
||||
FCurrentBar.Data := currentBar;
|
||||
FCurrentBar.Time := barStartTime;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Update the currently aggregating bar
|
||||
currentBar := FCurrentBar.Data;
|
||||
currentBar.High := Max(currentBar.High, midPrice);
|
||||
currentBar.Low := Min(currentBar.Low, midPrice);
|
||||
currentBar.Close := midPrice;
|
||||
// Volume is the number of ticks needed to build the complete bar.
|
||||
currentBar.Volume := currentBar.Volume + 1;
|
||||
FCurrentBar.Data := currentBar;
|
||||
end;
|
||||
Result := Broadcast(FCurrentBar);
|
||||
end;
|
||||
|
||||
Result := TState.All(states.ToArray);
|
||||
finally
|
||||
states.Free;
|
||||
// Start a new bar, Volume is 1 because this is the first tick.
|
||||
currentBar := TOhlcItem.Create(midPrice, midPrice, midPrice, midPrice, 1);
|
||||
FCurrentBar.Data := currentBar;
|
||||
FCurrentBar.Time := barStartTime;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Update the currently aggregating bar
|
||||
currentBar := FCurrentBar.Data;
|
||||
currentBar.High := Max(currentBar.High, midPrice);
|
||||
currentBar.Low := Min(currentBar.Low, midPrice);
|
||||
currentBar.Close := midPrice;
|
||||
// Volume is the number of ticks needed to build the complete bar.
|
||||
currentBar.Volume := currentBar.Volume + 1;
|
||||
FCurrentBar.Data := currentBar;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TMycGenericConverter<S, T> }
|
||||
|
||||
constructor TMycGenericConverter<S, T>.Create(const AFunc: TConvertFunc);
|
||||
function TTicker<T>.ProcessData(const Values: TArray<T>): TState;
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
end;
|
||||
var done := TLatch.CreateLatch( Length(Values) );
|
||||
|
||||
function TMycGenericConverter<S, T>.ProcessData(const Value: S): TState;
|
||||
begin
|
||||
Result := Broadcast(FFunc(Value));
|
||||
end;
|
||||
// Process each incoming data point
|
||||
for var i:=0 to High(Values) do
|
||||
Broadcast(Values[i]).Signal.Subscribe(done);
|
||||
|
||||
{ TIndicator<S,T> }
|
||||
|
||||
function TIndicator<S, T>.ProcessData(const Value: S): TState;
|
||||
begin
|
||||
Result := Broadcast(Calculate(Value));
|
||||
end;
|
||||
|
||||
{ TGenericIndicator<S,T> }
|
||||
|
||||
constructor TGenericIndicator<S, T>.Create(const AFunc: TIndicatorFunc<S, T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
end;
|
||||
|
||||
function TGenericIndicator<S, T>.Calculate(const Value: S): T;
|
||||
begin
|
||||
Result := FFunc(Value);
|
||||
Result := done.State;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -167,7 +167,10 @@ begin
|
||||
|
||||
var timeframe := TTimeframe.S15;
|
||||
|
||||
var OhlcPoint: IMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>> := TTicksToTimeframe.Create(timeframe);
|
||||
var ticker := TTicker<TDataPoint<TAskBidItem>>.Create;
|
||||
|
||||
var OhlcPoint := TTicksToBars.Create(timeframe);
|
||||
ticker.Sender.Link(OhlcPoint);
|
||||
|
||||
var Timestamps: IMycConverter<TDataPoint<TOhlcItem>, TDateTime> :=
|
||||
TMycGenericConverter<TDataPoint<TOhlcItem>, TDateTime>
|
||||
@@ -272,7 +275,7 @@ begin
|
||||
Stoch.Sender.Link(StochD);
|
||||
Panel.AddDoubleSeries(StochD.Sender, TAlphaColors.Red);
|
||||
|
||||
var done := ExecuteStrategy(Symbol, OhlcPoint);
|
||||
var done := ExecuteStrategy(Symbol, ticker);
|
||||
|
||||
/////
|
||||
|
||||
|
||||
Reference in New Issue
Block a user