DataServer 1st Version

This commit is contained in:
Michael Schimmel
2025-06-06 12:43:51 +02:00
parent f70cfe0ec1
commit 98de7176c3
9 changed files with 271 additions and 50 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ type
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
end;
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriteableMutable)
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriteable)
private
FValue: T;
FChanged: TEvent;
+3 -3
View File
@@ -18,7 +18,7 @@ type
property Value: T read GetValue;
end;
IWriteableMutable = interface(IMutable)
IWriteable = interface(IMutable)
procedure SetValue(const Value: T);
end;
@@ -42,7 +42,7 @@ type
class function Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>; overload; static;
class function CreateWriteable(const Init: T): IWriteableMutable; overload; static;
class function CreateWriteable(const Init: T): IWriteable; overload; static;
property Changed: TSignal read GetChanged;
end;
@@ -105,7 +105,7 @@ begin
Result := TMycFuncMutable<T>.Create(Changing, Proc);
end;
class function TMutable<T>.CreateWriteable(const Init: T): IWriteableMutable;
class function TMutable<T>.CreateWriteable(const Init: T): IWriteable;
begin
Result := TMycWriteableMutable<T>.Create(Init);
end;
+155
View File
@@ -0,0 +1,155 @@
unit Myc.Trade.DataPoint;
interface
uses
System.SysUtils;
type
IDataPoint<T> = interface
['{6A52697A-2868-42A9-982D-993542B7B360}']
function GetData: T;
function GetTime: TDateTime;
function GetVolume: Double;
property Time: TDateTime read GetTime;
property Volume: Double read GetVolume;
property Data: T read GetData;
end;
ITick = interface
['{B342223B-D299-4A1C-82F3-569F54417A66}']
function GetAsk: Double;
function GetBid: Double;
property Ask: Double read GetAsk;
property Bid: Double read GetBid;
end;
IOHLC = interface
['{05374E3C-731B-44FA-A23E-051F1461B78D}']
function GetOpen: Double;
function GetHigh: Double;
function GetLow: Double;
function GetClose: Double;
property Open: Double read GetOpen;
property High: Double read GetHigh;
property Low: Double read GetLow;
property Close: Double read GetClose;
end;
// Abstract base class for all data points.
TDataPoint<T> = class abstract(TInterfacedObject, IDataPoint<T>)
protected
fTime: TDateTime;
fVolume: Double;
FData: T;
function GetTime: TDateTime;
function GetVolume: Double;
function GetData: T;
public
constructor Create(Time: TDateTime; Volume: Double; const AData: T);
end;
TTick = class(TInterfacedObject, ITick)
private
fAsk: Double;
fBid: Double;
function GetAsk: Double;
function GetBid: Double;
public
constructor Create(Ask: Double; Bid: Double);
end;
TOHLC = class(TInterfacedObject, IOHLC)
private
fOpen: Double;
fHigh: Double;
fLow: Double;
fClose: Double;
function GetClose: Double;
function GetHigh: Double;
function GetLow: Double;
function GetOpen: Double;
public
constructor Create(Open, High, Low, Close: Double);
end;
implementation
{ TDataPoint }
constructor TDataPoint<T>.Create(Time: TDateTime; Volume: Double; const AData: T);
begin
fTime := Time;
fVolume := Volume;
FData := AData;
end;
function TDataPoint<T>.GetData: T;
begin
Result := FData;
end;
function TDataPoint<T>.GetTime: TDateTime;
begin
result := fTime;
end;
function TDataPoint<T>.GetVolume: Double;
begin
result := fVolume;
end;
{ TTick }
constructor TTick.Create(Ask: Double; Bid: Double);
begin
inherited Create;
fAsk := Ask;
fBid := Bid;
end;
function TTick.GetAsk: Double;
begin
result := fAsk;
end;
function TTick.GetBid: Double;
begin
result := fBid;
end;
{ TOHLC }
constructor TOHLC.Create(Open, High, Low, Close: Double);
begin
inherited Create;
fOpen := Open;
fHigh := High;
fLow := Low;
fClose := Close;
end;
function TOHLC.GetClose: Double;
begin
result := fClose;
end;
function TOHLC.GetHigh: Double;
begin
result := fHigh;
end;
function TOHLC.GetLow: Double;
begin
result := fLow;
end;
function TOHLC.GetOpen: Double;
begin
result := fOpen;
end;
end.
+7 -45
View File
@@ -1,39 +1,5 @@
unit Myc.Trade.DataSeries;
(* ------------------------------------------------------------------------------
This unit provides functions to load tick data (TTickData) from
binary files. The data originates from a C# application
and has the structure OADateTime (Double), Ask (Single), and Bid (Single).
The files follow the naming convention:
For .tab files: Symbol_Year_MM.tab OR Symbol_Year_MM.tab_zip
For .tab-live files: Symbol_Year_MM.tab-live (NEVER compressed)
(MM represents the two-digit month, e.g., 01 for January, 12 for December)
If both compressed (.tab_zip) and uncompressed (.tab) versions of a .tab
file exist, the compressed version is preferred. Files ending with _zip
are expected to be ZIP archives containing the actual data file
(e.g., Symbol_Year_MM.tab).
Loading occurs in two phases:
1. All .tab files (regular or _zip) are processed chronologically by
month and year. Unique ticks are loaded, and the timestamp of the
latest tick across all .tab files is determined (overallLastTabTickTime).
Assumes timestamps are unique across all .tab files.
2. All .tab-live files (which are never compressed), for months/years
corresponding to found .tab files, are processed. Ticks from a
.tab-live file are only integrated if their timestamp is strictly later
than overallLastTabTickTime. Assumes such ticks are globally unique.
If a .tab-live file results in no new data being added under these rules,
it is deleted after processing.
The loading function automatically detects subsequent monthly/yearly files.
Main Function:
LoadTickDataSeries - Loads a series of tick data files.
------------------------------------------------------------------------------ *)
interface
uses
@@ -293,9 +259,9 @@ begin
for var i := 0 to High(fL) do
begin
FCachedFiles.Remove(fL[i].Name);
if not IsMemoryLow then
break;
FCachedFiles.Remove(fL[i].Name);
end;
end;
@@ -319,10 +285,6 @@ begin
if FileName.EndsWith('_zip', True) then
begin
var zipFileBytes := TFile.ReadAllBytes(FileName); // Read all bytes of the .zip file
if Length(zipFileBytes) = 0 then
exit;
var capFileName := FileName;
Result :=
TFuture<TBytes>
@@ -335,7 +297,7 @@ begin
.Chain<TArray<TDataPoint>>(
function(bytes: TBytes): TArray<TDataPoint>
begin
var zipMemoryStream := TBytesStream.Create(zipFileBytes); // Create a memory stream to hold the zip file content
var zipMemoryStream := TBytesStream.Create(bytes); // Create a memory stream to hold the zip file content
try
zipMemoryStream.Position := 0;
Result := ReadCompressedData(zipMemoryStream);
@@ -349,6 +311,7 @@ begin
var capFileName := FileName;
Result :=
TFuture<TArray<TDataPoint>>.Construct(
TLatch.Enqueue(LoadGate),
function: TArray<TDataPoint>
begin
if TFile.Exists(capFileName) then
@@ -375,7 +338,6 @@ begin
begin
var cachedFile: TCachedFile;
cachedFile.Name := Filename;
var age: TDateTime;
if FileAge(Filename, cachedFile.Age, true) then
begin
cachedFile.Data := Result;
@@ -418,7 +380,7 @@ begin // Start of LoadDataSeries
var maxTabYearFound := -1;
var maxTabMonthFound := -1;
var filesToLoadPhase1 := TList<TPhase1FileEntry>.Create; // Create list with the named record type
var filesToLoad := TList<TPhase1FileEntry>.Create; // Create list with the named record type
try
while True do
begin
@@ -443,7 +405,7 @@ begin // Start of LoadDataSeries
tabFileEntryRecord.Path := actualTabFileToLoad;
tabFileEntryRecord.Year := currentTabYear;
tabFileEntryRecord.Month := currentTabMonth;
filesToLoadPhase1.Add(tabFileEntryRecord);
filesToLoad.Add(tabFileEntryRecord);
// Advance to the next month
Inc(currentTabMonth);
@@ -460,7 +422,7 @@ begin // Start of LoadDataSeries
try
var LoadGate: TLatch;
for var tabFileEntry: TPhase1FileEntry in filesToLoadPhase1 do // Iterate with the correct type
for var tabFileEntry: TPhase1FileEntry in filesToLoad do // Iterate with the correct type
begin
var data := LoadDataFile(LoadGate, tabFileEntry.Path);
@@ -490,7 +452,7 @@ begin // Start of LoadDataSeries
loadedFileList.Free;
end;
finally
filesToLoadPhase1.Free;
filesToLoad.Free;
end;
Result :=
+50
View File
@@ -0,0 +1,50 @@
unit Myc.Trade.DataServer;
interface
uses
System.SysUtils,
System.Generics.Collections,
Myc.Lazy,
Myc.Trade.DataPoint,
Myc.Trade.DataSeries;
type
IDataProvider = interface
['{B2A9996C-724B-4416-A882-595B7E58066D}']
procedure Fetch;
end;
IDataServer<T> = interface
['{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;
TFileStreamServerNode<T> = class(TDataServerNode<T>)
end;
implementation
end.
+48
View File
@@ -0,0 +1,48 @@
unit Myc.Trade.Node;
interface
uses
System.SysUtils;
type
// Base interface for all objects created by an INode factory.
ITradeObject = interface
['{B8A29A67-272E-456A-A816-A19A76E43213}']
end;
// Represents a node in the trading graph, acting as a factory for a trade object.
INode = interface
['{E879B5E4-3A4E-434A-8A4F-A9F71B0B7E1C}']
function GetCaption: string;
// Factory method to create the runtime object.
function CreateObject: ITradeObject;
property Caption: string read GetCaption;
end;
// Abstract base class for all node implementations.
TNodeBase = class abstract(TInterfacedObject, INode)
protected
fCaption: string;
function GetCaption: string;
public
constructor Create(const Caption: string);
function CreateObject: ITradeObject; virtual; abstract;
end;
implementation
{ TNodeBase }
constructor TNodeBase.Create(const Caption: string);
begin
fCaption := Caption;
end;
function TNodeBase.GetCaption: string;
begin
result := fCaption;
end;
end.
+4 -1
View File
@@ -32,7 +32,10 @@ uses
Myc.Test.Core.Atomic in '..\Src\Myc.Test.Core.Atomic.pas',
Myc.Trade.Ticker in '..\Src\Myc.Trade.Ticker.pas',
Myc.Test.Signals.Latch in '..\Src\Myc.Test.Signals.Latch.pas',
Myc.Test.Signals.Dirty in '..\Src\Myc.Test.Signals.Dirty.pas';
Myc.Test.Signals.Dirty in '..\Src\Myc.Test.Signals.Dirty.pas',
Myc.Trade.DataPoint in '..\Src\Myc.Trade.DataPoint.pas',
Myc.Trade.DataServer in '..\Src\Myc.Trade.DataServer.pas',
Myc.Trade.Node in '..\Src\Myc.Trade.Node.pas';
{ keep comment here to protect the following conditional from being removed by the IDE when adding a unit }
{$IFNDEF TESTINSIGHT}
+3
View File
@@ -130,6 +130,9 @@ $(PreBuildEvent)]]></PreBuildEvent>
<DCCReference Include="..\Src\Myc.Trade.Ticker.pas"/>
<DCCReference Include="..\Src\Myc.Test.Signals.Latch.pas"/>
<DCCReference Include="..\Src\Myc.Test.Signals.Dirty.pas"/>
<DCCReference Include="..\Src\Myc.Trade.DataPoint.pas"/>
<DCCReference Include="..\Src\Myc.Trade.DataServer.pas"/>
<DCCReference Include="..\Src\Myc.Trade.Node.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
BIN
View File
Binary file not shown.