TTicker
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
<ProjectVersion>20.3</ProjectVersion>
|
<ProjectVersion>20.3</ProjectVersion>
|
||||||
<FrameworkType>FMX</FrameworkType>
|
<FrameworkType>FMX</FrameworkType>
|
||||||
<Base>True</Base>
|
<Base>True</Base>
|
||||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
<Config Condition="'$(Config)'==''">Release</Config>
|
||||||
<Platform Condition="'$(Platform)'==''">Win64</Platform>
|
<Platform Condition="'$(Platform)'==''">Win64</Platform>
|
||||||
<ProjectName Condition="'$(ProjectName)'==''">AuraTrader</ProjectName>
|
<ProjectName Condition="'$(ProjectName)'==''">AuraTrader</ProjectName>
|
||||||
<TargetedPlatforms>3</TargetedPlatforms>
|
<TargetedPlatforms>3</TargetedPlatforms>
|
||||||
|
|||||||
@@ -24,24 +24,6 @@ type
|
|||||||
constructor Create(const AFunc: TConvertFunc);
|
constructor Create(const AFunc: TConvertFunc);
|
||||||
end;
|
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>)
|
TIndicator<S, T> = class(TMycConverter<S, T>)
|
||||||
protected
|
protected
|
||||||
function ProcessData(const Value: S): TState; override; final;
|
function ProcessData(const Value: S): TState; override; final;
|
||||||
@@ -57,21 +39,77 @@ type
|
|||||||
constructor Create(const AFunc: TIndicatorFunc<S, T>);
|
constructor Create(const AFunc: TIndicatorFunc<S, T>);
|
||||||
end;
|
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
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.DateUtils,
|
System.DateUtils,
|
||||||
System.Math;
|
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
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FTimeframe := ATimeframe;
|
FTimeframe := ATimeframe;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TTicksToTimeframe.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
function TTicksToBars.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||||
var
|
var
|
||||||
baseTime: TDateTime;
|
baseTime: TDateTime;
|
||||||
begin
|
begin
|
||||||
@@ -119,34 +157,27 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TTicksToTimeframe.GetCurrentBar: TDataPoint<TOhlcItem>;
|
function TTicksToBars.GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||||
begin
|
begin
|
||||||
Result := FCurrentBar;
|
Result := FCurrentBar;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TTicksToTimeframe.GetTimeframe: TTimeframe;
|
function TTicksToBars.GetTimeframe: TTimeframe;
|
||||||
begin
|
begin
|
||||||
Result := FTimeframe;
|
Result := FTimeframe;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TTicksToTimeframe.ProcessData(const Values: TArray<TDataPoint<TAskBidItem>>): TState;
|
function TTicksToBars.ProcessData(const Value: TDataPoint<TAskBidItem>): TState;
|
||||||
var
|
var
|
||||||
point: TDataPoint<TAskBidItem>;
|
|
||||||
midPrice: Single;
|
midPrice: Single;
|
||||||
barStartTime: TDateTime;
|
barStartTime: TDateTime;
|
||||||
lastBarTime: TDateTime;
|
lastBarTime: TDateTime;
|
||||||
currentBar: TOhlcItem;
|
currentBar: TOhlcItem;
|
||||||
states: TList<TState>;
|
|
||||||
begin
|
begin
|
||||||
states := TList<TState>.Create;
|
midPrice := (Value.Data.Ask + Value.Data.Bid) / 2;
|
||||||
try
|
|
||||||
// Process each incoming data point
|
|
||||||
for point in Values do
|
|
||||||
begin
|
|
||||||
midPrice := (point.Data.Ask + point.Data.Bid) / 2;
|
|
||||||
|
|
||||||
// Update bar for the strategy's timeframe
|
// Update bar for the strategy's timeframe
|
||||||
barStartTime := GetBarStartTime(point.Time, FTimeframe);
|
barStartTime := GetBarStartTime(Value.Time, FTimeframe);
|
||||||
lastBarTime := FCurrentBar.Time;
|
lastBarTime := FCurrentBar.Time;
|
||||||
|
|
||||||
if (barStartTime > lastBarTime) then
|
if (barStartTime > lastBarTime) then
|
||||||
@@ -154,7 +185,7 @@ begin
|
|||||||
// A new bar starts, so the previous one is now complete.
|
// A new bar starts, so the previous one is now complete.
|
||||||
if (lastBarTime > 0) then
|
if (lastBarTime > 0) then
|
||||||
begin
|
begin
|
||||||
states.Add(Broadcast(FCurrentBar));
|
Result := Broadcast(FCurrentBar);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Start a new bar, Volume is 1 because this is the first tick.
|
// Start a new bar, Volume is 1 because this is the first tick.
|
||||||
@@ -175,43 +206,15 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := TState.All(states.ToArray);
|
function TTicker<T>.ProcessData(const Values: TArray<T>): TState;
|
||||||
finally
|
|
||||||
states.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TMycGenericConverter<S, T> }
|
|
||||||
|
|
||||||
constructor TMycGenericConverter<S, T>.Create(const AFunc: TConvertFunc);
|
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
var done := TLatch.CreateLatch( Length(Values) );
|
||||||
FFunc := AFunc;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycGenericConverter<S, T>.ProcessData(const Value: S): TState;
|
// Process each incoming data point
|
||||||
begin
|
for var i:=0 to High(Values) do
|
||||||
Result := Broadcast(FFunc(Value));
|
Broadcast(Values[i]).Signal.Subscribe(done);
|
||||||
end;
|
|
||||||
|
|
||||||
{ TIndicator<S,T> }
|
Result := done.State;
|
||||||
|
|
||||||
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;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -167,7 +167,10 @@ begin
|
|||||||
|
|
||||||
var timeframe := TTimeframe.S15;
|
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> :=
|
var Timestamps: IMycConverter<TDataPoint<TOhlcItem>, TDateTime> :=
|
||||||
TMycGenericConverter<TDataPoint<TOhlcItem>, TDateTime>
|
TMycGenericConverter<TDataPoint<TOhlcItem>, TDateTime>
|
||||||
@@ -272,7 +275,7 @@ begin
|
|||||||
Stoch.Sender.Link(StochD);
|
Stoch.Sender.Link(StochD);
|
||||||
Panel.AddDoubleSeries(StochD.Sender, TAlphaColors.Red);
|
Panel.AddDoubleSeries(StochD.Sender, TAlphaColors.Red);
|
||||||
|
|
||||||
var done := ExecuteStrategy(Symbol, OhlcPoint);
|
var done := ExecuteStrategy(Symbol, ticker);
|
||||||
|
|
||||||
/////
|
/////
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,11 @@ begin
|
|||||||
|
|
||||||
FWorkGate := TSemaphore.Create(nil, 0, MaxInt, ''); // Initial count is 0, workers will wait
|
FWorkGate := TSemaphore.Create(nil, 0, MaxInt, ''); // Initial count is 0, workers will wait
|
||||||
|
|
||||||
SetLength(FWorkThreads, TThread.ProcessorCount); // Create one thread per processor core
|
var threadCnt := TThread.ProcessorCount - 1;
|
||||||
|
if threadCnt < 2 then
|
||||||
|
threadCnt := 2;
|
||||||
|
|
||||||
|
SetLength(FWorkThreads, threadCnt);
|
||||||
for var i := 0 to High(FWorkThreads) do
|
for var i := 0 to High(FWorkThreads) do
|
||||||
begin
|
begin
|
||||||
FWorkThreads[i] := CreateAnonymousThread('Thread pool [' + IntToStr(i) + ']', WorkerThread);
|
FWorkThreads[i] := CreateAnonymousThread('Thread pool [' + IntToStr(i) + ']', WorkerThread);
|
||||||
@@ -158,7 +162,7 @@ begin
|
|||||||
if TThread.CurrentThread.ThreadID = MainThreadID then
|
if TThread.CurrentThread.ThreadID = MainThreadID then
|
||||||
begin
|
begin
|
||||||
prio := TThread.CurrentThread.Priority;
|
prio := TThread.CurrentThread.Priority;
|
||||||
if prio > Low(TThreadPriority) then
|
if prio > tpLowest then
|
||||||
prio := TThreadPriority(Ord(prio) - 1); // Decrease priority by one step
|
prio := TThreadPriority(Ord(prio) - 1); // Decrease priority by one step
|
||||||
|
|
||||||
Result.Priority := prio;
|
Result.Priority := prio;
|
||||||
|
|||||||
+18
-5
@@ -65,8 +65,12 @@ type
|
|||||||
protected
|
protected
|
||||||
function GetOwner: TMycChart; override; final;
|
function GetOwner: TMycChart; override; final;
|
||||||
// Paint crosshair and other axis related overlays
|
// Paint crosshair and other axis related overlays
|
||||||
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; ViewStartIndex, ViewCount: Int64; IdxAtMousePos: Integer); virtual;
|
procedure Paint(
|
||||||
abstract;
|
const Canvas: TCanvas;
|
||||||
|
const Viewport: TRectF;
|
||||||
|
ViewStartIndex, ViewCount: Int64;
|
||||||
|
IdxAtMousePos: Integer
|
||||||
|
); virtual; abstract;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TMycChart);
|
constructor Create(AOwner: TMycChart);
|
||||||
end;
|
end;
|
||||||
@@ -74,7 +78,12 @@ type
|
|||||||
TXAxisLayer = class(TAxisLayer)
|
TXAxisLayer = class(TAxisLayer)
|
||||||
protected
|
protected
|
||||||
// Paint crosshair and time caption
|
// Paint crosshair and time caption
|
||||||
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; ViewStartIndex, ViewCount: Int64; IdxAtMousePos: Integer); override;
|
procedure Paint(
|
||||||
|
const Canvas: TCanvas;
|
||||||
|
const Viewport: TRectF;
|
||||||
|
ViewStartIndex, ViewCount: Int64;
|
||||||
|
IdxAtMousePos: Integer
|
||||||
|
); override;
|
||||||
// This function delivers the text for the caption.
|
// This function delivers the text for the caption.
|
||||||
function GetCaption(Idx: Int64): String; virtual; abstract;
|
function GetCaption(Idx: Int64): String; virtual; abstract;
|
||||||
end;
|
end;
|
||||||
@@ -819,8 +828,12 @@ end;
|
|||||||
|
|
||||||
{ TMycChart.TXAxisLayer }
|
{ TMycChart.TXAxisLayer }
|
||||||
|
|
||||||
procedure TMycChart.TXAxisLayer.Paint(const Canvas: TCanvas; const Viewport: TRectF; ViewStartIndex, ViewCount: Int64; IdxAtMousePos:
|
procedure TMycChart.TXAxisLayer.Paint(
|
||||||
Integer);
|
const Canvas: TCanvas;
|
||||||
|
const Viewport: TRectF;
|
||||||
|
ViewStartIndex, ViewCount: Int64;
|
||||||
|
IdxAtMousePos: Integer
|
||||||
|
);
|
||||||
var
|
var
|
||||||
caption: string;
|
caption: string;
|
||||||
captionRect: TRectF;
|
captionRect: TRectF;
|
||||||
|
|||||||
@@ -34,9 +34,6 @@ type
|
|||||||
[TestCase('Writeable', '10,20')]
|
[TestCase('Writeable', '10,20')]
|
||||||
procedure TestWriteableMutable(InitialValue, NewValue: Integer);
|
procedure TestWriteableMutable(InitialValue, NewValue: Integer);
|
||||||
|
|
||||||
[Test]
|
|
||||||
procedure TestWriteableMutable_SignalOnSameValue;
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[TestCase('Protected', '100,200')]
|
[TestCase('Protected', '100,200')]
|
||||||
procedure TestProtectedMutable_Delegation(InitialValue, NewValue: Integer);
|
procedure TestProtectedMutable_Delegation(InitialValue, NewValue: Integer);
|
||||||
@@ -106,7 +103,8 @@ var
|
|||||||
func: TFunc<Integer>;
|
func: TFunc<Integer>;
|
||||||
begin
|
begin
|
||||||
callCount := 0;
|
callCount := 0;
|
||||||
func := function: Integer
|
func :=
|
||||||
|
function: Integer
|
||||||
begin
|
begin
|
||||||
Inc(callCount);
|
Inc(callCount);
|
||||||
Result := 42;
|
Result := 42;
|
||||||
@@ -125,10 +123,7 @@ var
|
|||||||
func: TFunc<Integer>;
|
func: TFunc<Integer>;
|
||||||
begin
|
begin
|
||||||
source := TEvent.CreateEvent;
|
source := TEvent.CreateEvent;
|
||||||
func := function: Integer
|
func := function: Integer begin Result := 123; end;
|
||||||
begin
|
|
||||||
Result := 123;
|
|
||||||
end;
|
|
||||||
mutable := TMutable<Integer>.Construct(source.Signal, func);
|
mutable := TMutable<Integer>.Construct(source.Signal, func);
|
||||||
|
|
||||||
lazy := TLazy<Integer>.Create(mutable);
|
lazy := TLazy<Integer>.Create(mutable);
|
||||||
@@ -156,19 +151,6 @@ begin
|
|||||||
subscription.Unsubscribe;
|
subscription.Unsubscribe;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TTestCoreMutable.TestWriteableMutable_SignalOnSameValue;
|
|
||||||
var
|
|
||||||
writeable: TWriteable<Integer>;
|
|
||||||
subscriber: TSignal.ISubscriber;
|
|
||||||
subscription: TSignal.TSubscription;
|
|
||||||
begin
|
|
||||||
writeable := TWriteable<Integer>.CreateWriteable(100);
|
|
||||||
subscriber := TTestSubscriber.Create(SubscriberCallback);
|
|
||||||
subscription := writeable.Changed.Subscribe(subscriber);
|
|
||||||
|
|
||||||
subscription.Unsubscribe;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TTestCoreMutable.TestProtectedMutable_Delegation(InitialValue, NewValue: Integer);
|
procedure TTestCoreMutable.TestProtectedMutable_Delegation(InitialValue, NewValue: Integer);
|
||||||
var
|
var
|
||||||
writeable: TWriteable<Integer>;
|
writeable: TWriteable<Integer>;
|
||||||
@@ -203,7 +185,10 @@ begin
|
|||||||
|
|
||||||
for i := 0 to ThreadCount - 1 do
|
for i := 0 to ThreadCount - 1 do
|
||||||
begin
|
begin
|
||||||
threads := threads + [TThread.CreateAnonymousThread(
|
threads :=
|
||||||
|
threads
|
||||||
|
+ [
|
||||||
|
TThread.CreateAnonymousThread(
|
||||||
procedure
|
procedure
|
||||||
var
|
var
|
||||||
j: Integer;
|
j: Integer;
|
||||||
|
|||||||
Reference in New Issue
Block a user