Skip to content

Commit

Permalink
TEMA indicator is added.
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Dec 28, 2023
1 parent 085b35f commit ad31b06
Show file tree
Hide file tree
Showing 5 changed files with 411 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 51 additions & 0 deletions trend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

<a name="Tema"></a>
## type [Tema](<https://github.com/cinar/indicator/blob/v2/trend/tema.go#L18-L22>)

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]
}
```

<a name="NewTema"></a>
### func [NewTema](<https://github.com/cinar/indicator/blob/v2/trend/tema.go#L26>)

```go
func NewTema[T helper.Number]() *Tema[T]
```

NewTema function initializes a new TEMA instance with the default parameters.

<a name="Tema[T].Compute"></a>
### func \(\*Tema\[T\]\) [Compute](<https://github.com/cinar/indicator/blob/v2/trend/tema.go#L36>)

```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.

<a name="Tema[T].IdlePeriod"></a>
### func \(\*Tema\[T\]\) [IdlePeriod](<https://github.com/cinar/indicator/blob/v2/trend/tema.go#L65>)

```go
func (t *Tema[T]) IdlePeriod() int
```

IdlePeriod is the initial period that TEMA won't yield any results.

Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)
67 changes: 67 additions & 0 deletions trend/tema.go
Original file line number Diff line number Diff line change
@@ -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
}
40 changes: 40 additions & 0 deletions trend/tema_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
Loading

0 comments on commit ad31b06

Please sign in to comment.