Chart Control V1

This commit is contained in:
Michael Schimmel
2025-07-02 14:49:33 +02:00
parent b453236b1e
commit 58ce84e567
11 changed files with 1746 additions and 256 deletions
+509 -91
View File
@@ -12,144 +12,144 @@ optimizations, and conducting statistical robustness analysis via Monte Carlo si
#### `IAuraObject`
Base interface for all Aura objects, providing a common `Name` property for identification.
* `Name`: A writeable string representing the object's name.
* `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.
* `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.
* `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.
* `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.
* `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`.
* `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.
* `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.
* `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.
* `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.
* `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`.
* `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.
* `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.
* `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.
* `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.
* `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.
* `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.
* `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`.
* `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.
* `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.
* `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.
* `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.
* `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.
* `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`
#### `IAuraModule`
An interface for creating instances of `IAuraStrategy`. Used for managing available strategy types.
* `CreateStrategy`: Creates and returns a new `IAuraStrategy` instance.
* `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.
* `Strategies`: An `IAuraChilds` collection of `IAuraModule` instances.
* `Workspaces`: An `IAuraChilds` collection of `IAuraWorkspace` instances.
---
*)
@@ -161,6 +161,7 @@ interface
uses
System.JSON.Writers,
System.Classes,
System.Generics.Collections,
Myc.Signals,
Myc.Futures,
Myc.Lazy,
@@ -179,6 +180,7 @@ type
FArray: TWriteable<TArray<T>>;
function GetItems: TWriteable<TArray<T>>;
public
class operator Initialize(out Dest: TAuraArray<T>);
procedure Insert(Idx: Integer; const Item: T);
procedure Delete(Idx: Integer);
function IndexOf(const Item: T): Integer;
@@ -200,10 +202,28 @@ type
property Caption: string read GetCaption;
end;
IAuraChilds<T: IAuraNode> = interface(IAuraNode)
function GetItems: TAuraArray<T>;
IAuraChilds<T> = interface(IAuraNode)
function GetItems: TWriteable<TArray<T>>;
procedure Insert(Idx: Integer; const Item: T);
procedure Delete(Idx: Integer);
function IndexOf(const Item: T): Integer;
// Nodes to add as childs in hierarchical representation
property Items: TAuraArray<T> read GetItems;
property Items: TWriteable<TArray<T>> read GetItems;
end;
TAuraChilds<T> = record
private
FChilds: IAuraChilds<T>;
function GetCaption: string;
public
constructor Create(const AChilds: IAuraChilds<T>);
class operator Implicit(const A: IAuraChilds<T>): TAuraChilds<T>; overload;
class operator Implicit(const A: TAuraChilds<T>): IAuraChilds<T>; overload;
procedure Serialize(const Write: TJsonWriter);
procedure Insert(Idx: Integer; const Item: T);
procedure Delete(Idx: Integer);
function IndexOf(const Item: T): Integer;
property Caption: string read GetCaption;
end;
TAuraParameterDef = record
@@ -346,57 +366,227 @@ type
property NumSlots: TWriteable<Integer> read GetNumSlots;
end;
IAuraTradingMode = (tmTesting, tmSim, tmLive);
TAuraTradingMode = (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;
['{8485F7E7-F097-4CB8-8186-A6C9AE0048AF}']
// function GetWalkForwardAnalysis: IAuraChilds<IAuraWFOStudy>;
// function GetBacktests: IAuraChilds<IAuraBacktestStudy>;
// function GetParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy>;
// function GetWalkForwardAnalysis: IAuraChilds<IAuraWFOStudy>;
// function GetBacktests: IAuraChilds<IAuraBacktestStudy>;
// function GetParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy>;
// property WalkForwardAnalysis: IAuraChilds<IAuraWFOStudy> read GetWalkForwardAnalysis;
// property Backtests: IAuraChilds<IAuraBacktestStudy> read GetBacktests;
// property ParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy> read GetParameterOptimizations;
// property WalkForwardAnalysis: IAuraChilds<IAuraWFOStudy> read GetWalkForwardAnalysis;
// property Backtests: IAuraChilds<IAuraBacktestStudy> read GetBacktests;
// property ParameterOptimizations: IAuraChilds<IAuraParameterOptimizationStudy> read GetParameterOptimizations;
function GetTradingMode: TWriteable<TAuraTradingMode>;
function GetStudies: TAuraArray<IAuraStudy>;
property TradingMode: TWriteable<TAuraTradingMode> read GetTradingMode;
property Studies: TAuraArray<IAuraStudy> read GetStudies;
end;
IAuraStrategyFactory = interface(IAuraNode)
function CreateStrategy: IAuraStrategy;
IAuraModule = interface
procedure SetupWorkspace(const Workspace: IAuraWorkspace);
end;
// Application singleton, the root for serialization
IAuraApplication = interface(IAuraNode)
function GetStrategies: IAuraChilds<IAuraStrategyFactory>;
function GetModuleNames: TMutable<TArray<String>>;
function GetModules(const Name: String): IAuraModule;
function GetWorkspaces: IAuraChilds<IAuraWorkspace>;
property Strategies: IAuraChilds<IAuraStrategyFactory> read GetStrategies;
procedure RegisterModule(const Name: String; const Module: IAuraModule);
property ModuleNames: TMutable<TArray<String>> read GetModuleNames;
property Modules[const Name: String]: IAuraModule read GetModules;
property Workspaces: IAuraChilds<IAuraWorkspace> read GetWorkspaces;
end;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Generic implementation for a collection of child nodes.
TMycAuraObject = class(TInterfacedObject, IAuraObject)
private
FName: TWriteable<String>;
function GetName: TWriteable<String>;
procedure Serialize(const Write: TJsonWriter);
public
constructor Create(const AName: string);
end;
// Generic implementation for a collection of child nodes.
TMycAuraNode = class(TInterfacedObject, IAuraNode)
private
FName: TWriteable<String>;
function GetName: TWriteable<String>;
protected
function GetCaption: string; virtual;
procedure Serialize(const Write: TJsonWriter);
public
constructor Create(const AName: string);
end;
// Generic implementation for a collection of child nodes.
TMycAuraChilds<T: IAuraNode> = class(TInterfacedObject, IAuraChilds<T>)
private
FName: TWriteable<String>;
FItems: TWriteable<TArray<T>>;
function GetCaption: string;
function GetItems: TWriteable<TArray<T>>;
function GetName: TWriteable<String>;
procedure Serialize(const Write: TJsonWriter);
public
constructor Create(const AName: string);
procedure Delete(Idx: Integer);
function IndexOf(const Item: T): Integer;
procedure Insert(Idx: Integer; const Item: T);
end;
TMycAuraWorkspace = class(TMycAuraNode, IAuraWorkspace)
private
FTradingMode: TWriteable<TAuraTradingMode>;
FStudies: TAuraArray<IAuraStudy>;
function GetStudies: TAuraArray<IAuraStudy>;
function GetTradingMode: TWriteable<TAuraTradingMode>;
protected
function GetCaption: string; override;
public
constructor Create(const AName: string; TradingMode: TAuraTradingMode);
end;
TMycAuraApplication = class(TInterfacedObject, IAuraApplication)
private
FName: TWriteable<String>;
FModules: TDictionary<String, IAuraModule>;
FModulesChanged: TEvent;
FModuleNames: TMutable<TArray<String>>;
FWorkspaces: TAuraChilds<IAuraWorkspace>;
function GetCaption: string;
function GetName: TWriteable<String>;
function GetModuleNames: TMutable<TArray<String>>;
function GetModules(const Name: String): IAuraModule;
function GetWorkspaces: IAuraChilds<IAuraWorkspace>;
procedure Serialize(const Write: TJsonWriter);
public
constructor Create;
procedure RegisterModule(const Name: String; const Module: IAuraModule);
end;
implementation
uses
System.Generics.Collections;
{ TMycAuraObject }
constructor TMycAuraObject.Create(const AName: string);
begin
inherited Create;
FName := TWriteable<String>.CreateWriteable;
FName.Value := AName;
end;
function TMycAuraObject.GetName: TWriteable<String>;
begin
Result := FName;
end;
procedure TMycAuraObject.Serialize(const Write: TJsonWriter);
begin
Write.WriteStartObject;
Write.WritePropertyName('Name');
Write.WriteValue(FName.Value);
Write.WriteEndObject;
end;
{ TMycAuraNode }
constructor TMycAuraNode.Create(const AName: string);
begin
inherited Create;
FName := TWriteable<String>.CreateWriteable;
FName.Value := AName;
end;
function TMycAuraNode.GetCaption: string;
begin
Result := FName.Value;
end;
function TMycAuraNode.GetName: TWriteable<String>;
begin
Result := FName;
end;
procedure TMycAuraNode.Serialize(const Write: TJsonWriter);
begin
Write.WriteStartObject;
inherited;
Write.WriteEndObject;
end;
{ TAuraArray<T> }
class operator TAuraArray<T>.Initialize(out Dest: TAuraArray<T>);
begin
Dest.FArray := TWriteable<TArray<T>>.CreateWriteable;
end;
procedure TAuraArray<T>.Delete(Idx: Integer);
var
oldArray: TArray<T>;
newArray: TArray<T>;
oldCount: Integer;
begin
if Idx < 0 then
oldArray := FArray.Value;
oldCount := Length(oldArray);
if (Idx < 0) or (Idx >= oldCount) 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;
SetLength(newArray, oldCount - 1);
// Copy elements before the index
if Idx > 0 then
TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);
// Copy elements after the index
if Idx < oldCount - 1 then
TArray.Copy<T>(oldArray, newArray, Idx + 1, Idx, oldCount - Idx - 1);
FArray.Value := newArray;
end;
procedure TAuraArray<T>.Insert(Idx: Integer; const Item: T);
var
oldArray: TArray<T>;
newArray: TArray<T>;
oldCount: Integer;
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;
oldArray := FArray.Value;
if not Assigned(oldArray) then
SetLength(oldArray, 0);
oldCount := Length(oldArray);
if Idx < 0 then
Idx := 0;
if Idx > oldCount then
Idx := oldCount;
SetLength(newArray, oldCount + 1);
// Copy elements before the index
if Idx > 0 then
TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);
newArray[Idx] := Item;
// Copy elements after the index
if Idx < oldCount then
TArray.Copy<T>(oldArray, newArray, Idx, Idx + 1, oldCount - Idx);
FArray.Value := newArray;
end;
function TAuraArray<T>.GetItems: TWriteable<TArray<T>>;
@@ -409,4 +599,232 @@ begin
Result := TArray.IndexOf<T>(FArray.Value, Item);
end;
{ TMycAuraChilds<T> }
constructor TMycAuraChilds<T>.Create(const AName: string);
begin
inherited Create;
FName := TWriteable<String>.CreateWriteable(AName);
FItems := TWriteable<TArray<T>>.CreateWriteable;
end;
procedure TMycAuraChilds<T>.Delete(Idx: Integer);
var
oldArray: TArray<T>;
newArray: TArray<T>;
oldCount: Integer;
begin
oldArray := FItems.Value;
oldCount := Length(oldArray);
if (Idx < 0) or (Idx >= oldCount) then
exit;
SetLength(newArray, oldCount - 1);
// Copy elements before the index
if Idx > 0 then
TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);
// Copy elements after the index
if Idx < oldCount - 1 then
TArray.Copy<T>(oldArray, newArray, Idx + 1, Idx, oldCount - Idx - 1);
FItems.Value := newArray;
end;
function TMycAuraChilds<T>.GetCaption: string;
begin
Result := FName.Value;
end;
function TMycAuraChilds<T>.GetItems: TWriteable<TArray<T>>;
begin
Result := FItems;
end;
function TMycAuraChilds<T>.GetName: TWriteable<String>;
begin
Result := FName;
end;
function TMycAuraChilds<T>.IndexOf(const Item: T): Integer;
begin
Result := TArray.IndexOf<T>(FItems.Value, Item);
end;
procedure TMycAuraChilds<T>.Insert(Idx: Integer; const Item: T);
var
oldArray: TArray<T>;
newArray: TArray<T>;
oldCount: Integer;
begin
oldArray := FItems.Value;
if not Assigned(oldArray) then
SetLength(oldArray, 0);
oldCount := Length(oldArray);
if Idx < 0 then
Idx := 0;
if Idx > oldCount then
Idx := oldCount;
SetLength(newArray, oldCount + 1);
// Copy elements before the index
if Idx > 0 then
TArray.Copy<T>(oldArray, newArray, 0, 0, Idx);
newArray[Idx] := Item;
// Copy elements after the index
if Idx < oldCount then
TArray.Copy<T>(oldArray, newArray, Idx, Idx + 1, oldCount - Idx);
FItems.Value := newArray;
end;
procedure TMycAuraChilds<T>.Serialize(const Write: TJsonWriter);
var
Item: T;
arr: TArray<T>;
begin
Write.WriteStartObject;
inherited;
Write.WritePropertyName('Items');
Write.WriteStartArray;
arr := FItems.Value;
for Item in arr do
Item.Serialize(Write);
Write.WriteEndArray;
Write.WriteEndObject;
end;
{ TMycAuraApplication }
constructor TMycAuraApplication.Create;
begin
inherited Create;
FName := TWriteable<String>.CreateWriteable('Application');
FModules := TDictionary<String, IAuraModule>.Create;
FModulesChanged := TEvent.CreateEvent;
FModuleNames :=
TMutable<TArray<String>>.Construct(FModulesChanged.Signal, function: TArray<String> begin Result := FModules.Keys.ToArray; end);
FWorkspaces := TMycAuraChilds<IAuraWorkspace>.Create('Workspaces');
end;
function TMycAuraApplication.GetCaption: string;
begin
Result := FName.Value;
end;
function TMycAuraApplication.GetName: TWriteable<String>;
begin
Result := FName;
end;
function TMycAuraApplication.GetModuleNames: TMutable<TArray<String>>;
begin
Result := FModuleNames;
end;
function TMycAuraApplication.GetModules(const Name: String): IAuraModule;
begin
if not FModules.TryGetValue(Name, Result) then
Result := nil;
end;
function TMycAuraApplication.GetWorkspaces: IAuraChilds<IAuraWorkspace>;
begin
Result := FWorkspaces;
end;
procedure TMycAuraApplication.RegisterModule(const Name: String; const Module: IAuraModule);
begin
FModules.Add(Name, Module);
FModulesChanged.Notify;
end;
procedure TMycAuraApplication.Serialize(const Write: TJsonWriter);
begin
Write.WriteStartObject;
Write.WritePropertyName('Name');
Write.WriteValue(FName.Value);
Write.WritePropertyName('Workspaces');
FWorkspaces.Serialize(Write);
Write.WriteEndObject;
end;
function CreateAuraApplication: IAuraApplication;
begin
Result := TMycAuraApplication.Create;
end;
constructor TMycAuraWorkspace.Create(const AName: string; TradingMode: TAuraTradingMode);
begin
inherited Create(AName);
FTradingMode := TWriteable<TAuraTradingMode>.CreateWriteable(TradingMode);
end;
function TMycAuraWorkspace.GetCaption: string;
begin
var mode := '';
case FTradingMode.Value of
tmTesting: mode := ' (Test)';
tmSim: mode := ' (Sim)';
tmLive: mode := ' (Live)';
end;
Result := inherited GetCaption + mode;
end;
function TMycAuraWorkspace.GetStudies: TAuraArray<IAuraStudy>;
begin
Result := FStudies;
end;
function TMycAuraWorkspace.GetTradingMode: TWriteable<TAuraTradingMode>;
begin
Result := FTradingMode;
end;
constructor TAuraChilds<T>.Create(const AChilds: IAuraChilds<T>);
begin
FChilds := AChilds;
end;
procedure TAuraChilds<T>.Delete(Idx: Integer);
begin
FChilds.Delete(Idx);
end;
function TAuraChilds<T>.GetCaption: string;
begin
Result := FChilds.Caption;
end;
function TAuraChilds<T>.IndexOf(const Item: T): Integer;
begin
Result := FChilds.IndexOf(Item);
end;
procedure TAuraChilds<T>.Insert(Idx: Integer; const Item: T);
begin
FChilds.Insert(Idx, Item);
end;
procedure TAuraChilds<T>.Serialize(const Write: TJsonWriter);
begin
FChilds.Serialize(Write);
end;
class operator TAuraChilds<T>.Implicit(const A: IAuraChilds<T>): TAuraChilds<T>;
begin
Result.Create(A);
end;
class operator TAuraChilds<T>.Implicit(const A: TAuraChilds<T>): IAuraChilds<T>;
begin
Result := A.FChilds;
end;
end.
+1 -2
View File
@@ -208,6 +208,7 @@ begin
exit;
Item := PItem(Tag);
Item.Receiver := nil;
if Item = FList then
FList := Item.Next;
@@ -217,8 +218,6 @@ begin
if Item.Next <> nil then
Item.Next.Prev := Item.Prev;
Item.Receiver := nil;
FreeItem(Item);
end;
+7 -1
View File
@@ -63,6 +63,7 @@ type
class operator Implicit(const A: TWriteable<T>): IWriteable; overload;
class function CreateWriteable: TWriteable<T>; overload; static;
class function CreateWriteable(const Init: T): TWriteable<T>; overload; static;
function Protect: TWriteable<T>;
@@ -212,7 +213,12 @@ end;
class function TWriteable<T>.CreateWriteable: TWriteable<T>;
begin
Result := TMycWriteableMutable<T>.Create(Default(T));
Result := CreateWriteable(Default(T));
end;
class function TWriteable<T>.CreateWriteable(const Init: T): TWriteable<T>;
begin
Result := TMycWriteableMutable<T>.Create(Init);
end;
function TWriteable<T>.GetChanged: TSignal;
+134 -65
View File
@@ -5,145 +5,214 @@ interface
uses
System.Classes,
System.SysUtils,
System.Generics.Collections,
System.Diagnostics,
System.Messaging,
Myc.Signals;
type
TSignalComponentHelper = class helper for TComponent
type
TMsgProc = reference to procedure(out IsDone: Boolean);
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
procedure ProcessSignal(const Signal: TSignal; const Proc: TMsgProc); overload;
procedure ProcessSignal(const Signal: TSignal; const Proc: TProc); overload;
function ProcessSignal(const Signal: TSignal; const Proc: TMsgProc): TComponent; overload;
function ProcessSignal(const Signal: TSignal; const Proc: TProc): TComponent; overload;
end;
TSignalSyncHelper = record helper for TSignal
function Queue(const Proc: TProc; Delay: Integer = 0): TSignal.TSubscription; overload;
function Queue(Thread: TThread; const Proc: TProc; Delay: Integer = 0): TSignal.TSubscription; overload;
end;
implementation
uses
System.Diagnostics,
System.Messaging,
FMX.Types;
{ TSignalComponentHelper.TSignalSubscription }
type
TSignalSubscriber = class(TComponent, TSignal.ISubscriber)
private
FSignal: TSignal;
FSigSubscr: TSignal.TSubscription;
FProc: TSignalComponentHelper.TMsgProc;
FNotified: Integer;
FIdleSubscrId: TMessageSubscriptionId;
FNext, FPrev: TSignalSubscriber;
class var
FQueued: Integer;
FCount: Integer;
FFirst: TSignalSubscriber;
FCurr: TSignalSubscriber;
class procedure HandleSignals(Timeout: Int64);
public
constructor Create(AOwner: TComponent; const ASignal: TSignal; const AProc: TSignalComponentHelper.TMsgProc); reintroduce;
destructor Destroy; override;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
function Notify: Boolean;
end;
constructor TSignalComponentHelper.TSignalSubscription.Create(AOwner: TComponent; const ASignal: TSignal; const AProc: TMsgProc);
TSyncSubscriber = class(TInterfacedObject, TSignal.ISubscriber)
Thread: TThread;
Delay: Integer;
Timestamp: Int64;
Proc: TProc;
class var
Timer: TStopwatch;
function Notify: Boolean;
class constructor CreateClass;
end;
class constructor TSyncSubscriber.CreateClass;
begin
Timer := TStopwatch.StartNew;
end;
function TSyncSubscriber.Notify: Boolean;
begin
if Assigned(Proc) then
begin
var cProc := Proc;
Proc := nil;
TThread.ForceQueue(Thread, procedure begin cProc() end, Delay - (Timer.ElapsedMilliseconds - Timestamp));
end;
Result := false;
end;
{ TSignalSubscription }
constructor TSignalSubscriber.Create(AOwner: TComponent; const ASignal: TSignal; const AProc: TSignalComponentHelper.TMsgProc);
begin
inherited Create(AOwner);
FSignal := ASignal;
FProc := AProc;
FIdx := 0;
if FItems = nil then
if FFirst = 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);
FPrev := nil;
if FFirst <> nil then
FNext := FFirst;
if FNext <> nil then
FNext.FPrev := Self;
FFirst := Self;
FSigSubscr := FSignal.Subscribe(Self);
end;
destructor TSignalComponentHelper.TSignalSubscription.Destroy;
destructor TSignalSubscriber.Destroy;
begin
FSigSubscr.Unsubscribe;
FItems.Remove(Self);
if FItems.Count = 0 then
if FFirst = Self then
FFirst := FNext;
if FNext <> nil then
FNext.FPrev := FPrev;
if FPrev <> nil then
FPrev.FNext := FNext;
if FFirst = nil then
begin
TMessageManager.DefaultManager.Unsubscribe(TIdleMessage, FIdleSubscrId);
FreeAndNil(FItems);
end;
inherited;
end;
class procedure TSignalComponentHelper.TSignalSubscription.HandleSignals(Timeout: Int64);
procedure TSignalSubscriber.AfterConstruction;
begin
inherited;
Notify;
end;
procedure TSignalSubscriber.BeforeDestruction;
begin
FSigSubscr.Unsubscribe;
if FCurr = Self then
FCurr := FCurr.FNext;
inherited;
end;
class procedure TSignalSubscriber.HandleSignals(Timeout: Int64);
begin
if AtomicExchange(FQueued, 0) = 0 then
exit;
var Stopwatch := TStopwatch.StartNew;
var doBreak := false;
if (FIdx = 0) or (FIdx > FItems.Count) then
FIdx := FItems.Count;
if FCurr = nil then
FCurr := FFirst;
while FIdx > 0 do
while FCurr <> nil do
begin
dec(FIdx);
var sub := FItems[FIdx];
var sub := FCurr;
FCurr := sub.FNext;
if AtomicExchange(sub.FNotified, 0) = 1 then
begin
var done := false;
try
sub.FProc(done);
finally
if done then
sub.Free;
if not (csDestroying in sub.ComponentState) then
begin
var done := false;
try
sub.FProc(done);
finally
if done then
sub.Free;
end;
end;
if AtomicDecrement(FCount) = 1 then
break;
doBreak := AtomicDecrement(FCount) = 1;
end;
if Stopwatch.ElapsedMilliseconds > Timeout then
doBreak := doBreak or (Stopwatch.ElapsedMilliseconds > Timeout);
if doBreak then
begin
AtomicExchange(FQueued, 1);
break;
end;
if FIdx = 0 then
FIdx := FItems.Count;
end;
end;
function TSignalComponentHelper.TSignalSubscription.Notify: Boolean;
function TSignalSubscriber.Notify: Boolean;
begin
if AtomicExchange(FNotified, 1) = 0 then
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;
procedure TSignalComponentHelper.ProcessSignal(const Signal: TSignal; const Proc: TProc);
function TSignalComponentHelper.ProcessSignal(const Signal: TSignal; const Proc: TProc): TComponent;
begin
var cProc: TProc := Proc;
TSignalSubscription.Create(Self, Signal, procedure(out IsDone: Boolean) begin cProc(); end);
Result := ProcessSignal(Signal, procedure(out IsDone: Boolean) begin cProc(); end);
end;
procedure TSignalComponentHelper.ProcessSignal(const Signal: TSignal; const Proc: TMsgProc);
function TSignalComponentHelper.ProcessSignal(const Signal: TSignal; const Proc: TMsgProc): TComponent;
begin
TSignalSubscription.Create(Self, Signal, Proc);
Result := TSignalSubscriber.Create(Self, Signal, Proc);
end;
{ TSignalSyncHelper }
function TSignalSyncHelper.Queue(const Proc: TProc; Delay: Integer = 0): TSignal.TSubscription;
begin
Result := Queue(nil, Proc, Delay);
end;
function TSignalSyncHelper.Queue(Thread: TThread; const Proc: TProc; Delay: Integer = 0): TSignal.TSubscription;
begin
var Subscr := TSyncSubscriber.Create;
Subscr.Thread := Thread;
Subscr.Proc := Proc;
Subscr.Delay := Delay;
Subscr.Timestamp := TSyncSubscriber.Timer.ElapsedMilliseconds;
Result := Subscribe(Subscr);
end;
end.