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