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.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; 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.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, Stream, StaticType, 'Financial OHLC Data Stream (Time, Open, High, Low, Close, Vol).'); end; procedure TFinanceSimulator.AddRandomCandle(const Scope: IExecutionScope; const VarName: string); var Addr: TResolvedAddress; Val: TDataValue; Stream: TRootStream; Def: IScalarRecordDefinition; RowData: TArray; 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.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 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, 'Creates a new OHLC data stream with the specified name.' // UPDATED: Doc ); Scope.Define( 'sim-tick', function(const Args: TArray): 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, 'Simulates and pushes a new 5-minute candle to the specified stream.' // UPDATED: Doc ); // Some Demo-Stream (This will now also be documented in the scope) Sim.CreateOHLCSeries(Scope, 'btc'); end; initialization TAst.RegisterLibrary(RegisterFinanceLibrary); end.