1st Aura Project Layout
This commit is contained in:
@@ -6,7 +6,9 @@ uses
|
|||||||
FMX.Forms,
|
FMX.Forms,
|
||||||
MainForm in 'MainForm.pas' {Form1},
|
MainForm in 'MainForm.pas' {Form1},
|
||||||
Myc.Trade.Core.DataPoint in '..\Src\Myc.Trade.Core.DataPoint.pas',
|
Myc.Trade.Core.DataPoint in '..\Src\Myc.Trade.Core.DataPoint.pas',
|
||||||
Myc.Trade.Ticker in '..\Src\Myc.Trade.Ticker.pas';
|
Myc.Trade.Ticker in '..\Src\Myc.Trade.Ticker.pas',
|
||||||
|
Myc.Aura.Module in '..\Src\Myc.Aura.Module.pas',
|
||||||
|
Myc.Aura.Parameter in '..\Src\Myc.Aura.Parameter.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,8 @@
|
|||||||
</DCCReference>
|
</DCCReference>
|
||||||
<DCCReference Include="..\Src\Myc.Trade.Core.DataPoint.pas"/>
|
<DCCReference Include="..\Src\Myc.Trade.Core.DataPoint.pas"/>
|
||||||
<DCCReference Include="..\Src\Myc.Trade.Ticker.pas"/>
|
<DCCReference Include="..\Src\Myc.Trade.Ticker.pas"/>
|
||||||
|
<DCCReference Include="..\Src\Myc.Aura.Module.pas"/>
|
||||||
|
<DCCReference Include="..\Src\Myc.Aura.Parameter.pas"/>
|
||||||
<BuildConfiguration Include="Base">
|
<BuildConfiguration Include="Base">
|
||||||
<Key>Base</Key>
|
<Key>Base</Key>
|
||||||
</BuildConfiguration>
|
</BuildConfiguration>
|
||||||
|
|||||||
+64
-41
@@ -11,85 +11,108 @@ object Form1: TForm1
|
|||||||
OnDestroy = FormDestroy
|
OnDestroy = FormDestroy
|
||||||
DesignerMasterStyle = 0
|
DesignerMasterStyle = 0
|
||||||
object Panel1: TPanel
|
object Panel1: TPanel
|
||||||
Align = Top
|
Align = Client
|
||||||
Size.Width = 796.00000000000000000
|
Size.Width = 796.00000000000000000
|
||||||
Size.Height = 529.00000000000000000
|
Size.Height = 616.00000000000000000
|
||||||
Size.PlatformDefault = False
|
Size.PlatformDefault = False
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
object RandomButton: TButton
|
object Layout: TFlowLayout
|
||||||
Position.X = 697.00000000000000000
|
Align = Client
|
||||||
Position.Y = 38.00000000000000000
|
Size.Width = 627.00000000000000000
|
||||||
TabOrder = 0
|
Size.Height = 616.00000000000000000
|
||||||
Text = 'Random'
|
|
||||||
TextSettings.Trimming = None
|
|
||||||
OnClick = RandomButtonClick
|
|
||||||
end
|
|
||||||
object Path1: TPath
|
|
||||||
Anchors = [akLeft, akTop, akRight]
|
|
||||||
Position.X = 8.00000000000000000
|
|
||||||
Position.Y = 8.00000000000000000
|
|
||||||
Size.Width = 261.00000000000000000
|
|
||||||
Size.Height = 513.00000000000000000
|
|
||||||
Size.PlatformDefault = False
|
Size.PlatformDefault = False
|
||||||
|
TabOrder = 1
|
||||||
|
Justify = Left
|
||||||
|
JustifyLastLine = Left
|
||||||
|
FlowDirection = LeftToRight
|
||||||
|
HorizontalGap = 2.00000000000000000
|
||||||
|
VerticalGap = 2.00000000000000000
|
||||||
|
OnResized = LayoutResized
|
||||||
end
|
end
|
||||||
object SymbolsComboBox: TComboBox
|
object SymbolsComboBox: TComboBox
|
||||||
Anchors = [akTop, akRight]
|
Anchors = [akTop, akRight]
|
||||||
Position.X = 493.00000000000000000
|
Position.X = 608.00000000000000000
|
||||||
Position.Y = 8.00000000000000000
|
Position.Y = 16.00000000000000000
|
||||||
Size.Width = 217.00000000000000000
|
Size.Width = 177.00000000000000000
|
||||||
Size.Height = 22.00000000000000000
|
Size.Height = 22.00000000000000000
|
||||||
Size.PlatformDefault = False
|
Size.PlatformDefault = False
|
||||||
|
TabOrder = 5
|
||||||
|
end
|
||||||
|
object RandomBox: TCheckBox
|
||||||
|
Anchors = [akTop, akRight]
|
||||||
|
Position.X = 609.00000000000000000
|
||||||
|
Position.Y = 46.00000000000000000
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
|
Text = 'Random'
|
||||||
end
|
end
|
||||||
object LoadButton: TButton
|
object LoadButton: TButton
|
||||||
Anchors = [akTop, akRight]
|
Anchors = [akTop, akRight]
|
||||||
Position.X = 718.00000000000000000
|
Position.X = 641.00000000000000000
|
||||||
Position.Y = 8.00000000000000000
|
Position.Y = 86.00000000000000000
|
||||||
Size.Width = 47.00000000000000000
|
Size.Width = 47.00000000000000000
|
||||||
Size.Height = 22.00000000000000000
|
Size.Height = 22.00000000000000000
|
||||||
Size.PlatformDefault = False
|
Size.PlatformDefault = False
|
||||||
TabOrder = 3
|
TabOrder = 6
|
||||||
Text = 'Load'
|
Text = 'Load'
|
||||||
TextSettings.Trimming = None
|
TextSettings.Trimming = None
|
||||||
OnClick = LoadButtonClick
|
OnClick = LoadButtonClick
|
||||||
end
|
end
|
||||||
object FlowLayout: TFlowLayout
|
object RandomButton: TButton
|
||||||
Position.X = 304.00000000000000000
|
Anchors = [akTop, akRight]
|
||||||
Position.Y = 104.00000000000000000
|
Position.X = 705.00000000000000000
|
||||||
Size.Width = 449.00000000000000000
|
Position.Y = 86.00000000000000000
|
||||||
Size.Height = 361.00000000000000000
|
TabOrder = 3
|
||||||
Size.PlatformDefault = False
|
Text = 'Random'
|
||||||
TabOrder = 5
|
TextSettings.Trimming = None
|
||||||
Justify = Left
|
OnClick = RandomButtonClick
|
||||||
JustifyLastLine = Left
|
|
||||||
FlowDirection = LeftToRight
|
|
||||||
end
|
end
|
||||||
object ChartButton: TButton
|
object ChartButton: TButton
|
||||||
Position.X = 712.00000000000000000
|
Anchors = [akTop, akRight]
|
||||||
Position.Y = 72.00000000000000000
|
Position.X = 665.00000000000000000
|
||||||
TabOrder = 6
|
Position.Y = 130.00000000000000000
|
||||||
|
TabOrder = 8
|
||||||
Text = 'ChartButton'
|
Text = 'ChartButton'
|
||||||
TextSettings.Trimming = None
|
TextSettings.Trimming = None
|
||||||
OnClick = ChartButtonClick
|
OnClick = ChartButtonClick
|
||||||
end
|
end
|
||||||
object StopButton: TButton
|
object StopButton: TButton
|
||||||
Position.X = 720.00000000000000000
|
Anchors = [akTop, akRight]
|
||||||
Position.Y = 96.00000000000000000
|
Position.X = 665.00000000000000000
|
||||||
TabOrder = 8
|
Position.Y = 160.00000000000000000
|
||||||
|
TabOrder = 10
|
||||||
Text = 'StopButton'
|
Text = 'StopButton'
|
||||||
TextSettings.Trimming = None
|
TextSettings.Trimming = None
|
||||||
OnClick = StopButtonClick
|
OnClick = StopButtonClick
|
||||||
end
|
end
|
||||||
|
object TreeView1: TTreeView
|
||||||
|
Align = Left
|
||||||
|
Size.Width = 161.00000000000000000
|
||||||
|
Size.Height = 616.00000000000000000
|
||||||
|
Size.PlatformDefault = False
|
||||||
|
TabOrder = 0
|
||||||
|
Viewport.Width = 157.00000000000000000
|
||||||
|
Viewport.Height = 612.00000000000000000
|
||||||
|
end
|
||||||
|
object Splitter1: TSplitter
|
||||||
|
Align = Left
|
||||||
|
Cursor = crHSplit
|
||||||
|
MinSize = 20.00000000000000000
|
||||||
|
Position.X = 161.00000000000000000
|
||||||
|
Size.Width = 8.00000000000000000
|
||||||
|
Size.Height = 616.00000000000000000
|
||||||
|
Size.PlatformDefault = False
|
||||||
|
end
|
||||||
end
|
end
|
||||||
object LogMemo: TMemo
|
object LogMemo: TMemo
|
||||||
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
|
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
|
||||||
DataDetectorTypes = []
|
DataDetectorTypes = []
|
||||||
Align = Client
|
Align = Bottom
|
||||||
|
Position.Y = 616.00000000000000000
|
||||||
Size.Width = 796.00000000000000000
|
Size.Width = 796.00000000000000000
|
||||||
Size.Height = 311.00000000000000000
|
Size.Height = 224.00000000000000000
|
||||||
Size.PlatformDefault = False
|
Size.PlatformDefault = False
|
||||||
TabOrder = 1
|
TabOrder = 1
|
||||||
Viewport.Width = 792.00000000000000000
|
Viewport.Width = 792.00000000000000000
|
||||||
Viewport.Height = 307.00000000000000000
|
Viewport.Height = 220.00000000000000000
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+59
-28
@@ -34,25 +34,30 @@ uses
|
|||||||
Myc.Signals.FMX,
|
Myc.Signals.FMX,
|
||||||
Myc.TaskManager,
|
Myc.TaskManager,
|
||||||
FMX.ListBox,
|
FMX.ListBox,
|
||||||
FMX.Layouts;
|
FMX.Layouts,
|
||||||
|
FMX.MultiView,
|
||||||
|
FMX.TreeView;
|
||||||
|
|
||||||
type
|
type
|
||||||
TForm1 = class(TForm)
|
TForm1 = class(TForm)
|
||||||
LogMemo: TMemo;
|
LogMemo: TMemo;
|
||||||
Panel1: TPanel;
|
Panel1: TPanel;
|
||||||
RandomButton: TButton;
|
Layout: TFlowLayout;
|
||||||
Path1: TPath;
|
|
||||||
SymbolsComboBox: TComboBox;
|
SymbolsComboBox: TComboBox;
|
||||||
|
RandomBox: TCheckBox;
|
||||||
LoadButton: TButton;
|
LoadButton: TButton;
|
||||||
FlowLayout: TFlowLayout;
|
RandomButton: TButton;
|
||||||
ChartButton: TButton;
|
ChartButton: TButton;
|
||||||
StopButton: TButton;
|
StopButton: TButton;
|
||||||
|
TreeView1: TTreeView;
|
||||||
|
Splitter1: TSplitter;
|
||||||
procedure RandomButtonClick(Sender: TObject);
|
procedure RandomButtonClick(Sender: TObject);
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
procedure FormDestroy(Sender: TObject);
|
procedure FormDestroy(Sender: TObject);
|
||||||
procedure LoadButtonClick(Sender: TObject);
|
procedure LoadButtonClick(Sender: TObject);
|
||||||
procedure ChartButtonClick(Sender: TObject);
|
procedure ChartButtonClick(Sender: TObject);
|
||||||
procedure StopButtonClick(Sender: TObject);
|
procedure StopButtonClick(Sender: TObject);
|
||||||
|
procedure LayoutResized(Sender: TObject);
|
||||||
private
|
private
|
||||||
const
|
const
|
||||||
cnt = 20;
|
cnt = 20;
|
||||||
@@ -70,6 +75,8 @@ type
|
|||||||
FRandom: TList<TRndItem>;
|
FRandom: TList<TRndItem>;
|
||||||
FTerminate: TEvent;
|
FTerminate: TEvent;
|
||||||
FLoadDone: TState;
|
FLoadDone: TState;
|
||||||
|
function SelectedSymbol: String;
|
||||||
|
function SelectedStream: IDataStream<TAskBidItem>;
|
||||||
public
|
public
|
||||||
{ Public declarations }
|
{ Public declarations }
|
||||||
published
|
published
|
||||||
@@ -102,7 +109,7 @@ begin
|
|||||||
|
|
||||||
FSymbols := FServer.EnumerateSymbols;
|
FSymbols := FServer.EnumerateSymbols;
|
||||||
|
|
||||||
SymbolsComboBox.AddIdleHandler(
|
SymbolsComboBox.ProcessSignal(
|
||||||
FSymbols.Done.Signal,
|
FSymbols.Done.Signal,
|
||||||
procedure
|
procedure
|
||||||
begin
|
begin
|
||||||
@@ -130,10 +137,14 @@ begin
|
|||||||
FSymbols.WaitFor;
|
FSymbols.WaitFor;
|
||||||
FTerminate.Notify;
|
FTerminate.Notify;
|
||||||
TaskManager.WaitFor(FLoadDone);
|
TaskManager.WaitFor(FLoadDone);
|
||||||
Application.OnIdle := nil;
|
|
||||||
FRandom.Free;
|
FRandom.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TForm1.LayoutResized(Sender: TObject);
|
||||||
|
begin
|
||||||
|
LogMemo.Lines.Add('resize');
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TForm1.LoadButtonClick(Sender: TObject);
|
procedure TForm1.LoadButtonClick(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
if SymbolsComboBox.ItemIndex < 0 then
|
if SymbolsComboBox.ItemIndex < 0 then
|
||||||
@@ -142,7 +153,11 @@ begin
|
|||||||
var Stream := FServer.CreateStream(FSymbols.WaitFor[SymbolsComboBox.ItemIndex]);
|
var Stream := FServer.CreateStream(FSymbols.WaitFor[SymbolsComboBox.ItemIndex]);
|
||||||
var Data := TDataStreamProvider.Create<TAskBidItem>(30000, 10000, Stream);
|
var Data := TDataStreamProvider.Create<TAskBidItem>(30000, 10000, Stream);
|
||||||
|
|
||||||
Path1.AddIdleHandler(
|
var path := TPath.Create(Self);
|
||||||
|
path.Parent := Layout;
|
||||||
|
path.Align := TAlignLayout.None;
|
||||||
|
|
||||||
|
path.ProcessSignal(
|
||||||
Data.Changed,
|
Data.Changed,
|
||||||
procedure
|
procedure
|
||||||
begin
|
begin
|
||||||
@@ -150,14 +165,14 @@ begin
|
|||||||
|
|
||||||
if Prices.Count > 0 then
|
if Prices.Count > 0 then
|
||||||
begin
|
begin
|
||||||
Path1.BeginUpdate;
|
path.BeginUpdate;
|
||||||
try
|
try
|
||||||
Path1.Data.Clear;
|
path.Data.Clear;
|
||||||
Path1.Data.MoveTo(PointF(Path1.Width - 1, Prices[0].Data.Ask));
|
path.Data.MoveTo(PointF(path.Width - 1, Prices[0].Data.Ask));
|
||||||
for var i := 1 to Prices.Count - 1 do
|
for var i := 1 to Prices.Count - 1 do
|
||||||
Path1.Data.LineTo(PointF(Path1.Width - i - 1, Prices[i].Data.Ask));
|
path.Data.LineTo(PointF(path.Width - i - 1, Prices[i].Data.Ask));
|
||||||
finally
|
finally
|
||||||
Path1.EndUpdate;
|
path.EndUpdate;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
@@ -168,12 +183,11 @@ procedure TForm1.RandomButtonClick(Sender: TObject);
|
|||||||
begin
|
begin
|
||||||
var rnd: TRndItem;
|
var rnd: TRndItem;
|
||||||
|
|
||||||
var ass := FSymbols.WaitFor;
|
rnd.Stream := FServer.CreateStream(FSymbols.WaitFor[Random(Length(FSymbols.WaitFor))]);
|
||||||
rnd.Stream := FServer.CreateStream(ass[Random(Length(FSymbols.WaitFor))]);
|
|
||||||
rnd.Data := TDataStreamProvider.Create<TAskBidItem>(3000, 1000, rnd.Stream);
|
rnd.Data := TDataStreamProvider.Create<TAskBidItem>(3000, 1000, rnd.Stream);
|
||||||
|
|
||||||
rnd.Labl := TLabel.Create(Self);
|
rnd.Labl := TLabel.Create(Self);
|
||||||
rnd.Labl.Parent := FlowLayout;
|
rnd.Labl.Parent := Layout;
|
||||||
rnd.Labl.Align := TAlignLayout.None;
|
rnd.Labl.Align := TAlignLayout.None;
|
||||||
rnd.Labl.WordWrap := false;
|
rnd.Labl.WordWrap := false;
|
||||||
rnd.Labl.Width := 300;
|
rnd.Labl.Width := 300;
|
||||||
@@ -185,7 +199,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
FRandom[idx]
|
FRandom[idx]
|
||||||
.Labl
|
.Labl
|
||||||
.AddIdleHandler(
|
.ProcessSignal(
|
||||||
FRandom[idx].Data.Changed,
|
FRandom[idx].Data.Changed,
|
||||||
procedure
|
procedure
|
||||||
begin
|
begin
|
||||||
@@ -209,15 +223,14 @@ end;
|
|||||||
|
|
||||||
procedure TForm1.ChartButtonClick(Sender: TObject);
|
procedure TForm1.ChartButtonClick(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
if SymbolsComboBox.ItemIndex < 0 then
|
var Symbol := SelectedSymbol;
|
||||||
|
if Symbol = '' then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
var Symbol := FSymbols.WaitFor[SymbolsComboBox.ItemIndex];
|
var currPathData := TWriteable<IObjectRef<TPathData>>.CreateWriteable.Protect;
|
||||||
|
var currLog := TWriteable<String>.CreateWriteable.Protect;
|
||||||
|
|
||||||
var currPathData := TMutable<IObjectRef<TPathData>>.CreateWriteable.Protect;
|
const width = 300;
|
||||||
var currLog := TMutable<String>.CreateWriteable.Protect;
|
|
||||||
|
|
||||||
const width = 3000;
|
|
||||||
|
|
||||||
var done :=
|
var done :=
|
||||||
TaskManager.RunTask(
|
TaskManager.RunTask(
|
||||||
@@ -255,18 +268,36 @@ begin
|
|||||||
|
|
||||||
FLoadDone := TState.All([FLoadDone, done]);
|
FLoadDone := TState.All([FLoadDone, done]);
|
||||||
|
|
||||||
LogMemo.AddIdleHandler(currLog.Changed, procedure begin LogMemo.Lines.Add(Symbol + ': ' + currLog.Value); end);
|
var path := TPath.Create(Self);
|
||||||
|
path.Parent := Layout;
|
||||||
|
path.Align := TAlignLayout.None;
|
||||||
|
|
||||||
Path1.AddIdleHandler(
|
path.ProcessSignal(
|
||||||
currPathData.Changed,
|
currPathData.Changed,
|
||||||
procedure
|
procedure(out IsDone: Boolean)
|
||||||
begin
|
begin
|
||||||
if currPathData.Value <> nil then
|
if currPathData.Value <> nil then
|
||||||
begin
|
path.Data.Assign(currPathData.Value.Obj);
|
||||||
Path1.Data.Assign(currPathData.Value.Obj);
|
IsDone := done.IsSet;
|
||||||
end;
|
|
||||||
end
|
end
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TForm1.SelectedStream: IDataStream<TAskBidItem>;
|
||||||
|
begin
|
||||||
|
var sym := SelectedSymbol;
|
||||||
|
if sym = '' then
|
||||||
|
exit(nil);
|
||||||
|
Result := FServer.CreateStream(sym);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TForm1.SelectedSymbol: String;
|
||||||
|
begin
|
||||||
|
Result := '';
|
||||||
|
if RandomBox.IsChecked then
|
||||||
|
Result := FSymbols.WaitFor[Random(Length(FSymbols.WaitFor))]
|
||||||
|
else if SymbolsComboBox.ItemIndex >= 0 then
|
||||||
|
Result := FSymbols.WaitFor[SymbolsComboBox.ItemIndex];
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -0,0 +1,412 @@
|
|||||||
|
(*
|
||||||
|
---
|
||||||
|
## Aura Module API Documentation
|
||||||
|
|
||||||
|
This unit defines the core interfaces and records for the Aura system, designed for robust algorithmic trading strategy development,
|
||||||
|
backtesting, and optimization. It encapsulates key concepts for defining strategies, executing backtests, performing walk-forward
|
||||||
|
optimizations, and conducting statistical robustness analysis via Monte Carlo simulations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Core Interfaces and Records
|
||||||
|
|
||||||
|
#### `IAuraObject`
|
||||||
|
Base interface for all Aura objects, providing a common `Name` property for identification.
|
||||||
|
* `Name`: A writeable string representing the object's name.
|
||||||
|
|
||||||
|
#### `TAuraArray<T: IAuraObject>`
|
||||||
|
A record providing a mutable, observable array-like collection for `IAuraObject` instances. It wraps a `TWriteable<TArray<T>>` and offers basic array manipulation methods.
|
||||||
|
* `Items`: A writeable array of `IAuraObject` instances.
|
||||||
|
* `Insert(Idx: Integer; const Item: T)`: Inserts an item at a specified index.
|
||||||
|
* `Delete(Idx: Integer)`: Deletes an item at a specified index.
|
||||||
|
* `IndexOf(const Item: T)`: Returns the index of a given item.
|
||||||
|
|
||||||
|
#### `IAuraLiveObject`
|
||||||
|
Extends `IAuraObject` for entities that are dynamically produced and actively executing operations, providing logging capabilities and a running status.
|
||||||
|
* `Log`: A `TStrings` object for logging events and status messages.
|
||||||
|
* `IsRunning`: Indicates whether the object is currently active.
|
||||||
|
|
||||||
|
#### `IAuraNode`
|
||||||
|
Extends `IAuraObject` for objects that can be part of a hierarchical structure and can be serialized.
|
||||||
|
* `Caption`: A human-readable representation of the object's name.
|
||||||
|
* `Serialize(const Write: TJsonWriter)`: Serializes the object's state to a JSON writer.
|
||||||
|
|
||||||
|
#### `IAuraChilds<T: IAuraNode>`
|
||||||
|
A generic interface for `IAuraNode`s that can have child nodes, enabling hierarchical organization within the Aura system.
|
||||||
|
* `Items`: A `TAuraArray` containing the child nodes.
|
||||||
|
|
||||||
|
#### `TAuraParameterDef`
|
||||||
|
Defines the metadata for a single strategy parameter, including its name, type, and default value.
|
||||||
|
* `Name`: The name of the parameter.
|
||||||
|
* `ParamType`: The data type of the parameter (e.g., integer, float, string, UTC time).
|
||||||
|
* `DefaultValue`: The default value for the parameter, stored as a `TAuraParameterValue`.
|
||||||
|
|
||||||
|
#### `TAuraParameterRecordDef`
|
||||||
|
Represents a collection of `TAuraParameterDef` records, defining all parameters for a specific strategy. This is an alias for `TArray<TAuraParameterDef>`.
|
||||||
|
|
||||||
|
#### `IAuraBot`
|
||||||
|
An interface representing an instance that executes a trading strategy with given parameters over a specified time period.
|
||||||
|
* `PnL`: A mutable data series representing the Profit and Loss (PnL) curve of the bot's execution.
|
||||||
|
|
||||||
|
#### `IAuraStrategy`
|
||||||
|
Defines a trading strategy, providing methods to create `IAuraBot` instances and specifying its parameter structure.
|
||||||
|
* `CreateBot(const Parameters: TArray<TAuraParameterValue>; StartTime, EndTime: TDateTime)`: Creates and returns an `IAuraBot` instance configured with the given parameters and time range.
|
||||||
|
* `ParameterRecordDef`: Defines the set of parameters expected by this strategy.
|
||||||
|
|
||||||
|
#### `TAuraTradePerformance`
|
||||||
|
A record encapsulating key performance metrics from a backtest or optimization run.
|
||||||
|
* `PnL`: Total Profit and Loss.
|
||||||
|
* `SharpeRatio`: Risk-adjusted return.
|
||||||
|
* `MaxDrawdown`: Maximum peak-to-trough decline.
|
||||||
|
* `ProfitFactor`: Ratio of gross profits to gross losses.
|
||||||
|
* `CalmarRatio`: Annualized return divided by the maximum drawdown.
|
||||||
|
* `WinRate`: Percentage of winning trades.
|
||||||
|
|
||||||
|
#### `TAuraMonteCarloResult`
|
||||||
|
Stores the results of a Monte Carlo simulation, providing a probabilistic distribution of `TAuraTradePerformance` outcomes.
|
||||||
|
* `Percentile`: An array of `TAuraTradePerformance` records, sorted by PnL and distributed into 10% percentiles, allowing for statistical risk assessment.
|
||||||
|
|
||||||
|
#### `IAuraTradeResult`
|
||||||
|
Represents the comprehensive outcome of a trade simulation (e.g., from a backtest or optimization), including performance metrics, trade details, and the ability to perform Monte Carlo analysis.
|
||||||
|
* `Performance`: The `TAuraTradePerformance` metrics for this result.
|
||||||
|
* `Trades`: A `TDataSeries<Double>` representing the PnLs of individual trades, serving as the basis for Monte Carlo simulation.
|
||||||
|
* `CalcMonteCarloSimulation(Steps: Integer)`: Asynchronously performs a Monte Carlo simulation on the `Trades` data, returning a `TFuture` that resolves to a `TAuraMonteCarloResult`.
|
||||||
|
|
||||||
|
#### `TAuraTimeRange`
|
||||||
|
A record defining a specific time window with a start and end date/time.
|
||||||
|
* `StartTime`: The start of the time range.
|
||||||
|
* `EndTime`: The end of the time range.
|
||||||
|
|
||||||
|
#### `IAuraBacktest`
|
||||||
|
An interface for executing a single backtest of a trading strategy.
|
||||||
|
* `BacktestResult`: A `TFuture` that resolves to the `IAuraTradeResult` of this backtest.
|
||||||
|
* `Bot`: The `IAuraBot` instance executing the backtest.
|
||||||
|
* `Parameters`: The specific parameters used for this backtest.
|
||||||
|
* `Strategy`: The `IAuraStrategy` being backtested.
|
||||||
|
* `TimeRange`: The historical time window over which the backtest is conducted.
|
||||||
|
|
||||||
|
#### `IAuraParameterOptimization`
|
||||||
|
Represents a single optimization slot (window) within a Walk-Forward Optimization process. It manages the execution of multiple in-sample backtests to find optimal parameters, followed by an out-of-sample test.
|
||||||
|
* `InSampleTests`: A mutable array of `IAuraBacktest` instances representing the backtests run during the in-sample optimization phase. New tests are added, and less optimal ones may be removed.
|
||||||
|
* `OutOfSampleTest`: A mutable `IAuraBacktest` representing the test run with the best-fit parameters from the in-sample optimization on the subsequent out-of-sample data.
|
||||||
|
|
||||||
|
#### `IAuraWalkForwardOptimizer`
|
||||||
|
Orchestrates the entire Walk-Forward Optimization (WFO) process, coordinating multiple `IAuraParameterOptimization` slots sequentially to test strategy adaptability across various market phases.
|
||||||
|
* `OptimizationResult`: A `TFuture` that resolves to the aggregated `IAuraTradeResult` representing the combined performance of all out-of-sample tests.
|
||||||
|
* `Slot`: An array of `IAuraParameterOptimization` instances, each representing a distinct optimization window.
|
||||||
|
|
||||||
|
#### `IAuraParameterNode`
|
||||||
|
Represents a single configurable parameter for optimization algorithms, allowing its value to be set and observed.
|
||||||
|
* `Value`: A mutable `TAuraParameterValue` representing the parameter's current setting.
|
||||||
|
|
||||||
|
#### `IAuraParameterList`
|
||||||
|
An alias for `IAuraChilds<IAuraParameterNode>`, providing a structured way to manage collections of `IAuraParameterNode`s.
|
||||||
|
|
||||||
|
#### `IAuraStudy`
|
||||||
|
Base interface for all types of strategy analysis studies (e.g., single backtests, parameter optimizations, WFOs).
|
||||||
|
* `EventLog`: A `TStrings` object for logging events specific to the study.
|
||||||
|
* `Strategy`: The `IAuraStrategy` that is the subject of the study.
|
||||||
|
|
||||||
|
#### `TAuraParameterRange`
|
||||||
|
Defines the allowed range for a strategy parameter during optimization.
|
||||||
|
* `MinValue`: The minimum allowed value for the parameter, as a `TAuraParameterValue`.
|
||||||
|
* `MaxValue`: The maximum allowed value for the parameter, as a `TAuraParameterValue`.
|
||||||
|
|
||||||
|
#### `IAuraBacktestStudy`
|
||||||
|
A specialized `IAuraStudy` for setting up and initiating a single backtest.
|
||||||
|
* `TimeRange`: The `TAuraTimeRange` for the backtest.
|
||||||
|
* `Start`: Initiates and returns an `IAuraBacktest` instance.
|
||||||
|
|
||||||
|
#### `IAuraParameterOptimizationStudy`
|
||||||
|
A specialized `IAuraStudy` for configuring and initiating a parameter optimization process (e.g., using genetic algorithms) over a specific time range.
|
||||||
|
* `Parameters`: An `IAuraChilds` collection of `IAuraParameterNode`s defining the configuration parameters for the optimization algorithm itself (e.g., max generations, population size).
|
||||||
|
* `TimeRange`: The `TAuraTimeRange` for the optimization.
|
||||||
|
* `Start`: Initiates and returns an `IAuraParameterOptimization` instance.
|
||||||
|
|
||||||
|
#### `IAuraWFOStudy`
|
||||||
|
A specialized `IAuraStudy` for configuring and initiating a Walk-Forward Optimization process.
|
||||||
|
* `ParameterRange[Idx: Integer]`: A writeable `TAuraParameterRange` defining the search space for each strategy parameter during the optimization phases.
|
||||||
|
* `NumSlots`: A writeable integer indicating the number of time windows (slots) for the WFO.
|
||||||
|
* `Start`: Initiates and returns an `IAuraWalkForwardOptimizer` instance.
|
||||||
|
|
||||||
|
#### `IAuraTradingMode`
|
||||||
|
An enumeration defining different operational modes for a trading environment.
|
||||||
|
* `tmTesting`: Mode for historical backtesting.
|
||||||
|
* `tmSim`: Mode for simulated live trading (paper trading).
|
||||||
|
* `tmLive`: Mode for actual live trading.
|
||||||
|
|
||||||
|
#### `IAuraWorkspace`
|
||||||
|
Represents the scope of a complete testing and trading environment, containing various studies and managing the overall trading mode.
|
||||||
|
* `WalkForwardAnalysis`: An `IAuraChilds` collection of `IAuraWFOStudy` instances.
|
||||||
|
* `Backtests`: An `IAuraChilds` collection of `IAuraBacktestStudy` instances.
|
||||||
|
* `ParameterOptimizations`: An `IAuraChilds` collection of `IAuraParameterOptimizationStudy` instances.
|
||||||
|
* `TradingMode`: A writeable `IAuraTradingMode` indicating the current operational mode.
|
||||||
|
|
||||||
|
#### `IAuraStrategyFactory`
|
||||||
|
An interface for creating instances of `IAuraStrategy`. Used for managing available strategy types.
|
||||||
|
* `CreateStrategy`: Creates and returns a new `IAuraStrategy` instance.
|
||||||
|
|
||||||
|
#### `IAuraApplication`
|
||||||
|
The application-level singleton, serving as the root for serialization and providing access to all defined strategies and workspaces.
|
||||||
|
* `Strategies`: An `IAuraChilds` collection of `IAuraStrategyFactory` instances.
|
||||||
|
* `Workspaces`: An `IAuraChilds` collection of `IAuraWorkspace` instances.
|
||||||
|
|
||||||
|
---
|
||||||
|
*)
|
||||||
|
|
||||||
|
unit Myc.Aura.Module;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.JSON.Writers,
|
||||||
|
System.Classes,
|
||||||
|
Myc.Signals,
|
||||||
|
Myc.Futures,
|
||||||
|
Myc.Lazy,
|
||||||
|
Myc.Trade.DataPoint,
|
||||||
|
Myc.Aura.Parameter;
|
||||||
|
|
||||||
|
type
|
||||||
|
// Base class of all Aura objects
|
||||||
|
IAuraObject = interface
|
||||||
|
function GetName: TWriteable<String>;
|
||||||
|
property Name: TWriteable<String> read GetName;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TAuraArray<T: IAuraObject> = record
|
||||||
|
private
|
||||||
|
FArray: TWriteable<TArray<T>>;
|
||||||
|
function GetItems: TWriteable<TArray<T>>;
|
||||||
|
public
|
||||||
|
procedure Insert(Idx: Integer; const Item: T);
|
||||||
|
procedure Delete(Idx: Integer);
|
||||||
|
function IndexOf(const Item: T): Integer;
|
||||||
|
property Items: TWriteable<TArray<T>> read GetItems;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Base class for objects that are dynamically produced and are executing
|
||||||
|
IAuraLiveObject = interface(IAuraObject)
|
||||||
|
function GetLog: TStrings;
|
||||||
|
function IsRunning: Boolean;
|
||||||
|
property Log: TStrings read GetLog;
|
||||||
|
end;
|
||||||
|
|
||||||
|
IAuraNode = interface(IAuraObject)
|
||||||
|
function GetCaption: string;
|
||||||
|
// Serialize the whole project
|
||||||
|
procedure Serialize(const Write: TJsonWriter);
|
||||||
|
// Pretty print of Name
|
||||||
|
property Caption: string read GetCaption;
|
||||||
|
end;
|
||||||
|
|
||||||
|
IAuraChilds<T: IAuraNode> = interface(IAuraNode)
|
||||||
|
function GetItems: TAuraArray<T>;
|
||||||
|
// Nodes to add as childs in hierarchical representation
|
||||||
|
property Items: TAuraArray<T> read GetItems;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TAuraParameterDef = record
|
||||||
|
Name: String;
|
||||||
|
ParamType: TAuraParameterType;
|
||||||
|
DefaultValue: TAuraParameterValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TAuraParameterRecordDef = TArray<TAuraParameterDef>;
|
||||||
|
|
||||||
|
// A bot executes a strategy
|
||||||
|
IAuraBot = interface(IAuraLiveObject)
|
||||||
|
function GetPnL: TMutable<TDataSeries<Double>>;
|
||||||
|
// Zero-based PnL series
|
||||||
|
property PnL: TMutable<TDataSeries<Double>> read GetPnL;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// A strategy creates a bot that executes that strategy with given parameters and within a given time slot.
|
||||||
|
IAuraStrategy = interface(IAuraObject)
|
||||||
|
function CreateBot(const Parameters: TArray<TAuraParameterValue>; StartTime, EndTime: TDateTime): IAuraBot;
|
||||||
|
function GetParameterRecordDef: TAuraParameterRecordDef;
|
||||||
|
// Defines the parameters of this strategy
|
||||||
|
property ParameterRecordDef: TAuraParameterRecordDef read GetParameterRecordDef;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// The performance of a backtest or walk forward optimization
|
||||||
|
TAuraTradePerformance = record
|
||||||
|
PnL: Double;
|
||||||
|
SharpeRatio, MaxDrawdown, ProfitFactor, CalmarRatio, WinRate: Double;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TAuraMonteCarloResult = record
|
||||||
|
// Performance sorted by PnL and distributed to percentiles of 10% each
|
||||||
|
Percentile: array[0..10] of TAuraTradePerformance;
|
||||||
|
end;
|
||||||
|
|
||||||
|
IAuraTradeResult = interface
|
||||||
|
function GetPerformance: TAuraTradePerformance;
|
||||||
|
function GetTrades: TDataSeries<Double>;
|
||||||
|
// Perform Monte Carlo Simulation and generate performance distribution
|
||||||
|
function CalcMonteCarloSimulation(Steps: Integer): TFuture<TAuraMonteCarloResult>;
|
||||||
|
// PnLs of each trade = Equity curve
|
||||||
|
property Trades: TDataSeries<Double> read GetTrades;
|
||||||
|
// The performance of this equity curve
|
||||||
|
property Performance: TAuraTradePerformance read GetPerformance;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TAuraTimeRange = record
|
||||||
|
StartTime: TDateTime;
|
||||||
|
EndTime: TDateTime;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Backtesting
|
||||||
|
IAuraBacktest = interface(IAuraLiveObject)
|
||||||
|
function GetParameters: TArray<TAuraParameterValue>;
|
||||||
|
function GetStrategy: IAuraStrategy;
|
||||||
|
function GetBot: IAuraBot;
|
||||||
|
function GetBacktestResult: TFuture<IAuraTradeResult>;
|
||||||
|
function GetTimeRange: TAuraTimeRange;
|
||||||
|
// The bot executing the backtest (generated by Strategy.CreateBot with the given parameters).
|
||||||
|
property Bot: IAuraBot read GetBot;
|
||||||
|
// The backtest result
|
||||||
|
property BacktestResult: TFuture<IAuraTradeResult> read GetBacktestResult;
|
||||||
|
// The strategy parameters for this backtest
|
||||||
|
property Parameters: TArray<TAuraParameterValue> read GetParameters;
|
||||||
|
// The strategy to backtest
|
||||||
|
property Strategy: IAuraStrategy read GetStrategy;
|
||||||
|
// The tested time window
|
||||||
|
property TimeRange: TAuraTimeRange read GetTimeRange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Generates parameter sets using genetic algorithms (or other techniques).
|
||||||
|
// Executes backtests and finds the best run by evaluating the backtest results.
|
||||||
|
IAuraParameterOptimization = interface(IAuraLiveObject)
|
||||||
|
function GetInSampleTests: TMutable<TArray<IAuraBacktest>>;
|
||||||
|
function GetOutOfSampleTest: TMutable<IAuraBacktest>;
|
||||||
|
// The running in-sample-tests. New tests will be added until the optimization stops.
|
||||||
|
property InSampleTests: TMutable<TArray<IAuraBacktest>> read GetInSampleTests;
|
||||||
|
// After in sample testing is finished, the out-of-sample-test with the best parameter set is executed.
|
||||||
|
property OutOfSampleTest: TMutable<IAuraBacktest> read GetOutOfSampleTest;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Walk-Forward-Optimizer
|
||||||
|
IAuraWalkForwardOptimizer = interface(IAuraLiveObject)
|
||||||
|
function GetOptimizationResult: TFuture<IAuraTradeResult>;
|
||||||
|
function GetSlot: TArray<IAuraParameterOptimization>;
|
||||||
|
// When optimization is ready, this will contain the result for the combined WFO slots
|
||||||
|
property OptimizationResult: TFuture<IAuraTradeResult> read GetOptimizationResult;
|
||||||
|
// The concurrently running optimization slots
|
||||||
|
property Slot: TArray<IAuraParameterOptimization> read GetSlot;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// A Parameter Value
|
||||||
|
IAuraParameterNode = interface(IAuraNode)
|
||||||
|
function GetValue: TMutable<TAuraParameterValue>;
|
||||||
|
property Value: TMutable<TAuraParameterValue> read GetValue;
|
||||||
|
end;
|
||||||
|
IAuraParameterList = IAuraChilds<IAuraParameterNode>;
|
||||||
|
|
||||||
|
// A study on a strategy
|
||||||
|
IAuraStudy = interface(IAuraNode)
|
||||||
|
function GetEventLog: TStrings;
|
||||||
|
function GetStrategy: IAuraStrategy;
|
||||||
|
property EventLog: TStrings read GetEventLog;
|
||||||
|
// The Strategy to optimize
|
||||||
|
property Strategy: IAuraStrategy read GetStrategy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TAuraParameterRange = record
|
||||||
|
MinValue, MaxValue: TAuraParameterValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Study on a single backtest
|
||||||
|
IAuraBacktestStudy = interface(IAuraStudy)
|
||||||
|
function GetTimeRange: TAuraTimeRange;
|
||||||
|
function Start: IAuraBacktest;
|
||||||
|
property TimeRange: TAuraTimeRange read GetTimeRange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Study parameter optimization
|
||||||
|
IAuraParameterOptimizationStudy = interface(IAuraStudy)
|
||||||
|
function GetParameters: IAuraChilds<IAuraParameterNode>;
|
||||||
|
function GetTimeRange: TAuraTimeRange;
|
||||||
|
function Start: IAuraParameterOptimization;
|
||||||
|
// Depending on the implementation this may be MaxGenerations, PopulationSize, Mutations, etc
|
||||||
|
property Parameters: IAuraChilds<IAuraParameterNode> read GetParameters;
|
||||||
|
property TimeRange: TAuraTimeRange read GetTimeRange;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Walk forward optimization
|
||||||
|
IAuraWFOStudy = interface(IAuraStudy)
|
||||||
|
function GetParameterRange(Idx: Integer): TWriteable<TAuraParameterRange>;
|
||||||
|
function GetNumSlots: TWriteable<Integer>;
|
||||||
|
// Starts WalkForwardOptimizer with the given number of NumSlots.
|
||||||
|
function Start: IAuraWalkForwardOptimizer;
|
||||||
|
// A list of parameter values to test, one for each parameter in the Strategy's parameter definition.
|
||||||
|
// This defines the parameter space for optimization algorithms.
|
||||||
|
property ParameterRange[Idx: Integer]: TWriteable<TAuraParameterRange> read GetParameterRange;
|
||||||
|
// Number of time slots (windows) to test.
|
||||||
|
property NumSlots: TWriteable<Integer> read GetNumSlots;
|
||||||
|
end;
|
||||||
|
|
||||||
|
IAuraTradingMode = (tmTesting, tmSim, tmLive);
|
||||||
|
|
||||||
|
// Repesents the scope of a workspace
|
||||||
|
IAuraWorkspace = interface(IAuraNode)
|
||||||
|
function GetWalkForwardAnalysis: IAuraChilds<IAuraWFOStudy>;
|
||||||
|
function GetBacktests: IAuraChilds<IAuraBacktestStudy>;
|
||||||
|
function GetParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy>;
|
||||||
|
function GetTradingMode: TWriteable<IAuraTradingMode>;
|
||||||
|
property WalkForwardAnalysis: IAuraChilds<IAuraWFOStudy> read GetWalkForwardAnalysis;
|
||||||
|
property Backtests: IAuraChilds<IAuraBacktestStudy> read GetBacktests;
|
||||||
|
property ParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy> read GetParameterOptimizations;
|
||||||
|
property TradingMode: TWriteable<IAuraTradingMode> read GetTradingMode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
IAuraStrategyFactory = interface(IAuraNode)
|
||||||
|
function CreateStrategy: IAuraStrategy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Application singleton, the root for serialization
|
||||||
|
IAuraApplication = interface(IAuraNode)
|
||||||
|
function GetStrategies: IAuraChilds<IAuraStrategyFactory>;
|
||||||
|
function GetWorkspaces: IAuraChilds<IAuraWorkspace>;
|
||||||
|
property Strategies: IAuraChilds<IAuraStrategyFactory> read GetStrategies;
|
||||||
|
property Workspaces: IAuraChilds<IAuraWorkspace> read GetWorkspaces;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.Generics.Collections;
|
||||||
|
|
||||||
|
procedure TAuraArray<T>.Delete(Idx: Integer);
|
||||||
|
begin
|
||||||
|
if Idx < 0 then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
var Arr: TArray<T>;
|
||||||
|
SetLength(Arr, Length(FArray.Value) - 1);
|
||||||
|
TArray.Copy<T>(FArray.Value, Arr, 0, 0, Idx - 1);
|
||||||
|
TArray.Copy<T>(FArray.Value, Arr, Idx + 1, Idx, High(Arr) - Idx);
|
||||||
|
FArray.Value := Arr;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAuraArray<T>.Insert(Idx: Integer; const Item: T);
|
||||||
|
begin
|
||||||
|
var Arr: TArray<T>;
|
||||||
|
SetLength(Arr, Length(FArray.Value) + 1);
|
||||||
|
TArray.Copy<T>(FArray.Value, Arr, 0, 0, Idx - 1);
|
||||||
|
Arr[Idx] := Item;
|
||||||
|
TArray.Copy<T>(FArray.Value, Arr, Idx, Idx + 1, High(Arr) - Idx);
|
||||||
|
FArray.Value := Arr;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAuraArray<T>.GetItems: TWriteable<TArray<T>>;
|
||||||
|
begin
|
||||||
|
Result := FArray;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAuraArray<T>.IndexOf(const Item: T): Integer;
|
||||||
|
begin
|
||||||
|
Result := TArray.IndexOf<T>(FArray.Value, Item);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
unit Myc.Aura.Parameter;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
type
|
||||||
|
TAuraParameterType = (apNone, apInteger, apFloat, apString, apUTC);
|
||||||
|
|
||||||
|
TAuraParameterValue = record
|
||||||
|
private
|
||||||
|
FKind: TAuraParameterType;
|
||||||
|
FPtr: Pointer;
|
||||||
|
function GetAsInteger: Int64;
|
||||||
|
procedure SetAsInteger(const Value: Int64);
|
||||||
|
procedure SetKind(Kind: TAuraParameterType); inline;
|
||||||
|
procedure FreeValue;
|
||||||
|
function GetAsFloat: Double;
|
||||||
|
function GetAsString: string;
|
||||||
|
function GetAsUTC: TDateTime;
|
||||||
|
procedure SetAsFloat(const Value: Double);
|
||||||
|
procedure SetAsString(const Value: string);
|
||||||
|
procedure SetAsUTC(const Value: TDateTime);
|
||||||
|
public
|
||||||
|
class operator Initialize(out Dest: TAuraParameterValue);
|
||||||
|
class operator Finalize(var Dest: TAuraParameterValue);
|
||||||
|
class operator Assign(var Dest: TAuraParameterValue; const [ref] Src: TAuraParameterValue);
|
||||||
|
property AsInteger: Int64 read GetAsInteger write SetAsInteger;
|
||||||
|
property AsFloat: Double read GetAsFloat write SetAsFloat;
|
||||||
|
property AsString: string read GetAsString write SetAsString;
|
||||||
|
property AsUTC: TDateTime read GetAsUTC write SetAsUTC;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TAuraParameterValue }
|
||||||
|
|
||||||
|
function TAuraParameterValue.GetAsInteger: Int64;
|
||||||
|
begin
|
||||||
|
SetKind(apInteger);
|
||||||
|
if FPtr = nil then
|
||||||
|
exit(0);
|
||||||
|
Result := Int64(FPtr^);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAuraParameterValue.SetAsInteger(const Value: Int64);
|
||||||
|
begin
|
||||||
|
SetKind(apInteger);
|
||||||
|
if FPtr = nil then
|
||||||
|
GetMem(FPtr, sizeof(Int64));
|
||||||
|
Int64(FPtr^) := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAuraParameterValue.SetKind(Kind: TAuraParameterType);
|
||||||
|
begin
|
||||||
|
if FKind <> Kind then
|
||||||
|
begin
|
||||||
|
FreeValue;
|
||||||
|
FKind := Kind;
|
||||||
|
FPtr := nil;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TAuraParameterValue.Assign(var Dest: TAuraParameterValue; const [ref] Src: TAuraParameterValue);
|
||||||
|
begin
|
||||||
|
case Src.FKind of
|
||||||
|
apInteger: Dest.AsInteger := Src.AsInteger;
|
||||||
|
apFloat: Dest.AsInteger := Src.AsInteger;
|
||||||
|
apString: Dest.AsString := Src.AsString;
|
||||||
|
apUTC: Dest.AsUTC := Src.AsUTC;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TAuraParameterValue.Finalize(var Dest: TAuraParameterValue);
|
||||||
|
begin
|
||||||
|
if Dest.FPtr <> nil then
|
||||||
|
FreeMem(Dest.FPtr);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TAuraParameterValue.Initialize(out Dest: TAuraParameterValue);
|
||||||
|
begin
|
||||||
|
Dest.FPtr := nil;
|
||||||
|
Dest.FKind := apInteger;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAuraParameterValue.FreeValue;
|
||||||
|
begin
|
||||||
|
if FPtr <> nil then
|
||||||
|
begin
|
||||||
|
case FKind of
|
||||||
|
apInteger, apFloat: FreeMem(FPtr);
|
||||||
|
apString:
|
||||||
|
begin
|
||||||
|
String(FPtr^) := '';
|
||||||
|
FreeMem(FPtr);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
FPtr := nil;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAuraParameterValue.GetAsFloat: Double;
|
||||||
|
begin
|
||||||
|
SetKind(apFloat);
|
||||||
|
if FPtr = nil then
|
||||||
|
exit(0);
|
||||||
|
Result := Double(FPtr^);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAuraParameterValue.GetAsString: string;
|
||||||
|
begin
|
||||||
|
SetKind(apString);
|
||||||
|
if FPtr = nil then
|
||||||
|
exit('');
|
||||||
|
Result := String(FPtr^);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAuraParameterValue.GetAsUTC: TDateTime;
|
||||||
|
begin
|
||||||
|
SetKind(apUTC);
|
||||||
|
if FPtr = nil then
|
||||||
|
exit(0);
|
||||||
|
Result := TDateTime(FPtr^);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAuraParameterValue.SetAsFloat(const Value: Double);
|
||||||
|
begin
|
||||||
|
SetKind(apFloat);
|
||||||
|
if FPtr = nil then
|
||||||
|
GetMem(FPtr, sizeof(Double));
|
||||||
|
Double(FPtr^) := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAuraParameterValue.SetAsString(const Value: string);
|
||||||
|
begin
|
||||||
|
SetKind(apString);
|
||||||
|
if FPtr = nil then
|
||||||
|
begin
|
||||||
|
GetMem(FPtr, sizeof(String));
|
||||||
|
FillChar(FPtr^, sizeof(String), 0);
|
||||||
|
end;
|
||||||
|
String(FPtr^) := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAuraParameterValue.SetAsUTC(const Value: TDateTime);
|
||||||
|
begin
|
||||||
|
SetKind(apUTC);
|
||||||
|
if FPtr = nil then
|
||||||
|
GetMem(FPtr, sizeof(TDateTime));
|
||||||
|
TDateTime(FPtr^) := Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+18
-40
@@ -13,7 +13,6 @@ type
|
|||||||
private
|
private
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
function GetValue: T;
|
function GetValue: T;
|
||||||
function GetWriter: TMutable<T>.IWriter;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycMutableBase<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
TMycMutableBase<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
||||||
@@ -21,7 +20,6 @@ type
|
|||||||
FChanged: TEvent;
|
FChanged: TEvent;
|
||||||
FChangeState: TSignal.TSubscription;
|
FChangeState: TSignal.TSubscription;
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
function GetWriter: TMutable<T>.IWriter;
|
|
||||||
protected
|
protected
|
||||||
function GetValue: T; virtual; abstract;
|
function GetValue: T; virtual; abstract;
|
||||||
public
|
public
|
||||||
@@ -38,14 +36,13 @@ type
|
|||||||
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriter)
|
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TWriteable<T>.IWriteable)
|
||||||
private
|
private
|
||||||
FValue: T;
|
FValue: T;
|
||||||
FChanged: TEvent;
|
FChanged: TEvent;
|
||||||
protected
|
protected
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
function GetValue: T;
|
function GetValue: T;
|
||||||
function GetWriter: TMutable<T>.IWriter;
|
|
||||||
function Exchange(const Value: T): T;
|
function Exchange(const Value: T): T;
|
||||||
public
|
public
|
||||||
constructor Create(const AValue: T);
|
constructor Create(const AValue: T);
|
||||||
@@ -81,24 +78,26 @@ type
|
|||||||
constructor Create(const AChanged: TSignal.ISignal; const AProc: TFunc<T>);
|
constructor Create(const AChanged: TSignal.ISignal; const AProc: TFunc<T>);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycProtectedMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriter)
|
TMycProtectedMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TWriteable<T>.IWriteable)
|
||||||
private
|
private
|
||||||
FMutable: TMutable<T>.IMutable;
|
FWriteable: TWriteable<T>.IWriteable;
|
||||||
FLock: Integer;
|
FLock: Integer;
|
||||||
protected
|
protected
|
||||||
function Exchange(const Value: T): T;
|
function Exchange(const Value: T): T;
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
function GetValue: T;
|
function GetValue: T;
|
||||||
function GetWriter: TMutable<T>.IWriter;
|
|
||||||
procedure Lock; inline;
|
procedure Lock; inline;
|
||||||
procedure Release; inline;
|
procedure Release; inline;
|
||||||
public
|
public
|
||||||
constructor Create(const AMutable: TMutable<T>.IMutable);
|
constructor Create(const AWriteable: TWriteable<T>.IWriteable);
|
||||||
procedure SetValue(const Value: T);
|
procedure SetValue(const Value: T);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.Generics.Defaults;
|
||||||
|
|
||||||
{ TMycNullMutable<T> }
|
{ TMycNullMutable<T> }
|
||||||
|
|
||||||
function TMycNullMutable<T>.GetChanged: TSignal;
|
function TMycNullMutable<T>.GetChanged: TSignal;
|
||||||
@@ -111,11 +110,6 @@ begin
|
|||||||
Result := Default(T);
|
Result := Default(T);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycNullMutable<T>.GetWriter: TMutable<T>.IWriter;
|
|
||||||
begin
|
|
||||||
Result := nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TMycMutableBase<T> }
|
{ TMycMutableBase<T> }
|
||||||
|
|
||||||
constructor TMycMutableBase<T>.Create(const AChanged: TSignal);
|
constructor TMycMutableBase<T>.Create(const AChanged: TSignal);
|
||||||
@@ -136,11 +130,6 @@ begin
|
|||||||
Result := FChanged.Signal;
|
Result := FChanged.Signal;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycMutableBase<T>.GetWriter: TMutable<T>.IWriter;
|
|
||||||
begin
|
|
||||||
Result := nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TMycWriteableMutable<T> }
|
{ TMycWriteableMutable<T> }
|
||||||
|
|
||||||
constructor TMycWriteableMutable<T>.Create(const AValue: T);
|
constructor TMycWriteableMutable<T>.Create(const AValue: T);
|
||||||
@@ -166,15 +155,13 @@ begin
|
|||||||
Result := FValue;
|
Result := FValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycWriteableMutable<T>.GetWriter: TMutable<T>.IWriter;
|
|
||||||
begin
|
|
||||||
Result := Self;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMycWriteableMutable<T>.SetValue(const Value: T);
|
procedure TMycWriteableMutable<T>.SetValue(const Value: T);
|
||||||
begin
|
begin
|
||||||
FValue := Value;
|
if not TEqualityComparer<T>.Default.Equals(FValue, Value) then
|
||||||
FChanged.Notify;
|
begin
|
||||||
|
FValue := Value;
|
||||||
|
FChanged.Notify;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TMycNullLazy<T> }
|
{ TMycNullLazy<T> }
|
||||||
@@ -248,18 +235,17 @@ end;
|
|||||||
|
|
||||||
{ TMycProtectedMutable<T> }
|
{ TMycProtectedMutable<T> }
|
||||||
|
|
||||||
constructor TMycProtectedMutable<T>.Create(const AMutable: TMutable<T>.IMutable);
|
constructor TMycProtectedMutable<T>.Create(const AWriteable: TWriteable<T>.IWriteable);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FMutable := AMutable;
|
FWriteable := AWriteable;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycProtectedMutable<T>.Exchange(const Value: T): T;
|
function TMycProtectedMutable<T>.Exchange(const Value: T): T;
|
||||||
begin
|
begin
|
||||||
Lock;
|
Lock;
|
||||||
try
|
try
|
||||||
Assert(FMutable.Writer <> nil);
|
Result := FWriteable.Exchange(Value);
|
||||||
Result := FMutable.Writer.Exchange(Value);
|
|
||||||
finally
|
finally
|
||||||
Release;
|
Release;
|
||||||
end;
|
end;
|
||||||
@@ -267,26 +253,19 @@ end;
|
|||||||
|
|
||||||
function TMycProtectedMutable<T>.GetChanged: TSignal;
|
function TMycProtectedMutable<T>.GetChanged: TSignal;
|
||||||
begin
|
begin
|
||||||
Result := FMutable.Changed;
|
Result := FWriteable.Changed;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycProtectedMutable<T>.GetValue: T;
|
function TMycProtectedMutable<T>.GetValue: T;
|
||||||
begin
|
begin
|
||||||
Lock;
|
Lock;
|
||||||
try
|
try
|
||||||
Result := FMutable.Value;
|
Result := FWriteable.Value;
|
||||||
finally
|
finally
|
||||||
Release;
|
Release;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycProtectedMutable<T>.GetWriter: TMutable<T>.IWriter;
|
|
||||||
begin
|
|
||||||
Result := nil;
|
|
||||||
if FMutable.Writer <> nil then
|
|
||||||
Result := Self;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMycProtectedMutable<T>.Lock;
|
procedure TMycProtectedMutable<T>.Lock;
|
||||||
begin
|
begin
|
||||||
while AtomicExchange(FLock, 1) = 1 do
|
while AtomicExchange(FLock, 1) = 1 do
|
||||||
@@ -302,8 +281,7 @@ procedure TMycProtectedMutable<T>.SetValue(const Value: T);
|
|||||||
begin
|
begin
|
||||||
Lock;
|
Lock;
|
||||||
try
|
try
|
||||||
Assert(FMutable.Writer <> nil);
|
FWriteable.SetValue(Value);
|
||||||
FMutable.Writer.SetValue(Value);
|
|
||||||
finally
|
finally
|
||||||
Release;
|
Release;
|
||||||
end;
|
end;
|
||||||
|
|||||||
+72
-36
@@ -9,20 +9,13 @@ uses
|
|||||||
type
|
type
|
||||||
TMutable<T> = record
|
TMutable<T> = record
|
||||||
type
|
type
|
||||||
IWriter = interface
|
|
||||||
procedure SetValue(const Value: T);
|
|
||||||
function Exchange(const Value: T): T;
|
|
||||||
end;
|
|
||||||
|
|
||||||
IMutable = interface
|
IMutable = interface
|
||||||
{$REGION 'property access'}
|
{$REGION 'property access'}
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
function GetValue: T;
|
function GetValue: T;
|
||||||
function GetWriter: IWriter;
|
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
property Changed: TSignal read GetChanged;
|
property Changed: TSignal read GetChanged;
|
||||||
property Value: T read GetValue;
|
property Value: T read GetValue;
|
||||||
property Writer: IWriter read GetWriter;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{$REGION 'private'}
|
{$REGION 'private'}
|
||||||
@@ -34,9 +27,7 @@ type
|
|||||||
private
|
private
|
||||||
FMutable: IMutable;
|
FMutable: IMutable;
|
||||||
function GetChanged: TSignal; inline;
|
function GetChanged: TSignal; inline;
|
||||||
function GetIsWriteable: Boolean;
|
|
||||||
function GetValue: T; inline;
|
function GetValue: T; inline;
|
||||||
procedure SetValue(const Value: T);
|
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
public
|
public
|
||||||
constructor Create(const AMutable: IMutable);
|
constructor Create(const AMutable: IMutable);
|
||||||
@@ -47,13 +38,36 @@ type
|
|||||||
class property Null: IMutable read FNull;
|
class property Null: IMutable read FNull;
|
||||||
|
|
||||||
class function Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>; overload; static;
|
class function Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>; overload; static;
|
||||||
class function CreateWriteable: TMutable<T>; overload; static;
|
|
||||||
|
|
||||||
function Protect: TMutable<T>;
|
property Value: T read GetValue;
|
||||||
|
property Changed: TSignal read GetChanged;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TWriteable<T> = record
|
||||||
|
type
|
||||||
|
IWriteable = interface(TMutable<T>.IMutable)
|
||||||
|
procedure SetValue(const Value: T);
|
||||||
|
function Exchange(const Value: T): T;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$REGION 'private'}
|
||||||
|
private
|
||||||
|
FWriteable: IWriteable;
|
||||||
|
function GetChanged: TSignal; inline;
|
||||||
|
function GetValue: T; inline;
|
||||||
|
procedure SetValue(const Value: T);
|
||||||
|
{$ENDREGION}
|
||||||
|
public
|
||||||
|
constructor Create(const AWriteable: IWriteable);
|
||||||
|
class operator Implicit(const A: IWriteable): TWriteable<T>; overload;
|
||||||
|
class operator Implicit(const A: TWriteable<T>): IWriteable; overload;
|
||||||
|
|
||||||
|
class function CreateWriteable: TWriteable<T>; overload; static;
|
||||||
|
|
||||||
|
function Protect: TWriteable<T>;
|
||||||
|
|
||||||
property Value: T read GetValue write SetValue;
|
property Value: T read GetValue write SetValue;
|
||||||
property Changed: TSignal read GetChanged;
|
property Changed: TSignal read GetChanged;
|
||||||
property IsWriteable: Boolean read GetIsWriteable;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TLazy<T> = record
|
TLazy<T> = record
|
||||||
@@ -114,40 +128,16 @@ begin
|
|||||||
Result := TMycFuncMutable<T>.Create(Changing, Proc);
|
Result := TMycFuncMutable<T>.Create(Changing, Proc);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TMutable<T>.CreateWriteable: TMutable<T>;
|
|
||||||
begin
|
|
||||||
Result := TMycWriteableMutable<T>.Create(Default(T));
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMutable<T>.GetChanged: TSignal;
|
function TMutable<T>.GetChanged: TSignal;
|
||||||
begin
|
begin
|
||||||
Result := FMutable.Changed;
|
Result := FMutable.Changed;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMutable<T>.GetIsWriteable: Boolean;
|
|
||||||
begin
|
|
||||||
Result := Assigned(FMutable.Writer);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMutable<T>.GetValue: T;
|
function TMutable<T>.GetValue: T;
|
||||||
begin
|
begin
|
||||||
Result := FMutable.Value;
|
Result := FMutable.Value;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMutable<T>.Protect: TMutable<T>;
|
|
||||||
begin
|
|
||||||
if not (FMutable is TMycProtectedMutable<T>) then
|
|
||||||
Result := TMycProtectedMutable<T>.Create(FMutable)
|
|
||||||
else
|
|
||||||
Result := FMutable;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMutable<T>.SetValue(const Value: T);
|
|
||||||
begin
|
|
||||||
Assert(IsWriteable);
|
|
||||||
FMutable.Writer.SetValue(Value);
|
|
||||||
end;
|
|
||||||
|
|
||||||
class operator TMutable<T>.Implicit(const A: TMutable<T>): IMutable;
|
class operator TMutable<T>.Implicit(const A: TMutable<T>): IMutable;
|
||||||
begin
|
begin
|
||||||
Result := A.FMutable;
|
Result := A.FMutable;
|
||||||
@@ -212,4 +202,50 @@ begin
|
|||||||
Dest.FLazy := FNull;
|
Dest.FLazy := FNull;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TWriteable<T> }
|
||||||
|
|
||||||
|
constructor TWriteable<T>.Create(const AWriteable: IWriteable);
|
||||||
|
begin
|
||||||
|
FWriteable := AWriteable;
|
||||||
|
Assert(Assigned(FWriteable));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TWriteable<T>.CreateWriteable: TWriteable<T>;
|
||||||
|
begin
|
||||||
|
Result := TMycWriteableMutable<T>.Create(Default(T));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TWriteable<T>.GetChanged: TSignal;
|
||||||
|
begin
|
||||||
|
Result := FWriteable.Changed;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TWriteable<T>.GetValue: T;
|
||||||
|
begin
|
||||||
|
Result := FWriteable.Value;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TWriteable<T>.Protect: TWriteable<T>;
|
||||||
|
begin
|
||||||
|
if not (FWriteable is TMycProtectedMutable<T>) then
|
||||||
|
Result := TMycProtectedMutable<T>.Create(FWriteable)
|
||||||
|
else
|
||||||
|
Result := FWriteable;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TWriteable<T>.SetValue(const Value: T);
|
||||||
|
begin
|
||||||
|
FWriteable.SetValue(Value);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TWriteable<T>.Implicit(const A: IWriteable): TWriteable<T>;
|
||||||
|
begin
|
||||||
|
Result.Create(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TWriteable<T>.Implicit(const A: TWriteable<T>): IWriteable;
|
||||||
|
begin
|
||||||
|
Result := A.FWriteable;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+110
-37
@@ -5,72 +5,145 @@ interface
|
|||||||
uses
|
uses
|
||||||
System.Classes,
|
System.Classes,
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
|
System.Generics.Collections,
|
||||||
|
System.Diagnostics,
|
||||||
System.Messaging,
|
System.Messaging,
|
||||||
Myc.Signals;
|
Myc.Signals;
|
||||||
|
|
||||||
type
|
type
|
||||||
TMsgProc = reference to procedure(const Sender: TObject; const M: TMessage);
|
TSignalComponentHelper = class helper for TComponent
|
||||||
|
type
|
||||||
TComponentValidation = class(TComponent)
|
TMsgProc = reference to procedure(out IsDone: Boolean);
|
||||||
private
|
|
||||||
FReceived: TFlag;
|
|
||||||
FProc: TMsgProc;
|
|
||||||
FSubscription: TMessageSubscriptionId;
|
|
||||||
FMsgClass: TClass;
|
|
||||||
procedure DoMsg(const Sender: TObject; const M: TMessage);
|
|
||||||
|
|
||||||
|
TSignalSubscription = class(TComponent, TSignal.ISubscriber)
|
||||||
|
private
|
||||||
|
FSignal: TSignal;
|
||||||
|
FSigSubscr: TSignal.TSubscription;
|
||||||
|
FProc: TMsgProc;
|
||||||
|
FNotified: Integer;
|
||||||
|
FIdleSubscrId: TMessageSubscriptionId;
|
||||||
|
class var
|
||||||
|
FQueued: Integer;
|
||||||
|
FCount: Integer;
|
||||||
|
FItems: TList<TSignalSubscription>;
|
||||||
|
FIdx: Integer;
|
||||||
|
class procedure HandleSignals(Timeout: Int64);
|
||||||
|
function Notify: Boolean;
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TComponent; const ASignal: TSignal; const AProc: TMsgProc); reintroduce;
|
||||||
|
destructor Destroy; override;
|
||||||
|
end;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent; const AMsgClass: TClass; const ASignal: TSignal; const AProc: TMsgProc); reintroduce;
|
procedure ProcessSignal(const Signal: TSignal; const Proc: TMsgProc); overload;
|
||||||
destructor Destroy; override;
|
procedure ProcessSignal(const Signal: TSignal; const Proc: TProc); overload;
|
||||||
end;
|
|
||||||
|
|
||||||
TComponentValidationHelper = class helper for TComponent
|
|
||||||
procedure AddMsgHandler(const MsgClass: TClass; const Signal: TSignal; const Proc: TMsgProc);
|
|
||||||
procedure AddIdleHandler(const Signal: TSignal; const Proc: TProc);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
{$IFDEF FRAMEWORK_FMX}
|
FMX.Types;
|
||||||
FMX.Types
|
|
||||||
{$ELSE}
|
|
||||||
VCL.Types // to be checked
|
|
||||||
{$IFEND}
|
|
||||||
;
|
|
||||||
|
|
||||||
{ TComponentValidation }
|
{ TSignalComponentHelper.TSignalSubscription }
|
||||||
|
|
||||||
constructor TComponentValidation.Create(AOwner: TComponent; const AMsgClass: TClass; const ASignal: TSignal; const AProc: TMsgProc);
|
constructor TSignalComponentHelper.TSignalSubscription.Create(AOwner: TComponent; const ASignal: TSignal; const AProc: TMsgProc);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
FMsgClass := AMsgClass;
|
FSignal := ASignal;
|
||||||
FProc := AProc;
|
FProc := AProc;
|
||||||
FReceived := TFlag.CreateObserver(ASignal);
|
FIdx := 0;
|
||||||
FSubscription := TMessageManager.DefaultManager.SubscribeToMessage(FMsgClass, DoMsg);
|
|
||||||
|
if FItems = nil then
|
||||||
|
begin
|
||||||
|
FItems := TList<TSignalSubscription>.Create;
|
||||||
|
FIdleSubscrId :=
|
||||||
|
TMessageManager
|
||||||
|
.DefaultManager
|
||||||
|
.SubscribeToMessage(TIdleMessage, procedure(const Sender: TObject; const M: TMessage) begin HandleSignals(50); end);
|
||||||
|
end;
|
||||||
|
FItems.Add(Self);
|
||||||
|
|
||||||
|
FSigSubscr := FSignal.Subscribe(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TComponentValidation.Destroy;
|
destructor TSignalComponentHelper.TSignalSubscription.Destroy;
|
||||||
begin
|
begin
|
||||||
TMessageManager.DefaultManager.Unsubscribe(FMsgClass, FSubscription);
|
FSigSubscr.Unsubscribe;
|
||||||
|
|
||||||
|
FItems.Remove(Self);
|
||||||
|
if FItems.Count = 0 then
|
||||||
|
begin
|
||||||
|
TMessageManager.DefaultManager.Unsubscribe(TIdleMessage, FIdleSubscrId);
|
||||||
|
FreeAndNil(FItems);
|
||||||
|
end;
|
||||||
|
|
||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TComponentValidation.DoMsg(const Sender: TObject; const M: TMessage);
|
class procedure TSignalComponentHelper.TSignalSubscription.HandleSignals(Timeout: Int64);
|
||||||
begin
|
begin
|
||||||
if FReceived.Reset then
|
if AtomicExchange(FQueued, 0) = 0 then
|
||||||
FProc(Sender, M);
|
exit;
|
||||||
|
|
||||||
|
var Stopwatch := TStopwatch.StartNew;
|
||||||
|
|
||||||
|
if (FIdx = 0) or (FIdx > FItems.Count) then
|
||||||
|
FIdx := FItems.Count;
|
||||||
|
|
||||||
|
while FIdx > 0 do
|
||||||
|
begin
|
||||||
|
dec(FIdx);
|
||||||
|
var sub := FItems[FIdx];
|
||||||
|
|
||||||
|
if AtomicExchange(sub.FNotified, 0) = 1 then
|
||||||
|
begin
|
||||||
|
var done := false;
|
||||||
|
try
|
||||||
|
sub.FProc(done);
|
||||||
|
finally
|
||||||
|
if done then
|
||||||
|
sub.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if AtomicDecrement(FCount) = 1 then
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if Stopwatch.ElapsedMilliseconds > Timeout then
|
||||||
|
begin
|
||||||
|
AtomicExchange(FQueued, 1);
|
||||||
|
break;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FIdx = 0 then
|
||||||
|
FIdx := FItems.Count;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TComponentValidationHelper.AddIdleHandler(const Signal: TSignal; const Proc: TProc);
|
function TSignalComponentHelper.TSignalSubscription.Notify: Boolean;
|
||||||
begin
|
begin
|
||||||
var capProc := Proc;
|
if AtomicExchange(FNotified, 1) = 0 then
|
||||||
AddMsgHandler(TIdleMessage, Signal, procedure(const Sender: TObject; const M: TMessage) begin capProc(); end);
|
AtomicIncrement(FCount);
|
||||||
|
AtomicExchange(FQueued, 1);
|
||||||
|
|
||||||
|
// if AtomicExchange(FQueued, 1) = 0 then
|
||||||
|
// TThread.Queue( nil,
|
||||||
|
// procedure
|
||||||
|
// begin
|
||||||
|
// if AtomicExchange(FQueued, 0) = 1 then
|
||||||
|
// HandleSignals( 40 );
|
||||||
|
// end);
|
||||||
|
Result := true;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TComponentValidationHelper.AddMsgHandler(const MsgClass: TClass; const Signal: TSignal; const Proc: TMsgProc);
|
procedure TSignalComponentHelper.ProcessSignal(const Signal: TSignal; const Proc: TProc);
|
||||||
begin
|
begin
|
||||||
TComponentValidation.Create(Self, MsgClass, Signal, Proc);
|
var cProc: TProc := Proc;
|
||||||
|
TSignalSubscription.Create(Self, Signal, procedure(out IsDone: Boolean) begin cProc(); end);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSignalComponentHelper.ProcessSignal(const Signal: TSignal; const Proc: TMsgProc);
|
||||||
|
begin
|
||||||
|
TSignalSubscription.Create(Self, Signal, Proc);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ type
|
|||||||
FLookback: Int64;
|
FLookback: Int64;
|
||||||
function GetChanged: TSignal;
|
function GetChanged: TSignal;
|
||||||
function GetValue: TDataSeries<T>;
|
function GetValue: TDataSeries<T>;
|
||||||
function GetWriter: TMutable<TDataSeries<T>>.IWriter;
|
|
||||||
public
|
public
|
||||||
constructor Create(ALookback, AMaxChunkSize: Int64; const AStream: IDataStream<T>);
|
constructor Create(ALookback, AMaxChunkSize: Int64; const AStream: IDataStream<T>);
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@@ -63,11 +62,6 @@ begin
|
|||||||
Result := FDataSeries;
|
Result := FDataSeries;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDataStreamProvider<T>.GetWriter: TMutable<TDataSeries<T>>.IWriter;
|
|
||||||
begin
|
|
||||||
Result := nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TDataStreamProvider }
|
{ TDataStreamProvider }
|
||||||
|
|
||||||
class function TDataStreamProvider.Create<T>(Lookback, MaxChunkSize: Int64; const Stream: IDataStream<T>): TMutable<TDataSeries<T>>;
|
class function TDataStreamProvider.Create<T>(Lookback, MaxChunkSize: Int64; const Stream: IDataStream<T>): TMutable<TDataSeries<T>>;
|
||||||
|
|||||||
Reference in New Issue
Block a user