Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions indicator_stochastic_rsi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package techan

import (
"github.com/sdcoffey/big"
)

type stochasticRSIIndicator struct {
curRSI Indicator
minRSI Indicator
maxRSI Indicator
}

// NewStochasticRSIIndicator returns a derivative Indicator which returns the stochastic RSI indicator for the given
// RSI window.
// https://www.investopedia.com/terms/s/stochrsi.asp
func NewStochasticRSIIndicator(indicator Indicator, timeframe int) Indicator {
rsiIndicator := NewRelativeStrengthIndexIndicator(indicator, timeframe)
return stochasticRSIIndicator{
curRSI: rsiIndicator,
minRSI: NewMinimumValueIndicator(rsiIndicator, timeframe),
maxRSI: NewMaximumValueIndicator(rsiIndicator, timeframe),
}
}

func (sri stochasticRSIIndicator) Calculate(index int) big.Decimal {
curRSI := sri.curRSI.Calculate(index)
minRSI := sri.minRSI.Calculate(index)
maxRSI := sri.maxRSI.Calculate(index)

if minRSI.EQ(maxRSI) {
return big.NewDecimal(100)
}

return curRSI.Sub(minRSI).Div(maxRSI.Sub(minRSI)).Mul(big.NewDecimal(100))
}

type stochRSIKIndicator struct {
stochasticRSI Indicator
window int
}

// NewFastStochasticRSIIndicator returns a derivative Indicator which returns the fast stochastic RSI indicator (%K)
// for the given stochastic window.
func NewFastStochasticRSIIndicator(stochasticRSI Indicator, timeframe int) Indicator {
return stochRSIKIndicator{stochasticRSI, timeframe}
}

func (k stochRSIKIndicator) Calculate(index int) big.Decimal {
return NewSimpleMovingAverage(k.stochasticRSI, k.window).Calculate(index)
}

type stochRSIDIndicator struct {
fastStochasticRSI Indicator
window int
}

// NewSlowStochasticRSIIndicator returns a derivative Indicator which returns the slow stochastic RSI indicator (%D)
// for the given stochastic window.
func NewSlowStochasticRSIIndicator(fastStochasticRSI Indicator, timeframe int) Indicator {
return stochRSIDIndicator{fastStochasticRSI, timeframe}
}

func (d stochRSIDIndicator) Calculate(index int) big.Decimal {
return NewSimpleMovingAverage(d.fastStochasticRSI, d.window).Calculate(index)
}
79 changes: 79 additions & 0 deletions indicator_stochastic_rsi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package techan

import (
"testing"

"github.com/sdcoffey/big"
"github.com/stretchr/testify/assert"
)

func TestStochasticRSIIndicator(t *testing.T) {
indicator := NewStochasticRSIIndicator(NewClosePriceIndicator(mockedTimeSeries), 5)

expectedValues := []float64{
100,
100,
100,
100,
100,
100,
100,
95.9481,
54.5245,
93.1791,
0,
21.6754,
}

indicatorEquals(t, expectedValues, indicator)
}

func TestFastStochasticRSIIndicator(t *testing.T) {
indicator := NewFastStochasticRSIIndicator(NewStochasticRSIIndicator(NewClosePriceIndicator(mockedTimeSeries),
5), 3)

expectedValues := []float64{
0,
0,
100,
100,
100,
100,
100,
98.6494,
83.4909,
81.2173,
49.2346,
38.2848,
}

indicatorEquals(t, expectedValues, indicator)
}

func TestSlowStochasticRSIIndicator(t *testing.T) {
indicator := NewSlowStochasticRSIIndicator(NewFastStochasticRSIIndicator(NewStochasticRSIIndicator(
NewClosePriceIndicator(mockedTimeSeries), 5), 3), 3)

expectedValues := []float64{
0,
0,
33.3333,
66.6667,
100,
100,
100,
99.5498,
94.0468,
87.7858,
71.3142,
56.2456,
}

indicatorEquals(t, expectedValues, indicator)
}

func TestFastStochasticRSIIndicatorNoPriceChange(t *testing.T) {
close := NewClosePriceIndicator(mockTimeSeries("42.0", "42.0"))
rsInd := NewStochasticRSIIndicator(close, 2)
assert.Equal(t, big.NewDecimal(100).FormattedString(2), rsInd.Calculate(1).FormattedString(2))
}