DataServer 1st Version
This commit is contained in:
@@ -35,7 +35,7 @@ type
|
|||||||
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriteableMutable)
|
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriteable)
|
||||||
private
|
private
|
||||||
FValue: T;
|
FValue: T;
|
||||||
FChanged: TEvent;
|
FChanged: TEvent;
|
||||||
|
|||||||
+3
-3
@@ -18,7 +18,7 @@ type
|
|||||||
property Value: T read GetValue;
|
property Value: T read GetValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
IWriteableMutable = interface(IMutable)
|
IWriteable = interface(IMutable)
|
||||||
procedure SetValue(const Value: T);
|
procedure SetValue(const Value: T);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ type
|
|||||||
|
|
||||||
class function Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>; overload; static;
|
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;
|
property Changed: TSignal read GetChanged;
|
||||||
end;
|
end;
|
||||||
@@ -105,7 +105,7 @@ begin
|
|||||||
Result := TMycFuncMutable<T>.Create(Changing, Proc);
|
Result := TMycFuncMutable<T>.Create(Changing, Proc);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TMutable<T>.CreateWriteable(const Init: T): IWriteableMutable;
|
class function TMutable<T>.CreateWriteable(const Init: T): IWriteable;
|
||||||
begin
|
begin
|
||||||
Result := TMycWriteableMutable<T>.Create(Init);
|
Result := TMycWriteableMutable<T>.Create(Init);
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -1,39 +1,5 @@
|
|||||||
unit Myc.Trade.DataSeries;
|
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
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
@@ -293,9 +259,9 @@ begin
|
|||||||
|
|
||||||
for var i := 0 to High(fL) do
|
for var i := 0 to High(fL) do
|
||||||
begin
|
begin
|
||||||
FCachedFiles.Remove(fL[i].Name);
|
|
||||||
if not IsMemoryLow then
|
if not IsMemoryLow then
|
||||||
break;
|
break;
|
||||||
|
FCachedFiles.Remove(fL[i].Name);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -319,10 +285,6 @@ begin
|
|||||||
|
|
||||||
if FileName.EndsWith('_zip', True) then
|
if FileName.EndsWith('_zip', True) then
|
||||||
begin
|
begin
|
||||||
var zipFileBytes := TFile.ReadAllBytes(FileName); // Read all bytes of the .zip file
|
|
||||||
if Length(zipFileBytes) = 0 then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
var capFileName := FileName;
|
var capFileName := FileName;
|
||||||
Result :=
|
Result :=
|
||||||
TFuture<TBytes>
|
TFuture<TBytes>
|
||||||
@@ -335,7 +297,7 @@ begin
|
|||||||
.Chain<TArray<TDataPoint>>(
|
.Chain<TArray<TDataPoint>>(
|
||||||
function(bytes: TBytes): TArray<TDataPoint>
|
function(bytes: TBytes): TArray<TDataPoint>
|
||||||
begin
|
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
|
try
|
||||||
zipMemoryStream.Position := 0;
|
zipMemoryStream.Position := 0;
|
||||||
Result := ReadCompressedData(zipMemoryStream);
|
Result := ReadCompressedData(zipMemoryStream);
|
||||||
@@ -349,6 +311,7 @@ begin
|
|||||||
var capFileName := FileName;
|
var capFileName := FileName;
|
||||||
Result :=
|
Result :=
|
||||||
TFuture<TArray<TDataPoint>>.Construct(
|
TFuture<TArray<TDataPoint>>.Construct(
|
||||||
|
TLatch.Enqueue(LoadGate),
|
||||||
function: TArray<TDataPoint>
|
function: TArray<TDataPoint>
|
||||||
begin
|
begin
|
||||||
if TFile.Exists(capFileName) then
|
if TFile.Exists(capFileName) then
|
||||||
@@ -375,7 +338,6 @@ begin
|
|||||||
begin
|
begin
|
||||||
var cachedFile: TCachedFile;
|
var cachedFile: TCachedFile;
|
||||||
cachedFile.Name := Filename;
|
cachedFile.Name := Filename;
|
||||||
var age: TDateTime;
|
|
||||||
if FileAge(Filename, cachedFile.Age, true) then
|
if FileAge(Filename, cachedFile.Age, true) then
|
||||||
begin
|
begin
|
||||||
cachedFile.Data := Result;
|
cachedFile.Data := Result;
|
||||||
@@ -418,7 +380,7 @@ begin // Start of LoadDataSeries
|
|||||||
var maxTabYearFound := -1;
|
var maxTabYearFound := -1;
|
||||||
var maxTabMonthFound := -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
|
try
|
||||||
while True do
|
while True do
|
||||||
begin
|
begin
|
||||||
@@ -443,7 +405,7 @@ begin // Start of LoadDataSeries
|
|||||||
tabFileEntryRecord.Path := actualTabFileToLoad;
|
tabFileEntryRecord.Path := actualTabFileToLoad;
|
||||||
tabFileEntryRecord.Year := currentTabYear;
|
tabFileEntryRecord.Year := currentTabYear;
|
||||||
tabFileEntryRecord.Month := currentTabMonth;
|
tabFileEntryRecord.Month := currentTabMonth;
|
||||||
filesToLoadPhase1.Add(tabFileEntryRecord);
|
filesToLoad.Add(tabFileEntryRecord);
|
||||||
|
|
||||||
// Advance to the next month
|
// Advance to the next month
|
||||||
Inc(currentTabMonth);
|
Inc(currentTabMonth);
|
||||||
@@ -460,7 +422,7 @@ begin // Start of LoadDataSeries
|
|||||||
try
|
try
|
||||||
var LoadGate: TLatch;
|
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
|
begin
|
||||||
var data := LoadDataFile(LoadGate, tabFileEntry.Path);
|
var data := LoadDataFile(LoadGate, tabFileEntry.Path);
|
||||||
|
|
||||||
@@ -490,7 +452,7 @@ begin // Start of LoadDataSeries
|
|||||||
loadedFileList.Free;
|
loadedFileList.Free;
|
||||||
end;
|
end;
|
||||||
finally
|
finally
|
||||||
filesToLoadPhase1.Free;
|
filesToLoad.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result :=
|
Result :=
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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
@@ -32,7 +32,10 @@ uses
|
|||||||
Myc.Test.Core.Atomic in '..\Src\Myc.Test.Core.Atomic.pas',
|
Myc.Test.Core.Atomic in '..\Src\Myc.Test.Core.Atomic.pas',
|
||||||
Myc.Trade.Ticker in '..\Src\Myc.Trade.Ticker.pas',
|
Myc.Trade.Ticker in '..\Src\Myc.Trade.Ticker.pas',
|
||||||
Myc.Test.Signals.Latch in '..\Src\Myc.Test.Signals.Latch.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 }
|
{ keep comment here to protect the following conditional from being removed by the IDE when adding a unit }
|
||||||
{$IFNDEF TESTINSIGHT}
|
{$IFNDEF TESTINSIGHT}
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ $(PreBuildEvent)]]></PreBuildEvent>
|
|||||||
<DCCReference Include="..\Src\Myc.Trade.Ticker.pas"/>
|
<DCCReference Include="..\Src\Myc.Trade.Ticker.pas"/>
|
||||||
<DCCReference Include="..\Src\Myc.Test.Signals.Latch.pas"/>
|
<DCCReference Include="..\Src\Myc.Test.Signals.Latch.pas"/>
|
||||||
<DCCReference Include="..\Src\Myc.Test.Signals.Dirty.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">
|
<BuildConfiguration Include="Base">
|
||||||
<Key>Base</Key>
|
<Key>Base</Key>
|
||||||
</BuildConfiguration>
|
</BuildConfiguration>
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user