Skip to content

Commit

Permalink
Added support for SMMA. (#247)
Browse files Browse the repository at this point in the history
# Describe Request

Added support for Smoothed Moving Average (SMMA).

Fixed #246 

# Change Type

New Indicator.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced the Smoothed Moving Average (SMMA) indicator to enhance
trend analysis capabilities.
	- Added documentation for the SMMA indicator in the README file.

- **Tests**
- Implemented a new test suite to validate the functionality of the SMMA
computation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
cinar authored Dec 23, 2024
1 parent 6020bfe commit ee386c8
Show file tree
Hide file tree
Showing 5 changed files with 464 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ The following list of indicators are currently supported by this package:
- [Rolling Moving Average (RMA)](trend/README.md#type-rma)
- [Simple Moving Average (SMA)](trend/README.md#type-sma)
- [Since Change](helper/README.md#func-since)
- [Smoothed Moving Average (SMMA)](trend/README.md#type-smma)
- [Triple Exponential Moving Average (TEMA)](trend/README.md#type-tema)
- [Triangular Moving Average (TRIMA)](trend/README.md#type-trima)
- [Triple Exponential Average (TRIX)](trend/README.md#type-trix)
Expand Down
86 changes: 86 additions & 0 deletions trend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ The information provided on this project is strictly for informational purposes
- [func \(s \*Sma\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Sma[T].Compute>)
- [func \(s \*Sma\[T\]\) IdlePeriod\(\) int](<#Sma[T].IdlePeriod>)
- [func \(s \*Sma\[T\]\) String\(\) string](<#Sma[T].String>)
- [type Smma](<#Smma>)
- [func NewSmma\[T helper.Number\]\(\) \*Smma\[T\]](<#NewSmma>)
- [func NewSmmaWithPeriod\[T helper.Number\]\(period int\) \*Smma\[T\]](<#NewSmmaWithPeriod>)
- [func \(s \*Smma\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Smma[T].Compute>)
- [func \(s \*Smma\[T\]\) IdlePeriod\(\) int](<#Smma[T].IdlePeriod>)
- [func \(s \*Smma\[T\]\) String\(\) string](<#Smma[T].String>)
- [type Tema](<#Tema>)
- [func NewTema\[T helper.Number\]\(\) \*Tema\[T\]](<#NewTema>)
- [func \(t \*Tema\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Tema[T].Compute>)
Expand Down Expand Up @@ -300,6 +306,15 @@ const (
)
```

<a name="DefaultSmmaPeriod"></a>

```go
const (
// DefaultSmmaPeriod is the default SMMA period of 7.
DefaultSmmaPeriod = 7
)
```

<a name="DefaultTrimaPeriod"></a>

```go
Expand Down Expand Up @@ -1424,6 +1439,77 @@ func (s *Sma[T]) String() string

String is the string representation of the SMA.

<a name="Smma"></a>
## type [Smma](<https://github.com/cinar/indicator/blob/master/trend/smma.go#L29-L32>)

Smma represents the parameters for calculating the Smoothed Moving Average \(SMMA\).

```
SMMA[0] = SMA(N)
SMMA[i] = ((SMMA[i-1] * (N - 1)) + Close[i]) / N
```

Example:

```
smma := trend.NewSmma[float64]()
smma.Period = 10

result := smma.Compute(c)
```

```go
type Smma[T helper.Number] struct {
// Time period.
Period int
}
```

<a name="NewSmma"></a>
### func [NewSmma](<https://github.com/cinar/indicator/blob/master/trend/smma.go#L35>)

```go
func NewSmma[T helper.Number]() *Smma[T]
```

NewSmma function initializes a new SMMA instance with the default parameters.

<a name="NewSmmaWithPeriod"></a>
### func [NewSmmaWithPeriod](<https://github.com/cinar/indicator/blob/master/trend/smma.go#L42>)

```go
func NewSmmaWithPeriod[T helper.Number](period int) *Smma[T]
```

NewSmmaWithPeriod function initializes a new SMMA instance with the given period.

<a name="Smma[T].Compute"></a>
### func \(\*Smma\[T\]\) [Compute](<https://github.com/cinar/indicator/blob/master/trend/smma.go#L50>)

```go
func (s *Smma[T]) Compute(c <-chan T) <-chan T
```

Compute function takes a channel of numbers and computes the SMMA over the specified period.

<a name="Smma[T].IdlePeriod"></a>
### func \(\*Smma\[T\]\) [IdlePeriod](<https://github.com/cinar/indicator/blob/master/trend/smma.go#L72>)

```go
func (s *Smma[T]) IdlePeriod() int
```

IdlePeriod is the initial period that SMMA yield any results.

<a name="Smma[T].String"></a>
### func \(\*Smma\[T\]\) [String](<https://github.com/cinar/indicator/blob/master/trend/smma.go#L77>)

```go
func (s *Smma[T]) String() string
```

String is the string representation of the SMMA.

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

Expand Down
76 changes: 76 additions & 0 deletions trend/smma.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) 2021-2024 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package trend

import (
"fmt"

"github.com/cinar/indicator/v2/helper"
)

const (
// DefaultSmmaPeriod is the default SMMA period of 7.
DefaultSmmaPeriod = 7
)

// Smma represents the parameters for calculating the Smoothed Moving Average (SMMA).
//
// SMMA[0] = SMA(N)
// SMMA[i] = ((SMMA[i-1] * (N - 1)) + Close[i]) / N
//
// Example:
//
// smma := trend.NewSmma[float64]()
// smma.Period = 10
//
// result := smma.Compute(c)
type Smma[T helper.Number] struct {
// Time period.
Period int
}

// NewSmma function initializes a new SMMA instance with the default parameters.
func NewSmma[T helper.Number]() *Smma[T] {
return NewSmmaWithPeriod[T](DefaultSmmaPeriod)
}

// NewSmmaWithPeriod function initializes a new SMMA instance with the given period.
func NewSmmaWithPeriod[T helper.Number](period int) *Smma[T] {
return &Smma[T]{
Period: period,
}
}

// Compute function takes a channel of numbers and computes the SMMA over the specified period.
func (s *Smma[T]) Compute(c <-chan T) <-chan T {
result := make(chan T, cap(c))

go func() {
defer close(result)

// Initial SMMA value is the SMA.
sma := NewSmaWithPeriod[T](s.Period)

before := <-sma.Compute(helper.Head(c, s.Period))
result <- before

for n := range c {
before = ((before * (T(s.Period) - 1)) + n) / T(s.Period)
result <- before
}
}()

return result
}

// IdlePeriod is the initial period that SMMA yield any results.
func (s *Smma[T]) IdlePeriod() int {
return s.Period - 1
}

// String is the string representation of the SMMA.
func (s *Smma[T]) String() string {
return fmt.Sprintf("SMMA(%d)", s.Period)
}
49 changes: 49 additions & 0 deletions trend/smma_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2021-2024 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/v2/helper"
"github.com/cinar/indicator/v2/trend"
)

func TestSmma(t *testing.T) {
type Data struct {
Close float64
Smma float64
}

input, err := helper.ReadFromCsvFile[Data]("testdata/smma.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.Smma })

smma := trend.NewSmma[float64]()

actual := smma.Compute(closing)
actual = helper.RoundDigits(actual, 2)

expected = helper.Skip(expected, smma.IdlePeriod())

err = helper.CheckEquals(actual, expected)
if err != nil {
t.Fatal(err)
}
}

func TestSmmaString(t *testing.T) {
expected := "SMMA(10)"
actual := trend.NewSmmaWithPeriod[float64](10).String()

if actual != expected {
t.Fatalf("actual %v expected %v", actual, expected)
}
}
Loading

0 comments on commit ee386c8

Please sign in to comment.