From 78e89f345e6701f40384c49aefe5515bbeb3da30 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sun, 27 Jul 2025 21:58:15 +0200 Subject: [PATCH] Generic indicator factory --- AuraTrader/MainForm.pas | 28 +++++++++- AuraTrader/StrategyTest.pas | 14 ++++- Src/Myc.Data.Records.pas | 13 +---- Src/Myc.Trade.Indicators.Common.pas | 81 ++++++++++++++++------------- 4 files changed, 85 insertions(+), 51 deletions(-) diff --git a/AuraTrader/MainForm.pas b/AuraTrader/MainForm.pas index 52a422b..fdc3b89 100644 --- a/AuraTrader/MainForm.pas +++ b/AuraTrader/MainForm.pas @@ -318,7 +318,19 @@ begin var Lowest: Double := Double.MaxValue; var Highest: Double := Double.MinValue; - var ATR := Ohlc.Chain(TATR.CreateATR(50)).MakeParallel; + var ATR := + Ohlc + .Chain( + TConverter.CreateConverter( + function(const Ohlc: TOhlcItem): TATR.TArgs + begin + Result.Close := Ohlc.Close; + Result.High := Ohlc.High; + Result.Low := Ohlc.Low; + end + )) + .Chain(TATR.CreateATR(50)) + .MakeParallel; // next stage @@ -622,7 +634,19 @@ begin var Boli := Closes.MakeParallel.Chain(TBollingerBands.CreateBollingerBands(20, 2.0)); var Rsi := Closes.Chain(TRSI.CreateRSI(14)); var Macd := Closes.MakeParallel.Chain(TMACD.CreateMACD(12, 26, 9)); - var Stoch := Ohlc.Chain(TStochastic.CreateStochastic(14, 3)); + + var Stoch := + Ohlc + .Chain( + TConverter.CreateConverter( + function(const Ohlc: TOhlcItem): TStochastic.TArgs + begin + Result.Close := Ohlc.Close; + Result.High := Ohlc.High; + Result.Low := Ohlc.Low; + end + )) + .Chain(TStochastic.CreateStochastic(14, 3)); chart.SetXAxisSeries(timeframe, Timestamps); diff --git a/AuraTrader/StrategyTest.pas b/AuraTrader/StrategyTest.pas index 1a81e15..c4f1b0f 100644 --- a/AuraTrader/StrategyTest.pas +++ b/AuraTrader/StrategyTest.pas @@ -37,7 +37,19 @@ begin var Hull := Closes.Chain(THMA.CreateHMA(250)).MakeParallel; var Sma := Closes.Chain(TSMA.CreateSMA(200)).MakeParallel; - var ATR := Ohlc.Chain(TATR.CreateATR(50)).MakeParallel; + + var ATR := + Ohlc + .Chain( + TConverter.CreateConverter( + function(const Ohlc: TOhlcItem): TATR.TArgs + begin + Result.Close := Ohlc.Close; + Result.High := Ohlc.High; + Result.Low := Ohlc.Low; + end + )) + .Chain(TATR.CreateATR(50)); var conv := TConverter.Join([Ohlc.Field('Low'), Ohlc.Field('High'), Closes, ATR, Hull, Sma]); diff --git a/Src/Myc.Data.Records.pas b/Src/Myc.Data.Records.pas index 07cbce7..7767c2f 100644 --- a/Src/Myc.Data.Records.pas +++ b/Src/Myc.Data.Records.pas @@ -35,7 +35,6 @@ type procedure Copy(Src, Dst: Pointer); - function GetSize: Integer; inline; function GetAlignedSize: Integer; inline; public @@ -44,7 +43,7 @@ type property FieldType: TFieldType read FFieldType; property Name: string read FName; - property Size: Integer read GetSize; + property Size: Integer read FSize; property AlignedSize: Integer read GetAlignedSize; property TypeInfo: PTypeInfo read FTypeInfo; end; @@ -286,17 +285,9 @@ begin Copy(@Src, @Buffer[FOffset]); end; -function TDataRecord.TField.GetSize: Integer; -const - DataSize: array[TDataRecord.TFieldType] of Integer = - (sizeof(Double), sizeof(Int64), sizeof(String), sizeof(TDateTime), sizeof(TDataRecord)); -begin - Result := DataSize[FFieldType]; -end; - function TDataRecord.TField.GetAlignedSize: Integer; begin - Result := (GetSize + (Align - 1)) and not (Align - 1); + Result := (FSize + (Align - 1)) and not (Align - 1); end; procedure TDataRecord.TField.ToType(const [ref] Buffer: TBytes; var Dst); diff --git a/Src/Myc.Trade.Indicators.Common.pas b/Src/Myc.Trade.Indicators.Common.pas index d257bf7..da96a17 100644 --- a/Src/Myc.Trade.Indicators.Common.pas +++ b/Src/Myc.Trade.Indicators.Common.pas @@ -148,7 +148,9 @@ type DPeriod: Integer; end; TArgs = record - Value: TOhlcItem; + High: Double; + Low: Double; + Close: Double; end; TResult = record K: Double; // %K line @@ -156,12 +158,12 @@ type end; [IndicatorFactory] class function CreateFactory: TIndicatorFactoryProc; static; - class function CreateStochastic(KPeriod, DPeriod: Integer): TConvertFunc; overload; static; - // Creates a Stochastic Oscillator using an injectable moving average for the %D line. + + class function CreateStochastic(KPeriod, DPeriod: Integer): TConvertFunc; overload; static; class function CreateStochastic( KPeriod: Integer; const SmaD: TConvertFunc - ): TConvertFunc; overload; static; + ): TConvertFunc; overload; static; end; [IndicatorName('StdDev', 'Standard Deviation')] @@ -214,15 +216,17 @@ type Period: Integer; end; TArgs = record - Value: TOhlcItem; + High: Double; + Low: Double; + Close: Double; end; TResult = record ATR: Double; end; [IndicatorFactory] class function CreateFactory: TIndicatorFactoryProc; static; - class function CreateATR(Period: Integer): TConvertFunc; overload; static; - class function CreateATR(const MovAvgTR: TConvertFunc): TConvertFunc; overload; static; + class function CreateATR(Period: Integer): TConvertFunc; overload; static; + class function CreateATR(const MovAvgTR: TConvertFunc): TConvertFunc; overload; static; end; [IndicatorName('KC', 'Keltner Channels')] @@ -235,7 +239,9 @@ type Multiplier: Double; end; TArgs = record - Value: TOhlcItem; + High: Double; + Low: Double; + Close: Double; end; TResult = record UpperBand: Double; @@ -244,12 +250,12 @@ type end; [IndicatorFactory] class function CreateFactory: TIndicatorFactoryProc; static; - class function CreateKeltnerChannels(Period: Integer; Multiplier: Double): TConvertFunc; overload; static; + class function CreateKeltnerChannels(Period: Integer; Multiplier: Double): TConvertFunc; overload; static; class function CreateKeltnerChannels( const MovAvgMiddle: TConvertFunc; - const AtrFunc: TConvertFunc; + const AtrFunc: TConvertFunc; Multiplier: Double - ): TConvertFunc; overload; static; + ): TConvertFunc; overload; static; end; [IndicatorName('Mean', 'Mean Value')] @@ -708,25 +714,22 @@ begin Result := function(const Params: TParams): TConvertFunc var - stochFunc: TConvertFunc; + stochFunc: TConvertFunc; begin stochFunc := CreateStochastic(Params.KPeriod, Params.DPeriod); - Result := function(const Value: TArgs): TResult begin Result := stochFunc(Value.Value); end; + Result := function(const Value: TArgs): TResult begin Result := stochFunc(Value); end; end; end; -class function TStochastic.CreateStochastic(KPeriod, DPeriod: Integer): TConvertFunc; +class function TStochastic.CreateStochastic(KPeriod, DPeriod: Integer): TConvertFunc; begin Result := CreateStochastic(KPeriod, TSMA.CreateSMA(DPeriod)); end; // Creates a Stochastic Oscillator using an injectable moving average for the %D line. -class function TStochastic.CreateStochastic( - KPeriod: Integer; - const SmaD: TConvertFunc -): TConvertFunc; +class function TStochastic.CreateStochastic(KPeriod: Integer; const SmaD: TConvertFunc): TConvertFunc; var - buffer: TArray; + buffer: TArray; highDeque: TLightDeque; lowDeque: TLightDeque; valueCount: Int64; @@ -734,7 +737,7 @@ begin if (KPeriod <= 0) then begin Result := - function(const Value: TOhlcItem): TResult + function(const Value: TArgs): TResult begin Result.K := Double.NaN; Result.D := Double.NaN; @@ -748,7 +751,7 @@ begin valueCount := 0; Result := - function(const Value: TOhlcItem): TStochastic.TResult + function(const Value: TArgs): TStochastic.TResult var currentIndex, firstIndex, lastIndex: Integer; highestHigh, lowestLow: Double; @@ -926,25 +929,25 @@ begin Result := function(const Params: TParams): TConvertFunc var - atrFunc: TConvertFunc; + atrFunc: TConvertFunc; begin atrFunc := CreateATR(Params.Period); - Result := function(const Value: TArgs): TResult begin Result.ATR := atrFunc(Value.Value); end; + Result := function(const Value: TArgs): TResult begin Result.ATR := atrFunc(Value); end; end; end; -class function TATR.CreateATR(Period: Integer): TConvertFunc; +class function TATR.CreateATR(Period: Integer): TConvertFunc; begin Result := CreateATR(TEMA.CreateEMA(Period)); end; // Calculates the Average True Range (ATR) using an injectable moving average. -class function TATR.CreateATR(const MovAvgTR: TConvertFunc): TConvertFunc; +class function TATR.CreateATR(const MovAvgTR: TConvertFunc): TConvertFunc; begin - var sourceData: TSeries; + var sourceData: TSeries; Result := - function(const Value: TOhlcItem): Double + function(const Value: TArgs): Double var tr: Double; begin @@ -968,20 +971,19 @@ end; { TKeltnerChannels } -class function TKeltnerChannels.CreateFactory: - TIndicatorFactoryProc; +class function TKeltnerChannels.CreateFactory: TIndicatorFactoryProc; begin Result := function(const Params: TParams): TConvertFunc var - kcFunc: TConvertFunc; + kcFunc: TConvertFunc; begin kcFunc := CreateKeltnerChannels(Params.Period, Params.Multiplier); - Result := function(const Value: TArgs): TResult begin Result := kcFunc(Value.Value); end; + Result := function(const Value: TArgs): TResult begin Result := kcFunc(Value); end; end; end; -class function TKeltnerChannels.CreateKeltnerChannels(Period: Integer; Multiplier: Double): TConvertFunc; +class function TKeltnerChannels.CreateKeltnerChannels(Period: Integer; Multiplier: Double): TConvertFunc; begin Result := CreateKeltnerChannels(TEMA.CreateEMA(Period), TATR.CreateATR(Period), Multiplier); end; @@ -989,12 +991,12 @@ end; // Calculates Keltner Channels using an injectable ATR and middle band moving average. class function TKeltnerChannels.CreateKeltnerChannels( const MovAvgMiddle: TConvertFunc; - const AtrFunc: TConvertFunc; + const AtrFunc: TConvertFunc; Multiplier: Double -): TConvertFunc; +): TConvertFunc; begin Result := - function(const Value: TOhlcItem): TKeltnerChannels.TResult + function(const Value: TArgs): TKeltnerChannels.TResult var atrValue, middleValue, typicalPrice: Double; begin @@ -1003,7 +1005,12 @@ begin // Get values from the provided indicator functions. middleValue := MovAvgMiddle(typicalPrice); - atrValue := AtrFunc(Value); + + var atrArgs: TATR.TArgs; + atrArgs.High := Value.High; + atrArgs.Low := Value.Low; + atrArgs.Close := Value.Close; + atrValue := AtrFunc(atrArgs); // Set default NaN values for the warm-up period. Result.MiddleBand := middleValue; @@ -1064,6 +1071,6 @@ initialization IndicatorRegistry.RegisterTemplate; IndicatorRegistry.RegisterTemplate; IndicatorRegistry.RegisterTemplate; - IndicatorRegistry.RegisterTemplate; + // IndicatorRegistry.RegisterTemplate; end.