diff --git a/README.md b/README.md index eafef9f..da772db 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/trend/README.md b/trend/README.md index 8ae3a60..1b71ad7 100644 --- a/trend/README.md +++ b/trend/README.md @@ -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>) @@ -169,6 +172,15 @@ const ( ) ``` + + +```go +const ( + // DefaultRmaPeriod is the default RMA period. + DefaultRmaPeriod = 20 +) +``` + ```go @@ -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. + +## type [Rma]() + +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 +} +``` + + +### func [NewRma]() + +```go +func NewRma[T helper.Number]() *Rma[T] +``` + +NewRma function initializes a new RMA instance with the default parameters. + + +### func \(\*Rma\[T\]\) [Compute]() + +```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. + ## type [Sma]() diff --git a/trend/rma.go b/trend/rma.go new file mode 100644 index 0000000..c5b42e9 --- /dev/null +++ b/trend/rma.go @@ -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 +} diff --git a/trend/rma_test.go b/trend/rma_test.go new file mode 100644 index 0000000..4e5fa9a --- /dev/null +++ b/trend/rma_test.go @@ -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) + } +} diff --git a/trend/testdata/rma.csv b/trend/testdata/rma.csv new file mode 100644 index 0000000..d8fbb98 --- /dev/null +++ b/trend/testdata/rma.csv @@ -0,0 +1,252 @@ +Close,Rma +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,308.28 +307.820007,308.25 +302.690002,307.88 +306.48999,307.78 +305.549988,307.63 +303.429993,307.35 +309.059998,307.47 +308.899994,307.56 +309.910004,307.72 +314.549988,308.18 +312.899994,308.49 +318.690002,309.17 +315.529999,309.59 +316.350006,310.04 +320.369995,310.73 +318.929993,311.28 +317.640015,311.7 +314.859985,311.91 +308.299988,311.67 +305.230011,311.24 +309.869995,311.15 +310.420013,311.1 +311.299988,311.12 +311.899994,311.17 +310.950012,311.15 +309.170013,311.02 +307.329987,310.78 +311.519989,310.83 +310.570007,310.81 +311.859985,310.88 +308.51001,310.72 +308.429993,310.57 +312.970001,310.73 +308.480011,310.58 +307.209991,310.35 +309.890015,310.32 +313.73999,310.55 +310.790009,310.57 +309.630005,310.5 +308.179993,310.35 +308.23999,310.21 +302.720001,309.71 +303.160004,309.27 +303.070007,308.86 +304.019989,308.54 +304.660004,308.28 +305.179993,308.07 +304.619995,307.84 +307.75,307.84 +312.450012,308.14 +316.970001,308.73 +311.119995,308.89 +311.369995,309.06 +304.820007,308.77 +303.630005,308.43 +302.880005,308.06 +305.329987,307.88 +297.880005,307.21 +302.01001,306.87 +293.51001,305.97 +301.059998,305.65 +303.850006,305.53 +299.730011,305.14 +298.369995,304.69 +298.920013,304.3 +302.140015,304.16 +302.320007,304.04 +305.299988,304.12 +305.079987,304.19 +308.769989,304.49 +310.309998,304.88 +309.070007,305.16 +310.390015,305.51 +312.51001,305.97 +312.619995,306.42 +313.700012,306.9 +314.549988,307.41 +318.049988,308.12 +319.73999,308.9 +323.790009,309.89 +324.630005,310.87 +323.089996,311.69 +323.820007,312.5 +324.329987,313.28 +326.049988,314.14 +324.339996,314.82 +320.529999,315.2 +326.230011,315.93 +328.549988,316.77 +330.170013,317.67 +325.859985,318.21 +323.220001,318.55 +320,318.64 +323.880005,318.99 +326.140015,319.47 +324.869995,319.83 +322.98999,320.04 +322.640015,320.21 +322.48999,320.36 +323.529999,320.58 +323.75,320.79 +327.390015,321.23 +329.76001,321.8 +330.390015,322.37 +329.130005,322.82 +323.109985,322.84 +320.200012,322.66 +319.019989,322.42 +320.600006,322.3 +322.190002,322.29 +321.079987,322.21 +323.119995,322.27 +329.480011,322.75 +328.579987,323.14 +333.410004,323.83 +335.420013,324.6 +335.950012,325.36 +335.290009,326.02 +333.600006,326.52 +336.390015,327.18 +335.899994,327.76 +339.820007,328.57 +338.309998,329.22 +338.670013,329.85 +338.609985,330.43 +336.959991,330.87 +335.25,331.16 +334.119995,331.36 +335.339996,331.62 +334.149994,331.79 +336.910004,332.13 +341,332.72 +342,333.34 +341.559998,333.89 +341.459991,334.39 +340.899994,334.83 +341.130005,335.25 +343.369995,335.79 +345.350006,336.43 +343.540009,336.9 +341.089996,337.18 +344.25,337.65 +345.339996,338.16 +342.429993,338.45 +346.609985,338.99 +345.76001,339.44 +349.630005,340.12 +347.579987,340.62 +349.799988,341.23 +349.309998,341.77 +349.809998,342.31 +351.959991,342.95 +352.26001,343.57 +351.190002,344.08 +353.809998,344.73 +349.98999,345.08 +362.579987,346.24 +363.730011,347.41 +358.019989,348.12 +356.980011,348.71 +358.350006,349.35 +358.480011,349.96 +354.5,350.26 +354.109985,350.52 +353.190002,350.7 +352.559998,350.82 +352.089996,350.91 +350.570007,350.88 +354.26001,351.11 +354.299988,351.32 +355.929993,351.63 +355.549988,351.89 +358.290009,352.32 +361.059998,352.9 +360.200012,353.39 +362.459991,353.99 +360.470001,354.42 +361.670013,354.91 +361.799988,355.37 +363.149994,355.88 +365.519989,356.53 +367.779999,357.28 +367.820007,357.98 +369.5,358.75 +367.859985,359.36 +370.429993,360.09 +370.480011,360.79 +366.820007,361.19 +363.279999,361.33 +360.160004,361.25 +361.709991,361.28 +359.420013,361.16 +357.779999,360.93 +357.059998,360.67 +350.299988,359.98 +348.079987,359.19 +343.040009,358.11 +343.690002,357.15 +345.059998,356.34 +346.339996,355.68 +345.450012,355 +348.559998,354.57 +348.429993,354.16 +345.660004,353.59 +345.089996,353.02 +346.230011,352.57 +345.390015,352.09 +340.890015,351.35 +338.660004,350.5 +335.859985,349.52 +336.839996,348.68 +338.630005,348.01 +336.899994,347.27 +336.160004,346.53 +331.709991,345.54 +337.410004,345 +341.329987,344.75 +343.75,344.69 +349.019989,344.98 +351.809998,345.43 +346.630005,345.51 +346.170013,345.55 +346.299988,345.6 +348.179993,345.78 +350.559998,346.1 +350.01001,346.36 +354.25,346.88 +356.790009,347.54 +359.859985,348.36 +358.929993,349.07 +361.329987,349.89 +361,350.63 +361.799988,351.37 +362.679993,352.13 +361.339996,352.74 +360.049988,353.23 +358.690002,353.59