Fixed concurrent processing
This commit is contained in:
+25
-19
@@ -108,6 +108,7 @@ type
|
|||||||
TEquitySum = class(TMycConverter<Double, Double>)
|
TEquitySum = class(TMycConverter<Double, Double>)
|
||||||
private
|
private
|
||||||
FEquity: Double;
|
FEquity: Double;
|
||||||
|
FInit: Boolean;
|
||||||
protected
|
protected
|
||||||
function ProcessData(const Value: Double): TState; override;
|
function ProcessData(const Value: Double): TState; override;
|
||||||
public
|
public
|
||||||
@@ -360,12 +361,12 @@ begin
|
|||||||
var Ohlc := OhlcPoint.Field<TOhlcItem>('Data');
|
var Ohlc := OhlcPoint.Field<TOhlcItem>('Data');
|
||||||
var Closes := Ohlc.Field<Double>('Close');
|
var Closes := Ohlc.Field<Double>('Close');
|
||||||
|
|
||||||
var Hull := Closes.Chain<Double>(TIndicators.CreateHMA(150));
|
var Hull := Closes.MakeParallel.Chain<Double>(TIndicators.CreateHMA(150));
|
||||||
var Sma := Closes.Chain<Double>(TIndicators.CreateSMA(50));
|
var Sma := Closes.Chain<Double>(TIndicators.CreateSMA(50));
|
||||||
var Ema := Closes.Chain<Double>(TIndicators.CreateEMA(21));
|
var Ema := Closes.Chain<Double>(TIndicators.CreateEMA(21));
|
||||||
var Boli := Closes.Chain<TBollingerBandsResult>(TIndicators.CreateBollingerBands(20, 2.0));
|
var Boli := Closes.MakeParallel.Chain<TBollingerBandsResult>(TIndicators.CreateBollingerBands(20, 2.0));
|
||||||
var Rsi := Closes.Chain<Double>(TIndicators.CreateRSI(14));
|
var Rsi := Closes.MakeParallel.Chain<Double>(TIndicators.CreateRSI(14));
|
||||||
var Macd := Closes.Chain<TMacdResult>(TIndicators.CreateMACD(12, 26, 9));
|
var Macd := Closes.MakeParallel.Chain<TMacdResult>(TIndicators.CreateMACD(12, 26, 9));
|
||||||
var Stoch := Ohlc.Chain<TStochasticResult>(TIndicators.CreateStochastic(14, 3));
|
var Stoch := Ohlc.Chain<TStochasticResult>(TIndicators.CreateStochastic(14, 3));
|
||||||
|
|
||||||
chart.SetXAxisSeries(timeframe, Timestamps.Sender);
|
chart.SetXAxisSeries(timeframe, Timestamps.Sender);
|
||||||
@@ -429,7 +430,7 @@ type
|
|||||||
pnl: Double;
|
pnl: Double;
|
||||||
end;
|
end;
|
||||||
begin
|
begin
|
||||||
var timeframe := TTimeframe.M15;
|
var timeframe := TTimeframe.D;
|
||||||
|
|
||||||
var ticker := TConverter.CreateTicker<TDataPoint<TAskBidItem>>;
|
var ticker := TConverter.CreateTicker<TDataPoint<TAskBidItem>>;
|
||||||
|
|
||||||
@@ -440,37 +441,35 @@ begin
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
var OhlcPoint := lastPrice.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateAggregation(timeframe));
|
var OhlcPoint := lastPrice.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateAggregation(timeframe)).MakeParallel;
|
||||||
|
|
||||||
var Ohlc := TConverter.CreateSequence<TOhlcItem>(2, OhlcPoint.Field<TOhlcItem>('Data').Sender);
|
var Ohlc := TConverter.CreateSequence<TOhlcItem>(2, OhlcPoint.Field<TOhlcItem>('Data').Sender);
|
||||||
|
|
||||||
var Closes := Ohlc[0].Field<Double>('Close');
|
var Closes := Ohlc[0].Field<Double>('Close');
|
||||||
|
|
||||||
var Hull := Closes.Chain<Double>(TIndicators.CreateHMA(250));
|
var Hull := Closes.Chain<Double>(TIndicators.CreateHMA(250)).MakeParallel;
|
||||||
var Sma := Closes.Chain<Double>(TIndicators.CreateSMA(200));
|
var Sma := Closes.Chain<Double>(TIndicators.CreateSMA(200)).MakeParallel;
|
||||||
|
|
||||||
var HullSeries := TConverter.CreateEndpoint<Double>(Hull.Sender, 5);
|
|
||||||
var SmaSeries := TConverter.CreateEndpoint<Double>(Sma.Sender, 5);
|
|
||||||
|
|
||||||
var Lowest: Double := Double.MaxValue;
|
var Lowest: Double := Double.MaxValue;
|
||||||
var Highest: Double := Double.MinValue;
|
var Highest: Double := Double.MinValue;
|
||||||
|
|
||||||
|
var ATR := Ohlc[0].Chain<Double>(TIndicators.CreateATR(15));
|
||||||
|
var ATRSeries := TConverter.CreateEndpoint<Double>(ATR.Sender, 5);
|
||||||
|
|
||||||
|
var HullSeries := TConverter.CreateEndpoint<Double>(Hull.Sender, 5);
|
||||||
|
var SmaSeries := TConverter.CreateEndpoint<Double>(Sma.Sender, 5);
|
||||||
|
|
||||||
|
// next stage
|
||||||
|
|
||||||
var curr: TSignal;
|
var curr: TSignal;
|
||||||
curr.SL := Double.NaN;
|
curr.SL := Double.NaN;
|
||||||
curr.Entry := Double.NaN;
|
curr.Entry := Double.NaN;
|
||||||
|
|
||||||
var ATR := Ohlc[0].Chain<Double>(TIndicators.CreateATR(50));
|
|
||||||
var ATRSeries := TConverter.CreateEndpoint<Double>(ATR.Sender, 5);
|
|
||||||
|
|
||||||
// next stage
|
|
||||||
|
|
||||||
var Signal :=
|
var Signal :=
|
||||||
Ohlc[1]
|
Ohlc[1]
|
||||||
.Chain<TSignal>(
|
.Chain<TSignal>(
|
||||||
function(const Ohlc: TOhlcItem): TSignal
|
function(const Ohlc: TOhlcItem): TSignal
|
||||||
begin
|
begin
|
||||||
var pnl: Double := 0;
|
|
||||||
|
|
||||||
if Ohlc.Low < Lowest then
|
if Ohlc.Low < Lowest then
|
||||||
Lowest := Ohlc.Low;
|
Lowest := Ohlc.Low;
|
||||||
if Ohlc.High > Highest then
|
if Ohlc.High > Highest then
|
||||||
@@ -478,7 +477,7 @@ begin
|
|||||||
|
|
||||||
Result := curr;
|
Result := curr;
|
||||||
Result.Sig := 0;
|
Result.Sig := 0;
|
||||||
pnl := NaN;
|
var pnl: double := NaN;
|
||||||
|
|
||||||
if (HullSeries.Value[0] < SmaSeries.Value[0]) and (HullSeries.Value[1] >= SmaSeries.Value[1]) then
|
if (HullSeries.Value[0] < SmaSeries.Value[0]) and (HullSeries.Value[1] >= SmaSeries.Value[1]) then
|
||||||
begin
|
begin
|
||||||
@@ -600,10 +599,17 @@ constructor TEquitySum.Create(AEquity: Double);
|
|||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FEquity := AEquity;
|
FEquity := AEquity;
|
||||||
|
FInit := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TEquitySum.ProcessData(const Value: Double): TState;
|
function TEquitySum.ProcessData(const Value: Double): TState;
|
||||||
begin
|
begin
|
||||||
|
if not FInit then
|
||||||
|
begin
|
||||||
|
FInit := true;
|
||||||
|
Broadcast(FEquity);
|
||||||
|
end;
|
||||||
|
|
||||||
if not IsNan(Value) then
|
if not IsNan(Value) then
|
||||||
begin
|
begin
|
||||||
FEquity := FEquity + Value;
|
FEquity := FEquity + Value;
|
||||||
|
|||||||
@@ -15,27 +15,13 @@ uses
|
|||||||
Myc.Fmx.Chart;
|
Myc.Fmx.Chart;
|
||||||
|
|
||||||
type
|
type
|
||||||
TChartSeriesReceiver<T> = class(TMycProcessor<T>)
|
|
||||||
strict private
|
|
||||||
FCurrData: TSeries<T>;
|
|
||||||
FLookback: TMutable<Int64>;
|
|
||||||
private
|
|
||||||
FData: TWriteable<TSeries<T>>;
|
|
||||||
protected
|
|
||||||
function ProcessData(const Value: T): TState; override;
|
|
||||||
public
|
|
||||||
constructor Create(const ALookback: TMutable<Int64>);
|
|
||||||
property Data: TWriteable<TSeries<T>> read FData;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TChartSeriesProcessor<T> = class(TMycChart.TSeries)
|
TChartSeriesProcessor<T> = class(TMycChart.TSeries)
|
||||||
strict private
|
strict private
|
||||||
FDataSeries: TSeries<T>;
|
FDataSeries: TSeries<T>;
|
||||||
private
|
private
|
||||||
FData: TSeries<T>;
|
FData: TSeries<T>;
|
||||||
FDataProvider: TDataProvider<T>;
|
FDataProvider: TDataProvider<T>;
|
||||||
FReceiver: TChartSeriesReceiver<T>;
|
FReceiver: TMutable<TSeries<T>>;
|
||||||
FReceiverTag: TDataProvider<T>.TTag;
|
|
||||||
protected
|
protected
|
||||||
function GetCount: Int64; override;
|
function GetCount: Int64; override;
|
||||||
function GetTotalCount: Int64; override;
|
function GetTotalCount: Int64; override;
|
||||||
@@ -140,22 +126,6 @@ uses
|
|||||||
System.Math,
|
System.Math,
|
||||||
FMX.Types;
|
FMX.Types;
|
||||||
|
|
||||||
{ TChartSeriesReceiver<T> }
|
|
||||||
|
|
||||||
constructor TChartSeriesReceiver<T>.Create(const ALookback: TMutable<Int64>);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
FLookback := ALookback;
|
|
||||||
FData := TWriteable<TSeries<T>>.CreateWriteable(FCurrData).Protect;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TChartSeriesReceiver<T>.ProcessData(const Value: T): TState;
|
|
||||||
begin
|
|
||||||
Result := TState.Null;
|
|
||||||
FCurrData := FCurrData.Add(Value, FLookback.Value);
|
|
||||||
FData.Value := FCurrData;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TChartSeriesProcessor<T> }
|
{ TChartSeriesProcessor<T> }
|
||||||
|
|
||||||
constructor TChartSeriesProcessor<T>.Create(const ADataProvider: TDataProvider<T>; const ALookback: TMutable<Int64>);
|
constructor TChartSeriesProcessor<T>.Create(const ADataProvider: TDataProvider<T>; const ALookback: TMutable<Int64>);
|
||||||
@@ -164,13 +134,11 @@ begin
|
|||||||
FDataProvider := ADataProvider;
|
FDataProvider := ADataProvider;
|
||||||
FData := FDataSeries;
|
FData := FDataSeries;
|
||||||
|
|
||||||
FReceiver := TChartSeriesReceiver<T>.Create(ALookback);
|
FReceiver := TConverter.CreateEndpoint<T>(FDataProvider, ALookback.Value);
|
||||||
FReceiverTag := FDataProvider.Link(FReceiver);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TChartSeriesProcessor<T>.Destroy;
|
destructor TChartSeriesProcessor<T>.Destroy;
|
||||||
begin
|
begin
|
||||||
FDataProvider.Unlink(FReceiverTag);
|
|
||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -186,7 +154,7 @@ end;
|
|||||||
|
|
||||||
procedure TChartSeriesProcessor<T>.Update;
|
procedure TChartSeriesProcessor<T>.Update;
|
||||||
begin
|
begin
|
||||||
FData := FReceiver.Data.Value;
|
FData := FReceiver.Value;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TChartLineLayer }
|
{ TChartLineLayer }
|
||||||
|
|||||||
+22
-29
@@ -64,18 +64,19 @@ type
|
|||||||
constructor Create(AParent: TPanel);
|
constructor Create(AParent: TPanel);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TDragPoint = record
|
||||||
|
Point: TPointF;
|
||||||
|
Idx: Int64;
|
||||||
|
BarX: Single;
|
||||||
|
end;
|
||||||
|
|
||||||
TAxisLayer = class abstract(TLayer)
|
TAxisLayer = class abstract(TLayer)
|
||||||
private
|
private
|
||||||
FOwner: TMycChart;
|
FOwner: TMycChart;
|
||||||
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(
|
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; const MousePos: TDragPoint); virtual; 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;
|
||||||
@@ -83,12 +84,7 @@ type
|
|||||||
TXAxisLayer = class(TAxisLayer)
|
TXAxisLayer = class(TAxisLayer)
|
||||||
protected
|
protected
|
||||||
// Paint crosshair and time caption
|
// Paint crosshair and time caption
|
||||||
procedure Paint(
|
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; const MousePos: TDragPoint); override;
|
||||||
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;
|
||||||
@@ -124,11 +120,6 @@ type
|
|||||||
property Weight: Single read FWeight write SetWeight;
|
property Weight: Single read FWeight write SetWeight;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TDragPoint = record
|
|
||||||
Point: TPointF;
|
|
||||||
Idx: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
private
|
private
|
||||||
FPanelList: TObjectList<TPanel>;
|
FPanelList: TObjectList<TPanel>;
|
||||||
FXAxisSeries: TMycChart.TXAxisLayer;
|
FXAxisSeries: TMycChart.TXAxisLayer;
|
||||||
@@ -175,6 +166,8 @@ type
|
|||||||
property Panels[Index: Integer]: TPanel read GetPanel; default;
|
property Panels[Index: Integer]: TPanel read GetPanel; default;
|
||||||
// The rectangle occupied by the jump-to-latest button, used for hit testing.
|
// The rectangle occupied by the jump-to-latest button, used for hit testing.
|
||||||
property JumpButtonRect: TRectF read FJumpButtonRect;
|
property JumpButtonRect: TRectF read FJumpButtonRect;
|
||||||
|
property ViewCount: Int64 read FViewCount;
|
||||||
|
property ViewStartIndex: Int64 read FViewStartIndex;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -232,11 +225,16 @@ end;
|
|||||||
|
|
||||||
function TMycChart.CreateDragPoint(X, Y: Single): TDragPoint;
|
function TMycChart.CreateDragPoint(X, Y: Single): TDragPoint;
|
||||||
begin
|
begin
|
||||||
|
var rect := LocalRect;
|
||||||
|
|
||||||
Result.Point.X := X;
|
Result.Point.X := X;
|
||||||
Result.Point.Y := Y;
|
Result.Point.Y := Y;
|
||||||
Result.Idx := Round((LocalRect.Right - X) * (FViewCount - 1) / LocalRect.Width);
|
Result.Idx := Round((rect.Right - X) * (FViewCount - 1) / rect.Width);
|
||||||
if (Result.Idx < 0) or (Result.Idx >= FXAxisSeries.Series.Count) then
|
if (Result.Idx < 0) or (Result.Idx >= FXAxisSeries.Series.Count) then
|
||||||
Result.Idx := -1;
|
Result.Idx := -1;
|
||||||
|
Result.BarX := NaN;
|
||||||
|
if Result.Idx >= 0 then
|
||||||
|
Result.BarX := rect.Right - (Result.Idx * (FViewCount - 1)) / (FViewCount - 1) * (rect.Width / (FViewCount - 1));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycChart.GetPanel(Index: Integer): TPanel;
|
function TMycChart.GetPanel(Index: Integer): TPanel;
|
||||||
@@ -356,7 +354,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
// Do not draw crosshair if mouse is over the jump button
|
// Do not draw crosshair if mouse is over the jump button
|
||||||
if not FJumpButtonRect.Contains(FMousePos.Point) then
|
if not FJumpButtonRect.Contains(FMousePos.Point) then
|
||||||
FXAxisSeries.Paint(Self.Canvas, rect, FViewStartIndex, FViewCount, FViewStartIndex + FMousePos.Idx);
|
FXAxisSeries.Paint(Self.Canvas, rect, FMousePos);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- Draw the "jump to latest" button (code unchanged) ---
|
// --- Draw the "jump to latest" button (code unchanged) ---
|
||||||
@@ -839,12 +837,7 @@ end;
|
|||||||
|
|
||||||
{ TMycChart.TXAxisLayer }
|
{ TMycChart.TXAxisLayer }
|
||||||
|
|
||||||
procedure TMycChart.TXAxisLayer.Paint(
|
procedure TMycChart.TXAxisLayer.Paint(const Canvas: TCanvas; const Viewport: TRectF; const MousePos: TDragPoint);
|
||||||
const Canvas: TCanvas;
|
|
||||||
const Viewport: TRectF;
|
|
||||||
ViewStartIndex, ViewCount: Int64;
|
|
||||||
IdxAtMousePos: Integer
|
|
||||||
);
|
|
||||||
var
|
var
|
||||||
caption: string;
|
caption: string;
|
||||||
captionRect: TRectF;
|
captionRect: TRectF;
|
||||||
@@ -853,11 +846,11 @@ var
|
|||||||
textSize: TRectF;
|
textSize: TRectF;
|
||||||
begin
|
begin
|
||||||
// Do not draw if no data or view is invalid
|
// Do not draw if no data or view is invalid
|
||||||
if (not Assigned(Series)) or (Series.Count = 0) or (ViewCount <= 1) then
|
if (not Assigned(Series)) or (Series.Count = 0) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
// Snap X to the candle's center
|
// 1. Snap X to the candle's center
|
||||||
candleX := Viewport.Right - ((IdxAtMousePos - ViewStartIndex) * (ViewCount - 1)) / (ViewCount - 1) * (Viewport.Width / (ViewCount - 1));
|
candleX := MousePos.BarX;
|
||||||
|
|
||||||
// 2. Draw the vertical line at the snapped position
|
// 2. Draw the vertical line at the snapped position
|
||||||
Canvas.Stroke.Kind := TBrushKind.Solid;
|
Canvas.Stroke.Kind := TBrushKind.Solid;
|
||||||
@@ -866,7 +859,7 @@ begin
|
|||||||
Canvas.DrawLine(PointF(candleX, Viewport.Top), PointF(candleX, Viewport.Bottom), 1.0);
|
Canvas.DrawLine(PointF(candleX, Viewport.Top), PointF(candleX, Viewport.Bottom), 1.0);
|
||||||
|
|
||||||
// 3. Get the caption for the index
|
// 3. Get the caption for the index
|
||||||
caption := GetCaption(IdxAtMousePos);
|
caption := GetCaption(FOwner.ViewStartIndex + MousePos.Idx);
|
||||||
if caption.IsEmpty then
|
if caption.IsEmpty then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
|||||||
+19
-1
@@ -29,7 +29,10 @@ type
|
|||||||
|
|
||||||
// Run a task when the gate is opened.
|
// Run a task when the gate is opened.
|
||||||
// Returns a State to await thread completion.
|
// Returns a State to await thread completion.
|
||||||
function RunTask(const Gate: TState; const Proc: TFunc<TState>): TState;
|
function RunTask(const Proc: TFunc<TState>): TState; overload;
|
||||||
|
function RunTask(const Gate: TState; const Proc: TFunc<TState>): TState; overload;
|
||||||
|
procedure RunTask(const Gate: TState; const Proc: TProc); overload;
|
||||||
|
procedure RunTask(const Proc: TProc); overload;
|
||||||
|
|
||||||
class function RunSequence(const Gate: TState; First, Count: Integer; const Proc: TFunc<Integer, TState>): TState; static;
|
class function RunSequence(const Gate: TState; First, Count: Integer; const Proc: TFunc<Integer, TState>): TState; static;
|
||||||
|
|
||||||
@@ -181,6 +184,21 @@ begin
|
|||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TTaskManager.RunTask(const Proc: TFunc<TState>): TState;
|
||||||
|
begin
|
||||||
|
Result := RunTask(TState.Null, Proc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTaskManager.RunTask(const Proc: TProc);
|
||||||
|
begin
|
||||||
|
RunTask(TState.Null, Proc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTaskManager.RunTask(const Gate: TState; const Proc: TProc);
|
||||||
|
begin
|
||||||
|
FTaskManager.RunTask(Gate, Proc);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TTaskManager.WaitFor(const State: TState);
|
procedure TTaskManager.WaitFor(const State: TState);
|
||||||
begin
|
begin
|
||||||
FTaskManager.WaitFor(State);
|
FTaskManager.WaitFor(State);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
|
System.SyncObjs,
|
||||||
Myc.Signals,
|
Myc.Signals,
|
||||||
Myc.Mutable,
|
Myc.Mutable,
|
||||||
Myc.Core.Notifier,
|
Myc.Core.Notifier,
|
||||||
@@ -15,6 +16,7 @@ type
|
|||||||
// Abstract base class for data consumers.
|
// Abstract base class for data consumers.
|
||||||
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
||||||
protected
|
protected
|
||||||
|
function Execute(const Value: T; const Proc: TConstFunc<T, TState>): TState;
|
||||||
function ProcessData(const Value: T): TState; virtual; abstract;
|
function ProcessData(const Value: T): TState; virtual; abstract;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -86,7 +88,16 @@ type
|
|||||||
constructor Create(const AFunc: TConstFunc<S, T>);
|
constructor Create(const AFunc: TConstFunc<S, T>);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// A converter specialized for calculating indicators.
|
TMycGenericParallelConverter<S, T> = class(TMycConverter<S, T>)
|
||||||
|
private
|
||||||
|
FFunc: TConstFunc<S, T>;
|
||||||
|
FQueue: TState;
|
||||||
|
protected
|
||||||
|
function ProcessData(const Value: S): TState; override;
|
||||||
|
public
|
||||||
|
constructor Create(const AFunc: TConstFunc<S, T>);
|
||||||
|
end;
|
||||||
|
|
||||||
TMycIdentityConverter<T> = class(TMycConverter<T, T>)
|
TMycIdentityConverter<T> = class(TMycConverter<T, T>)
|
||||||
protected
|
protected
|
||||||
function ProcessData(const Value: T): TState; override; final;
|
function ProcessData(const Value: T): TState; override; final;
|
||||||
@@ -126,14 +137,12 @@ type
|
|||||||
|
|
||||||
// A generic processor implementation that uses a function reference.
|
// A generic processor implementation that uses a function reference.
|
||||||
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
||||||
type
|
|
||||||
TProc = reference to function(const Value: T): TState;
|
|
||||||
private
|
private
|
||||||
FProc: TProc;
|
FProc: TConstFunc<T, TState>;
|
||||||
protected
|
protected
|
||||||
function ProcessData(const Value: T): TState; override; final;
|
function ProcessData(const Value: T): TState; override; final;
|
||||||
public
|
public
|
||||||
constructor Create(const AProc: TProc);
|
constructor Create(const AProc: TConstFunc<T, TState>);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// A processor implementation that is owned by a controller.
|
// A processor implementation that is owned by a controller.
|
||||||
@@ -156,6 +165,7 @@ type
|
|||||||
FLookback: Int64;
|
FLookback: Int64;
|
||||||
FData: TSeries<T>;
|
FData: TSeries<T>;
|
||||||
FChanged: TEvent;
|
FChanged: TEvent;
|
||||||
|
FLock: TLightweightMREW;
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
function GetValue: TSeries<T>;
|
function GetValue: TSeries<T>;
|
||||||
function ProcessData(const Value: T): TState;
|
function ProcessData(const Value: T): TState;
|
||||||
@@ -178,6 +188,13 @@ type
|
|||||||
property Timeframe: TTimeframe read GetTimeframe;
|
property Timeframe: TTimeframe read GetTimeframe;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TMycParallelConverter<T> = class(TMycConverter<T, T>)
|
||||||
|
private
|
||||||
|
FQueue: TState;
|
||||||
|
protected
|
||||||
|
function ProcessData(const Value: T): TState; override; final;
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
@@ -187,6 +204,14 @@ uses
|
|||||||
System.Math,
|
System.Math,
|
||||||
Myc.TaskManager;
|
Myc.TaskManager;
|
||||||
|
|
||||||
|
{ TMycProcessor<T> }
|
||||||
|
|
||||||
|
function TMycProcessor<T>.Execute(const Value: T; const Proc: TConstFunc<T, TState>): TState;
|
||||||
|
begin
|
||||||
|
var cValue := Value;
|
||||||
|
Result := TaskManager.RunTask(function: TState begin Result := Proc(cValue); end);
|
||||||
|
end;
|
||||||
|
|
||||||
{ TMycContainedDataProvider<T> }
|
{ TMycContainedDataProvider<T> }
|
||||||
|
|
||||||
constructor TMycContainedDataProvider<T>.Create(const Controller: IInterface);
|
constructor TMycContainedDataProvider<T>.Create(const Controller: IInterface);
|
||||||
@@ -314,7 +339,7 @@ end;
|
|||||||
|
|
||||||
function TMycIndicator<S, T>.ProcessData(const Value: S): TState;
|
function TMycIndicator<S, T>.ProcessData(const Value: S): TState;
|
||||||
begin
|
begin
|
||||||
Result := Broadcast(Calculate(Value));
|
Result := Execute(Value, function(const Value: S): TState begin Result := Broadcast(Calculate(Value)); end);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TMycDataCounter<T> }
|
{ TMycDataCounter<T> }
|
||||||
@@ -385,7 +410,7 @@ end;
|
|||||||
|
|
||||||
{ TMycGenericProcessor<T> }
|
{ TMycGenericProcessor<T> }
|
||||||
|
|
||||||
constructor TMycGenericProcessor<T>.Create(const AProc: TProc);
|
constructor TMycGenericProcessor<T>.Create(const AProc: TConstFunc<T, TState>);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FProc := AProc;
|
FProc := AProc;
|
||||||
@@ -419,6 +444,7 @@ begin
|
|||||||
|
|
||||||
FProcessor := TMycContainedProcessor<T>.Create(Self, ProcessData);
|
FProcessor := TMycContainedProcessor<T>.Create(Self, ProcessData);
|
||||||
FTag := FDataProvider.Link(FProcessor);
|
FTag := FDataProvider.Link(FProcessor);
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TMycDataEndpoint<T>.Destroy;
|
destructor TMycDataEndpoint<T>.Destroy;
|
||||||
@@ -435,15 +461,24 @@ end;
|
|||||||
|
|
||||||
function TMycDataEndpoint<T>.GetValue: TSeries<T>;
|
function TMycDataEndpoint<T>.GetValue: TSeries<T>;
|
||||||
begin
|
begin
|
||||||
Result := FData;
|
FLock.BeginRead;
|
||||||
|
try
|
||||||
|
Result := FData;
|
||||||
|
finally
|
||||||
|
FLock.EndRead;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycDataEndpoint<T>.ProcessData(const Value: T): TState;
|
function TMycDataEndpoint<T>.ProcessData(const Value: T): TState;
|
||||||
begin
|
begin
|
||||||
Result := TState.Null;
|
Result := TState.Null;
|
||||||
|
FLock.BeginWrite;
|
||||||
// Add new data point, respecting the lookback period.
|
try
|
||||||
FData := FData.Add(Value, FLookback);
|
// Add new data point, respecting the lookback period.
|
||||||
|
FData := FData.Add(Value, FLookback);
|
||||||
|
finally
|
||||||
|
FLock.EndWrite;
|
||||||
|
end;
|
||||||
FChanged.Notify;
|
FChanged.Notify;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -599,4 +634,27 @@ begin
|
|||||||
Result := Broadcast(Value);
|
Result := Broadcast(Value);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TMycGenericParallelConverter<S, T> }
|
||||||
|
|
||||||
|
constructor TMycGenericParallelConverter<S, T>.Create(const AFunc: TConstFunc<S, T>);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FFunc := AFunc;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycGenericParallelConverter<S, T>.ProcessData(const Value: S): TState;
|
||||||
|
begin
|
||||||
|
var cValue := Value;
|
||||||
|
Result := TaskManager.RunTask(FQueue, function: TState begin Result := Broadcast(FFunc(cValue)); end);
|
||||||
|
|
||||||
|
FQueue := Result;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycParallelConverter<T>.ProcessData(const Value: T): TState;
|
||||||
|
begin
|
||||||
|
var cValue := Value;
|
||||||
|
Result := TaskManager.RunTask(FQueue, function: TState begin Result := Broadcast(cValue); end);
|
||||||
|
FQueue := Result;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -82,9 +82,13 @@ type
|
|||||||
class operator Implicit(const A: TConverter<S, T>): IConverter; overload;
|
class operator Implicit(const A: TConverter<S, T>): IConverter; overload;
|
||||||
|
|
||||||
class function CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
|
class function CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
|
||||||
|
class function CreateParallel(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
|
||||||
|
|
||||||
function Chain<R>(const Next: TConverter<T, R>): TConverter<T, R>; overload; inline;
|
function Chain<R>(const Next: TConverter<T, R>): TConverter<T, R>; overload; inline;
|
||||||
function Chain<R>(const Func: TConstFunc<T, R>): TConverter<T, R>; overload; inline;
|
function Chain<R>(const Func: TConstFunc<T, R>): TConverter<T, R>; overload; inline;
|
||||||
|
function ChainParallel<R>(const Func: TConstFunc<T, R>): TConverter<T, R>; overload; inline;
|
||||||
|
|
||||||
|
function MakeParallel: TConverter<T, T>; overload; inline;
|
||||||
|
|
||||||
// Extracts the field of a record by it's name (using RTTI).
|
// Extracts the field of a record by it's name (using RTTI).
|
||||||
function Field<R>(const FieldName: String): TConverter<T, R>; overload; inline;
|
function Field<R>(const FieldName: String): TConverter<T, R>; overload; inline;
|
||||||
@@ -112,6 +116,8 @@ type
|
|||||||
class function CreateDataPointConverter<S, T>(const Func: TConstFunc<S, T>): TConverter<TDataPoint<S>, TDataPoint<T>>; static;
|
class function CreateDataPointConverter<S, T>(const Func: TConstFunc<S, T>): TConverter<TDataPoint<S>, TDataPoint<T>>; static;
|
||||||
|
|
||||||
class function CreateSequence<T>(Count: Integer; const Parent: TDataProvider<T>): TArray<TConverter<T, T>>; overload; static;
|
class function CreateSequence<T>(Count: Integer; const Parent: TDataProvider<T>): TArray<TConverter<T, T>>; overload; static;
|
||||||
|
|
||||||
|
class function Parallel<T>(Parent: TDataProvider<T>): TConverter<T, T>; static;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -184,11 +190,21 @@ begin
|
|||||||
Result := Chain<R>(TMycGenericConverter<T, R>.Create(Func));
|
Result := Chain<R>(TMycGenericConverter<T, R>.Create(Func));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TConverter<S, T>.ChainParallel<R>(const Func: TConstFunc<T, R>): TConverter<T, R>;
|
||||||
|
begin
|
||||||
|
Result := Chain<R>(TMycGenericParallelConverter<T, R>.Create(Func));
|
||||||
|
end;
|
||||||
|
|
||||||
class function TConverter<S, T>.CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>;
|
class function TConverter<S, T>.CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>;
|
||||||
begin
|
begin
|
||||||
Result := TMycGenericConverter<S, T>.Create(Func);
|
Result := TMycGenericConverter<S, T>.Create(Func);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class function TConverter<S, T>.CreateParallel(const Func: TConstFunc<S, T>): TConverter<S, T>;
|
||||||
|
begin
|
||||||
|
Result := TMycGenericParallelConverter<S, T>.Create(Func);
|
||||||
|
end;
|
||||||
|
|
||||||
function TConverter<S, T>.Field<R>(const FieldName: String): TConverter<T, R>;
|
function TConverter<S, T>.Field<R>(const FieldName: String): TConverter<T, R>;
|
||||||
begin
|
begin
|
||||||
Result := Chain<R>(TConverter.CreateRecordField<T, R>(FieldName));
|
Result := Chain<R>(TConverter.CreateRecordField<T, R>(FieldName));
|
||||||
@@ -199,6 +215,11 @@ begin
|
|||||||
Result := FConverter.Sender;
|
Result := FConverter.Sender;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TConverter<S, T>.MakeParallel: TConverter<T, T>;
|
||||||
|
begin
|
||||||
|
Result := Chain<T>(TMycGenericParallelConverter<T, T>.Create(function(const Value: T): T begin Result := Value; end));
|
||||||
|
end;
|
||||||
|
|
||||||
function TConverter<S, T>.Sequence(Count: Integer): IMycDataSequence<T>;
|
function TConverter<S, T>.Sequence(Count: Integer): IMycDataSequence<T>;
|
||||||
begin
|
begin
|
||||||
Result := TMycSequence<T>.Create(Count);
|
Result := TMycSequence<T>.Create(Count);
|
||||||
@@ -288,4 +309,10 @@ begin
|
|||||||
Parent.Link(seq);
|
Parent.Link(seq);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class function TConverter.Parallel<T>(Parent: TDataProvider<T>): TConverter<T, T>;
|
||||||
|
begin
|
||||||
|
Result := TMycParallelConverter<T>.Create;
|
||||||
|
Parent.Link(Result);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ type
|
|||||||
// Used by cache to actually load an uncached file.
|
// Used by cache to actually load an uncached file.
|
||||||
function DoLoad(const FileName: string): TFuture<TArray<TArray<TDataPoint<T>>>>;
|
function DoLoad(const FileName: string): TFuture<TArray<TArray<TDataPoint<T>>>>;
|
||||||
|
|
||||||
|
function ProcessChunks(
|
||||||
|
const DataChunks: TArray<TArray<TDataPoint<T>>>;
|
||||||
|
const Terminated: TState;
|
||||||
|
Processor: IMycProcessor<TArray<TDataPoint<T>>>
|
||||||
|
): TState;
|
||||||
|
|
||||||
function ProcessFile(
|
function ProcessFile(
|
||||||
FileInfo: TAuraDataFile;
|
FileInfo: TAuraDataFile;
|
||||||
const DataFile: TFuture<TArray<TArray<TDataPoint<T>>>>;
|
const DataFile: TFuture<TArray<TArray<TDataPoint<T>>>>;
|
||||||
@@ -354,6 +360,23 @@ begin
|
|||||||
Result := FCachedFiles.GetOrAdd(DataFile.GetFullFileName);
|
Result := FCachedFiles.GetOrAdd(DataFile.GetFullFileName);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAuraDataServer<T>.ProcessChunks(
|
||||||
|
const DataChunks: TArray<TArray<TDataPoint<T>>>;
|
||||||
|
const Terminated: TState;
|
||||||
|
Processor: IMycProcessor<TArray<TDataPoint<T>>>
|
||||||
|
): TState;
|
||||||
|
begin
|
||||||
|
var done := TLatch.CreateLatch(Length(DataChunks));
|
||||||
|
|
||||||
|
for var i := 0 to High(DataChunks) do
|
||||||
|
if not Terminated.IsSet then
|
||||||
|
Processor.ProcessData(DataChunks[i]).Signal.Subscribe(done)
|
||||||
|
else
|
||||||
|
done.Notify;
|
||||||
|
|
||||||
|
Result := done.State;
|
||||||
|
end;
|
||||||
|
|
||||||
function TAuraDataServer<T>.ProcessFile(
|
function TAuraDataServer<T>.ProcessFile(
|
||||||
FileInfo: TAuraDataFile;
|
FileInfo: TAuraDataFile;
|
||||||
const DataFile: TFuture<TArray<TArray<TDataPoint<T>>>>;
|
const DataFile: TFuture<TArray<TArray<TDataPoint<T>>>>;
|
||||||
@@ -374,19 +397,12 @@ begin
|
|||||||
function(const DataChunks: TArray<TArray<TDataPoint<T>>>): TState
|
function(const DataChunks: TArray<TArray<TDataPoint<T>>>): TState
|
||||||
begin
|
begin
|
||||||
// Process each chunk, checking for termination between chunks.
|
// Process each chunk, checking for termination between chunks.
|
||||||
var done := TLatch.CreateLatch(Length(DataChunks));
|
var done := ProcessChunks(DataChunks, Terminated, Processor);
|
||||||
for var i := 0 to High(DataChunks) do
|
|
||||||
if not cTerminated.IsSet then
|
|
||||||
Processor.ProcessData(DataChunks[i]).Signal.Subscribe(done)
|
|
||||||
else
|
|
||||||
done.Notify;
|
|
||||||
|
|
||||||
// Move to next file (which is currently preloading) once all Processors are done
|
// Move to next file (which is currently preloading) once all Processors are done
|
||||||
Result :=
|
Result :=
|
||||||
TaskManager.RunTask(
|
TaskManager
|
||||||
done.State,
|
.RunTask(done, function: TState begin Result := ProcessFile(nextFileInfo, nextFile, cTerminated, Processor); end);
|
||||||
function: TState begin Result := ProcessFile(nextFileInfo, nextFile, cTerminated, Processor); end
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
TConstFunc<S, T> = reference to function(const Value: S): T;
|
TConstFunc<S, T> = reference to function(const Value: S): T;
|
||||||
|
TConstProc<T> = reference to procedure(const Value: T);
|
||||||
TConstFuncPredicate<S, T> = reference to function(const Value: S; out Res: T): Boolean;
|
TConstFuncPredicate<S, T> = reference to function(const Value: S; out Res: T): Boolean;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|||||||
Reference in New Issue
Block a user