Work in Progress
This commit is contained in:
+124
-10
@@ -29,6 +29,13 @@ type
|
||||
LowerBand: Double;
|
||||
end;
|
||||
|
||||
// Result for the Keltner Channels indicator.
|
||||
TKeltnerChannelsResult = record
|
||||
UpperBand: Double;
|
||||
MiddleBand: Double;
|
||||
LowerBand: Double;
|
||||
end;
|
||||
|
||||
TIndicators = record
|
||||
private
|
||||
class function CalculateSMA(const Series: TSeries<Double>; const Period: Integer): Double; static;
|
||||
@@ -44,11 +51,33 @@ type
|
||||
// Relative Strength Index
|
||||
class function CreateRSI(Period: Integer): TConstFunc<Double, Double>; static;
|
||||
// Moving Average Convergence Divergence
|
||||
class function CreateMACD(FastPeriod, SlowPeriod, SignalPeriod: Integer): TConstFunc<Double, TMacdResult>; static;
|
||||
class function CreateMACD(FastPeriod, SlowPeriod, SignalPeriod: Integer): TConstFunc<Double, TMacdResult>; overload; static;
|
||||
class function CreateMACD(
|
||||
const EmaFast,
|
||||
EmaSlow,
|
||||
EmaSignal: TConstFunc<Double, Double>
|
||||
): TConstFunc<Double, TMacdResult>; overload; static;
|
||||
// Stochastic Oscillator
|
||||
class function CreateStochastic(KPeriod, DPeriod: Integer): TConstFunc<TOhlcItem, TStochasticResult>; static;
|
||||
class function CreateStochastic(KPeriod, DPeriod: Integer): TConstFunc<TOhlcItem, TStochasticResult>; overload; static;
|
||||
class function CreateStochastic(
|
||||
KPeriod: Integer;
|
||||
const SmaD: TConstFunc<Double, Double>
|
||||
): TConstFunc<TOhlcItem, TStochasticResult>; overload; static;
|
||||
// Bollinger Bands
|
||||
class function CreateBollingerBands(Period: Integer; Multiplier: Double): TConstFunc<Double, TBollingerBandsResult>; static;
|
||||
// Average True Range
|
||||
class function CreateATR(Period: Integer): TConstFunc<TOhlcItem, Double>; overload; static;
|
||||
class function CreateATR(const MovAvgTR: TConstFunc<Double, Double>): TConstFunc<TOhlcItem, Double>; overload; static;
|
||||
// Keltner Channels
|
||||
class function CreateKeltnerChannels(
|
||||
Period: Integer;
|
||||
Multiplier: Double
|
||||
): TConstFunc<TOhlcItem, TKeltnerChannelsResult>; overload; static;
|
||||
class function CreateKeltnerChannels(
|
||||
const MovAvgMiddle: TConstFunc<Double, Double>;
|
||||
const AtrFunc: TConstFunc<TOhlcItem, Double>;
|
||||
Multiplier: Double
|
||||
): TConstFunc<TOhlcItem, TKeltnerChannelsResult>; overload; static;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -208,19 +237,22 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
// Standard MACD using EMAs.
|
||||
class function TIndicators.CreateMACD(FastPeriod, SlowPeriod, SignalPeriod: Integer): TConstFunc<Double, TMacdResult>;
|
||||
begin
|
||||
var emaFast := CreateEMA(FastPeriod);
|
||||
var emaSlow := CreateEMA(SlowPeriod);
|
||||
var emaSignal := CreateEMA(SignalPeriod);
|
||||
Result := CreateMACD(CreateEMA(FastPeriod), CreateEMA(SlowPeriod), CreateEMA(SignalPeriod));
|
||||
end;
|
||||
|
||||
// Creates a MACD indicator from three provided moving average functions.
|
||||
class function TIndicators.CreateMACD(const EmaFast, EmaSlow, EmaSignal: TConstFunc<Double, Double>): TConstFunc<Double, TMacdResult>;
|
||||
begin
|
||||
Result :=
|
||||
function(const Value: Double): TMacdResult
|
||||
var
|
||||
fastVal, slowVal: Double;
|
||||
begin
|
||||
fastVal := emaFast(Value);
|
||||
slowVal := emaSlow(Value);
|
||||
fastVal := EmaFast(Value);
|
||||
slowVal := EmaSlow(Value);
|
||||
|
||||
if IsNan(slowVal) then // slowVal will be the last one to become non-NaN
|
||||
begin
|
||||
@@ -231,7 +263,7 @@ begin
|
||||
else
|
||||
begin
|
||||
Result.MacdLine := fastVal - slowVal;
|
||||
Result.SignalLine := emaSignal(Result.MacdLine);
|
||||
Result.SignalLine := EmaSignal(Result.MacdLine);
|
||||
if not IsNan(Result.SignalLine) then
|
||||
Result.Histogram := Result.MacdLine - Result.SignalLine
|
||||
else
|
||||
@@ -313,10 +345,19 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
// Standard Stochastic Oscillator using an SMA for the %D line.
|
||||
class function TIndicators.CreateStochastic(KPeriod, DPeriod: Integer): TConstFunc<TOhlcItem, TStochasticResult>;
|
||||
begin
|
||||
Result := CreateStochastic(KPeriod, CreateSMA(DPeriod));
|
||||
end;
|
||||
|
||||
// Creates a Stochastic Oscillator using an injectable moving average for the %D line.
|
||||
class function TIndicators.CreateStochastic(
|
||||
KPeriod: Integer;
|
||||
const SmaD: TConstFunc<Double, Double>
|
||||
): TConstFunc<TOhlcItem, TStochasticResult>;
|
||||
begin
|
||||
var sourceData: TSeries<TOhlcItem>;
|
||||
var smaD := CreateSMA(DPeriod);
|
||||
|
||||
Result :=
|
||||
function(const Value: TOhlcItem): TStochasticResult
|
||||
@@ -347,7 +388,80 @@ begin
|
||||
else
|
||||
Result.K := 100; // Or 50, depends on convention
|
||||
|
||||
Result.D := smaD(Result.K);
|
||||
Result.D := SmaD(Result.K);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Standard ATR using an EMA for smoothing.
|
||||
class function TIndicators.CreateATR(Period: Integer): TConstFunc<TOhlcItem, Double>;
|
||||
begin
|
||||
Result := CreateATR(CreateEMA(Period));
|
||||
end;
|
||||
|
||||
// Calculates the Average True Range (ATR) using an injectable moving average.
|
||||
class function TIndicators.CreateATR(const MovAvgTR: TConstFunc<Double, Double>): TConstFunc<TOhlcItem, Double>;
|
||||
begin
|
||||
var sourceData: TSeries<TOhlcItem>;
|
||||
|
||||
Result :=
|
||||
function(const Value: TOhlcItem): Double
|
||||
var
|
||||
tr: Double;
|
||||
begin
|
||||
// We only need the previous bar to calculate true range.
|
||||
sourceData := sourceData.Add(Value, 2);
|
||||
|
||||
if (sourceData.Count < 2) then
|
||||
begin
|
||||
// Feed a dummy value to keep the moving average count in sync. It will correctly return NaN.
|
||||
Result := MovAvgTR(0);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
// Calculate current True Range.
|
||||
tr := Max(Value.High - Value.Low, Max(Abs(Value.High - sourceData[1].Close), Abs(Value.Low - sourceData[1].Close)));
|
||||
|
||||
// Feed the calculated TR into the provided moving average function.
|
||||
Result := MovAvgTR(tr);
|
||||
end;
|
||||
end;
|
||||
|
||||
// Standard Keltner Channels using an EMA for the middle line and an EMA-based ATR.
|
||||
class function TIndicators.CreateKeltnerChannels(Period: Integer; Multiplier: Double): TConstFunc<TOhlcItem, TKeltnerChannelsResult>;
|
||||
begin
|
||||
Result := CreateKeltnerChannels(CreateEMA(Period), CreateATR(Period), Multiplier);
|
||||
end;
|
||||
|
||||
// Calculates Keltner Channels using an injectable ATR and middle band moving average.
|
||||
class function TIndicators.CreateKeltnerChannels(
|
||||
const MovAvgMiddle: TConstFunc<Double, Double>;
|
||||
const AtrFunc: TConstFunc<TOhlcItem, Double>;
|
||||
Multiplier: Double
|
||||
): TConstFunc<TOhlcItem, TKeltnerChannelsResult>;
|
||||
begin
|
||||
Result :=
|
||||
function(const Value: TOhlcItem): TKeltnerChannelsResult
|
||||
var
|
||||
atrValue, middleValue, typicalPrice: Double;
|
||||
begin
|
||||
// Calculate Typical Price for the middle band.
|
||||
typicalPrice := (Value.High + Value.Low + Value.Close) / 3.0;
|
||||
|
||||
// Get values from the provided indicator functions.
|
||||
middleValue := MovAvgMiddle(typicalPrice);
|
||||
atrValue := AtrFunc(Value);
|
||||
|
||||
// Set default NaN values for the warm-up period.
|
||||
Result.MiddleBand := middleValue;
|
||||
Result.UpperBand := Double.NaN;
|
||||
Result.LowerBand := Double.NaN;
|
||||
|
||||
// Once both middle band and ATR have valid (non-NaN) values, calculate the channels.
|
||||
if not IsNan(middleValue) and not IsNan(atrValue) then
|
||||
begin
|
||||
Result.UpperBand := middleValue + (atrValue * Multiplier);
|
||||
Result.LowerBand := middleValue - (atrValue * Multiplier);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
Reference in New Issue
Block a user