Skip to content

Commit

Permalink
Merge pull request #35 from tsubus/stochastic
Browse files Browse the repository at this point in the history
Implement Stochastic Oscillator Indicator
  • Loading branch information
sdcoffey authored Mar 17, 2021
2 parents a2f4ef8 + 2c94849 commit 572c478
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 2 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ go 1.12
require (
github.com/sdcoffey/big v0.7.0
github.com/stretchr/testify v1.7.0
)
)
2 changes: 1 addition & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
54 changes: 54 additions & 0 deletions indicator_stochastic_oscillator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package techan

import (
"math"

"github.com/sdcoffey/big"
)

type kIndicator struct {
closePrice Indicator
minValue Indicator
maxValue Indicator
window int
}

// NewFastStochasticIndicator returns a derivative Indicator which returns the fast stochastic indicator (%K) for the
// given window.
// https://www.investopedia.com/terms/s/stochasticoscillator.asp
func NewFastStochasticIndicator(series *TimeSeries, timeframe int) Indicator {
return kIndicator{
closePrice: NewClosePriceIndicator(series),
minValue: NewMinimumValueIndicator(NewLowPriceIndicator(series), timeframe),
maxValue: NewMaximumValueIndicator(NewHighPriceIndicator(series), timeframe),
window: timeframe,
}
}

func (k kIndicator) Calculate(index int) big.Decimal {
closeVal := k.closePrice.Calculate(index)
minVal := k.minValue.Calculate(index)
maxVal := k.maxValue.Calculate(index)

if minVal.EQ(maxVal) {
return big.NewDecimal(math.Inf(1))
}

return closeVal.Sub(minVal).Div(maxVal.Sub(minVal)).Mul(big.NewDecimal(100))
}

type dIndicator struct {
k Indicator
window int
}

// NewSlowStochasticIndicator returns a derivative Indicator which returns the slow stochastic indicator (%D) for the
// given window.
// https://www.investopedia.com/terms/s/stochasticoscillator.asp
func NewSlowStochasticIndicator(k Indicator, window int) Indicator {
return dIndicator{k, window}
}

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

import (
"math"
"testing"

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

var fastStochValues = []float64{
100,
100,
100.0 * 12.0 / 16.0,
100.0 * 2.0 / 16.0,
100.0 * 6.0 / 16.0,
100.0 * 2.0 / 16.0,
100.0 * 3.0 / 15.0,
100.0 * 2.0 / 16.0,
100.0 * 4.0 / 13.0,
100.0 * 11.0 / 17.0,
100.0 * 24.0 / 49.0,
}

func TestFastStochasticIndicator(t *testing.T) {
ts := mockTimeSeriesOCHL(
[]float64{10, 12, 12, 8},
[]float64{11, 14, 14, 9},
[]float64{10, 20, 24, 10},
[]float64{9, 10, 11, 9},
[]float64{11, 14, 14, 9},
[]float64{9, 10, 11, 9},
[]float64{10, 12, 12, 10},
[]float64{9, 10, 11, 8},
[]float64{6, 5, 8, 1},
[]float64{15, 12, 18, 9},
[]float64{35, 25, 50, 20},
)

window := 6

k := NewFastStochasticIndicator(ts, window)

decimalEquals(t, fastStochValues[0], k.Calculate(0))
decimalEquals(t, fastStochValues[1], k.Calculate(1))
decimalEquals(t, fastStochValues[2], k.Calculate(2))
decimalEquals(t, fastStochValues[3], k.Calculate(3))
decimalEquals(t, fastStochValues[4], k.Calculate(4))
decimalEquals(t, fastStochValues[5], k.Calculate(5))
decimalEquals(t, fastStochValues[6], k.Calculate(6))
decimalEquals(t, fastStochValues[7], k.Calculate(7))
decimalEquals(t, fastStochValues[8], k.Calculate(8))
decimalEquals(t, fastStochValues[9], k.Calculate(9))
decimalEquals(t, fastStochValues[10], k.Calculate(10))
}

func TestSlowStochasticIndicator(t *testing.T) {
ts := mockTimeSeriesFl(fastStochValues...)

window := 3

d := NewSlowStochasticIndicator(NewClosePriceIndicator(ts), window)

decimalEquals(t, 0, d.Calculate(0))
decimalEquals(t, 0, d.Calculate(1))
decimalEquals(t, 100.0*(12.0/16.0+1+1)/3.0, d.Calculate(2))
decimalEquals(t, 100.0*(2.0/16.0+12.0/16.0+1)/3.0, d.Calculate(3))
decimalEquals(t, 100.0*(6.0/16.0+2.0/16.0+12.0/16.0)/3.0, d.Calculate(4))
decimalEquals(t, 100.0*(2.0/16.0+6.0/16.0+2.0/16.0)/3.0, d.Calculate(5))
decimalEquals(t, 100.0*(3.0/15.0+2.0/16.0+6.0/16.0)/3.0, d.Calculate(6))
decimalEquals(t, 100.0*(2.0/16.0+3.0/15.0+2.0/16.0)/3.0, d.Calculate(7))
decimalEquals(t, 100.0*(4.0/13.0+2.0/16.0+3.0/15.0)/3.0, d.Calculate(8))
decimalEquals(t, 100.0*(11.0/17.0+4.0/13.0+2.0/16.0)/3.0, d.Calculate(9))
decimalEquals(t, 100.0*(24.0/49.0+11.0/17.0+4.0/13.0)/3.0, d.Calculate(10))
}

func TestFastStochasticIndicatorNoPriceChange(t *testing.T) {
ts := mockTimeSeriesOCHL(
[]float64{42, 42, 42, 42},
[]float64{42, 42, 42, 42},
)

k := NewFastStochasticIndicator(ts, 2)
assert.Equal(t, big.NewDecimal(math.Inf(1)).FormattedString(2), k.Calculate(1).FormattedString(2))
}

0 comments on commit 572c478

Please sign in to comment.