From ad31b06b2ebfb4f6e14b06329dbe7eb34b7467fa Mon Sep 17 00:00:00 2001 From: Onur Cinar Date: Thu, 28 Dec 2023 07:52:46 -0800 Subject: [PATCH] TEMA indicator is added. --- README.md | 2 +- trend/README.md | 51 ++++++++ trend/tema.go | 67 +++++++++++ trend/tema_test.go | 40 +++++++ trend/testdata/tema.csv | 252 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 trend/tema.go create mode 100644 trend/tema_test.go create mode 100644 trend/testdata/tema.csv diff --git a/README.md b/README.md index 0a0b314..82cd787 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ The following list of indicators are currently supported by this package: - Rolling Moving Average (RMA) - [Simple Moving Average (SMA)](trend/README.md#type-sma) - [Since Change](helper/README.md#func-since) -- Triple Exponential Moving Average (TEMA) +- [Triple Exponential Moving Average (TEMA)](trend/README.md#type-tema) - Triangular Moving Average (TRIMA) - Triple Exponential Average (TRIX) - Typical Price diff --git a/trend/README.md b/trend/README.md index a3b874a..1055c2c 100644 --- a/trend/README.md +++ b/trend/README.md @@ -65,6 +65,10 @@ The information provided on this project is strictly for informational purposes - [type Sma](<#Sma>) - [func NewSma\[T helper.Number\]\(\) \*Sma\[T\]](<#NewSma>) - [func \(s \*Sma\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Sma[T].Compute>) +- [type Tema](<#Tema>) + - [func NewTema\[T helper.Number\]\(\) \*Tema\[T\]](<#NewTema>) + - [func \(t \*Tema\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Tema[T].Compute>) + - [func \(t \*Tema\[T\]\) IdlePeriod\(\) int](<#Tema[T].IdlePeriod>) ## Constants @@ -691,4 +695,51 @@ func (s *Sma[T]) Compute(c <-chan T) <-chan T Compute function takes a channel of numbers and computes the SMA over the specified period. + +## type [Tema]() + +Tema represents the configuration parameters for calculating the Triple Exponential Moving Average \(TEMA\). + +``` +TEMA = (3 * EMA1) - (3 * EMA2) + EMA3 +EMA1 = EMA(values) +EMA2 = EMA(EMA1) +EMA3 = EMA(EMA2) +``` + +```go +type Tema[T helper.Number] struct { + Ema1 *Ema[T] + Ema2 *Ema[T] + Ema3 *Ema[T] +} +``` + + +### func [NewTema]() + +```go +func NewTema[T helper.Number]() *Tema[T] +``` + +NewTema function initializes a new TEMA instance with the default parameters. + + +### func \(\*Tema\[T\]\) [Compute]() + +```go +func (t *Tema[T]) Compute(c <-chan T) <-chan T +``` + +Compute function takes a channel of numbers and computes the TEMA and the signal line. + + +### func \(\*Tema\[T\]\) [IdlePeriod]() + +```go +func (t *Tema[T]) IdlePeriod() int +``` + +IdlePeriod is the initial period that TEMA won't yield any results. + Generated by [gomarkdoc]() diff --git a/trend/tema.go b/trend/tema.go new file mode 100644 index 0000000..d0883cb --- /dev/null +++ b/trend/tema.go @@ -0,0 +1,67 @@ +// Copyright (c) 2021-2023 Onur Cinar. +// The source code is provided under GNU AGPLv3 License. +// https://github.com/cinar/indicator + +package trend + +import ( + "github.com/cinar/indicator/helper" +) + +// Tema represents the configuration parameters for calculating the +// Triple Exponential Moving Average (TEMA). +// +// TEMA = (3 * EMA1) - (3 * EMA2) + EMA3 +// EMA1 = EMA(values) +// EMA2 = EMA(EMA1) +// EMA3 = EMA(EMA2) +type Tema[T helper.Number] struct { + Ema1 *Ema[T] + Ema2 *Ema[T] + Ema3 *Ema[T] +} + +// NewTema function initializes a new TEMA instance +// with the default parameters. +func NewTema[T helper.Number]() *Tema[T] { + return &Tema[T]{ + Ema1: NewEma[T](), + Ema2: NewEma[T](), + Ema3: NewEma[T](), + } +} + +// Compute function takes a channel of numbers and computes the TEMA +// and the signal line. +func (t *Tema[T]) Compute(c <-chan T) <-chan T { + ema1 := helper.Duplicate( + t.Ema1.Compute(c), + 2, + ) + + ema2 := helper.Duplicate( + t.Ema2.Compute(ema1[0]), + 2, + ) + + ema1[1] = helper.Skip(ema1[1], t.Ema2.Period-1) + + ema3 := t.Ema3.Compute(ema2[0]) + ema1[1] = helper.Skip(ema1[1], t.Ema3.Period-1) + ema2[1] = helper.Skip(ema2[1], t.Ema3.Period-1) + + tema := helper.Add( + helper.Subtract( + helper.MultiplyBy(ema1[1], 3), + helper.MultiplyBy(ema2[1], 3), + ), + ema3, + ) + + return tema +} + +// IdlePeriod is the initial period that TEMA won't yield any results. +func (t *Tema[T]) IdlePeriod() int { + return t.Ema1.Period + t.Ema2.Period + t.Ema3.Period - 3 +} diff --git a/trend/tema_test.go b/trend/tema_test.go new file mode 100644 index 0000000..a760b2c --- /dev/null +++ b/trend/tema_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2021-2023 Onur Cinar. +// The source code is provided under GNU AGPLv3 License. +// https://github.com/cinar/indicator + +package trend_test + +import ( + "testing" + + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" +) + +func TestTema(t *testing.T) { + type Data struct { + Close float64 + Tema float64 + } + + input, err := helper.ReadFromCsvFile[Data]("testdata/tema.csv", true) + if err != nil { + t.Fatal(err) + } + + inputs := helper.Duplicate(input, 2) + closing := helper.Map(inputs[0], func(d *Data) float64 { return d.Close }) + expected := helper.Map(inputs[1], func(d *Data) float64 { return d.Tema }) + + tema := trend.NewTema[float64]() + + actual := tema.Compute(closing) + actual = helper.RoundDigits(actual, 2) + + expected = helper.Skip(expected, tema.IdlePeriod()) + + err = helper.CheckEquals(actual, expected) + if err != nil { + t.Fatal(err) + } +} diff --git a/trend/testdata/tema.csv b/trend/testdata/tema.csv new file mode 100644 index 0000000..4ecae97 --- /dev/null +++ b/trend/testdata/tema.csv @@ -0,0 +1,252 @@ +Close,Tema +318.600006,0 +315.839996,0 +316.149994,0 +310.570007,0 +307.779999,0 +305.820007,0 +305.98999,0 +306.390015,0 +311.450012,0 +312.329987,0 +309.290009,0 +301.910004,0 +300,0 +300.029999,0 +302,0 +307.820007,0 +302.690002,0 +306.48999,0 +305.549988,0 +303.429993,0 +309.059998,0 +308.899994,0 +309.910004,0 +314.549988,0 +312.899994,0 +318.690002,0 +315.529999,0 +316.350006,0 +320.369995,0 +318.929993,0 +317.640015,0 +314.859985,0 +308.299988,0 +305.230011,0 +309.869995,0 +310.420013,0 +311.299988,0 +311.899994,0 +310.950012,0 +309.170013,0 +307.329987,0 +311.519989,0 +310.570007,0 +311.859985,0 +308.51001,0 +308.429993,0 +312.970001,0 +308.480011,0 +307.209991,0 +309.890015,0 +313.73999,0 +310.790009,0 +309.630005,0 +308.179993,0 +308.23999,0 +302.720001,0 +303.160004,0 +303.070007,305.1 +304.019989,304.46 +304.660004,304.13 +305.179993,304.03 +304.619995,303.82 +307.75,304.5 +312.450012,306.29 +316.970001,308.95 +311.119995,309.61 +311.369995,310.21 +304.820007,309 +303.630005,307.68 +302.880005,306.42 +305.329987,306.02 +297.880005,303.78 +302.01001,303.03 +293.51001,300.24 +301.059998,299.94 +303.850006,300.46 +299.730011,299.85 +298.369995,299.04 +298.920013,298.55 +302.140015,299.02 +302.320007,299.49 +305.299988,300.67 +305.079987,301.61 +308.769989,303.35 +310.309998,305.19 +309.070007,306.36 +310.390015,307.66 +312.51001,309.26 +312.619995,310.57 +313.700012,311.89 +314.549988,313.16 +318.049988,315.07 +319.73999,317.02 +323.790009,319.61 +324.630005,321.88 +323.089996,323.27 +323.820007,324.52 +324.329987,325.59 +326.049988,326.83 +324.339996,327.32 +320.529999,326.64 +326.230011,327.49 +328.549988,328.71 +330.170013,330.05 +325.859985,329.95 +323.220001,329.1 +320,327.51 +323.880005,327.15 +326.140015,327.39 +324.869995,327.21 +322.98999,326.53 +322.640015,325.84 +322.48999,325.21 +323.529999,324.93 +323.75,324.74 +327.390015,325.51 +329.76001,326.73 +330.390015,327.88 +329.130005,328.46 +323.109985,327.34 +320.200012,325.65 +319.019989,323.94 +320.600006,322.94 +322.190002,322.54 +321.079987,321.93 +323.119995,321.96 +329.480011,323.65 +328.579987,324.81 +333.410004,327 +335.420013,329.31 +335.950012,331.32 +335.290009,332.75 +333.600006,333.45 +336.390015,334.71 +335.899994,335.56 +339.820007,337.23 +338.309998,338.16 +338.670013,338.95 +338.609985,339.53 +336.959991,339.52 +335.25,339.01 +334.119995,338.25 +335.339996,337.9 +334.149994,337.27 +336.910004,337.44 +341,338.61 +342,339.79 +341.559998,340.61 +341.459991,341.22 +340.899994,341.54 +341.130005,341.81 +343.369995,342.58 +345.350006,343.69 +343.540009,344.09 +341.089996,343.73 +344.25,344.22 +345.339996,344.87 +342.429993,344.61 +346.609985,345.44 +345.76001,345.87 +349.630005,347.19 +347.579987,347.7 +349.799988,348.65 +349.309998,349.26 +349.809998,349.84 +351.959991,350.84 +352.26001,351.68 +351.190002,352.05 +353.809998,352.99 +349.98999,352.71 +362.579987,355.71 +363.730011,358.41 +358.019989,359.08 +356.980011,359.29 +358.350006,359.75 +358.480011,360.1 +354.5,359.29 +354.109985,358.47 +353.190002,357.51 +352.559998,356.53 +352.089996,355.56 +350.570007,354.36 +354.26001,354.31 +354.299988,354.27 +355.929993,354.65 +355.549988,354.85 +358.290009,355.72 +361.059998,357.14 +360.200012,358.05 +362.459991,359.37 +360.470001,359.9 +361.670013,360.62 +361.799988,361.22 +363.149994,362.02 +365.519989,363.26 +367.779999,364.82 +367.820007,366.07 +369.5,367.48 +367.859985,368.16 +370.429993,369.34 +370.480011,370.25 +366.820007,370 +363.279999,368.82 +360.160004,366.99 +361.709991,365.86 +359.420013,364.32 +357.779999,362.62 +357.059998,361.03 +350.299988,357.98 +348.079987,354.92 +343.040009,351.14 +343.690002,348.27 +345.059998,346.33 +346.339996,345.16 +345.450012,344.04 +348.559998,344.01 +348.429993,344.04 +345.660004,343.42 +345.089996,342.83 +346.230011,342.72 +345.390015,342.49 +340.890015,341.19 +338.660004,339.62 +335.859985,337.68 +336.839996,336.43 +338.630005,335.95 +336.899994,335.18 +336.160004,334.45 +331.709991,332.77 +337.410004,332.95 +341.329987,334.2 +343.75,335.93 +349.019989,338.76 +351.809998,341.85 +346.630005,343.06 +346.170013,343.94 +346.299988,344.7 +348.179993,345.81 +350.559998,347.33 +350.01001,348.41 +354.25,350.38 +356.790009,352.63 +359.859985,355.22 +358.929993,357.06 +361.329987,359.12 +361,360.66 +361.799988,362.05 +362.679993,363.35 +361.339996,363.98 +360.049988,364.08 +358.690002,363.73