Skip to content

Commit

Permalink
Rolling Moving Average (RMA).
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Dec 29, 2023
1 parent 62c6904 commit f3d47e7
Show file tree
Hide file tree
Showing 5 changed files with 408 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The following list of indicators are currently supported by this package:
- [Moving Sum](trend/README.md#type-movingsum)
- Parabolic SAR
- [Random Index (KDJ)](trend/README.md#type-kdj)
- Rolling Moving Average (RMA)
- [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)
- [Triple Exponential Moving Average (TEMA)](trend/README.md#type-tema)
Expand Down
56 changes: 56 additions & 0 deletions trend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ The information provided on this project is strictly for informational purposes
- [type MovingSum](<#MovingSum>)
- [func NewMovingSum\[T helper.Number\]\(\) \*MovingSum\[T\]](<#NewMovingSum>)
- [func \(m \*MovingSum\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#MovingSum[T].Compute>)
- [type Rma](<#Rma>)
- [func NewRma\[T helper.Number\]\(\) \*Rma\[T\]](<#NewRma>)
- [func \(rma \*Rma\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Rma[T].Compute>)
- [type Sma](<#Sma>)
- [func NewSma\[T helper.Number\]\(\) \*Sma\[T\]](<#NewSma>)
- [func \(s \*Sma\[T\]\) Compute\(c \<\-chan T\) \<\-chan T](<#Sma[T].Compute>)
Expand Down Expand Up @@ -169,6 +172,15 @@ const (
)
```

<a name="DefaultRmaPeriod"></a>

```go
const (
// DefaultRmaPeriod is the default RMA period.
DefaultRmaPeriod = 20
)
```

<a name="DefaultSmaPeriod"></a>

```go
Expand Down Expand Up @@ -685,6 +697,50 @@ func (m *MovingSum[T]) Compute(c <-chan T) <-chan T

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

<a name="Rma"></a>
## type [Rma](<https://github.com/cinar/indicator/blob/v2/trend/rma.go#L25-L28>)

Rma represents the parameters for calculating Rolling Moving Average \(RMA\).

```
R[0] to R[p-1] is SMA(values)
R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p
```

Example:

```
rma := trend.NewRma[float64]()
rma.Period = 10

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

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

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

```go
func NewRma[T helper.Number]() *Rma[T]
```

NewRma function initializes a new RMA instance with the default parameters.

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

```go
func (rma *Rma[T]) Compute(c <-chan T) <-chan T
```

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

<a name="Sma"></a>
## type [Sma](<https://github.com/cinar/indicator/blob/v2/trend/sma.go#L23-L26>)

Expand Down
58 changes: 58 additions & 0 deletions trend/rma.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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"

const (
// DefaultRmaPeriod is the default RMA period.
DefaultRmaPeriod = 20
)

// Rma represents the parameters for calculating Rolling Moving Average (RMA).
//
// R[0] to R[p-1] is SMA(values)
// R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p
//
// Example:
//
// rma := trend.NewRma[float64]()
// rma.Period = 10
//
// result := rma.Compute(c)
type Rma[T helper.Number] struct {
// Time period.
Period int
}

// NewRma function initializes a new RMA instance with the default parameters.
func NewRma[T helper.Number]() *Rma[T] {
return &Rma[T]{
Period: DefaultRmaPeriod,
}
}

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

go func() {
defer close(result)

// Initial RMA value is the SMA.
sma := NewSma[T]()
sma.Period = rma.Period

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

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

return result
}
41 changes: 41 additions & 0 deletions trend/rma_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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 TestRma(t *testing.T) {
type Data struct {
Close float64
Rma float64
}

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

rma := trend.NewRma[float64]()
rma.Period = 15

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

expected = helper.Skip(expected, rma.Period-1)

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

0 comments on commit f3d47e7

Please sign in to comment.