TLazy + Data-Endpoints refactoring
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)'==''">Release</Config>
|
<Config Condition="'$(Config)'==''">Debug</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>
|
||||||
|
|||||||
+17
-8
@@ -483,13 +483,18 @@ begin
|
|||||||
var Highest: Double := Double.MinValue;
|
var Highest: Double := Double.MinValue;
|
||||||
|
|
||||||
var ATR := Ohlc[0].Chain<Double>(TIndicators.CreateATR(50)).MakeParallel;
|
var ATR := Ohlc[0].Chain<Double>(TIndicators.CreateATR(50)).MakeParallel;
|
||||||
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
|
// next stage
|
||||||
|
|
||||||
|
var ATREndPoint := TConverter.CreateEndpoint<Double>(ATR.Sender, 5);
|
||||||
|
var ATRSeries: TSeries<Double>;
|
||||||
|
|
||||||
|
var HullEndPoint := TConverter.CreateEndpoint<Double>(Hull.Sender, 5);
|
||||||
|
var HullSeries: TSeries<Double>;
|
||||||
|
|
||||||
|
var SmaEndPoint := TConverter.CreateEndpoint<Double>(Sma.Sender, 5);
|
||||||
|
var SmaSeries: TSeries<Double>;
|
||||||
|
|
||||||
var curr: TSignal;
|
var curr: TSignal;
|
||||||
curr.SL := Double.NaN;
|
curr.SL := Double.NaN;
|
||||||
curr.Entry := Double.NaN;
|
curr.Entry := Double.NaN;
|
||||||
@@ -508,7 +513,11 @@ begin
|
|||||||
Result.Sig := 0;
|
Result.Sig := 0;
|
||||||
var pnl: double := NaN;
|
var pnl: double := NaN;
|
||||||
|
|
||||||
if (HullSeries.Value[0] < SmaSeries.Value[0]) and (HullSeries.Value[1] >= SmaSeries.Value[1]) then
|
ATREndPoint.Update(ATRSeries);
|
||||||
|
HullEndPoint.Update(HullSeries);
|
||||||
|
SmaEndPoint.Update(SmaSeries);
|
||||||
|
|
||||||
|
if (HullSeries[0] < SmaSeries[0]) and (HullSeries[1] >= SmaSeries[1]) then
|
||||||
begin
|
begin
|
||||||
if curr.Sig > 0 then
|
if curr.Sig > 0 then
|
||||||
pnl := Ohlc.Close - curr.Entry;
|
pnl := Ohlc.Close - curr.Entry;
|
||||||
@@ -518,7 +527,7 @@ begin
|
|||||||
curr.Entry := Ohlc.Close;
|
curr.Entry := Ohlc.Close;
|
||||||
Result := curr;
|
Result := curr;
|
||||||
end
|
end
|
||||||
else if (HullSeries.Value[0] > SmaSeries.Value[0]) and (HullSeries.Value[1] <= SmaSeries.Value[1]) then
|
else if (HullSeries[0] > SmaSeries[0]) and (HullSeries[1] <= SmaSeries[1]) then
|
||||||
begin
|
begin
|
||||||
if curr.Sig < 0 then
|
if curr.Sig < 0 then
|
||||||
pnl := curr.Entry - Ohlc.Close;
|
pnl := curr.Entry - Ohlc.Close;
|
||||||
@@ -529,7 +538,7 @@ begin
|
|||||||
Result := curr;
|
Result := curr;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var atr := 15 * ATRSeries.Value[0];
|
var atr := 15 * ATRSeries[0];
|
||||||
if curr.Sig > 0 then
|
if curr.Sig > 0 then
|
||||||
begin
|
begin
|
||||||
if Ohlc.Close > curr.SL then
|
if Ohlc.Close > curr.SL then
|
||||||
@@ -600,8 +609,8 @@ begin
|
|||||||
panel.AddOhlcSeries(Ohlc[0].Sender);
|
panel.AddOhlcSeries(Ohlc[0].Sender);
|
||||||
panel.AddDoubleSeries(Hull.Sender, TAlphaColors.Cornflowerblue, 2);
|
panel.AddDoubleSeries(Hull.Sender, TAlphaColors.Cornflowerblue, 2);
|
||||||
panel.AddDoubleSeries(Sma.Sender, TAlphaColors.Brown, 1.5);
|
panel.AddDoubleSeries(Sma.Sender, TAlphaColors.Brown, 1.5);
|
||||||
panel.AddDoubleSeries(Signal.Field<Double>('SL').Sender, TAlphaColors.Red, 2);
|
|
||||||
panel.AddDoubleSeries(Signal.Field<Double>('Entry').Sender, TAlphaColors.Green, 1);
|
panel.AddDoubleSeries(Signal.Field<Double>('Entry').Sender, TAlphaColors.Green, 1);
|
||||||
|
panel.AddDoubleSeries(Signal.Field<Double>('SL').Sender, TAlphaColors.Red, 2);
|
||||||
|
|
||||||
var pnlChart := TMycChart.Create(Self);
|
var pnlChart := TMycChart.Create(Self);
|
||||||
AlignControl(pnlChart);
|
AlignControl(pnlChart);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ uses
|
|||||||
Myc.Mutable;
|
Myc.Mutable;
|
||||||
|
|
||||||
type
|
type
|
||||||
TMycNullMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
TMycConstMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
||||||
private
|
private
|
||||||
FValue: T;
|
FValue: T;
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
@@ -64,25 +64,34 @@ type
|
|||||||
procedure SetValue(const Value: T);
|
procedure SetValue(const Value: T);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TMycConstLazy<T> = class(TInterfacedObject, TLazy<T>.ILazy)
|
||||||
|
private
|
||||||
|
FValue: T;
|
||||||
|
function GetChanged: TState;
|
||||||
|
function Update(var Value: T): Boolean;
|
||||||
|
public
|
||||||
|
constructor Create(AValue: T);
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.Generics.Defaults;
|
System.Generics.Defaults;
|
||||||
|
|
||||||
constructor TMycNullMutable<T>.Create(AValue: T);
|
constructor TMycConstMutable<T>.Create(AValue: T);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FValue := AValue;
|
FValue := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TMycNullMutable<T> }
|
{ TMycConstMutable<T> }
|
||||||
|
|
||||||
function TMycNullMutable<T>.GetChanged: TSignal;
|
function TMycConstMutable<T>.GetChanged: TSignal;
|
||||||
begin
|
begin
|
||||||
Result := TSignal.Null;
|
Result := TSignal.Null;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycNullMutable<T>.GetValue: T;
|
function TMycConstMutable<T>.GetValue: T;
|
||||||
begin
|
begin
|
||||||
Result := FValue;
|
Result := FValue;
|
||||||
end;
|
end;
|
||||||
@@ -179,4 +188,22 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
constructor TMycConstLazy<T>.Create(AValue: T);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FValue := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TMycConstLazy<T> }
|
||||||
|
|
||||||
|
function TMycConstLazy<T>.GetChanged: TState;
|
||||||
|
begin
|
||||||
|
Result := TState.Null;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycConstLazy<T>.Update(var Value: T): Boolean;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -15,32 +15,17 @@ uses
|
|||||||
Myc.Fmx.Chart;
|
Myc.Fmx.Chart;
|
||||||
|
|
||||||
type
|
type
|
||||||
TChartSeriesProcessor<T> = class(TMycChart.TSeries)
|
|
||||||
strict private
|
|
||||||
FDataSeries: TSeries<T>;
|
|
||||||
private
|
|
||||||
FData: TSeries<T>;
|
|
||||||
FDataProvider: TDataProvider<T>;
|
|
||||||
FReceiver: TMutable<TSeries<T>>;
|
|
||||||
protected
|
|
||||||
function GetCount: Int64; override;
|
|
||||||
function GetTotalCount: Int64; override;
|
|
||||||
procedure Update;
|
|
||||||
|
|
||||||
public
|
|
||||||
constructor Create(const ADataProvider: TDataProvider<T>; const ALookback: TMutable<Int64>);
|
|
||||||
destructor Destroy; override;
|
|
||||||
property Data: TSeries<T> read FData;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TChartCustomLayer }
|
{ TChartCustomLayer }
|
||||||
TChartCustomLayer<T> = class abstract(TMycChart.TDataLayer)
|
TChartCustomLayer<T> = class abstract(TMycChart.TDataLayer)
|
||||||
private
|
private
|
||||||
FSeries: TChartSeriesProcessor<T>;
|
FDataProvider: TDataProvider<T>;
|
||||||
|
FReceiver: TLazy<TSeries<T>>;
|
||||||
|
FData: TSeries<T>;
|
||||||
protected
|
protected
|
||||||
function GetSeries: TMycChart.TSeries; override;
|
function GetCount: Int64; override;
|
||||||
|
function GetTotalCount: Int64; override;
|
||||||
function GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; override; abstract;
|
function GetValueRange(First, Last: Int64; out MinValue, MaxValue: Double): Boolean; override; abstract;
|
||||||
procedure Update; override;
|
function Update: Boolean; override;
|
||||||
procedure Paint(
|
procedure Paint(
|
||||||
const Canvas: TCanvas;
|
const Canvas: TCanvas;
|
||||||
First, Last: Int64;
|
First, Last: Int64;
|
||||||
@@ -49,7 +34,7 @@ type
|
|||||||
); override; abstract;
|
); override; abstract;
|
||||||
public
|
public
|
||||||
constructor Create(AParent: TMycChart.TPanel; const ADataProvider: TDataProvider<T>);
|
constructor Create(AParent: TMycChart.TPanel; const ADataProvider: TDataProvider<T>);
|
||||||
destructor Destroy; override;
|
property Data: TSeries<T> read FData;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TChartOhlcLayer }
|
{ TChartOhlcLayer }
|
||||||
@@ -98,14 +83,17 @@ type
|
|||||||
{ TChartXAxisLayer }
|
{ TChartXAxisLayer }
|
||||||
TChartXAxisLayer<T> = class(TMycChart.TXAxisLayer)
|
TChartXAxisLayer<T> = class(TMycChart.TXAxisLayer)
|
||||||
private
|
private
|
||||||
FSeries: TChartSeriesProcessor<T>;
|
FDataProvider: TDataProvider<T>;
|
||||||
|
FReceiver: TLazy<TSeries<T>>;
|
||||||
|
FData: TSeries<T>;
|
||||||
protected
|
protected
|
||||||
function GetSeries: TMycChart.TSeries; override;
|
function Update: Boolean; override;
|
||||||
procedure Update; override;
|
|
||||||
function GetCaption(Idx: Int64): String; override;
|
function GetCaption(Idx: Int64): String; override;
|
||||||
|
function GetCount: Int64; override;
|
||||||
|
function GetTotalCount: Int64; override;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TMycChart; const ADataProvider: TDataProvider<T>);
|
constructor Create(AOwner: TMycChart; const ADataProvider: TDataProvider<T>);
|
||||||
destructor Destroy; override;
|
property Data: TSeries<T> read FData;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TChartXAxisTimestampLayer }
|
{ TChartXAxisTimestampLayer }
|
||||||
@@ -126,37 +114,6 @@ uses
|
|||||||
System.Math,
|
System.Math,
|
||||||
FMX.Types;
|
FMX.Types;
|
||||||
|
|
||||||
{ TChartSeriesProcessor<T> }
|
|
||||||
|
|
||||||
constructor TChartSeriesProcessor<T>.Create(const ADataProvider: TDataProvider<T>; const ALookback: TMutable<Int64>);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
FDataProvider := ADataProvider;
|
|
||||||
FData := FDataSeries;
|
|
||||||
|
|
||||||
FReceiver := TConverter.CreateEndpoint<T>(FDataProvider, ALookback.Value);
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TChartSeriesProcessor<T>.Destroy;
|
|
||||||
begin
|
|
||||||
inherited;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TChartSeriesProcessor<T>.GetCount: Int64;
|
|
||||||
begin
|
|
||||||
Result := FData.Count;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TChartSeriesProcessor<T>.GetTotalCount: Int64;
|
|
||||||
begin
|
|
||||||
Result := FData.TotalCount;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TChartSeriesProcessor<T>.Update;
|
|
||||||
begin
|
|
||||||
FData := FReceiver.Value;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TChartLineLayer }
|
{ TChartLineLayer }
|
||||||
|
|
||||||
constructor TChartLineLayer.Create(
|
constructor TChartLineLayer.Create(
|
||||||
@@ -179,14 +136,16 @@ begin
|
|||||||
MaxValue := -MaxDouble;
|
MaxValue := -MaxDouble;
|
||||||
for i := First to Last do
|
for i := First to Last do
|
||||||
begin
|
begin
|
||||||
var v := FSeries.Data.Items[i];
|
var v := Data.Items[i];
|
||||||
if not IsNaN(v) then
|
if not IsNaN(v) then
|
||||||
begin
|
begin
|
||||||
MinValue := Min(MinValue, v);
|
if v < MinValue then
|
||||||
MaxValue := Max(MaxValue, v);
|
MinValue := v;
|
||||||
|
if v > MaxValue then
|
||||||
|
MaxValue := v;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
Result := (MinValue < MaxValue);
|
Result := (MinValue <= MaxValue);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TChartLineLayer.Paint(
|
procedure TChartLineLayer.Paint(
|
||||||
@@ -205,18 +164,18 @@ begin
|
|||||||
while n < Last do
|
while n < Last do
|
||||||
begin
|
begin
|
||||||
// Skip warmup/gap data
|
// Skip warmup/gap data
|
||||||
while (n <= Last) and (IsNaN(FSeries.Data[n])) do
|
while (n <= Last) and (IsNaN(Data[n])) do
|
||||||
inc(n);
|
inc(n);
|
||||||
|
|
||||||
if n < Last then
|
if n < Last then
|
||||||
begin
|
begin
|
||||||
points.MoveTo(TPointF.Create(XForm(n), YForm(FSeries.Data[n])));
|
points.MoveTo(TPointF.Create(XForm(n), YForm(Data[n])));
|
||||||
inc(n);
|
inc(n);
|
||||||
while n <= Last do
|
while n <= Last do
|
||||||
begin
|
begin
|
||||||
if IsNaN(FSeries.Data[n]) then
|
if IsNaN(Data[n]) then
|
||||||
break;
|
break;
|
||||||
points.LineTo(TPointF.Create(XForm(n), YForm(FSeries.Data[n])));
|
points.LineTo(TPointF.Create(XForm(n), YForm(Data[n])));
|
||||||
inc(n);
|
inc(n);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -256,11 +215,13 @@ begin
|
|||||||
|
|
||||||
for i := First to Last do
|
for i := First to Last do
|
||||||
begin
|
begin
|
||||||
var item := FSeries.Data.Items[i];
|
var item := Data.Items[i];
|
||||||
MinValue := Min(MinValue, item.Low);
|
if item.Low < MinValue then
|
||||||
MaxValue := Max(MaxValue, item.High);
|
MinValue := item.Low;
|
||||||
|
if item.High > MaxValue then
|
||||||
|
MaxValue := item.High;
|
||||||
end;
|
end;
|
||||||
Result := (MinValue <> MaxDouble);
|
Result := MinValue <= MaxDouble;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TChartOhlcLayer.Paint(
|
procedure TChartOhlcLayer.Paint(
|
||||||
@@ -280,7 +241,7 @@ begin
|
|||||||
|
|
||||||
for i := First to Last do
|
for i := First to Last do
|
||||||
begin
|
begin
|
||||||
item := FSeries.Data.Items[i];
|
item := Data.Items[i];
|
||||||
x := XForm(i);
|
x := XForm(i);
|
||||||
yOpen := YForm(item.Open);
|
yOpen := YForm(item.Open);
|
||||||
yClose := YForm(item.Close);
|
yClose := YForm(item.Close);
|
||||||
@@ -309,23 +270,24 @@ end;
|
|||||||
constructor TChartCustomLayer<T>.Create(AParent: TMycChart.TPanel; const ADataProvider: TDataProvider<T>);
|
constructor TChartCustomLayer<T>.Create(AParent: TMycChart.TPanel; const ADataProvider: TDataProvider<T>);
|
||||||
begin
|
begin
|
||||||
inherited Create(AParent);
|
inherited Create(AParent);
|
||||||
FSeries := TChartSeriesProcessor<T>.Create(ADataProvider, AParent.Owner.Lookback.AsMutable);
|
FDataProvider := ADataProvider;
|
||||||
|
|
||||||
|
FReceiver := TConverter.CreateEndpoint<T>(FDataProvider, Owner.Lookback.Value);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TChartCustomLayer<T>.Destroy;
|
function TChartCustomLayer<T>.GetCount: Int64;
|
||||||
begin
|
begin
|
||||||
FSeries.Free;
|
Result := FData.Count;
|
||||||
inherited;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TChartCustomLayer<T>.GetSeries: TMycChart.TSeries;
|
function TChartCustomLayer<T>.GetTotalCount: Int64;
|
||||||
begin
|
begin
|
||||||
Result := FSeries;
|
Result := FData.TotalCount;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TChartCustomLayer<T>.Update;
|
function TChartCustomLayer<T>.Update: Boolean;
|
||||||
begin
|
begin
|
||||||
FSeries.Update;
|
Result := FReceiver.Update(FData);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TChartXAxisLayer }
|
{ TChartXAxisLayer }
|
||||||
@@ -333,30 +295,32 @@ end;
|
|||||||
constructor TChartXAxisLayer<T>.Create(AOwner: TMycChart; const ADataProvider: TDataProvider<T>);
|
constructor TChartXAxisLayer<T>.Create(AOwner: TMycChart; const ADataProvider: TDataProvider<T>);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
FSeries := TChartSeriesProcessor<T>.Create(ADataProvider, AOwner.Lookback.AsMutable);
|
FDataProvider := ADataProvider;
|
||||||
end;
|
FReceiver := TConverter.CreateEndpoint<T>(FDataProvider, Owner.Lookback.Value);
|
||||||
|
|
||||||
destructor TChartXAxisLayer<T>.Destroy;
|
|
||||||
begin
|
|
||||||
FSeries.Free;
|
|
||||||
inherited;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TChartXAxisLayer<T>.GetCaption(Idx: Int64): String;
|
function TChartXAxisLayer<T>.GetCaption(Idx: Int64): String;
|
||||||
begin
|
begin
|
||||||
Result := IntToStr(Idx);
|
Result := IntToStr(Owner.ViewStartIndex + Idx);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TChartXAxisLayer<T>.GetSeries: TMycChart.TSeries;
|
function TChartXAxisLayer<T>.GetCount: Int64;
|
||||||
begin
|
begin
|
||||||
Result := FSeries;
|
Result := FData.Count;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TChartXAxisLayer<T>.Update;
|
function TChartXAxisLayer<T>.GetTotalCount: Int64;
|
||||||
begin
|
begin
|
||||||
FSeries.Update;
|
Result := FData.TotalCount;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TChartXAxisLayer<T>.Update: Boolean;
|
||||||
|
begin
|
||||||
|
Result := FReceiver.Update(FData);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TChartXAxisTimestampLayer }
|
||||||
|
|
||||||
constructor TChartXAxisTimestampLayer.Create(AOwner: TMycChart; ATimeframe: TTimeframe; const ADataProvider: TDataProvider<TDateTime>);
|
constructor TChartXAxisTimestampLayer.Create(AOwner: TMycChart; ATimeframe: TTimeframe; const ADataProvider: TDataProvider<TDateTime>);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner, ADataProvider);
|
inherited Create(AOwner, ADataProvider);
|
||||||
@@ -369,9 +333,9 @@ var
|
|||||||
formatStr: string;
|
formatStr: string;
|
||||||
begin
|
begin
|
||||||
Result := '';
|
Result := '';
|
||||||
if (Idx >= 0) and (Idx < FSeries.Count) then
|
if (Idx >= 0) and (Idx < Data.Count) then
|
||||||
begin
|
begin
|
||||||
dt := FSeries.Data[Idx];
|
dt := Data[Idx];
|
||||||
|
|
||||||
// Choose format based on the series timeframe.
|
// Choose format based on the series timeframe.
|
||||||
if FTimeframe >= D then // Daily, Weekly, Monthly, Yearly
|
if FTimeframe >= D then // Daily, Weekly, Monthly, Yearly
|
||||||
|
|||||||
+59
-70
@@ -26,26 +26,18 @@ type
|
|||||||
type
|
type
|
||||||
TPanel = class;
|
TPanel = class;
|
||||||
|
|
||||||
// Abstract base for a data series within the chart.
|
|
||||||
TSeries = class abstract(TObject)
|
|
||||||
protected
|
|
||||||
function GetCount: Int64; virtual; abstract;
|
|
||||||
function GetTotalCount: Int64; virtual; abstract;
|
|
||||||
public
|
|
||||||
property Count: Int64 read GetCount;
|
|
||||||
property TotalCount: Int64 read GetTotalCount;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Abstract base for a drawable layer in a panel.
|
// Abstract base for a drawable layer in a panel.
|
||||||
TLayer = class abstract(TObject)
|
TLayer = class abstract(TObject)
|
||||||
protected
|
protected
|
||||||
|
function GetCount: Int64; virtual; abstract;
|
||||||
|
function GetTotalCount: Int64; virtual; abstract;
|
||||||
function GetOwner: TMycChart; virtual; abstract;
|
function GetOwner: TMycChart; virtual; abstract;
|
||||||
function GetSeries: TSeries; virtual; abstract;
|
function Update: Boolean; virtual; abstract;
|
||||||
procedure Update; virtual; abstract;
|
|
||||||
public
|
public
|
||||||
procedure Repaint;
|
procedure Repaint;
|
||||||
|
property Count: Int64 read GetCount;
|
||||||
|
property TotalCount: Int64 read GetTotalCount;
|
||||||
property Owner: TMycChart read GetOwner;
|
property Owner: TMycChart read GetOwner;
|
||||||
property Series: TSeries read GetSeries;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TDataLayer = class abstract(TLayer)
|
TDataLayer = class abstract(TLayer)
|
||||||
@@ -93,12 +85,12 @@ type
|
|||||||
TPanel = class(TObject)
|
TPanel = class(TObject)
|
||||||
private
|
private
|
||||||
FOwner: TMycChart;
|
FOwner: TMycChart;
|
||||||
FSeriesList: TObjectList<TMycChart.TDataLayer>;
|
FLayers: TObjectList<TMycChart.TDataLayer>;
|
||||||
FWeight: Single;
|
FWeight: Single;
|
||||||
procedure SetWeight(const Value: Single);
|
procedure SetWeight(const Value: Single);
|
||||||
protected
|
protected
|
||||||
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; MasterTotalCount: Int64; ViewStartIndex, ViewCount: Int64);
|
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; MasterTotalCount: Int64; ViewStartIndex, ViewCount: Int64);
|
||||||
procedure Update(MasterTotalCount: Int64; ViewStartIndex: Int64; out SeriesChanged, SeriesRepaint: Boolean);
|
function Update: Boolean;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TMycChart; AWeight: Single);
|
constructor Create(AOwner: TMycChart; AWeight: Single);
|
||||||
@@ -144,6 +136,7 @@ type
|
|||||||
protected
|
protected
|
||||||
procedure Paint; override;
|
procedure Paint; override;
|
||||||
procedure DoIdle;
|
procedure DoIdle;
|
||||||
|
function Update: Boolean;
|
||||||
procedure DoMouseLeave; override;
|
procedure DoMouseLeave; override;
|
||||||
procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override;
|
procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override;
|
||||||
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
|
||||||
@@ -168,6 +161,7 @@ type
|
|||||||
property JumpButtonRect: TRectF read FJumpButtonRect;
|
property JumpButtonRect: TRectF read FJumpButtonRect;
|
||||||
property ViewCount: Int64 read FViewCount;
|
property ViewCount: Int64 read FViewCount;
|
||||||
property ViewStartIndex: Int64 read FViewStartIndex;
|
property ViewStartIndex: Int64 read FViewStartIndex;
|
||||||
|
property XAxisSeries: TMycChart.TXAxisLayer read FXAxisSeries;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -230,7 +224,7 @@ begin
|
|||||||
Result.Point.X := X;
|
Result.Point.X := X;
|
||||||
Result.Point.Y := Y;
|
Result.Point.Y := Y;
|
||||||
Result.Idx := Round((rect.Right - X) * (FViewCount - 1) / rect.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.Count) then
|
||||||
Result.Idx := -1;
|
Result.Idx := -1;
|
||||||
Result.BarX := NaN;
|
Result.BarX := NaN;
|
||||||
if Result.Idx >= 0 then
|
if Result.Idx >= 0 then
|
||||||
@@ -248,34 +242,34 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycChart.DoIdle;
|
procedure TMycChart.DoIdle;
|
||||||
var
|
begin
|
||||||
prevTotalCount, newTotalCount: Int64;
|
if Update then
|
||||||
seriesChanged, seriesRepaint, repaintSignal: Boolean;
|
Repaint;
|
||||||
countDelta: Int64;
|
end;
|
||||||
|
|
||||||
|
function TMycChart.Update: Boolean;
|
||||||
begin
|
begin
|
||||||
if not Assigned(FXAxisSeries) then
|
if not Assigned(FXAxisSeries) then
|
||||||
exit;
|
exit(false);
|
||||||
|
|
||||||
prevTotalCount := FXAxisSeries.Series.TotalCount;
|
var prevTotalCount := FXAxisSeries.TotalCount;
|
||||||
FXAxisSeries.Update;
|
var seriesChanged := false;
|
||||||
newTotalCount := FXAxisSeries.Series.TotalCount;
|
|
||||||
|
|
||||||
seriesChanged := prevTotalCount <> newTotalCount;
|
if FXAxisSeries.Update then
|
||||||
seriesRepaint := (FViewStartIndex = 0);
|
seriesChanged := (FViewStartIndex = 0);
|
||||||
|
|
||||||
for var panel in FPanelList do
|
for var panel in FPanelList do
|
||||||
begin
|
begin
|
||||||
var panelChanged, panelRepaint: Boolean;
|
if panel.Update then
|
||||||
panel.Update(prevTotalCount, FViewStartIndex, panelChanged, panelRepaint);
|
seriesChanged := true;
|
||||||
seriesChanged := seriesChanged or panelChanged;
|
|
||||||
seriesRepaint := seriesRepaint or panelRepaint;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// When we are not displaying live data, adjust start index accordingly
|
// When we are not displaying live data, adjust start index accordingly
|
||||||
if (FViewStartIndex > 0) then
|
if FViewStartIndex > 0 then
|
||||||
begin
|
begin
|
||||||
countDelta := newTotalCount - prevTotalCount;
|
var newTotalCount := FXAxisSeries.TotalCount;
|
||||||
if (countDelta > 0) then
|
var countDelta := newTotalCount - prevTotalCount;
|
||||||
|
if countDelta > 0 then
|
||||||
begin
|
begin
|
||||||
FViewStartIndex := FViewStartIndex + countDelta;
|
FViewStartIndex := FViewStartIndex + countDelta;
|
||||||
if (FViewStartIndex + FViewCount > newTotalCount) then
|
if (FViewStartIndex + FViewCount > newTotalCount) then
|
||||||
@@ -283,10 +277,9 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
repaintSignal := FNeedRepaint.Reset;
|
var repaintSignal := FNeedRepaint.Reset;
|
||||||
|
|
||||||
if repaintSignal or (seriesRepaint and seriesChanged) then
|
Result := repaintSignal or seriesChanged;
|
||||||
Repaint;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycChart.DoMouseLeave;
|
procedure TMycChart.DoMouseLeave;
|
||||||
@@ -312,14 +305,14 @@ begin
|
|||||||
inherited;
|
inherited;
|
||||||
rect := Self.LocalRect;
|
rect := Self.LocalRect;
|
||||||
|
|
||||||
if (not Assigned(FXAxisSeries)) or (FXAxisSeries.Series.Count <= 1) or (FViewCount <= 1) or (PanelCount = 0) then
|
if (not Assigned(FXAxisSeries)) or (FXAxisSeries.Count <= 1) or (FViewCount <= 1) or (PanelCount = 0) then
|
||||||
begin
|
begin
|
||||||
Canvas.Fill.Color := TAlphaColors.Gray;
|
Canvas.Fill.Color := TAlphaColors.Gray;
|
||||||
Canvas.FillText(rect, 'No Data', false, 1, [], TTextAlign.Center, TTextAlign.Center);
|
Canvas.FillText(rect, 'No Data', false, 1, [], TTextAlign.Center, TTextAlign.Center);
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
masterTotalCount := FXAxisSeries.Series.TotalCount;
|
masterTotalCount := FXAxisSeries.TotalCount;
|
||||||
|
|
||||||
// Calculate total weight for proportional panel height
|
// Calculate total weight for proportional panel height
|
||||||
totalWeight := 0;
|
totalWeight := 0;
|
||||||
@@ -332,6 +325,7 @@ begin
|
|||||||
for var i := 0 to PanelCount - 1 do
|
for var i := 0 to PanelCount - 1 do
|
||||||
begin
|
begin
|
||||||
var panel := Panels[i];
|
var panel := Panels[i];
|
||||||
|
|
||||||
panelHeight := panel.Weight * rect.Height / totalWeight;
|
panelHeight := panel.Weight * rect.Height / totalWeight;
|
||||||
panelRect := TRectF.Create(rect.Left, currentY, rect.Right, currentY + panelHeight);
|
panelRect := TRectF.Create(rect.Left, currentY, rect.Right, currentY + panelHeight);
|
||||||
|
|
||||||
@@ -542,7 +536,7 @@ begin
|
|||||||
FViewStartIndex := FViewStartIndex - indexDelta;
|
FViewStartIndex := FViewStartIndex - indexDelta;
|
||||||
|
|
||||||
// Clamp values
|
// Clamp values
|
||||||
maxIndex := FXAxisSeries.Series.Count - FViewCount;
|
maxIndex := FXAxisSeries.Count - FViewCount;
|
||||||
if (maxIndex < 0) then
|
if (maxIndex < 0) then
|
||||||
maxIndex := 0;
|
maxIndex := 0;
|
||||||
if (FViewStartIndex < 0) then
|
if (FViewStartIndex < 0) then
|
||||||
@@ -602,7 +596,7 @@ begin
|
|||||||
if not Assigned(FXAxisSeries) then
|
if not Assigned(FXAxisSeries) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
var xAxisCount := FXAxisSeries.Series.Count;
|
var xAxisCount := FXAxisSeries.Count;
|
||||||
if (xAxisCount = 0) or (Width <= 0) then
|
if (xAxisCount = 0) or (Width <= 0) then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
@@ -667,13 +661,13 @@ constructor TMycChart.TPanel.Create(AOwner: TMycChart; AWeight: Single);
|
|||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FOwner := AOwner;
|
FOwner := AOwner;
|
||||||
FSeriesList := TObjectList<TMycChart.TDataLayer>.Create(true);
|
FLayers := TObjectList<TMycChart.TDataLayer>.Create(true);
|
||||||
FWeight := AWeight;
|
FWeight := AWeight;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TMycChart.TPanel.Destroy;
|
destructor TMycChart.TPanel.Destroy;
|
||||||
begin
|
begin
|
||||||
FSeriesList.Free;
|
FLayers.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -684,7 +678,7 @@ function TMycChart.TPanel.AddDoubleSeries(
|
|||||||
): TMycChart.TDataLayer;
|
): TMycChart.TDataLayer;
|
||||||
begin
|
begin
|
||||||
Result := TChartLineLayer.Create(Self, DataProvider, ALineColor, ALineWidth);
|
Result := TChartLineLayer.Create(Self, DataProvider, ALineColor, ALineWidth);
|
||||||
FSeriesList.Add(Result);
|
FLayers.Add(Result);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycChart.TPanel.AddOhlcSeries(
|
function TMycChart.TPanel.AddOhlcSeries(
|
||||||
@@ -695,7 +689,7 @@ function TMycChart.TPanel.AddOhlcSeries(
|
|||||||
begin
|
begin
|
||||||
Result :=
|
Result :=
|
||||||
TChartOhlcLayer.Create(Self, DataProvider, TMutable<TAlphaColor>.Constant(AUpColor), TMutable<TAlphaColor>.Constant(ADownColor));
|
TChartOhlcLayer.Create(Self, DataProvider, TMutable<TAlphaColor>.Constant(AUpColor), TMutable<TAlphaColor>.Constant(ADownColor));
|
||||||
FSeriesList.Add(Result);
|
FLayers.Add(Result);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycChart.TPanel.Paint(const Canvas: TCanvas; const Viewport: TRectF; MasterTotalCount, ViewStartIndex, ViewCount: Int64);
|
procedure TMycChart.TPanel.Paint(const Canvas: TCanvas; const Viewport: TRectF; MasterTotalCount, ViewStartIndex, ViewCount: Int64);
|
||||||
@@ -706,16 +700,16 @@ var
|
|||||||
xTransform: TFunc<Int64, Single>;
|
xTransform: TFunc<Int64, Single>;
|
||||||
yTransform: TFunc<Double, Single>;
|
yTransform: TFunc<Double, Single>;
|
||||||
|
|
||||||
function CalcView(Series: TMycChart.TSeries; out First, Last: Int64): Boolean;
|
function CalcView(Layer: TMycChart.TDataLayer; out First, Last: Int64): Boolean;
|
||||||
var
|
var
|
||||||
seriesTotalCount, offset, startIdx: Int64;
|
seriesTotalCount, offset, startIdx: Int64;
|
||||||
begin
|
begin
|
||||||
seriesTotalCount := series.TotalCount;
|
seriesTotalCount := Layer.TotalCount;
|
||||||
offset := MasterTotalCount - seriesTotalCount;
|
offset := MasterTotalCount - seriesTotalCount;
|
||||||
startIdx := ViewStartIndex - offset;
|
startIdx := ViewStartIndex - offset;
|
||||||
|
|
||||||
First := Max(0, startIdx);
|
First := Max(0, startIdx);
|
||||||
Last := Min(series.Count, startIdx + ViewCount) - 1;
|
Last := Min(Layer.Count, startIdx + ViewCount) - 1;
|
||||||
Result := First <= Last;
|
Result := First <= Last;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -724,12 +718,16 @@ begin
|
|||||||
localMin := 0;
|
localMin := 0;
|
||||||
localMax := 0;
|
localMax := 0;
|
||||||
|
|
||||||
|
// Update data state
|
||||||
|
for var layer in FLayers do
|
||||||
|
layer.Update;
|
||||||
|
|
||||||
// 1. Calculate value range (Y-Axis) for this panel only
|
// 1. Calculate value range (Y-Axis) for this panel only
|
||||||
for var series in FSeriesList do
|
for var layer in FLayers do
|
||||||
begin
|
begin
|
||||||
if CalcView(series.Series, seriesFirst, seriesLast) then
|
if CalcView(layer, seriesFirst, seriesLast) then
|
||||||
begin
|
begin
|
||||||
if series.GetValueRange(seriesFirst, seriesLast, seriesMin, seriesMax) then
|
if layer.GetValueRange(seriesFirst, seriesLast, seriesMin, seriesMax) then
|
||||||
begin
|
begin
|
||||||
if not rangeInitialized then
|
if not rangeInitialized then
|
||||||
begin
|
begin
|
||||||
@@ -760,18 +758,18 @@ begin
|
|||||||
yTransform := function(value: Double): Single begin Result := top + (1 - (value - localMin) / range) * height; end;
|
yTransform := function(value: Double): Single begin Result := top + (1 - (value - localMin) / range) * height; end;
|
||||||
|
|
||||||
// 3. Paint all layers in this panel
|
// 3. Paint all layers in this panel
|
||||||
for var series in FSeriesList do
|
for var layer in FLayers do
|
||||||
begin
|
begin
|
||||||
if CalcView(series.Series, seriesFirst, seriesLast) then
|
if CalcView(layer, seriesFirst, seriesLast) then
|
||||||
begin
|
begin
|
||||||
var offset := MasterTotalCount - series.Series.TotalCount;
|
var offset := MasterTotalCount - layer.TotalCount;
|
||||||
xTransform :=
|
xTransform :=
|
||||||
function(index: Int64): Single
|
function(index: Int64): Single
|
||||||
begin
|
begin
|
||||||
Result := Viewport.Right - (((index + offset - ViewStartIndex) / (ViewCount - 1)) * Viewport.Width);
|
Result := Viewport.Right - (((index + offset - ViewStartIndex) / (ViewCount - 1)) * Viewport.Width);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
series.Paint(Canvas, seriesFirst, seriesLast, xTransform, yTransform);
|
layer.Paint(Canvas, seriesFirst, seriesLast, xTransform, yTransform);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -789,23 +787,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycChart.TPanel.Update(MasterTotalCount: Int64; ViewStartIndex: Int64; out SeriesChanged, SeriesRepaint: Boolean);
|
function TMycChart.TPanel.Update: Boolean;
|
||||||
var
|
|
||||||
seriesCount: Int64;
|
|
||||||
begin
|
begin
|
||||||
SeriesChanged := false;
|
Result := false;
|
||||||
SeriesRepaint := false;
|
for var layer in FLayers do
|
||||||
for var series in FSeriesList do
|
|
||||||
begin
|
begin
|
||||||
seriesCount := series.Series.TotalCount;
|
if layer.Update then
|
||||||
|
if (Owner.ViewStartIndex <= Owner.FXAxisSeries.TotalCount - layer.TotalCount) then
|
||||||
if (ViewStartIndex <= MasterTotalCount - seriesCount) then
|
Result := true;
|
||||||
SeriesRepaint := true;
|
|
||||||
|
|
||||||
series.Update;
|
|
||||||
|
|
||||||
if (seriesCount <> series.Series.TotalCount) then
|
|
||||||
SeriesChanged := true;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -846,7 +835,7 @@ 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) then
|
if GetCount = 0 then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
// 1. Snap X to the candle's center
|
// 1. Snap X to the candle's center
|
||||||
|
|||||||
+63
-19
@@ -73,15 +73,38 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
TLazy<T> = record
|
TLazy<T> = record
|
||||||
|
type
|
||||||
|
ILazy = interface
|
||||||
|
{$REGION 'property access'}
|
||||||
|
function GetChanged: TState;
|
||||||
|
{$ENDREGION}
|
||||||
|
function Update(var Value: T): Boolean;
|
||||||
|
property Changed: TState read GetChanged;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$REGION 'private'}
|
||||||
|
strict private
|
||||||
|
class var
|
||||||
|
FNull: ILazy;
|
||||||
|
class constructor CreateClass;
|
||||||
|
|
||||||
private
|
private
|
||||||
FMutable: TMutable<T>;
|
FLazy: ILazy;
|
||||||
FChanged: TFlag;
|
function GetChanged: TState; inline;
|
||||||
FChangeState: TSignal.TSubscription;
|
{$ENDREGION}
|
||||||
function GetValue: T; inline;
|
|
||||||
public
|
public
|
||||||
constructor Create(const AMutable: TMutable<T>);
|
constructor Create(const ALazy: ILazy);
|
||||||
function Update: Boolean;
|
class operator Initialize(out Dest: TLazy<T>);
|
||||||
property Value: T read GetValue;
|
class operator Implicit(const A: ILazy): TLazy<T>; overload;
|
||||||
|
class operator Implicit(const A: TLazy<T>): ILazy; overload;
|
||||||
|
|
||||||
|
class property Null: ILazy read FNull;
|
||||||
|
|
||||||
|
class function Constant(const Value: T): TLazy<T>; overload; static;
|
||||||
|
|
||||||
|
function Update(var Value: T): Boolean; inline;
|
||||||
|
|
||||||
|
property Changed: TState read GetChanged;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -99,12 +122,12 @@ end;
|
|||||||
|
|
||||||
class constructor TMutable<T>.CreateClass;
|
class constructor TMutable<T>.CreateClass;
|
||||||
begin
|
begin
|
||||||
FNull := TMycNullMutable<T>.Create(Default(T));
|
FNull := TMycConstMutable<T>.Create(Default(T));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TMutable<T>.Constant(const Value: T): TMutable<T>;
|
class function TMutable<T>.Constant(const Value: T): TMutable<T>;
|
||||||
begin
|
begin
|
||||||
Result := TMycNullMutable<T>.Create(Value);
|
Result := TMycConstMutable<T>.Create(Value);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TMutable<T>.Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>;
|
class function TMutable<T>.Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>;
|
||||||
@@ -195,23 +218,44 @@ end;
|
|||||||
|
|
||||||
{ TLazy<T> }
|
{ TLazy<T> }
|
||||||
|
|
||||||
constructor TLazy<T>.Create(const AMutable: TMutable<T>);
|
constructor TLazy<T>.Create(const ALazy: ILazy);
|
||||||
begin
|
begin
|
||||||
FMutable := AMutable;
|
FLazy := ALazy;
|
||||||
FChanged := TFlag.CreateFlag;
|
|
||||||
FChangeState := AMutable.Changed.Subscribe(FChanged);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TLazy<T>.GetValue: T;
|
class constructor TLazy<T>.CreateClass;
|
||||||
begin
|
begin
|
||||||
Result := FMutable.Value;
|
FNull := TMycConstLazy<T>.Create(Default(T));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TLazy<T>.Update: Boolean;
|
class function TLazy<T>.Constant(const Value: T): TLazy<T>;
|
||||||
begin
|
begin
|
||||||
Result := FChanged.State.IsSet;
|
Result := TMycConstLazy<T>.Create(Value);
|
||||||
if Result then
|
end;
|
||||||
FChanged.Reset;
|
|
||||||
|
function TLazy<T>.GetChanged: TState;
|
||||||
|
begin
|
||||||
|
Result := FLazy.Changed;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TLazy<T>.Update(var Value: T): Boolean;
|
||||||
|
begin
|
||||||
|
Result := FLazy.Update(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TLazy<T>.Implicit(const A: TLazy<T>): ILazy;
|
||||||
|
begin
|
||||||
|
Result := A.FLazy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TLazy<T>.Implicit(const A: ILazy): TLazy<T>;
|
||||||
|
begin
|
||||||
|
Result.FLazy := A;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TLazy<T>.Initialize(out Dest: TLazy<T>);
|
||||||
|
begin
|
||||||
|
Dest.FLazy := FNull;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Endpoint that collects data into a series.
|
// Endpoint that collects data into a series.
|
||||||
TMycDataEndpoint<T> = class(TInterfacedObject, TMutable<TSeries<T>>.IMutable)
|
TMycDataEndpoint<T> = class(TInterfacedObject, TLazy<TSeries<T>>.ILazy)
|
||||||
type
|
type
|
||||||
PItem = ^TItem;
|
PItem = ^TItem;
|
||||||
TItem = record
|
TItem = record
|
||||||
@@ -169,16 +169,16 @@ type
|
|||||||
FTag: TDataProvider<T>.TTag;
|
FTag: TDataProvider<T>.TTag;
|
||||||
FDataProvider: TDataProvider<T>;
|
FDataProvider: TDataProvider<T>;
|
||||||
FLookback: Int64;
|
FLookback: Int64;
|
||||||
FData: TSeries<T>;
|
FChanged: TFlag;
|
||||||
FChanged: TEvent;
|
|
||||||
FLock: TLightweightMREW;
|
FLock: TLightweightMREW;
|
||||||
FFirst: PItem;
|
FFirst: PItem;
|
||||||
function GetChanged: TSignal;
|
FCount: Integer;
|
||||||
function GetValue: TSeries<T>;
|
function GetChanged: TState;
|
||||||
function ProcessData(const Value: T): TState;
|
function ProcessData(const Value: T): TState;
|
||||||
public
|
public
|
||||||
constructor Create(const ADataProvider: TDataProvider<T>; ALookback: Int64);
|
constructor Create(const ADataProvider: TDataProvider<T>; ALookback: Int64);
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
function Update(var Value: TSeries<T>): Boolean;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TTickAggregation = class(TMycConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>)
|
TTickAggregation = class(TMycConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>)
|
||||||
@@ -475,55 +475,13 @@ begin
|
|||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycDataEndpoint<T>.GetChanged: TSignal;
|
function TMycDataEndpoint<T>.GetChanged: TState;
|
||||||
begin
|
begin
|
||||||
Result := FChanged.Signal;
|
Result := FChanged.State
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycDataEndpoint<T>.GetValue: TSeries<T>;
|
|
||||||
begin
|
|
||||||
FLock.BeginWrite;
|
|
||||||
try
|
|
||||||
var cnt := 0;
|
|
||||||
var tmp: PItem := nil;
|
|
||||||
var item: PItem;
|
|
||||||
|
|
||||||
while FFirst <> nil do
|
|
||||||
begin
|
|
||||||
item := FFirst;
|
|
||||||
FFirst := item.Next;
|
|
||||||
item.Next := tmp;
|
|
||||||
tmp := item;
|
|
||||||
inc(cnt);
|
|
||||||
end;
|
|
||||||
|
|
||||||
if cnt > 0 then
|
|
||||||
begin
|
|
||||||
var Arr: TArray<T>;
|
|
||||||
SetLength(Arr, cnt);
|
|
||||||
|
|
||||||
cnt := 0;
|
|
||||||
while tmp <> nil do
|
|
||||||
begin
|
|
||||||
item := tmp;
|
|
||||||
tmp := item.Next;
|
|
||||||
Arr[cnt] := item.Value;
|
|
||||||
inc(cnt);
|
|
||||||
Dispose(item);
|
|
||||||
end;
|
|
||||||
|
|
||||||
FData := FData.Add(Arr, 0, cnt, FLookback);
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := FData;
|
|
||||||
finally
|
|
||||||
FLock.EndWrite;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycDataEndpoint<T>.ProcessData(const Value: T): TState;
|
function TMycDataEndpoint<T>.ProcessData(const Value: T): TState;
|
||||||
begin
|
begin
|
||||||
Result := TState.Null;
|
|
||||||
FLock.BeginWrite;
|
FLock.BeginWrite;
|
||||||
try
|
try
|
||||||
var P: PItem;
|
var P: PItem;
|
||||||
@@ -531,13 +489,43 @@ begin
|
|||||||
P.Next := FFirst;
|
P.Next := FFirst;
|
||||||
FFirst := P;
|
FFirst := P;
|
||||||
P.Value := Value;
|
P.Value := Value;
|
||||||
|
inc(FCount);
|
||||||
// Add new data point, respecting the lookback period.
|
FChanged.Notify;
|
||||||
// FData := FData.Add(Value, FLookback);
|
finally
|
||||||
|
FLock.EndWrite;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycDataEndpoint<T>.Update(var Value: TSeries<T>): Boolean;
|
||||||
|
begin
|
||||||
|
FLock.BeginWrite;
|
||||||
|
try
|
||||||
|
Result := FChanged.Reset;
|
||||||
|
if Result then
|
||||||
|
begin
|
||||||
|
if FCount > 0 then
|
||||||
|
begin
|
||||||
|
var item: PItem;
|
||||||
|
var Arr: TArray<T>;
|
||||||
|
|
||||||
|
SetLength(Arr, FCount);
|
||||||
|
while FFirst <> nil do
|
||||||
|
begin
|
||||||
|
item := FFirst;
|
||||||
|
FFirst := item.Next;
|
||||||
|
dec(FCount);
|
||||||
|
Assert(FCount >= 0);
|
||||||
|
Arr[FCount] := item.Value;
|
||||||
|
Dispose(item);
|
||||||
|
end;
|
||||||
|
Assert(FCount = 0);
|
||||||
|
|
||||||
|
Value := Value.Add(Arr, 0, Length(Arr), FLookback);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
finally
|
finally
|
||||||
FLock.EndWrite;
|
FLock.EndWrite;
|
||||||
end;
|
end;
|
||||||
FChanged.Notify;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TTickAggregation }
|
{ TTickAggregation }
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ type
|
|||||||
|
|
||||||
// Factory for creating specific converter instances.
|
// Factory for creating specific converter instances.
|
||||||
TConverter = record
|
TConverter = record
|
||||||
class function CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TMutable<TSeries<T>>; static;
|
class function CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TLazy<TSeries<T>>; static;
|
||||||
|
|
||||||
class function CreateCounter<T>: TConverter<T, Int64>; static;
|
class function CreateCounter<T>: TConverter<T, Int64>; static;
|
||||||
class function CreateTicker<T>: TConverter<TArray<T>, T>; static;
|
class function CreateTicker<T>: TConverter<TArray<T>, T>; static;
|
||||||
@@ -277,7 +277,7 @@ begin
|
|||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TConverter.CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TMutable<TSeries<T>>;
|
class function TConverter.CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TLazy<TSeries<T>>;
|
||||||
begin
|
begin
|
||||||
Result := TMycDataEndpoint<T>.Create(DataProvider, Lookback);
|
Result := TMycDataEndpoint<T>.Create(DataProvider, Lookback);
|
||||||
end;
|
end;
|
||||||
|
|||||||
Reference in New Issue
Block a user