diff --git a/AuraTrader/AuraTrader.dproj b/AuraTrader/AuraTrader.dproj index e0cbb98..78cf432 100644 --- a/AuraTrader/AuraTrader.dproj +++ b/AuraTrader/AuraTrader.dproj @@ -4,7 +4,7 @@ 20.3 FMX True - Debug + Release Win64 AuraTrader 3 diff --git a/AuraTrader/MainForm.fmx b/AuraTrader/MainForm.fmx index 2606846..cbf3bbe 100644 --- a/AuraTrader/MainForm.fmx +++ b/AuraTrader/MainForm.fmx @@ -64,21 +64,21 @@ object Form1: TForm1 JustifyLastLine = Left FlowDirection = LeftToRight end - object Button1: TButton + object ChartButton: TButton Position.X = 712.00000000000000000 Position.Y = 72.00000000000000000 TabOrder = 6 - Text = 'Button1' + Text = 'ChartButton' TextSettings.Trimming = None - OnClick = Button1Click + OnClick = ChartButtonClick end - object Button2: TButton + object StopButton: TButton Position.X = 720.00000000000000000 Position.Y = 96.00000000000000000 TabOrder = 8 - Text = 'Button2' + Text = 'StopButton' TextSettings.Trimming = None - OnClick = Button2Click + OnClick = StopButtonClick end end object LogMemo: TMemo diff --git a/AuraTrader/MainForm.pas b/AuraTrader/MainForm.pas index 5199b31..dd6fd7b 100644 --- a/AuraTrader/MainForm.pas +++ b/AuraTrader/MainForm.pas @@ -45,14 +45,14 @@ type SymbolsComboBox: TComboBox; LoadButton: TButton; FlowLayout: TFlowLayout; - Button1: TButton; - Button2: TButton; + ChartButton: TButton; + StopButton: TButton; procedure RandomButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure LoadButtonClick(Sender: TObject); - procedure Button1Click(Sender: TObject); - procedure Button2Click(Sender: TObject); + procedure ChartButtonClick(Sender: TObject); + procedure StopButtonClick(Sender: TObject); private const cnt = 20; @@ -83,84 +83,7 @@ implementation {$R *.fmx} -procedure TForm1.Button1Click(Sender: TObject); -begin - if SymbolsComboBox.ItemIndex < 0 then - exit; - - var curr := TWriteable>.CreateProtected; - var Symbol := FSymbols.WaitFor[SymbolsComboBox.ItemIndex]; - - var currPathData := TWriteable>.CreateProtected; - - LogMemo.AddIdleHandler(curr.Changed, procedure begin LogMemo.Lines.Add(Symbol + ': ' + curr.Value.TotalCount.ToString); end); - - var arr := TDataSeries.CreateArray(3000); - var done := - FServer.ProcessData( - Symbol, - FTerminate.Signal, - procedure(const Values: TArray>; const Terminated: TState) - begin - arr.Add(Values); - curr.Value := arr.Copy; - - var PathData := TPathData.Create; - var Prices := arr; - if Prices.Count > 0 then - begin - PathData.MoveTo(PointF(Prices.Count - 1, Prices[0].Data.Ask)); - for var i := 1 to Prices.Count - 1 do - PathData.LineTo(PointF(Prices.Count - i - 1, Prices[i].Data.Ask)); - end; - - currPathData.Value := TObjectRef.Create(PathData); - end - ); - - FLoadDone := TState.All([FLoadDone, done]); - - Path1.AddIdleHandler( - currPathData.Changed, - procedure - begin - if currPathData.Value<>nil then - begin - Path1.Data.Assign( currPathData.Value.Obj ); - currPathData.Value := nil; - end; - end ); - - -(* - FLoadDone := TState.All([FLoadDone, done]); - - Path1.AddIdleHandler( - curr.Changed, - procedure - begin - var Prices := curr.Value; - - if Prices.Count > 0 then - begin - Path1.BeginUpdate; - try - Path1.Data.Clear; - Path1.Data.MoveTo(PointF(Path1.Width - 1, Prices[0].Data.Ask)); - for var i := 1 to Prices.Count - 1 do - begin - Path1.Data.LineTo(PointF(Path1.Width - i - 1, Prices[i].Data.Ask)); - end; - finally - Path1.EndUpdate; - end; - end; - end - ); -*) -end; - -procedure TForm1.Button2Click(Sender: TObject); +procedure TForm1.StopButtonClick(Sender: TObject); begin FTerminate.Notify; end; @@ -174,6 +97,8 @@ begin FServer := TAuraTABFileServer.Create('\\COFFEE\TickData\Pepperstone'); SymbolsComboBox.Enabled := false; + ChartButton.Enabled := false; + LoadButton.Enabled := false; FSymbols := FServer.EnumerateSymbols; @@ -190,6 +115,8 @@ begin if SymbolsComboBox.ItemIndex < 0 then SymbolsComboBox.ItemIndex := SymbolsComboBox.Items.IndexOf('GER40'); SymbolsComboBox.Enabled := true; + ChartButton.Enabled := true; + LoadButton.Enabled := true; end; finally SymbolsComboBox.EndUpdate; @@ -200,6 +127,7 @@ end; procedure TForm1.FormDestroy(Sender: TObject); begin + FSymbols.WaitFor; FTerminate.Notify; TaskManager.WaitFor(FLoadDone); Application.OnIdle := nil; @@ -279,4 +207,83 @@ begin Proc(FRandom.Count - 1); end; +procedure TForm1.ChartButtonClick(Sender: TObject); +begin + if SymbolsComboBox.ItemIndex < 0 then + exit; + + var Symbol := FSymbols.WaitFor[SymbolsComboBox.ItemIndex]; + + var currPathData := TMutable>.CreateProtected; + var currLog := TMutable.CreateProtected; + + const width = 1000; + const incsize = 100; + begin + var arr := TDataSeries.CreateWriteable(width); + var done := + FServer.ProcessData( + Symbol, + FTerminate.Signal, + procedure(const Values: TArray>; const Terminated: TState) + begin + arr.Add(Values); + currLog.Value := arr.TotalCount.ToString; + + var PathData := TPathData.Create; + var Prices := arr; + if Prices.Count > 0 then + begin + PathData.MoveTo(PointF(Prices.Count - 1, Prices[0].Data.Ask)); + for var i := 1 to Prices.Count - 1 do + PathData.LineTo(PointF(Prices.Count - i - 1, Prices[i].Data.Ask)); + end; + + currPathData.Value := TObjectRef.Create(PathData); + end + ); + FLoadDone := TState.All([FLoadDone, done]); + end; + + LogMemo.AddIdleHandler(currLog.Changed, procedure begin LogMemo.Lines.Add(Symbol + ': ' + currLog.Value); end); + + Path1.AddIdleHandler( + currPathData.Changed, + procedure + begin + if currPathData.Value<>nil then + begin + Path1.Data.Assign( currPathData.Value.Obj ); + end; + end ); + + +(* + FLoadDone := TState.All([FLoadDone, done]); + + Path1.AddIdleHandler( + curr.Changed, + procedure + begin + var Prices := curr.Value; + + if Prices.Count > 0 then + begin + Path1.BeginUpdate; + try + Path1.Data.Clear; + Path1.Data.MoveTo(PointF(Path1.Width - 1, Prices[0].Data.Ask)); + for var i := 1 to Prices.Count - 1 do + begin + Path1.Data.LineTo(PointF(Path1.Width - i - 1, Prices[i].Data.Ask)); + end; + finally + Path1.EndUpdate; + end; + end; + end + ); +*) +end; + end. diff --git a/Src/Myc.Core.Lazy.pas b/Src/Myc.Core.Lazy.pas index e93d468..8706a5e 100644 --- a/Src/Myc.Core.Lazy.pas +++ b/Src/Myc.Core.Lazy.pas @@ -10,9 +10,10 @@ uses type TMycNullMutable = class(TInterfacedObject, TMutable.IMutable) - protected + private function GetChanged: TSignal; function GetValue: T; + function GetWriter: TMutable.IWriter; end; TMycMutableBase = class(TInterfacedObject, TMutable.IMutable) @@ -20,6 +21,7 @@ type FChanged: TEvent; FChangeState: TSignal.TSubscription; function GetChanged: TSignal; + function GetWriter: TMutable.IWriter; protected function GetValue: T; virtual; abstract; public @@ -36,13 +38,14 @@ type constructor Create(const AChanged: TSignal; const AProc: TFunc); end; - TMycWriteableMutable = class(TInterfacedObject, TMutable.IMutable, TWriteable.IWriteable) + TMycWriteableMutable = class(TInterfacedObject, TMutable.IMutable, TMutable.IWriter) private FValue: T; FChanged: TEvent; protected function GetChanged: TSignal; function GetValue: T; + function GetWriter: TMutable.IWriter; public constructor Create(const AValue: T); procedure SetValue(const Value: T); @@ -77,7 +80,7 @@ type constructor Create(const AChanged: TSignal.ISignal; const AProc: TFunc); end; - TMycProtectedWriteableMutable = class(TInterfacedObject, TMutable.IMutable, TWriteable.IWriteable) + TMycProtectedWriteableMutable = class(TInterfacedObject, TMutable.IMutable, TMutable.IWriter) private FValue: T; FChanged: TEvent; @@ -85,6 +88,7 @@ type protected function GetChanged: TSignal; function GetValue: T; + function GetWriter: TMutable.IWriter; procedure Lock; inline; procedure Release; inline; public @@ -106,6 +110,11 @@ begin Result := Default(T); end; +function TMycNullMutable.GetWriter: TMutable.IWriter; +begin + Result := nil; +end; + { TMycMutableBase } constructor TMycMutableBase.Create(const AChanged: TSignal); @@ -126,6 +135,11 @@ begin Result := FChanged.Signal; end; +function TMycMutableBase.GetWriter: TMutable.IWriter; +begin + Result := nil; +end; + { TMycWriteableMutable } constructor TMycWriteableMutable.Create(const AValue: T); @@ -145,6 +159,11 @@ begin Result := FValue; end; +function TMycWriteableMutable.GetWriter: TMutable.IWriter; +begin + Result := Self; +end; + procedure TMycWriteableMutable.SetValue(const Value: T); begin FValue := Value; @@ -244,6 +263,11 @@ begin end; end; +function TMycProtectedWriteableMutable.GetWriter: TMutable.IWriter; +begin + Result := Self; +end; + procedure TMycProtectedWriteableMutable.Lock; begin while AtomicExchange(FLock, 1) = 1 do diff --git a/Src/Myc.Lazy.pas b/Src/Myc.Lazy.pas index 55a2cfe..862c954 100644 --- a/Src/Myc.Lazy.pas +++ b/Src/Myc.Lazy.pas @@ -9,13 +9,19 @@ uses type TMutable = record type + IWriter = interface + procedure SetValue(const Value: 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'} @@ -27,7 +33,9 @@ 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); @@ -38,35 +46,12 @@ type class property Null: IMutable read FNull; class function Construct(const Changing: TSignal; const Proc: TFunc): TMutable; overload; static; - - property Value: T read GetValue; - property Changed: TSignal read GetChanged; - end; - - TWriteable = record - type - IWriteable = interface(TMutable.IMutable) - procedure SetValue(const Value: T); - property Value: T read GetValue write SetValue; - end; - - private - FWriteable: IWriteable; - function GetChanged: TSignal; inline; - function GetValue: T; inline; - procedure SetValue(const Value: T); inline; - - public - constructor Create(const AWriteable: IWriteable); - - class function CreateProtected: TWriteable; overload; static; - class function CreateWriteable: TWriteable; overload; static; - - class operator Implicit(const A: IWriteable): TWriteable; overload; - class operator Implicit(const A: TWriteable): IWriteable; overload; + class function CreateProtected: TMutable; overload; static; + class function CreateWriteable: TMutable; overload; static; property Value: T read GetValue write SetValue; property Changed: TSignal read GetChanged; + property IsWriteable: Boolean read GetIsWriteable; end; TLazy = record @@ -127,16 +112,37 @@ begin Result := TMycFuncMutable.Create(Changing, Proc); end; +class function TMutable.CreateProtected: TMutable; +begin + Result := TMycProtectedWriteableMutable.Create(Default(T)); +end; + +class function TMutable.CreateWriteable: TMutable; +begin + Result := TMycWriteableMutable.Create(Default(T)); +end; + function TMutable.GetChanged: TSignal; begin Result := FMutable.Changed; end; +function TMutable.GetIsWriteable: Boolean; +begin + Result := Assigned( FMutable.Writer ); +end; + function TMutable.GetValue: T; begin Result := FMutable.Value; end; +procedure TMutable.SetValue(const Value: T); +begin + Assert( IsWriteable ); + FMutable.Writer.SetValue( Value ); +end; + class operator TMutable.Implicit(const A: TMutable): IMutable; begin Result := A.FMutable; @@ -201,46 +207,4 @@ begin Dest.FLazy := FNull; end; -{ TWriteable } - -constructor TWriteable.Create(const AWriteable: IWriteable); -begin - FWriteable := AWriteable; -end; - -class function TWriteable.CreateProtected: TWriteable; -begin - Result := TMycProtectedWriteableMutable.Create(Default(T)); -end; - -class function TWriteable.CreateWriteable: TWriteable; -begin - Result := TMycWriteableMutable.Create(Default(T)); -end; - -function TWriteable.GetChanged: TSignal; -begin - Result := FWriteable.Changed; -end; - -function TWriteable.GetValue: T; -begin - Result := FWriteable.Value; -end; - -procedure TWriteable.SetValue(const Value: T); -begin - FWriteable.Value := Value; -end; - -class operator TWriteable.Implicit(const A: IWriteable): TWriteable; -begin - Result.Create(A); -end; - -class operator TWriteable.Implicit(const A: TWriteable): IWriteable; -begin - Result := A.FWriteable; -end; - end. diff --git a/Src/Myc.Trade.Core.DataPoint.pas b/Src/Myc.Trade.Core.DataPoint.pas index 848e499..0c590ae 100644 --- a/Src/Myc.Trade.Core.DataPoint.pas +++ b/Src/Myc.Trade.Core.DataPoint.pas @@ -7,7 +7,7 @@ uses type // The implementation class for IDataSeries. - TDataArray = class(TInterfacedObject, IDataSeries, IDataArray) + TDataArray = class(TInterfacedObject, IDataSeries, IDataSeriesWriter) const ChunkSize = 1024; type @@ -15,7 +15,7 @@ type private FChunks: TArray; FCount: Int64; - FMaxLookback: Int64; + FLookback: Int64; FTotalCount: Int64; function LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; inline; @@ -25,15 +25,16 @@ type // IDataSeries implementation function GetCount: Int64; function GetItems(Idx: Int64): TDataPoint; - function GetMaxLookback: Int64; + function GetLookback: Int64; function GetTotalCount: Int64; + function GetWriter: IDataSeriesWriter; public - constructor Create(AMaxLookback: Int64); + constructor Create(ALookback: Int64); procedure Add(const Data: TDataPoint); overload; procedure Add(const Data: array of TDataPoint; NumToAdd: Integer = -1); overload; procedure Clear; - function Copy: IDataSeries; - property MaxLookback: Int64 read GetMaxLookback; + function Immutable: IDataSeries; + property Lookback: Int64 read GetLookback; end; // Null object implementation for IDataSeries @@ -43,12 +44,13 @@ type FNull: IDataSeries; private class constructor CreateClass; + function GetWriter: IDataSeriesWriter; public function GetCount: Int64; function GetItems(Idx: Int64): TDataPoint; function GetTotalCount: Int64; function IndexOf(TimeStamp: TDateTime): Int64; - function Copy: IDataSeries; + function Immutable: IDataSeries; class property Null: IDataSeries read FNull; end; @@ -57,7 +59,7 @@ type private FChunks: TArray.TChunk>; FCount: Int64; - FMaxLookback: Int64; + FLookback: Int64; FTotalCount: Int64; function LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; inline; @@ -66,23 +68,24 @@ type // IDataSeries implementation function GetCount: Int64; function GetItems(Idx: Int64): TDataPoint; - function GetMaxLookback: Int64; + function GetLookback: Int64; function GetTotalCount: Int64; + function GetWriter: IDataSeriesWriter; public - constructor Create(const AChunks: TArray.TChunk>; ACount, AMaxLookback, ATotalCount: Int64); - function Copy: IDataSeries; - property MaxLookback: Int64 read GetMaxLookback; + constructor Create(const AChunks: TArray.TChunk>; ACount, ALookback, ATotalCount: Int64); + function Immutable: IDataSeries; + property Lookback: Int64 read GetLookback; end; implementation { TDataArray } -constructor TDataArray.Create(AMaxLookback: Int64); +constructor TDataArray.Create(ALookback: Int64); begin inherited Create; - FMaxLookback := AMaxLookback; + FLookback := ALookback; FCount := 0; FTotalCount := 0; FChunks := nil; @@ -170,16 +173,16 @@ begin FTotalCount := 0; end; -function TDataArray.Copy: IDataSeries; +function TDataArray.Immutable: IDataSeries; begin - Result := TStaticDataSeries.Create(FChunks, FCount, FMaxLookback, FTotalCount); + Result := TStaticDataSeries.Create(FChunks, FCount, FLookback, FTotalCount); end; function TDataArray.GetCount: Int64; begin Result := FCount; - if (FMaxLookback > 0) and (Result > FMaxLookback) then - Result := FMaxLookback; + if (FLookback > 0) and (Result > FLookback) then + Result := FLookback; end; function TDataArray.GetItems(Idx: Int64): TDataPoint; @@ -191,9 +194,9 @@ begin Result := FChunks[physicalIndex div ChunkSize][physicalIndex mod ChunkSize]; end; -function TDataArray.GetMaxLookback: Int64; +function TDataArray.GetLookback: Int64; begin - Result := FMaxLookback; + Result := FLookback; end; function TDataArray.GetTotalCount: Int64; @@ -201,9 +204,14 @@ begin Result := FTotalCount; end; +function TDataArray.GetWriter: IDataSeriesWriter; +begin + Result := Self; +end; + function TDataArray.IsIndexInLimits(LogicalIndex: Int64): Boolean; begin - Result := (FMaxLookback <= 0) or (LogicalIndex < FMaxLookback); + Result := (FLookback <= 0) or (LogicalIndex < FLookback); end; function TDataArray.LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; @@ -216,12 +224,12 @@ procedure TDataArray.Trim; var itemsToRemove, chunksToRemove: Int64; begin - if (FCount = 0) or (FMaxLookback <= 0) then + if (FCount = 0) or (FLookback <= 0) then Exit; itemsToRemove := 0; - if FCount > FMaxLookback then - itemsToRemove := FCount - FMaxLookback; + if FCount > FLookback then + itemsToRemove := FCount - FLookback; if itemsToRemove <= 0 then Exit; @@ -231,7 +239,7 @@ begin if chunksToRemove > 0 then begin var itemsInFreedChunks := chunksToRemove * ChunkSize; - FChunks := System.Copy(FChunks, chunksToRemove, Length(FChunks) - chunksToRemove); + FChunks := Copy(FChunks, chunksToRemove, Length(FChunks) - chunksToRemove); FCount := FCount - itemsInFreedChunks; end; end; @@ -243,7 +251,7 @@ begin FNull := TNullDataSeries.Create; end; -function TNullDataSeries.Copy: IDataSeries; +function TNullDataSeries.Immutable: IDataSeries; begin Result := FNull; end; @@ -264,6 +272,11 @@ begin Result := 0; end; +function TNullDataSeries.GetWriter: IDataSeriesWriter; +begin + Result := nil; +end; + function TNullDataSeries.IndexOf(TimeStamp: TDateTime): Int64; begin Result := -1; @@ -271,16 +284,16 @@ end; { TStaticDataSeries } -constructor TStaticDataSeries.Create(const AChunks: TArray.TChunk>; ACount, AMaxLookback, ATotalCount: Int64); +constructor TStaticDataSeries.Create(const AChunks: TArray.TChunk>; ACount, ALookback, ATotalCount: Int64); begin inherited Create; FChunks := AChunks; FCount := ACount; - FMaxLookback := AMaxLookback; + FLookback := ALookback; FTotalCount := ATotalCount; end; -function TStaticDataSeries.Copy: IDataSeries; +function TStaticDataSeries.Immutable: IDataSeries; begin Result := Self; end; @@ -288,8 +301,8 @@ end; function TStaticDataSeries.GetCount: Int64; begin Result := FCount; - if (FMaxLookback > 0) and (Result > FMaxLookback) then - Result := FMaxLookback; + if (FLookback > 0) and (Result > FLookback) then + Result := FLookback; end; function TStaticDataSeries.GetItems(Idx: Int64): TDataPoint; @@ -301,9 +314,9 @@ begin Result := FChunks[physicalIndex div TDataArray.ChunkSize][physicalIndex mod TDataArray.ChunkSize]; end; -function TStaticDataSeries.GetMaxLookback: Int64; +function TStaticDataSeries.GetLookback: Int64; begin - Result := FMaxLookback; + Result := FLookback; end; function TStaticDataSeries.GetTotalCount: Int64; @@ -311,9 +324,14 @@ begin Result := FTotalCount; end; +function TStaticDataSeries.GetWriter: IDataSeriesWriter; +begin + Result := nil; +end; + function TStaticDataSeries.IsIndexInLimits(LogicalIndex: Int64): Boolean; begin - Result := (FMaxLookback <= 0) or (LogicalIndex < FMaxLookback); + Result := (FLookback <= 0) or (LogicalIndex < FLookback); end; function TStaticDataSeries.LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; diff --git a/Src/Myc.Trade.DataPoint.pas b/Src/Myc.Trade.DataPoint.pas index cf09b02..48ddc62 100644 --- a/Src/Myc.Trade.DataPoint.pas +++ b/Src/Myc.Trade.DataPoint.pas @@ -17,25 +17,27 @@ type constructor Create(ATime: TDateTime; const AData: T); end; + IDataSeriesWriter = interface + procedure Add(const Data: TDataPoint); overload; + procedure Add(const Data: array of TDataPoint; NumToAdd: Integer = -1); overload; + procedure Clear; + end; + // A time-ordered series of data points, optimized for chronological additions. // The most recently added element has the logical index 0. IDataSeries = interface function GetCount: Int64; function GetItems(Idx: Int64): TDataPoint; function GetTotalCount: Int64; - function Copy: IDataSeries; + function Immutable: IDataSeries; + function GetWriter: IDataSeriesWriter; property Count: Int64 read GetCount; // Accesses data points by their logical index. // Index 0 is the newest element, Index (Count - 1) is the oldest. property Items[Idx: Int64]: TDataPoint read GetItems; default; // The total number of items ever added to the series. property TotalCount: Int64 read GetTotalCount; - end; - - IDataArray = interface(IDataSeries) - procedure Add(const Data: TDataPoint); overload; - procedure Add(const Data: array of TDataPoint; NumToAdd: Integer = -1); overload; - procedure Clear; + property Writer: IDataSeriesWriter read GetWriter; end; // Interface Helper for IDataSeries. @@ -43,12 +45,11 @@ type TDataSeries = record private FDataSeries: IDataSeries; - FMaxLookback: Int64; function GetCount: Int64; function GetItems(Idx: Int64): TDataPoint; function GetData(Idx: Int64): T; + function GetIsWriteable: Boolean; function GetTime(Idx: Int64): TDateTime; - function GetMaxLookback: Int64; class function GetNull: IDataSeries; static; function GetTotalCount: Int64; public @@ -60,10 +61,14 @@ type class operator Implicit(const A: TDataSeries): IDataSeries; class operator Implicit(const A: IDataSeries): TDataSeries; - class function CreateArray(MaxLookback: Int64): IDataArray; static; + class function CreateWriteable(MaxLookback: Int64): TDataSeries; static; - // Create a guaranteed immutable version of the given series. - function Copy: TDataSeries; + // Create an immutable version of the given series. + function Immutable: TDataSeries; + + // Writing (only if IsWriteable=true) + procedure Add(const Data: array of TDataPoint; NumToAdd: Integer = -1); + procedure Clear; // Searches for a data point by its timestamp. // Returns the logical index of the matching item. @@ -75,8 +80,8 @@ type property Count: Int64 read GetCount; property Items[Idx: Int64]: TDataPoint read GetItems; default; property Data[Idx: Int64]: T read GetData; + property IsWriteable: Boolean read GetIsWriteable; property Time[Idx: Int64]: TDateTime read GetTime; - property MaxLookback: Int64 read GetMaxLookback; property TotalCount: Int64 read GetTotalCount; end; @@ -109,12 +114,24 @@ begin FDataSeries := Null; end; -function TDataSeries.Copy: TDataSeries; +procedure TDataSeries.Add(const Data: array of TDataPoint; NumToAdd: Integer = -1); begin - Result := FDataSeries.Copy; + Assert( IsWriteable ); + FDataSeries.Writer.Add( Data, NumToAdd ); end; -class function TDataSeries.CreateArray(MaxLookback: Int64): IDataArray; +procedure TDataSeries.Clear; +begin + Assert( IsWriteable ); + FDataSeries.Writer.Clear; +end; + +function TDataSeries.Immutable: TDataSeries; +begin + Result := FDataSeries.Immutable; +end; + +class function TDataSeries.CreateWriteable(MaxLookback: Int64): TDataSeries; begin Result := TDataArray.Create(MaxLookback); end; @@ -154,16 +171,16 @@ begin Result := FDataSeries[Idx].Data; end; +function TDataSeries.GetIsWriteable: Boolean; +begin + Result := Assigned( FDataSeries.Writer ); +end; + function TDataSeries.GetTime(Idx: Int64): TDateTime; begin Result := FDataSeries[Idx].Time; end; -function TDataSeries.GetMaxLookback: Int64; -begin - Result := FMaxLookback; -end; - class function TDataSeries.GetNull: IDataSeries; begin Result := TNullDataSeries.Null; diff --git a/Src/Myc.Trade.DataProvider.pas b/Src/Myc.Trade.DataProvider.pas index 4fe60dc..ccf8fcc 100644 --- a/Src/Myc.Trade.DataProvider.pas +++ b/Src/Myc.Trade.DataProvider.pas @@ -12,12 +12,13 @@ type TDataStreamProvider = class(TInterfacedObject, TMutable>.IMutable) private FStream: IDataStream; - FDataArray: IDataArray; + FDataSeries: TDataSeries; FNewDataAvailable: TFlag; FChunk: TArray>; FLookback: Int64; function GetChanged: TSignal; function GetValue: TDataSeries; + function GetWriter: TMutable>.IWriter; public constructor Create(ALookback, AMaxChunkSize: Int64; const AStream: IDataStream); destructor Destroy; override; @@ -38,7 +39,7 @@ begin SetLength(FChunk, AMaxChunkSize); FStream := AStream; FNewDataAvailable := TFlag.CreateObserver(FStream.HasData); - FDataArray := TDataSeries.CreateArray(ALookback); + FDataSeries := TDataSeries.CreateWriteable(ALookback); end; destructor TDataStreamProvider.Destroy; @@ -57,9 +58,14 @@ begin begin var cnt := FStream.GetChunk(FChunk); if cnt > 0 then - FDataArray.Add(FChunk, cnt); + FDataSeries.Add(FChunk, cnt); end; - Result := FDataArray; + Result := FDataSeries; +end; + +function TDataStreamProvider.GetWriter: TMutable>.IWriter; +begin + Result := nil; end; { TDataStreamProvider } diff --git a/Src/Myc.Trade.DataStream.pas b/Src/Myc.Trade.DataStream.pas index 21579e2..049219a 100644 --- a/Src/Myc.Trade.DataStream.pas +++ b/Src/Myc.Trade.DataStream.pas @@ -101,7 +101,8 @@ type // Used by cache to actually load an uncached file. function DoLoad(const FileName: string): TFuture>>; - function LoadFile(DataFile: TAuraDataFile; Terminated: TState; Proc: TDataProc): TState; + function ProcessFile(FileInfo: TAuraDataFile; const DataFile: TFuture>>; Terminated: TState; Proc: TDataProc): + TState; strict private class var @@ -392,27 +393,35 @@ begin Result := FindFirstFile(Symbol) - .Chain(function(const FirstFile: TAuraDataFile): TState begin Result := LoadFile(FirstFile, terminated, capProc); end); + .Chain( + function(const FirstFileInfo: TAuraDataFile): TState + begin + Result := ProcessFile(FirstFileInfo, LoadDataFile(FirstFileInfo), terminated, capProc); + end); end; function TAuraDataServer.LoadDataFile(const DataFile: TAuraDataFile): TFuture>>; begin - Result := FCachedFiles.GetOrAdd(DataFile.GetFullFileName); + if DataFile.IsValid then + Result := FCachedFiles.GetOrAdd(DataFile.GetFullFileName); end; -function TAuraDataServer.LoadFile(DataFile: TAuraDataFile; Terminated: TState; Proc: TDataProc): TState; +function TAuraDataServer.ProcessFile(FileInfo: TAuraDataFile; const DataFile: TFuture>>; Terminated: TState; Proc: + TDataProc): TState; begin - if not DataFile.IsValid or Terminated.IsSet then + if not FileInfo.IsValid or Terminated.IsSet then exit(TState.Null); - var data := FCachedFiles.GetOrAdd(DataFile.GetFullFileName); + // Read ahead the next file, while processing the current one + var nextFileInfo := FileInfo.GetNextFile; + var nextFile := LoadDataFile( nextFileInfo ); Result := - data.Chain( + DataFile.Chain( function(const Data: TArray>): TState begin Proc(Data, Terminated); - Result := LoadFile(DataFile.GetNextFile, Terminated, Proc); + Result := ProcessFile(nextFileInfo, nextFile, Terminated, Proc); end ); end;