Process.Update for synchronization
This commit is contained in:
@@ -6,7 +6,6 @@ uses
|
||||
FMX.Forms,
|
||||
MainForm in 'MainForm.pas' {Form1},
|
||||
Myc.Trade.Core.DataPoint in '..\Src\Myc.Trade.Core.DataPoint.pas',
|
||||
Myc.Trade.Ticker in '..\Src\Myc.Trade.Ticker.pas',
|
||||
Myc.Aura.Module in '..\Src\Myc.Aura.Module.pas',
|
||||
Myc.Aura.Parameter in '..\Src\Myc.Aura.Parameter.pas',
|
||||
TestModule in 'TestModule.pas',
|
||||
|
||||
@@ -134,7 +134,6 @@
|
||||
<Form>Form1</Form>
|
||||
</DCCReference>
|
||||
<DCCReference Include="..\Src\Myc.Trade.Core.DataPoint.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Trade.Ticker.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Aura.Module.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Aura.Parameter.pas"/>
|
||||
<DCCReference Include="TestModule.pas"/>
|
||||
@@ -185,12 +184,6 @@
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="Win64\Debug\AuraTrader.rsm" Configuration="Debug" Class="DebugSymbols">
|
||||
<Platform Name="Win64">
|
||||
<RemoteName>AuraTrader.rsm</RemoteName>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="Win64\Release\AuraTrader.exe" Configuration="Release" Class="ProjectOutput">
|
||||
<Platform Name="Win64">
|
||||
<RemoteName>AuraTrader.exe</RemoteName>
|
||||
|
||||
Binary file not shown.
+85
-228
@@ -7,49 +7,28 @@ uses
|
||||
Myc.Signals,
|
||||
Myc.Lazy,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Trade.DataArray,
|
||||
Myc.Core.Notifier;
|
||||
Myc.Trade.DataArray;
|
||||
|
||||
type
|
||||
TTimeframe = (M1, M5, H1, D);
|
||||
|
||||
TTag = Pointer;
|
||||
|
||||
IMycBroadcast<T> = interface
|
||||
function Link(const Strategy: IMycProcessor<T>): TTag;
|
||||
procedure Unlink(Tag: TTag);
|
||||
end;
|
||||
|
||||
// A contained object that broadcasts Values to linked strategies
|
||||
TMycBroadcast<T> = class(TContainedObject, IMycBroadcast<T>)
|
||||
private
|
||||
FLinkedStrategies: TMycNotifyList<IMycProcessor<T>>;
|
||||
protected
|
||||
// Link a strategy
|
||||
function Link(const Strategy: IMycProcessor<T>): TTag;
|
||||
// Unlink a linked strategy
|
||||
procedure Unlink(Tag: TTag);
|
||||
// Broadcasts the given data points to all linked strategies.
|
||||
procedure Broadcast(const Value: T);
|
||||
public
|
||||
constructor Create(const Controller: IInterface);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
IMycConverter<S, T> = interface(IMycProcessor<S>)
|
||||
function GetObservers: IMycBroadcast<T>;
|
||||
property Observers: IMycBroadcast<T> read GetObservers;
|
||||
function GetSender: IMycDataProvider<T>;
|
||||
property Sender: IMycDataProvider<T> read GetSender;
|
||||
end;
|
||||
|
||||
TMycConverter<S, T> = class abstract(TMycProcessor<S>, IMycConverter<S, T>)
|
||||
private
|
||||
FObservers: TMycBroadcast<T>;
|
||||
FSender: TMycDataProvider<T>;
|
||||
function GetSender: IMycDataProvider<T>;
|
||||
protected
|
||||
// Broadcasts the given data to all linked processors.
|
||||
procedure Broadcast(const Value: T);
|
||||
function GetObservers: IMycBroadcast<T>;
|
||||
procedure Update; override;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
property Sender: IMycDataProvider<T> read GetSender;
|
||||
end;
|
||||
|
||||
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
||||
@@ -63,40 +42,7 @@ type
|
||||
constructor Create(const AFunc: TConvertFunc);
|
||||
end;
|
||||
|
||||
// Series
|
||||
|
||||
IMycSeriesConverter<S, T> = interface(IMycConverter<TArray<S>, T>)
|
||||
function GetLookback: Integer;
|
||||
property Lookback: Integer read GetLookback;
|
||||
end;
|
||||
|
||||
// Indicator
|
||||
|
||||
IMycIndicator<S, T> = interface(IMycSeriesConverter<S, TArray<T>>)
|
||||
end;
|
||||
|
||||
TMycIndicator<S, T> = class abstract(TMycConverter<TArray<S>, TArray<T>>, IMycIndicator<S, T>)
|
||||
protected
|
||||
function GetLookback: Integer; virtual; abstract;
|
||||
function ProcessData(const Value: TArray<S>): Boolean; override; abstract;
|
||||
public
|
||||
property Lookback: Integer read GetLookback;
|
||||
end;
|
||||
|
||||
TMycGenericIndicator<S, T> = class(TMycIndicator<S, T>)
|
||||
type
|
||||
TConvertFunc = reference to function(const Value: S): T;
|
||||
private
|
||||
FLookback: Integer;
|
||||
FFunc: TConvertFunc;
|
||||
protected
|
||||
function GetLookback: Integer; override;
|
||||
function ProcessData(const Value: TArray<S>): Boolean; override;
|
||||
public
|
||||
constructor Create(ALookback: Integer; const AFunc: TConvertFunc);
|
||||
end;
|
||||
|
||||
ITicksToTimeframe = interface(IMycConverter<TArray<TDataPoint<TAskBidItem>>, TArray<TDataPoint<TOhlcItem>>>)
|
||||
ITicksToTimeframe = interface(IMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>>)
|
||||
function GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||
function GetStateText: TWriteable<String>;
|
||||
function GetTimeframe: TTimeframe;
|
||||
@@ -105,7 +51,7 @@ type
|
||||
property Timeframe: TTimeframe read GetTimeframe;
|
||||
end;
|
||||
|
||||
TTicksToTimeframe = class(TMycConverter<TArray<TDataPoint<TAskBidItem>>, TArray<TDataPoint<TOhlcItem>>>, ITicksToTimeframe)
|
||||
TTicksToTimeframe = class(TMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>>, ITicksToTimeframe)
|
||||
private
|
||||
FTimeframe: TTimeframe;
|
||||
FStateText: TWriteable<String>;
|
||||
@@ -127,7 +73,7 @@ type
|
||||
end;
|
||||
|
||||
// Implements the Hull Moving Average indicator.
|
||||
THullMovingAverage = class(TMycIndicator<Double, Double>)
|
||||
THullMovingAverage = class(TMycConverter<Double, Double>)
|
||||
private
|
||||
FPeriod: Integer;
|
||||
FPeriodHalf: Integer;
|
||||
@@ -139,8 +85,7 @@ type
|
||||
// Calculates the Weighted Moving Average for the most recent data.
|
||||
function CalculateWMA(const Series: TMycDataArray<Double>; const Period: Integer): Double;
|
||||
protected
|
||||
function ProcessData(const Values: TArray<Double>): Boolean; override;
|
||||
function GetLookback: Integer; override;
|
||||
function ProcessData(const Value: Double): Boolean; override;
|
||||
public
|
||||
constructor Create(const APeriod: Integer);
|
||||
end;
|
||||
@@ -152,57 +97,6 @@ uses
|
||||
System.DateUtils,
|
||||
System.Math;
|
||||
|
||||
{ TMycBroadcast<T> }
|
||||
|
||||
constructor TMycBroadcast<T>.Create(const Controller: IInterface);
|
||||
begin
|
||||
inherited Create(Controller);
|
||||
end;
|
||||
|
||||
destructor TMycBroadcast<T>.Destroy;
|
||||
begin
|
||||
FLinkedStrategies.Finalize;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMycBroadcast<T>.Broadcast(const Value: T);
|
||||
begin
|
||||
var cValue := Value;
|
||||
|
||||
FLinkedStrategies.Lock;
|
||||
try
|
||||
FLinkedStrategies.Notify(
|
||||
function(const Processor: IMycProcessor<T>): Boolean
|
||||
begin
|
||||
Result := Processor.ProcessData(cValue);
|
||||
end
|
||||
);
|
||||
finally
|
||||
FLinkedStrategies.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMycBroadcast<T>.Link(const Strategy: IMycProcessor<T>): TTag;
|
||||
begin
|
||||
// Add the strategy to the notification list
|
||||
FLinkedStrategies.Lock;
|
||||
try
|
||||
Result := FLinkedStrategies.Advise(Strategy);
|
||||
finally
|
||||
FLinkedStrategies.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycBroadcast<T>.Unlink(Tag: TTag);
|
||||
begin
|
||||
FLinkedStrategies.Lock;
|
||||
try
|
||||
FLinkedStrategies.Unadvise(Tag);
|
||||
finally
|
||||
FLinkedStrategies.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TTicksToTimeframe }
|
||||
|
||||
constructor TTicksToTimeframe.Create(const ATimeframe: TTimeframe; const AStateText: TWriteable<String>);
|
||||
@@ -247,54 +141,47 @@ var
|
||||
barStartTime: TDateTime;
|
||||
lastBarTime: TDateTime;
|
||||
currentBar: TOhlcItem;
|
||||
producedBars: TList<TDataPoint<TOhlcItem>>;
|
||||
begin
|
||||
Result := true;
|
||||
producedBars := TList<TDataPoint<TOhlcItem>>.Create;
|
||||
try
|
||||
// Process each incoming data point
|
||||
for point in Values do
|
||||
|
||||
// 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
|
||||
barStartTime := GetBarStartTime(point.Time, FTimeframe);
|
||||
lastBarTime := FCurrentBar.Time;
|
||||
|
||||
if (barStartTime > lastBarTime) 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
|
||||
// A new bar starts, so the previous one is now complete.
|
||||
if (lastBarTime > 0) then
|
||||
begin
|
||||
// A new bar starts, so the previous one is now complete.
|
||||
if (lastBarTime > 0) then
|
||||
begin
|
||||
producedBars.Add(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;
|
||||
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;
|
||||
|
||||
with FCurrentBar do
|
||||
FStateText.Value :=
|
||||
Format('Cuur Bar: O:%.5f H:%.5f L:%.5f C:%.5f V:%.0f', [Data.Open, Data.High, Data.Low, Data.Close, Data.Volume]);
|
||||
|
||||
Broadcast(producedBars.ToArray);
|
||||
finally
|
||||
producedBars.Free;
|
||||
end;
|
||||
|
||||
with FCurrentBar do
|
||||
FStateText.Value :=
|
||||
Format('Cuur Bar: O:%.5f H:%.5f L:%.5f C:%.5f V:%.0f', [Data.Open, Data.High, Data.Low, Data.Close, Data.Volume]);
|
||||
end;
|
||||
|
||||
{ THullMovingAverage }
|
||||
@@ -337,62 +224,43 @@ begin
|
||||
Result := numerator / denominator;
|
||||
end;
|
||||
|
||||
function THullMovingAverage.GetLookback: Integer;
|
||||
begin
|
||||
Result := FPeriod + FPeriodSqrt - 1;
|
||||
end;
|
||||
|
||||
function THullMovingAverage.ProcessData(const Values: TArray<Double>): Boolean;
|
||||
function THullMovingAverage.ProcessData(const Value: Double): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
price: Double;
|
||||
wmaHalf, wmaFull, diff: Double;
|
||||
hma: Double;
|
||||
resultArray: TArray<Double>;
|
||||
begin
|
||||
Result := true;
|
||||
|
||||
// Pre-allocate the result array since its size is known in advance.
|
||||
SetLength(resultArray, Length(Values));
|
||||
price := Value;
|
||||
|
||||
for i := 0 to High(Values) do
|
||||
// Default HMA to NaN for the warm-up period.
|
||||
hma := Double.NaN;
|
||||
|
||||
// Add new price to the source data array, respecting the lookback period.
|
||||
FSourceData := FSourceData.Add(price, FPeriod);
|
||||
|
||||
// Check if there is enough data to start the first stage of calculation.
|
||||
if (FSourceData.Count >= FPeriod) then
|
||||
begin
|
||||
price := Values[i];
|
||||
// Calculate the two WMAs for the first step.
|
||||
wmaHalf := CalculateWMA(FSourceData, FPeriodHalf);
|
||||
wmaFull := CalculateWMA(FSourceData, FPeriod);
|
||||
|
||||
// Default HMA to NaN for the warm-up period.
|
||||
hma := Double.NaN;
|
||||
// Calculate the difference and add to the intermediate series.
|
||||
diff := 2 * wmaHalf - wmaFull;
|
||||
FDiffSeries := FDiffSeries.Add(diff, FPeriodSqrt);
|
||||
|
||||
// Add new price to the source data array, respecting the lookback period.
|
||||
FSourceData := FSourceData.Add(price, FPeriod);
|
||||
|
||||
// Check if there is enough data to start the first stage of calculation.
|
||||
if (FSourceData.Count >= FPeriod) then
|
||||
// Check if there is enough intermediate data for the final calculation.
|
||||
if (FDiffSeries.Count >= FPeriodSqrt) then
|
||||
begin
|
||||
// Calculate the two WMAs for the first step.
|
||||
wmaHalf := CalculateWMA(FSourceData, FPeriodHalf);
|
||||
wmaFull := CalculateWMA(FSourceData, FPeriod);
|
||||
|
||||
// Calculate the difference and add to the intermediate series.
|
||||
diff := 2 * wmaHalf - wmaFull;
|
||||
FDiffSeries := FDiffSeries.Add(diff, FPeriodSqrt);
|
||||
|
||||
// Check if there is enough intermediate data for the final calculation.
|
||||
if (FDiffSeries.Count >= FPeriodSqrt) then
|
||||
begin
|
||||
// Calculate the final HMA value, overwriting the default 0.0.
|
||||
hma := CalculateWMA(FDiffSeries, FPeriodSqrt);
|
||||
end;
|
||||
// Calculate the final HMA value, overwriting the default 0.0.
|
||||
hma := CalculateWMA(FDiffSeries, FPeriodSqrt);
|
||||
end;
|
||||
|
||||
// Assign the result (either the calculated HMA or 0.0) directly into the array.
|
||||
resultArray[i] := hma;
|
||||
end;
|
||||
|
||||
// Broadcast the result array if the input was not empty.
|
||||
if (Length(resultArray) > 0) then
|
||||
begin
|
||||
Broadcast(resultArray);
|
||||
end;
|
||||
// Broadcast the result
|
||||
Broadcast(hma);
|
||||
end;
|
||||
|
||||
{ TMycGenericConverter<S, T> }
|
||||
@@ -409,51 +277,40 @@ begin
|
||||
Broadcast(FFunc(Value));
|
||||
end;
|
||||
|
||||
constructor TMycGenericIndicator<S, T>.Create(ALookback: Integer; const AFunc: TConvertFunc);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
FLookback := ALookback;
|
||||
end;
|
||||
|
||||
function TMycGenericIndicator<S, T>.GetLookback: Integer;
|
||||
begin
|
||||
Result := FLookback;
|
||||
end;
|
||||
|
||||
function TMycGenericIndicator<S, T>.ProcessData(const Value: TArray<S>): Boolean;
|
||||
var
|
||||
Arr: TArray<T>;
|
||||
begin
|
||||
Result := true;
|
||||
SetLength(Arr, Length(Value));
|
||||
for var i := 0 to High(Arr) do
|
||||
Arr[i] := FFunc(Value[i]);
|
||||
Broadcast(Arr);
|
||||
end;
|
||||
|
||||
{ TMycConverter<S, T> }
|
||||
|
||||
constructor TMycConverter<S, T>.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FObservers := TMycBroadcast<T>.Create(Self);
|
||||
FSender := TMycDataProvider<T>.Create(Self);
|
||||
end;
|
||||
|
||||
destructor TMycConverter<S, T>.Destroy;
|
||||
begin
|
||||
FObservers.Free;
|
||||
FSender.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMycConverter<S, T>.Broadcast(const Value: T);
|
||||
begin
|
||||
FObservers.Broadcast(Value);
|
||||
var cValue := Value;
|
||||
FSender.Notify(function(const Processor: IMycProcessor<T>): Boolean begin Result := Processor.ProcessData(cValue) end);
|
||||
end;
|
||||
|
||||
function TMycConverter<S, T>.GetObservers: IMycBroadcast<T>;
|
||||
function TMycConverter<S, T>.GetSender: IMycDataProvider<T>;
|
||||
begin
|
||||
Result := FObservers;
|
||||
Result := FSender;
|
||||
end;
|
||||
|
||||
procedure TMycConverter<S, T>.Update;
|
||||
begin
|
||||
FSender.Notify(
|
||||
function(const Processor: IMycProcessor<T>): Boolean
|
||||
begin
|
||||
Processor.Update;
|
||||
Result := true
|
||||
end
|
||||
);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+12
-11
@@ -165,34 +165,35 @@ begin
|
||||
var chart := TMycChart.Create(Self);
|
||||
AlignControl( chart );
|
||||
chart.Height := Layout.ChildrenRect.Width*9/16;
|
||||
chart.Lookback := 1000;
|
||||
|
||||
/////
|
||||
|
||||
var strategy: ITicksToTimeframe := TTicksToTimeframe.Create( D, stateText );
|
||||
var OhlcPoint: ITicksToTimeframe := TTicksToTimeframe.Create( H1, stateText );
|
||||
|
||||
var Ohlc: IMycIndicator<TDataPoint<TOhlcItem>, TOhlcItem> := TMycGenericIndicator<TDataPoint<TOhlcItem>, TOhlcItem>.Create( 0,
|
||||
var Ohlc: IMycConverter<TDataPoint<TOhlcItem>, TOhlcItem> := TMycGenericConverter<TDataPoint<TOhlcItem>, TOhlcItem>.Create(
|
||||
function( const Ohlc: TDataPoint<TOhlcItem> ): TOhlcItem
|
||||
begin
|
||||
Result := Ohlc.Data;
|
||||
end );
|
||||
|
||||
strategy.Observers.Link(Ohlc);
|
||||
OhlcPoint.Sender.Link(Ohlc);
|
||||
|
||||
var Closes: IMycIndicator<TOhlcItem, Double> := TMycGenericIndicator<TOhlcItem, Double>.Create( 0,
|
||||
var Closes: IMycConverter<TOhlcItem, Double> := TMycGenericConverter<TOhlcItem, Double>.Create(
|
||||
function( const Ohlc: TOhlcItem ): Double
|
||||
begin
|
||||
Result := Ohlc.Close;
|
||||
end );
|
||||
|
||||
Ohlc.Observers.Link( Closes );
|
||||
Ohlc.Sender.Link( Closes );
|
||||
|
||||
var Hull: IMycIndicator<Double, Double> := THullMovingAverage.Create( 50 );
|
||||
var Hull: IMycConverter<Double, Double> := THullMovingAverage.Create( 250 );
|
||||
|
||||
Closes.Observers.Link( Hull );
|
||||
Closes.Sender.Link( Hull );
|
||||
|
||||
Hull.Observers.Link(chart.CreateDoubleListener);
|
||||
chart.AddDoubleSeries( Hull.Sender );
|
||||
|
||||
var done := ExecuteStrategy( Symbol, strategy );
|
||||
var done := ExecuteStrategy( Symbol, OhlcPoint );
|
||||
|
||||
/////
|
||||
|
||||
@@ -205,7 +206,7 @@ begin
|
||||
|
||||
FProcessDone := TState.All([FProcessDone, done]);
|
||||
|
||||
strategy.Observers.Link(chart.CreateOhlcListener);
|
||||
chart.AddOhlcSeries( OhlcPoint.Sender );
|
||||
end;
|
||||
|
||||
procedure TForm1.TreeViewDblClick(Sender: TObject);
|
||||
@@ -531,7 +532,7 @@ begin
|
||||
end;
|
||||
end );
|
||||
|
||||
dataProvider.Observers.Link( Processor );
|
||||
dataProvider.Sender.Link( Processor );
|
||||
|
||||
Result := FServer.ProcessData( Symbol, terminated, dataProvider );
|
||||
end;
|
||||
|
||||
+132
-72
@@ -15,17 +15,18 @@ uses
|
||||
FMX.Controls,
|
||||
FMX.Graphics,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Signals;
|
||||
Myc.Signals,
|
||||
Myc.Lazy;
|
||||
|
||||
type
|
||||
TCandleStyle = (csCandleStick, csHiLoBar);
|
||||
|
||||
TMycChart = class(TStyledControl)
|
||||
type
|
||||
TSeries = class abstract(TContainedObject)
|
||||
TSeries = class abstract(TObject)
|
||||
private
|
||||
FOwner: TMycChart;
|
||||
function GetMainSeries: TSeries;
|
||||
function GetOwner: TMycChart;
|
||||
protected
|
||||
function GetCount: Int64; virtual; abstract;
|
||||
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; virtual; abstract;
|
||||
@@ -36,7 +37,7 @@ type
|
||||
constructor Create(AOwner: TMycChart);
|
||||
property Count: Int64 read GetCount;
|
||||
property MainSeries: TSeries read GetMainSeries;
|
||||
property Owner: TMycChart read GetOwner;
|
||||
property Owner: TMycChart read FOwner;
|
||||
end;
|
||||
|
||||
private
|
||||
@@ -51,15 +52,19 @@ type
|
||||
destructor Destroy; override;
|
||||
|
||||
// Creates an OHLC candlestick/bar series
|
||||
function CreateOhlcListener(
|
||||
procedure AddOhlcSeries(
|
||||
const DataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||
const AUpColor: TAlphaColor = TAlphaColors.Green;
|
||||
const ADownColor: TAlphaColor = TAlphaColors.Red;
|
||||
const AStyle: TCandleStyle = csCandleStick
|
||||
): IMycProcessor<TArray<TDataPoint<TOhlcItem>>>;
|
||||
);
|
||||
|
||||
// Creates a simple line series for double values
|
||||
function CreateDoubleListener(const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue; const ALineWidth: Single = 1.5):
|
||||
IMycProcessor<TArray<Double>>;
|
||||
procedure AddDoubleSeries(
|
||||
const DataProvider: IMycDataProvider<Double>;
|
||||
const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue;
|
||||
const ALineWidth: Single = 1.5
|
||||
);
|
||||
|
||||
// The maximum number of data points to display from the main series.
|
||||
property Lookback: Integer read FLookback write FLookback;
|
||||
@@ -69,24 +74,42 @@ implementation
|
||||
|
||||
uses
|
||||
System.Math,
|
||||
System.SyncObjs, WinApi.Windows,
|
||||
System.SyncObjs,
|
||||
WinApi.Windows,
|
||||
Myc.Trade.DataArray;
|
||||
|
||||
type
|
||||
TChartSeriesProcessor<T> = class(TMycChart.TSeries, IMycProcessor<TArray<T>>)
|
||||
TChartSeriesReceiver<T> = class(TMycProcessor<T>)
|
||||
strict private
|
||||
FCurrData: TMycDataArray<T>;
|
||||
FLookback: Int64;
|
||||
private
|
||||
FData: TWriteable<TMycDataArray<T>>;
|
||||
protected
|
||||
function ProcessData(const Value: T): Boolean; override;
|
||||
procedure Update; override;
|
||||
public
|
||||
constructor Create(ALookback: Int64);
|
||||
function GetData(var Data: TMycDataArray<T>): Boolean;
|
||||
property Data: TWriteable<TMycDataArray<T>> read FData;
|
||||
end;
|
||||
|
||||
TChartSeriesProcessor<T> = class(TMycChart.TSeries)
|
||||
strict private
|
||||
FDataSeries: TMycDataArray<T>;
|
||||
FChanged: Boolean;
|
||||
FLock: TSpinLock;
|
||||
function ProcessData(const Values: TArray<T>): Boolean;
|
||||
private
|
||||
FData: TMycDataArray<T>;
|
||||
FDataProvider: IMycDataProvider<T>;
|
||||
FReceiver: TChartSeriesReceiver<T>;
|
||||
FReceiverTag: TTag;
|
||||
protected
|
||||
function GetCount: Int64; override;
|
||||
function Update: Boolean; override;
|
||||
|
||||
public
|
||||
constructor Create(AOwner: TMycChart);
|
||||
constructor Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider<T>);
|
||||
destructor Destroy; override;
|
||||
property Data: TMycDataArray<T> read FData;
|
||||
end;
|
||||
|
||||
@@ -100,7 +123,12 @@ type
|
||||
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; override;
|
||||
procedure Paint(const ACanvas: TCanvas; const AXForm, AYForm: TFunc<Double, Single>); override;
|
||||
public
|
||||
constructor Create(AOwner: TMycChart; const AUpColor, ADownColor: TAlphaColor; AStyle: TCandleStyle);
|
||||
constructor Create(
|
||||
AOwner: TMycChart;
|
||||
const ADataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||
const AUpColor, ADownColor: TAlphaColor;
|
||||
AStyle: TCandleStyle
|
||||
);
|
||||
end;
|
||||
|
||||
{ TChartLineSeries }
|
||||
@@ -112,14 +140,20 @@ type
|
||||
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; override;
|
||||
procedure Paint(const ACanvas: TCanvas; const AXForm, AYForm: TFunc<Double, Single>); override;
|
||||
public
|
||||
constructor Create(AOwner: TMycChart; const ALineColor: TAlphaColor; ALineWidth: Single);
|
||||
constructor Create(
|
||||
AOwner: TMycChart;
|
||||
const ADataProvider: IMycDataProvider<Double>;
|
||||
const ALineColor: TAlphaColor;
|
||||
ALineWidth: Single
|
||||
);
|
||||
end;
|
||||
|
||||
{ TMycChart.TSeries }
|
||||
|
||||
constructor TMycChart.TSeries.Create(AOwner: TMycChart);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
inherited Create;
|
||||
FOwner := AOwner;
|
||||
end;
|
||||
|
||||
function TMycChart.TSeries.GetMainSeries: TSeries;
|
||||
@@ -129,18 +163,13 @@ begin
|
||||
Result := Owner.FSeriesList[0];
|
||||
end;
|
||||
|
||||
function TMycChart.TSeries.GetOwner: TMycChart;
|
||||
begin
|
||||
Result := Controller as TMycChart;
|
||||
end;
|
||||
|
||||
{ TMycChart }
|
||||
|
||||
constructor TMycChart.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FSeriesList := TObjectList<TSeries>.Create(true);
|
||||
FLookback := 100; // Default lookback
|
||||
FLookback := 10000;
|
||||
|
||||
FIdleSubscrId :=
|
||||
TMessageManager
|
||||
@@ -156,27 +185,29 @@ begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TMycChart.CreateDoubleListener(const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue; const ALineWidth: Single = 1.5):
|
||||
IMycProcessor<TArray<Double>>;
|
||||
procedure TMycChart.AddDoubleSeries(
|
||||
const DataProvider: IMycDataProvider<Double>;
|
||||
const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue;
|
||||
const ALineWidth: Single = 1.5
|
||||
);
|
||||
var
|
||||
series: TChartLineSeries;
|
||||
begin
|
||||
series := TChartLineSeries.Create(Self, ALineColor, ALineWidth);
|
||||
series := TChartLineSeries.Create(Self, DataProvider, ALineColor, ALineWidth);
|
||||
FSeriesList.Add(series);
|
||||
Result := series;
|
||||
end;
|
||||
|
||||
function TMycChart.CreateOhlcListener(
|
||||
procedure TMycChart.AddOhlcSeries(
|
||||
const DataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||
const AUpColor: TAlphaColor = TAlphaColors.Green;
|
||||
const ADownColor: TAlphaColor = TAlphaColors.Red;
|
||||
const AStyle: TCandleStyle = csCandleStick
|
||||
): IMycProcessor<TArray<TDataPoint<TOhlcItem>>>;
|
||||
);
|
||||
var
|
||||
series: TChartOhlcSeries;
|
||||
begin
|
||||
series := TChartOhlcSeries.Create(Self, AUpColor, ADownColor, AStyle);
|
||||
series := TChartOhlcSeries.Create(Self, DataProvider, AUpColor, ADownColor, AStyle);
|
||||
FSeriesList.Add(series);
|
||||
Result := series;
|
||||
end;
|
||||
|
||||
procedure TMycChart.DoIdle;
|
||||
@@ -241,14 +272,11 @@ begin
|
||||
xTransform := function(index: Double): Single begin Result := rect.Right - (index / (FLookback - 1)) * rect.Width; end;
|
||||
|
||||
yTransform :=
|
||||
function(value: Double): Single
|
||||
begin
|
||||
Result := rect.Top + (1 - (value - globalMin) / (globalMax - globalMin)) * rect.Height;
|
||||
end;
|
||||
function(value: Double): Single begin Result := rect.Top + (1 - (value - globalMin) / (globalMax - globalMin)) * rect.Height; end;
|
||||
|
||||
// var T :=
|
||||
// TMatrix.CreateTranslation(rect.Left, rect.Top + rect.Height*globalMax / (globalMax - globalMin)) *
|
||||
// TMatrix.CreateScaling(rect.Width / (FLookback - 1), -rect.Height / (globalMax - globalMin));
|
||||
// var T :=
|
||||
// TMatrix.CreateTranslation(rect.Left, rect.Top + rect.Height*globalMax / (globalMax - globalMin)) *
|
||||
// TMatrix.CreateScaling(rect.Width / (FLookback - 1), -rect.Height / (globalMax - globalMin));
|
||||
|
||||
for series in FSeriesList do
|
||||
begin
|
||||
@@ -258,12 +286,21 @@ end;
|
||||
|
||||
{ TChartSeriesProcessor<T> }
|
||||
|
||||
constructor TChartSeriesProcessor<T>.Create(AOwner: TMycChart);
|
||||
constructor TChartSeriesProcessor<T>.Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider<T>);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FLock := TSpinLock.Create(false);
|
||||
FDataSeries := TMycDataArray<T>.CreateEmpty;
|
||||
FData := FDataSeries;
|
||||
FDataProvider := ADataProvider;
|
||||
FReceiver := TChartSeriesReceiver<T>.Create(AOwner.Lookback);
|
||||
FReceiverTag := FDataProvider.Link(FReceiver);
|
||||
end;
|
||||
|
||||
destructor TChartSeriesProcessor<T>.Destroy;
|
||||
begin
|
||||
FDataProvider.Unlink(FReceiverTag);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TChartSeriesProcessor<T>.GetCount: Int64;
|
||||
@@ -271,36 +308,21 @@ begin
|
||||
Result := FData.Count;
|
||||
end;
|
||||
|
||||
function TChartSeriesProcessor<T>.ProcessData(const Values: TArray<T>): Boolean;
|
||||
begin
|
||||
Result := true;
|
||||
FLock.Enter;
|
||||
try
|
||||
FDataSeries := FDataSeries.Add(Values, 0, Length(Values), Owner.Lookback);
|
||||
FChanged := true;
|
||||
finally
|
||||
FLock.Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TChartSeriesProcessor<T>.Update: Boolean;
|
||||
begin
|
||||
FLock.Enter;
|
||||
try
|
||||
Result := FChanged;
|
||||
FChanged := false;
|
||||
if Result then
|
||||
FData := FDataSeries;
|
||||
finally
|
||||
FLock.Exit;
|
||||
end;
|
||||
Result := FReceiver.GetData(FData);
|
||||
end;
|
||||
|
||||
{ TChartOhlcSeries }
|
||||
|
||||
constructor TChartOhlcSeries.Create(AOwner: TMycChart; const AUpColor, ADownColor: TAlphaColor; AStyle: TCandleStyle);
|
||||
constructor TChartOhlcSeries.Create(
|
||||
AOwner: TMycChart;
|
||||
const ADataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||
const AUpColor, ADownColor: TAlphaColor;
|
||||
AStyle: TCandleStyle
|
||||
);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
inherited Create(AOwner, ADataProvider);
|
||||
FUpColor := AUpColor;
|
||||
FDownColor := ADownColor;
|
||||
FStyle := AStyle;
|
||||
@@ -318,8 +340,9 @@ begin
|
||||
Max := -MaxDouble;
|
||||
for i := StartIndex to System.Math.Min(GetCount - 1, StartIndex + Count - 1) do
|
||||
begin
|
||||
Min := System.Math.Min(Min, Data.Items[i].Data.Low);
|
||||
Max := System.Math.Max(Max, Data.Items[i].Data.High);
|
||||
var dp := Data.Items[i];
|
||||
Min := System.Math.Min(Min, dp.Data.Low);
|
||||
Max := System.Math.Max(Max, dp.Data.High);
|
||||
end;
|
||||
Result := (Min <> MaxDouble);
|
||||
end;
|
||||
@@ -372,9 +395,14 @@ end;
|
||||
|
||||
{ TChartLineSeries }
|
||||
|
||||
constructor TChartLineSeries.Create(AOwner: TMycChart; const ALineColor: TAlphaColor; ALineWidth: Single);
|
||||
constructor TChartLineSeries.Create(
|
||||
AOwner: TMycChart;
|
||||
const ADataProvider: IMycDataProvider<Double>;
|
||||
const ALineColor: TAlphaColor;
|
||||
ALineWidth: Single
|
||||
);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
inherited Create(AOwner, ADataProvider);
|
||||
FLineColor := ALineColor;
|
||||
FLineWidth := ALineWidth;
|
||||
end;
|
||||
@@ -389,10 +417,14 @@ begin
|
||||
|
||||
Min := MaxDouble;
|
||||
Max := -MaxDouble;
|
||||
for i := StartIndex to System.Math.Min(GetCount - 1, StartIndex + Count - 1) do
|
||||
for i := StartIndex to System.Math.Min(Data.Count - 1, StartIndex + Count - 1) do
|
||||
begin
|
||||
Min := System.Math.Min(Min, Data.Items[i]);
|
||||
Max := System.Math.Max(Max, Data.Items[i]);
|
||||
var v := Data.Items[i];
|
||||
if not IsNaN(v) then
|
||||
begin
|
||||
Min := System.Math.Min(Min, v);
|
||||
Max := System.Math.Max(Max, v);
|
||||
end;
|
||||
end;
|
||||
Result := (Min <> MaxDouble);
|
||||
end;
|
||||
@@ -412,18 +444,46 @@ begin
|
||||
var n := 0;
|
||||
while IsNaN(Data[n]) do
|
||||
begin
|
||||
inc( n );
|
||||
if n >= displaycount-2 then
|
||||
inc(n);
|
||||
if n >= displaycount - 2 then
|
||||
exit;
|
||||
end;
|
||||
|
||||
points.MoveTo( TPointF.Create(AXForm(n), AYForm(Data[n])) );
|
||||
points.MoveTo(TPointF.Create(AXForm(n), AYForm(Data[n])));
|
||||
for var i := n to displayCount - 1 do
|
||||
points.LineTo( TPointF.Create(AXForm(i), AYForm(Data[i])) );
|
||||
points.LineTo(TPointF.Create(AXForm(i), AYForm(Data[i])));
|
||||
|
||||
ACanvas.Stroke.Color := FLineColor;
|
||||
ACanvas.Stroke.Thickness := FLineWidth;
|
||||
ACanvas.DrawPath(points, 1);
|
||||
end;
|
||||
|
||||
{ TChartSeriesReceiver<T> }
|
||||
|
||||
constructor TChartSeriesReceiver<T>.Create(ALookback: Int64);
|
||||
begin
|
||||
inherited Create;
|
||||
FLookback := ALookback;
|
||||
FCurrData := TMycDataArray<T>.CreateEmpty;
|
||||
FData := TWriteable<TMycDataArray<T>>.CreateWriteable( FCurrData ).Protect;
|
||||
end;
|
||||
|
||||
function TChartSeriesReceiver<T>.GetData(var Data: TMycDataArray<T>): Boolean;
|
||||
begin
|
||||
var prevTotalCount := Data.TotalCount;
|
||||
Data := FData.Value;
|
||||
Result := prevTotalCount <> Data.TotalCount;
|
||||
end;
|
||||
|
||||
function TChartSeriesReceiver<T>.ProcessData(const Value: T): Boolean;
|
||||
begin
|
||||
Result := true;
|
||||
FCurrData := FCurrData.Add(Value, FLookback);
|
||||
end;
|
||||
|
||||
procedure TChartSeriesReceiver<T>.Update;
|
||||
begin
|
||||
FData.Value := FCurrData;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -15,17 +15,19 @@ type
|
||||
private
|
||||
FChunks: TArray<TChunk>;
|
||||
FCount: Int64;
|
||||
FTotalCount: Int64;
|
||||
|
||||
function LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; inline;
|
||||
function GetItems(Idx: Int64): T; inline;
|
||||
public
|
||||
constructor Create(const AChunks: TArray<TChunk>; ACount: Int64);
|
||||
constructor Create(const AChunks: TArray<TChunk>; ACount, ATotalCount: Int64);
|
||||
function Add(const Data: T; Lookback: Int64): TMycDataArray<T>; overload;
|
||||
function Add(const Data: array of T; First, Count, Lookback: Int64): TMycDataArray<T>; overload;
|
||||
class function CreateEmpty: TMycDataArray<T>; static;
|
||||
// Helper to create a data array from a raw TArray.
|
||||
class function CreateFromArray(const AData: TArray<T>; First, Count: Integer): TMycDataArray<T>; static;
|
||||
property Count: Int64 read FCount;
|
||||
property TotalCount: Int64 read FTotalCount;
|
||||
property Items[Idx: Int64]: T read GetItems; default;
|
||||
end;
|
||||
|
||||
@@ -33,10 +35,11 @@ implementation
|
||||
|
||||
{ TMycDataArray<T> }
|
||||
|
||||
constructor TMycDataArray<T>.Create(const AChunks: TArray<TChunk>; ACount: Int64);
|
||||
constructor TMycDataArray<T>.Create(const AChunks: TArray<TChunk>; ACount, ATotalCount: Int64);
|
||||
begin
|
||||
FChunks := AChunks;
|
||||
FCount := ACount;
|
||||
FTotalCount := ATotalCount;
|
||||
end;
|
||||
|
||||
class function TMycDataArray<T>.CreateEmpty: TMycDataArray<T>;
|
||||
@@ -98,7 +101,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := TMycDataArray<T>.Create(newChunks, newCount);
|
||||
Result := TMycDataArray<T>.Create(newChunks, newCount, FTotalCount + Count);
|
||||
end;
|
||||
|
||||
function TMycDataArray<T>.Add(const Data: T; Lookback: Int64): TMycDataArray<T>;
|
||||
|
||||
@@ -3,7 +3,8 @@ unit Myc.Trade.DataPoint;
|
||||
interface
|
||||
|
||||
uses
|
||||
System.TimeSpan;
|
||||
System.TimeSpan,
|
||||
Myc.Core.Notifier;
|
||||
|
||||
type
|
||||
// A data record for an Ask/Bid price pair.
|
||||
@@ -31,11 +32,34 @@ type
|
||||
|
||||
IMycProcessor<T> = interface
|
||||
function ProcessData(const Value: T): Boolean;
|
||||
procedure Update;
|
||||
end;
|
||||
|
||||
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
||||
protected
|
||||
function ProcessData(const Value: T): Boolean; virtual; abstract;
|
||||
procedure Update; virtual;
|
||||
end;
|
||||
|
||||
TTag = Pointer;
|
||||
|
||||
IMycDataProvider<T> = interface
|
||||
function Link(const Receiver: IMycProcessor<T>): TTag;
|
||||
procedure Unlink(Tag: TTag);
|
||||
end;
|
||||
|
||||
TMycDataProvider<T> = class abstract(TContainedObject, IMycDataProvider<T>)
|
||||
private
|
||||
FListeners: TMycNotifyList<IMycProcessor<T>>;
|
||||
public
|
||||
constructor Create(const Controller: IInterface);
|
||||
destructor Destroy; override;
|
||||
// Notifies all linked processors.
|
||||
procedure Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
|
||||
// Link a Processor
|
||||
function Link(const Processor: IMycProcessor<T>): TTag;
|
||||
// Unlink a linked strategy
|
||||
procedure Unlink(Tag: TTag);
|
||||
end;
|
||||
|
||||
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
||||
@@ -43,6 +67,7 @@ type
|
||||
TProc = reference to function(const Value: T): Boolean;
|
||||
private
|
||||
FProc: TProc;
|
||||
protected
|
||||
function ProcessData(const Value: T): Boolean; override;
|
||||
public
|
||||
constructor Create(const AProc: TProc);
|
||||
@@ -54,6 +79,7 @@ type
|
||||
private
|
||||
FProc: TProc;
|
||||
function ProcessData(const Value: T): Boolean;
|
||||
procedure Update;
|
||||
public
|
||||
constructor Create(const Controller: IInterface; const AProc: TProc);
|
||||
end;
|
||||
@@ -441,4 +467,58 @@ begin
|
||||
Result := FProc(Value);
|
||||
end;
|
||||
|
||||
procedure TMycContainedProcessor<T>.Update;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
{ TMycDataProvider<S, T> }
|
||||
|
||||
constructor TMycDataProvider<T>.Create(const Controller: IInterface);
|
||||
begin
|
||||
inherited Create(Controller);
|
||||
end;
|
||||
|
||||
destructor TMycDataProvider<T>.Destroy;
|
||||
begin
|
||||
FListeners.Finalize;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMycDataProvider<T>.Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
|
||||
begin
|
||||
FListeners.Lock;
|
||||
try
|
||||
FListeners.Notify(Func);
|
||||
finally
|
||||
FListeners.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMycDataProvider<T>.Link(const Processor: IMycProcessor<T>): TTag;
|
||||
begin
|
||||
// Add the Processor to the notification list
|
||||
FListeners.Lock;
|
||||
try
|
||||
Result := FListeners.Advise(Processor);
|
||||
finally
|
||||
FListeners.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycDataProvider<T>.Unlink(Tag: TTag);
|
||||
begin
|
||||
FListeners.Lock;
|
||||
try
|
||||
FListeners.Unadvise(Tag);
|
||||
finally
|
||||
FListeners.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycProcessor<T>.Update;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -437,7 +437,8 @@ begin
|
||||
function(const Data: TArray<TDataPoint<T>>): TState
|
||||
begin
|
||||
if not Processor.ProcessData(Data) then
|
||||
exit( TState.Null );
|
||||
exit(TState.Null);
|
||||
Processor.Update;
|
||||
|
||||
Result := ProcessFile(nextFileInfo, nextFile, cTerminated, Processor);
|
||||
end
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
unit Myc.Trade.Ticker;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
Myc.Signals,
|
||||
Myc.Lazy,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Trade.DataStream,
|
||||
Myc.Trade.DataProvider;
|
||||
|
||||
type
|
||||
TTickProc<T> = reference to procedure(const Tick: TDataPoint<T>);
|
||||
|
||||
TTicker<T> = class(TInterfacedObject, TSignal.ISubscriber)
|
||||
type
|
||||
TConsumer = record
|
||||
Proc: TTickProc<T>;
|
||||
Lookback: Int64;
|
||||
end;
|
||||
private
|
||||
FStream: IDataStream<T>;
|
||||
FConsumers: TArray<TConsumer>;
|
||||
FProvider: TMutable<TDataSeries<T>>;
|
||||
|
||||
function Notify: Boolean;
|
||||
public
|
||||
constructor Create(const AStream: IDataStream<T>; const AConsumers: TArray<TConsumer>);
|
||||
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
constructor TTicker<T>.Create(const AStream: IDataStream<T>; const AConsumers: TArray<TConsumer>);
|
||||
begin
|
||||
inherited Create;
|
||||
FStream := AStream;
|
||||
FConsumers := AConsumers;
|
||||
|
||||
var ml: Int64 := 0;
|
||||
for var consumer in FConsumers do
|
||||
if consumer.Lookback > ml then
|
||||
ml := consumer.Lookback;
|
||||
|
||||
FProvider := TDataStreamProvider<T>.Create(ml, 100, AStream);
|
||||
end;
|
||||
|
||||
{ TTicker<T> }
|
||||
|
||||
function TTicker<T>.Notify: Boolean;
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
end.
|
||||
Reference in New Issue
Block a user