1st Aura Project Layout

This commit is contained in:
Michael Schimmel
2025-06-28 12:43:19 +02:00
parent 9802c8c924
commit b453236b1e
10 changed files with 891 additions and 189 deletions
+3 -1
View File
@@ -6,7 +6,9 @@ uses
FMX.Forms,
MainForm in 'MainForm.pas' {Form1},
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}
+2
View File
@@ -135,6 +135,8 @@
</DCCReference>
<DCCReference Include="..\Src\Myc.Trade.Core.DataPoint.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">
<Key>Base</Key>
</BuildConfiguration>
+64 -41
View File
@@ -11,85 +11,108 @@ object Form1: TForm1
OnDestroy = FormDestroy
DesignerMasterStyle = 0
object Panel1: TPanel
Align = Top
Align = Client
Size.Width = 796.00000000000000000
Size.Height = 529.00000000000000000
Size.Height = 616.00000000000000000
Size.PlatformDefault = False
TabOrder = 2
object RandomButton: TButton
Position.X = 697.00000000000000000
Position.Y = 38.00000000000000000
TabOrder = 0
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
object Layout: TFlowLayout
Align = Client
Size.Width = 627.00000000000000000
Size.Height = 616.00000000000000000
Size.PlatformDefault = False
TabOrder = 1
Justify = Left
JustifyLastLine = Left
FlowDirection = LeftToRight
HorizontalGap = 2.00000000000000000
VerticalGap = 2.00000000000000000
OnResized = LayoutResized
end
object SymbolsComboBox: TComboBox
Anchors = [akTop, akRight]
Position.X = 493.00000000000000000
Position.Y = 8.00000000000000000
Size.Width = 217.00000000000000000
Position.X = 608.00000000000000000
Position.Y = 16.00000000000000000
Size.Width = 177.00000000000000000
Size.Height = 22.00000000000000000
Size.PlatformDefault = False
TabOrder = 5
end
object RandomBox: TCheckBox
Anchors = [akTop, akRight]
Position.X = 609.00000000000000000
Position.Y = 46.00000000000000000
TabOrder = 2
Text = 'Random'
end
object LoadButton: TButton
Anchors = [akTop, akRight]
Position.X = 718.00000000000000000
Position.Y = 8.00000000000000000
Position.X = 641.00000000000000000
Position.Y = 86.00000000000000000
Size.Width = 47.00000000000000000
Size.Height = 22.00000000000000000
Size.PlatformDefault = False
TabOrder = 3
TabOrder = 6
Text = 'Load'
TextSettings.Trimming = None
OnClick = LoadButtonClick
end
object FlowLayout: TFlowLayout
Position.X = 304.00000000000000000
Position.Y = 104.00000000000000000
Size.Width = 449.00000000000000000
Size.Height = 361.00000000000000000
Size.PlatformDefault = False
TabOrder = 5
Justify = Left
JustifyLastLine = Left
FlowDirection = LeftToRight
object RandomButton: TButton
Anchors = [akTop, akRight]
Position.X = 705.00000000000000000
Position.Y = 86.00000000000000000
TabOrder = 3
Text = 'Random'
TextSettings.Trimming = None
OnClick = RandomButtonClick
end
object ChartButton: TButton
Position.X = 712.00000000000000000
Position.Y = 72.00000000000000000
TabOrder = 6
Anchors = [akTop, akRight]
Position.X = 665.00000000000000000
Position.Y = 130.00000000000000000
TabOrder = 8
Text = 'ChartButton'
TextSettings.Trimming = None
OnClick = ChartButtonClick
end
object StopButton: TButton
Position.X = 720.00000000000000000
Position.Y = 96.00000000000000000
TabOrder = 8
Anchors = [akTop, akRight]
Position.X = 665.00000000000000000
Position.Y = 160.00000000000000000
TabOrder = 10
Text = 'StopButton'
TextSettings.Trimming = None
OnClick = StopButtonClick
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
object LogMemo: TMemo
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
DataDetectorTypes = []
Align = Client
Align = Bottom
Position.Y = 616.00000000000000000
Size.Width = 796.00000000000000000
Size.Height = 311.00000000000000000
Size.Height = 224.00000000000000000
Size.PlatformDefault = False
TabOrder = 1
Viewport.Width = 792.00000000000000000
Viewport.Height = 307.00000000000000000
Viewport.Height = 220.00000000000000000
end
end
+59 -28
View File
@@ -34,25 +34,30 @@ uses
Myc.Signals.FMX,
Myc.TaskManager,
FMX.ListBox,
FMX.Layouts;
FMX.Layouts,
FMX.MultiView,
FMX.TreeView;
type
TForm1 = class(TForm)
LogMemo: TMemo;
Panel1: TPanel;
RandomButton: TButton;
Path1: TPath;
Layout: TFlowLayout;
SymbolsComboBox: TComboBox;
RandomBox: TCheckBox;
LoadButton: TButton;
FlowLayout: TFlowLayout;
RandomButton: TButton;
ChartButton: TButton;
StopButton: TButton;
TreeView1: TTreeView;
Splitter1: TSplitter;
procedure RandomButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure LoadButtonClick(Sender: TObject);
procedure ChartButtonClick(Sender: TObject);
procedure StopButtonClick(Sender: TObject);
procedure LayoutResized(Sender: TObject);
private
const
cnt = 20;
@@ -70,6 +75,8 @@ type
FRandom: TList<TRndItem>;
FTerminate: TEvent;
FLoadDone: TState;
function SelectedSymbol: String;
function SelectedStream: IDataStream<TAskBidItem>;
public
{ Public declarations }
published
@@ -102,7 +109,7 @@ begin
FSymbols := FServer.EnumerateSymbols;
SymbolsComboBox.AddIdleHandler(
SymbolsComboBox.ProcessSignal(
FSymbols.Done.Signal,
procedure
begin
@@ -130,10 +137,14 @@ begin
FSymbols.WaitFor;
FTerminate.Notify;
TaskManager.WaitFor(FLoadDone);
Application.OnIdle := nil;
FRandom.Free;
end;
procedure TForm1.LayoutResized(Sender: TObject);
begin
LogMemo.Lines.Add('resize');
end;
procedure TForm1.LoadButtonClick(Sender: TObject);
begin
if SymbolsComboBox.ItemIndex < 0 then
@@ -142,7 +153,11 @@ begin
var Stream := FServer.CreateStream(FSymbols.WaitFor[SymbolsComboBox.ItemIndex]);
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,
procedure
begin
@@ -150,14 +165,14 @@ begin
if Prices.Count > 0 then
begin
Path1.BeginUpdate;
path.BeginUpdate;
try
Path1.Data.Clear;
Path1.Data.MoveTo(PointF(Path1.Width - 1, Prices[0].Data.Ask));
path.Data.Clear;
path.Data.MoveTo(PointF(path.Width - 1, Prices[0].Data.Ask));
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
Path1.EndUpdate;
path.EndUpdate;
end;
end;
end
@@ -168,12 +183,11 @@ procedure TForm1.RandomButtonClick(Sender: TObject);
begin
var rnd: TRndItem;
var ass := FSymbols.WaitFor;
rnd.Stream := FServer.CreateStream(ass[Random(Length(FSymbols.WaitFor))]);
rnd.Stream := FServer.CreateStream(FSymbols.WaitFor[Random(Length(FSymbols.WaitFor))]);
rnd.Data := TDataStreamProvider.Create<TAskBidItem>(3000, 1000, rnd.Stream);
rnd.Labl := TLabel.Create(Self);
rnd.Labl.Parent := FlowLayout;
rnd.Labl.Parent := Layout;
rnd.Labl.Align := TAlignLayout.None;
rnd.Labl.WordWrap := false;
rnd.Labl.Width := 300;
@@ -185,7 +199,7 @@ begin
begin
FRandom[idx]
.Labl
.AddIdleHandler(
.ProcessSignal(
FRandom[idx].Data.Changed,
procedure
begin
@@ -209,15 +223,14 @@ end;
procedure TForm1.ChartButtonClick(Sender: TObject);
begin
if SymbolsComboBox.ItemIndex < 0 then
var Symbol := SelectedSymbol;
if Symbol = '' then
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;
var currLog := TMutable<String>.CreateWriteable.Protect;
const width = 3000;
const width = 300;
var done :=
TaskManager.RunTask(
@@ -255,18 +268,36 @@ begin
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,
procedure
procedure(out IsDone: Boolean)
begin
if currPathData.Value <> nil then
begin
Path1.Data.Assign(currPathData.Value.Obj);
end;
path.Data.Assign(currPathData.Value.Obj);
IsDone := done.IsSet;
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.
+412
View File
@@ -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.
+151
View File
@@ -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
View File
@@ -13,7 +13,6 @@ type
private
function GetChanged: TSignal;
function GetValue: T;
function GetWriter: TMutable<T>.IWriter;
end;
TMycMutableBase<T> = class(TInterfacedObject, TMutable<T>.IMutable)
@@ -21,7 +20,6 @@ type
FChanged: TEvent;
FChangeState: TSignal.TSubscription;
function GetChanged: TSignal;
function GetWriter: TMutable<T>.IWriter;
protected
function GetValue: T; virtual; abstract;
public
@@ -38,14 +36,13 @@ type
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
end;
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriter)
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TWriteable<T>.IWriteable)
private
FValue: T;
FChanged: TEvent;
protected
function GetChanged: TSignal;
function GetValue: T;
function GetWriter: TMutable<T>.IWriter;
function Exchange(const Value: T): T;
public
constructor Create(const AValue: T);
@@ -81,24 +78,26 @@ type
constructor Create(const AChanged: TSignal.ISignal; const AProc: TFunc<T>);
end;
TMycProtectedMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriter)
TMycProtectedMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TWriteable<T>.IWriteable)
private
FMutable: TMutable<T>.IMutable;
FWriteable: TWriteable<T>.IWriteable;
FLock: Integer;
protected
function Exchange(const Value: T): T;
function GetChanged: TSignal;
function GetValue: T;
function GetWriter: TMutable<T>.IWriter;
procedure Lock; inline;
procedure Release; inline;
public
constructor Create(const AMutable: TMutable<T>.IMutable);
constructor Create(const AWriteable: TWriteable<T>.IWriteable);
procedure SetValue(const Value: T);
end;
implementation
uses
System.Generics.Defaults;
{ TMycNullMutable<T> }
function TMycNullMutable<T>.GetChanged: TSignal;
@@ -111,11 +110,6 @@ begin
Result := Default(T);
end;
function TMycNullMutable<T>.GetWriter: TMutable<T>.IWriter;
begin
Result := nil;
end;
{ TMycMutableBase<T> }
constructor TMycMutableBase<T>.Create(const AChanged: TSignal);
@@ -136,11 +130,6 @@ begin
Result := FChanged.Signal;
end;
function TMycMutableBase<T>.GetWriter: TMutable<T>.IWriter;
begin
Result := nil;
end;
{ TMycWriteableMutable<T> }
constructor TMycWriteableMutable<T>.Create(const AValue: T);
@@ -166,15 +155,13 @@ begin
Result := FValue;
end;
function TMycWriteableMutable<T>.GetWriter: TMutable<T>.IWriter;
begin
Result := Self;
end;
procedure TMycWriteableMutable<T>.SetValue(const Value: T);
begin
FValue := Value;
FChanged.Notify;
if not TEqualityComparer<T>.Default.Equals(FValue, Value) then
begin
FValue := Value;
FChanged.Notify;
end;
end;
{ TMycNullLazy<T> }
@@ -248,18 +235,17 @@ end;
{ TMycProtectedMutable<T> }
constructor TMycProtectedMutable<T>.Create(const AMutable: TMutable<T>.IMutable);
constructor TMycProtectedMutable<T>.Create(const AWriteable: TWriteable<T>.IWriteable);
begin
inherited Create;
FMutable := AMutable;
FWriteable := AWriteable;
end;
function TMycProtectedMutable<T>.Exchange(const Value: T): T;
begin
Lock;
try
Assert(FMutable.Writer <> nil);
Result := FMutable.Writer.Exchange(Value);
Result := FWriteable.Exchange(Value);
finally
Release;
end;
@@ -267,26 +253,19 @@ end;
function TMycProtectedMutable<T>.GetChanged: TSignal;
begin
Result := FMutable.Changed;
Result := FWriteable.Changed;
end;
function TMycProtectedMutable<T>.GetValue: T;
begin
Lock;
try
Result := FMutable.Value;
Result := FWriteable.Value;
finally
Release;
end;
end;
function TMycProtectedMutable<T>.GetWriter: TMutable<T>.IWriter;
begin
Result := nil;
if FMutable.Writer <> nil then
Result := Self;
end;
procedure TMycProtectedMutable<T>.Lock;
begin
while AtomicExchange(FLock, 1) = 1 do
@@ -302,8 +281,7 @@ procedure TMycProtectedMutable<T>.SetValue(const Value: T);
begin
Lock;
try
Assert(FMutable.Writer <> nil);
FMutable.Writer.SetValue(Value);
FWriteable.SetValue(Value);
finally
Release;
end;
+72 -36
View File
@@ -9,20 +9,13 @@ uses
type
TMutable<T> = record
type
IWriter = interface
procedure SetValue(const Value: T);
function Exchange(const Value: T): T;
end;
IMutable = interface
{$REGION 'property access'}
function GetChanged: TSignal;
function GetValue: T;
function GetWriter: IWriter;
{$ENDREGION}
property Changed: TSignal read GetChanged;
property Value: T read GetValue;
property Writer: IWriter read GetWriter;
end;
{$REGION 'private'}
@@ -34,9 +27,7 @@ type
private
FMutable: IMutable;
function GetChanged: TSignal; inline;
function GetIsWriteable: Boolean;
function GetValue: T; inline;
procedure SetValue(const Value: T);
{$ENDREGION}
public
constructor Create(const AMutable: IMutable);
@@ -47,13 +38,36 @@ type
class property Null: IMutable read FNull;
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 Changed: TSignal read GetChanged;
property IsWriteable: Boolean read GetIsWriteable;
end;
TLazy<T> = record
@@ -114,40 +128,16 @@ begin
Result := TMycFuncMutable<T>.Create(Changing, Proc);
end;
class function TMutable<T>.CreateWriteable: TMutable<T>;
begin
Result := TMycWriteableMutable<T>.Create(Default(T));
end;
function TMutable<T>.GetChanged: TSignal;
begin
Result := FMutable.Changed;
end;
function TMutable<T>.GetIsWriteable: Boolean;
begin
Result := Assigned(FMutable.Writer);
end;
function TMutable<T>.GetValue: T;
begin
Result := FMutable.Value;
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;
begin
Result := A.FMutable;
@@ -212,4 +202,50 @@ begin
Dest.FLazy := FNull;
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.
+110 -37
View File
@@ -5,72 +5,145 @@ interface
uses
System.Classes,
System.SysUtils,
System.Generics.Collections,
System.Diagnostics,
System.Messaging,
Myc.Signals;
type
TMsgProc = reference to procedure(const Sender: TObject; const M: TMessage);
TComponentValidation = class(TComponent)
private
FReceived: TFlag;
FProc: TMsgProc;
FSubscription: TMessageSubscriptionId;
FMsgClass: TClass;
procedure DoMsg(const Sender: TObject; const M: TMessage);
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
constructor Create(AOwner: TComponent; const AMsgClass: TClass; const ASignal: TSignal; const AProc: TMsgProc); reintroduce;
destructor Destroy; override;
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);
procedure ProcessSignal(const Signal: TSignal; const Proc: TMsgProc); overload;
procedure ProcessSignal(const Signal: TSignal; const Proc: TProc); overload;
end;
implementation
uses
{$IFDEF FRAMEWORK_FMX}
FMX.Types
{$ELSE}
VCL.Types // to be checked
{$IFEND}
;
FMX.Types;
{ 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
inherited Create(AOwner);
FMsgClass := AMsgClass;
FSignal := ASignal;
FProc := AProc;
FReceived := TFlag.CreateObserver(ASignal);
FSubscription := TMessageManager.DefaultManager.SubscribeToMessage(FMsgClass, DoMsg);
FIdx := 0;
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;
destructor TComponentValidation.Destroy;
destructor TSignalComponentHelper.TSignalSubscription.Destroy;
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;
end;
procedure TComponentValidation.DoMsg(const Sender: TObject; const M: TMessage);
class procedure TSignalComponentHelper.TSignalSubscription.HandleSignals(Timeout: Int64);
begin
if FReceived.Reset then
FProc(Sender, M);
if AtomicExchange(FQueued, 0) = 0 then
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;
procedure TComponentValidationHelper.AddIdleHandler(const Signal: TSignal; const Proc: TProc);
function TSignalComponentHelper.TSignalSubscription.Notify: Boolean;
begin
var capProc := Proc;
AddMsgHandler(TIdleMessage, Signal, procedure(const Sender: TObject; const M: TMessage) begin capProc(); end);
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 TComponentValidationHelper.AddMsgHandler(const MsgClass: TClass; const Signal: TSignal; const Proc: TMsgProc);
procedure TSignalComponentHelper.ProcessSignal(const Signal: TSignal; const Proc: TProc);
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.
-6
View File
@@ -18,7 +18,6 @@ type
FLookback: Int64;
function GetChanged: TSignal;
function GetValue: TDataSeries<T>;
function GetWriter: TMutable<TDataSeries<T>>.IWriter;
public
constructor Create(ALookback, AMaxChunkSize: Int64; const AStream: IDataStream<T>);
destructor Destroy; override;
@@ -63,11 +62,6 @@ begin
Result := FDataSeries;
end;
function TDataStreamProvider<T>.GetWriter: TMutable<TDataSeries<T>>.IWriter;
begin
Result := nil;
end;
{ TDataStreamProvider }
class function TDataStreamProvider.Create<T>(Lookback, MaxChunkSize: Int64; const Stream: IDataStream<T>): TMutable<TDataSeries<T>>;