031b99acc8
Bugfix in DataSeries
197 lines
6.1 KiB
ObjectPascal
197 lines
6.1 KiB
ObjectPascal
unit Myc.Trade.DataServer;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils,
|
|
System.Generics.Collections,
|
|
Myc.Lazy,
|
|
Myc.Trade.DataPoint,
|
|
Myc.Trade.DataSeries,
|
|
Myc.Signals, // Added for TLatch
|
|
Myc.Futures; // Added for TFuture
|
|
|
|
type
|
|
IDataProvider = interface
|
|
['{B2A9996C-724B-4416-A882-595B7E58066D}']
|
|
procedure Fetch;
|
|
end;
|
|
|
|
IDataServer<T> = interface(IDataProvider)
|
|
['{A6E246A2-E84E-49AB-A63E-333E561E488C}']
|
|
function GetData: TMutable<TArray<T>>;
|
|
function GetIsLiveData: TMutable<Boolean>;
|
|
property Data: TMutable<TArray<T>> read GetData;
|
|
property IsLiveData: TMutable<Boolean> read GetIsLiveData;
|
|
end;
|
|
|
|
TDataServer<T> = class(TInterfacedObject, IDataServer<T>, IDataProvider)
|
|
protected
|
|
function GetData: TMutable<TArray<T>>; virtual; abstract;
|
|
function GetIsLiveData: TMutable<Boolean>; virtual; abstract;
|
|
procedure Fetch; virtual; abstract;
|
|
end;
|
|
|
|
IDataServerNode<T> = interface
|
|
['{DA3531F1-158E-4A63-8E5A-34089C537B1B}']
|
|
function CreateDataServer: IDataServer<T>;
|
|
end;
|
|
|
|
TDataServerNode<T> = class(TInterfacedObject, IDataServerNode<T>)
|
|
public
|
|
// Factory method to create the actual data server instance.
|
|
function CreateDataServer: IDataServer<T>; virtual; abstract;
|
|
end;
|
|
|
|
TTABFileServer = class(TDataServer<ITickData>)
|
|
private
|
|
FFilename: String;
|
|
// The maximum number of data points to be produced per Fetch call.
|
|
FMaxFetch: Integer;
|
|
FHasNewData: TEvent;
|
|
FNeedNewChunk: Boolean;
|
|
FIsLiveData: TMutable<Boolean>.IWriteable;
|
|
|
|
// Internal state
|
|
FCurrentFileName: string;
|
|
FCurrentData: TFuture<TArray<TAskBid.TTick>>;
|
|
FNextFileName: string;
|
|
FNextData: TFuture<TArray<TAskBid.TTick>>;
|
|
FData: TMutable<TArray<ITickData>>;
|
|
FCurrPosInFile: Int64;
|
|
FCurrChunk: TArray<ITickData>;
|
|
FLoadGate: TLatch;
|
|
FCurrentIdx: Int64;
|
|
FLastTimeStamp: TDateTime;
|
|
|
|
function ConvertTicks(var Idx: Int64; const Ticks: TArray<TAskBid.TTick>): TArray<ITickData>;
|
|
function UpdateChunk: TArray<ITickData>;
|
|
protected
|
|
procedure Fetch; override;
|
|
function GetData: TMutable<TArray<ITickData>>; override;
|
|
function GetIsLiveData: TMutable<Boolean>; override;
|
|
public
|
|
constructor Create(const AFilename: String; AMaxFetch: Integer);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.IOUtils;
|
|
|
|
{ TTABFileServer<T> }
|
|
|
|
constructor TTABFileServer.Create(const AFilename: String; AMaxFetch: Integer);
|
|
begin
|
|
inherited Create;
|
|
FFilename := AFilename;
|
|
FMaxFetch := AMaxFetch;
|
|
|
|
FNextData := TFuture<TArray<TAskBid.TTick>>.Null;
|
|
FNeedNewChunk := true;
|
|
FIsLiveData := TMutable<Boolean>.CreateWriteable(false);
|
|
|
|
FHasNewData := TEvent.CreateEvent;
|
|
FData := TMutable<TArray<ITickData>>.Construct(FHasNewData.Signal, function: TArray<ITickData> begin Result := UpdateChunk; end);
|
|
end;
|
|
|
|
function TTABFileServer.ConvertTicks(var Idx: Int64; const Ticks: TArray<TAskBid.TTick>): TArray<ITickData>;
|
|
begin
|
|
SetLength(Result, Length(Ticks));
|
|
for var i := 0 to High(Ticks) do
|
|
begin
|
|
Result[i] := TDataPoint<ITick>.Create(Idx, Ticks[i].OADateTime, 1, TTick.Create(Ticks[i].Data.Ask, Ticks[i].Data.Bid));
|
|
inc(Idx);
|
|
end;
|
|
end;
|
|
|
|
procedure TTABFileServer.Fetch;
|
|
begin
|
|
|
|
// Check if the source filename has changed.
|
|
if (FFilename <> FCurrentFileName) and (FFilename <> '') then
|
|
begin
|
|
// Filename has changed, initialize by loading the new file.
|
|
FCurrentFileName := FFilename;
|
|
FCurrentData := TAskBid.LoadDataFile(TLatch.Enqueue(FLoadGate), FCurrentFileName);
|
|
FCurrPosInFile := 0;
|
|
FCurrentIdx := 0;
|
|
FIsLiveData.SetValue(false);
|
|
FLastTimeStamp := 0;
|
|
FFilename := '';
|
|
|
|
FNextFileName := FindNextDataFile(FCurrentFileName);
|
|
if FNextFileName <> '' then
|
|
FNextData := TAskBid.LoadDataFile(TLatch.Enqueue(FLoadGate), FNextFileName);
|
|
end;
|
|
|
|
FHasNewData.Notify;
|
|
FNeedNewChunk := true;
|
|
FCurrChunk := nil;
|
|
end;
|
|
|
|
function TTABFileServer.GetData: TMutable<TArray<ITickData>>;
|
|
begin
|
|
Result := FData;
|
|
end;
|
|
|
|
function TTABFileServer.GetIsLiveData: TMutable<Boolean>;
|
|
begin
|
|
Result := FIsLiveData;
|
|
end;
|
|
|
|
function TTABFileServer.UpdateChunk: TArray<ITickData>;
|
|
begin
|
|
if not FNeedNewChunk then
|
|
exit(FCurrChunk);
|
|
FNeedNewChunk := false;
|
|
|
|
if FCurrentData.Done.IsSet then
|
|
begin
|
|
SetLength(FCurrChunk, FMaxFetch);
|
|
var n := 0;
|
|
while n < FMaxFetch do
|
|
begin
|
|
if FCurrPosInFile >= Length(FCurrentData.Value) then
|
|
begin
|
|
// Next File!
|
|
FCurrentData := FNextData;
|
|
FCurrentFileName := FNextFileName;
|
|
FCurrPosInFile := 0;
|
|
|
|
FNextFileName := '';
|
|
FNextData := TFuture<TArray<TAskBid.TTick>>.Null;
|
|
|
|
if FCurrentFileName <> '' then
|
|
begin
|
|
FNextFileName := FindNextDataFile(FCurrentFileName);
|
|
if FNextFileName <> '' then
|
|
FNextData := TAskBid.LoadDataFile(TLatch.Enqueue(FLoadGate), FNextFileName);
|
|
end
|
|
else
|
|
begin
|
|
// We're done with this series
|
|
FIsLiveData.SetValue(true);
|
|
end;
|
|
|
|
SetLength(FCurrChunk, n);
|
|
break;
|
|
end;
|
|
|
|
var currData := FCurrentData.Value[FCurrPosInFile];
|
|
if FLastTimeStamp < currData.OADateTime then
|
|
begin
|
|
FLastTimeStamp := currData.OADateTime;
|
|
FCurrChunk[n] :=
|
|
TDataPoint<ITick>.Create(FCurrentIdx, FLastTimeStamp, 1, TTick.Create(currData.Data.Ask, currData.Data.Bid));
|
|
inc(FCurrentIdx);
|
|
inc(n);
|
|
end;
|
|
inc(FCurrPosInFile);
|
|
end;
|
|
end;
|
|
Result := FCurrChunk;
|
|
end;
|
|
|
|
end.
|