Process.Update for synchronization
This commit is contained in:
+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.
|
||||
|
||||
Reference in New Issue
Block a user