Files
MycLib/Test/Demo.Finance.pas
T
2026-01-04 15:07:02 +01:00

195 lines
5.6 KiB
ObjectPascal

unit Demo.Finance;
interface
uses
System.SysUtils,
System.Math,
System.DateUtils,
// Framework
Myc.Data.Value,
Myc.Data.Scalar,
Myc.Data.Keyword,
Myc.Data.Stream, // IStream
Myc.Data.Stream.Pipes, // TRootStream
Myc.Ast.Types,
Myc.Ast.Scope,
Myc.Ast;
type
IFinanceSimulator = interface
procedure CreateOHLCSeries(const Scope: IExecutionScope; const VarName: string);
procedure AddRandomCandle(const Scope: IExecutionScope; const VarName: string);
end;
TFinanceSimulator = class(TInterfacedObject, IFinanceSimulator)
private
FLastClose: Double;
FLastTime: TDateTime;
kTime, kOpen, kHigh, kLow, kClose, kVol: IKeyword;
procedure InitKeywords;
public
constructor Create;
procedure CreateOHLCSeries(const Scope: IExecutionScope; const VarName: string);
procedure AddRandomCandle(const Scope: IExecutionScope; const VarName: string);
end;
implementation
{ TFinanceSimulator }
constructor TFinanceSimulator.Create;
begin
inherited;
FLastClose := 150.00;
FLastTime := Now;
end;
procedure TFinanceSimulator.InitKeywords;
begin
if kTime = nil then
begin
kTime := TKeywordRegistry.Intern('Time');
kOpen := TKeywordRegistry.Intern('Open');
kHigh := TKeywordRegistry.Intern('High');
kLow := TKeywordRegistry.Intern('Low');
kClose := TKeywordRegistry.Intern('Close');
kVol := TKeywordRegistry.Intern('Vol');
end;
end;
procedure TFinanceSimulator.CreateOHLCSeries(const Scope: IExecutionScope; const VarName: string);
var
Fields: TArray<TScalarRecordField>;
Def: IScalarRecordDefinition;
Stream: TRootStream;
StaticType: IStaticType;
begin
InitKeywords;
SetLength(Fields, 6);
Fields[0] := TScalarRecordField.Create(kTime, TScalar.TKind.DateTime);
Fields[1] := TScalarRecordField.Create(kOpen, TScalar.TKind.Float);
Fields[2] := TScalarRecordField.Create(kHigh, TScalar.TKind.Float);
Fields[3] := TScalarRecordField.Create(kLow, TScalar.TKind.Float);
Fields[4] := TScalarRecordField.Create(kClose, TScalar.TKind.Float);
Fields[5] := TScalarRecordField.Create(kVol, TScalar.TKind.Ordinal);
Def := TKeywordMappingRegistry<TScalar.TKind>.Intern(Fields);
// Wir erzeugen einen aktiven RootStream
Stream := TRootStream.Create(Def);
// Für den Compiler ist es eine RecordSeries (Typ), zur Laufzeit ein Stream (Wert)
StaticType := TTypes.CreateRecordSeries(Def);
Scope.Define(VarName, TDataValue.FromStream(Stream), StaticType);
end;
procedure TFinanceSimulator.AddRandomCandle(const Scope: IExecutionScope; const VarName: string);
var
Addr: TResolvedAddress;
Val: TDataValue;
Stream: TRootStream;
Def: IScalarRecordDefinition;
RowData: TArray<TScalar.TValue>;
i: Integer;
Key: IKeyword;
O, H, L, C: Double;
V: Int64;
begin
InitKeywords;
Addr := Scope.Resolve(VarName);
if Addr.Kind = akUnresolved then
raise Exception.CreateFmt('Variable "%s" not found.', [VarName]);
Val := Scope[Addr];
// Wir benötigen zwingend einen Stream für sim-tick, um Events zu feuern
if Val.Kind <> vkStream then
raise Exception.CreateFmt('Variable "%s" is not a Stream.', [VarName]);
// Sicherer Cast auf TRootStream
if not (Val.AsStream is TRootStream) then
raise Exception.CreateFmt('Variable "%s" is a Stream, but not a TRootStream (cannot push manually).', [VarName]);
Stream := TRootStream(Val.AsStream);
Def := Stream.Series.Def;
// --- Simulation ---
FLastTime := IncMinute(FLastTime, 5);
O := FLastClose;
C := O + (Random - 0.5) * 2.0;
H := Max(O, C) + Random * 0.5;
L := Min(O, C) - Random * 0.5;
V := Random(1000) + 100;
FLastClose := C;
// ------------------
SetLength(RowData, Def.Count);
for i := 0 to Def.Count - 1 do
begin
Key := Def.Keywords[i];
if Key = kTime then
RowData[i] := FLastTime
else if Key = kOpen then
RowData[i] := O
else if Key = kHigh then
RowData[i] := H
else if Key = kLow then
RowData[i] := L
else if Key = kClose then
RowData[i] := C
else if Key = kVol then
RowData[i] := V;
end;
// Push triggert das Notify-Event für alle angeschlossenen Pipes
Stream.Push(RowData);
end;
procedure RegisterFinanceLibrary(const Scope: IExecutionScope);
var
Sim: IFinanceSimulator;
Sig: IStaticType;
begin
Sim := TFinanceSimulator.Create;
Sig := TTypes.CreateMethod([TTypes.Text], TTypes.Void);
Scope.Define(
'sim-create',
function(const Args: TArray<TDataValue>): TDataValue
begin
if Length(Args) <> 1 then
raise Exception.Create('sim-create(Name) expects 1 argument');
Sim.CreateOHLCSeries(Scope, Args[0].AsText);
Result := TDataValue.Void;
end,
Sig
);
Scope.Define(
'sim-tick',
function(const Args: TArray<TDataValue>): TDataValue
begin
if Length(Args) <> 1 then
raise Exception.Create('sim-tick(Name) expects 1 argument');
Sim.AddRandomCandle(Scope, Args[0].AsText);
Result := TDataValue.Void;
end,
Sig
);
// Some Demo-Stream
Sim.CreateOHLCSeries(Scope, 'btc');
end;
initialization
TAst.RegisterLibrary(RegisterFinanceLibrary);
end.