Skip to content

Commit

Permalink
KDJ indicator (Random Index). (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar authored Nov 6, 2021
1 parent cf8d4fb commit 0f0e2d5
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
38 changes: 38 additions & 0 deletions trend_indicators.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,44 @@ func Qstick(period int, opening, closing []float64) []float64 {
return qs
}

// The Kdj function calculates the KDJ indicator, also known as
// the Random Index. KDJ is calculated similar to the Stochastic
// Oscillator with the difference of having the J line. It is
// used to analyze the trend and entry points.
//
// The K and D lines show if the asset is overbought when they
// crosses above 80%, and oversold when they crosses below
// 20%. The J line represents the divergence.
//
//
// RSV = ((Closing - Min(Low, rPeriod))
// / (Max(High, rPeriod) - Min(Low, rPeriod))) * 100
// K = Sma(RSV, kPeriod)
// D = Sma(K, dPeriod)
// J = (3 * K) - (2 * D)
//
// Returns k, d, j.
func Kdj(rPeriod, kPeriod, dPeriod int, high, low, closing []float64) ([]float64, []float64, []float64) {
highest := Max(rPeriod, high)
lowest := Min(rPeriod, low)

rsv := multiplyBy(divide(substract(closing, lowest), substract(highest, lowest)), 100)

k := Sma(kPeriod, rsv)
d := Sma(dPeriod, k)
j := substract(multiplyBy(k, 3), multiplyBy(d, 2))

return k, d, j
}

// The DefaultKdj function calculates KDJ based on default periods
// consisting of rPeriod of 9, kPeriod of 3, and dPeriod of 3.
//
// Returns k, d, j.
func DefaultKdj(high, low, closing []float64) ([]float64, []float64, []float64) {
return Kdj(9, 3, 3, high, low, closing)
}

// Simple Moving Average (SMA).
func Sma(period int, values []float64) []float64 {
result := make([]float64, len(values))
Expand Down
24 changes: 24 additions & 0 deletions trend_indicators.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Trend indicators measure the direction and strength of a trend.
- [Moving Min](#moving-min)
- [Parabolic SAR](#parabolic-sar)
- [Qstick](trend_indicator.md#qstick)
- [Random Index (KDJ)](#random-index-kdj)
- [Simple Moving Average (SMA)](#simple-moving-average-sma)
- [Since Change](#since-change)
- [Triangular Moving Average (TRIMA)](#triangular-moving-average-trima)
Expand Down Expand Up @@ -172,6 +173,29 @@ QS = Sma(Closing - Opening)
qs := indicator.Qstick(period, closing, opening)
```

#### Random Index (KDJ)

The [Kdj](https://pkg.go.dev/github.com/cinar/indicator#Kdj) function calculates the KDJ indicator, also known as the Random Index. KDJ is calculated similar to the Stochastic Oscillator with the difference of having the J line. It is used to analyze the trend and entry points.

The K and D lines show if the asset is overbought when they crosses above 80%, and oversold when they crosses below 20%. The J line represents the divergence.

```
RSV = ((Closing - Min(Low, rPeriod)) / (Max(High, rPeriod) - Min(Low, rPeriod))) * 100
K = Sma(RSV, kPeriod)
D = Sma(K, dPeriod)
J = (3 * K) - (2 * D)
```

```Golang
k, d, j := indicator.Kdj(rPeriod, kPeriod, dPeriod, high, low, closing)
```

By default, _rPeriod_ of 9, _kPeriod_ of 3, and _dPeriod_ of 3 are used. The [DefaultKdj](https://pkg.go.dev/github.com/cinar/indicator#DefaultKdj) function can be used with those periods.

```Golang
k, d, j := indicator.DefaultKdj(high, low, closing)
```

#### Simple Moving Average (SMA)

The [Sma](https://pkg.go.dev/github.com/cinar/indicator#Sma) function calculates the simple moving average for a given period.
Expand Down
29 changes: 29 additions & 0 deletions trend_indicators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,35 @@ func TestQstick(t *testing.T) {
}
}

func TestKdj(t *testing.T) {
low := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
high := []float64{10, 20, 30, 40, 50, 60, 70, 80, 90, 100}
closing := []float64{5, 10, 15, 20, 25, 30, 35, 40, 45, 50}
expectedK := []float64{44.44, 45.91, 46.70, 48.12, 48.66, 48.95, 49.14, 49.26, 49.36, 49.26}
expectedD := []float64{44.44, 45.18, 45.68, 46.91, 47.82, 48.58, 48.91, 49.12, 49.25, 49.30}
expectedJ := []float64{44.44, 47.37, 48.72, 50.55, 50.32, 49.70, 49.58, 49.56, 49.57, 49.19}

k, d, j := DefaultKdj(high, low, closing)

for i := 0; i < len(k); i++ {
actualK := roundDigits(k[i], 2)
actualD := roundDigits(d[i], 2)
actualJ := roundDigits(j[i], 2)

if actualK != expectedK[i] {
t.Fatalf("k %d actual %f expected %f", i, actualK, expectedK[i])
}

if actualD != expectedD[i] {
t.Fatalf("d %d actual %f expected %f", i, actualD, expectedK[i])
}

if actualJ != expectedJ[i] {
t.Fatalf("j %d actual %f expected %f", i, actualJ, expectedK[i])
}
}
}

func TestSma(t *testing.T) {
values := []float64{2, 4, 6, 8, 10}
sma := []float64{2, 3, 5, 7, 9}
Expand Down
37 changes: 37 additions & 0 deletions trend_strategies.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,43 @@ func MakeMovingChandeForecastOscillatorStrategy(period int) StrategyFunction {
}
}

// The KdjStrategy function uses the k, d, j values that are generated by
// the Kdj indicator function to provide a BUY action when k crosses
// above d and j. It is stronger when below 20%. Also the SELL
// action is when k crosses below d and j. It is strong when
// above 80%.
//
// Returns actions.
func KdjStrategy(rPeriod, kPeriod, dPeriod int, asset Asset) []Action {
actions := make([]Action, len(asset.Date))

k, d, j := Kdj(rPeriod, kPeriod, dPeriod, asset.High, asset.Low, asset.Closing)

for i := 0; i < len(actions); i++ {
if (k[i] > d[i]) && (k[i] > j[i]) && (k[i] <= 20) {
actions[i] = BUY
} else if (k[i] < d[i]) && (k[i] < j[i]) && (k[i] >= 80) {
actions[i] = SELL
} else {
actions[i] = HOLD
}
}

return actions
}

// Make KDJ strategy function.
func MakeKdjStrategy(rPeriod, kPeriod, dPeriod int) StrategyFunction {
return func(asset Asset) []Action {
return KdjStrategy(rPeriod, kPeriod, dPeriod, asset)
}
}

// Default KDJ strategy function.
func DefaultKdjStrategy(asset Asset) []Action {
return KdjStrategy(9, 3, 3, asset)
}

// MACD strategy.
func MacdStrategy(asset Asset) []Action {
actions := make([]Action, len(asset.Date))
Expand Down
22 changes: 22 additions & 0 deletions trend_strategies.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Trend strategies generate signals based on a trend indicator.

- [Chande Forecast Oscillator Strategy](#chande-forecast-oscillator-strategy)
- [MACD Strategy](#macd-strategy)
- [KDJ Strategy](#kdj-strategy)
- [Trend Strategy](#trend-strategy)

#### Chande Forecast Oscillator Strategy
Expand All @@ -14,6 +15,27 @@ The [ChandeForecastOscillatorStrategy](https://pkg.go.dev/github.com/cinar/indic
actions := indicator.ChandeForecastOscillatorStrategy(asset)
```

#### KDJ Strategy

The [KdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#KdjStrategy) function uses the _k_, _d_, _j_ values that are generated by the [Kdj](https://pkg.go.dev/github.com/cinar/indicator#Kdj) indicator function to provide a BUY action when _k_ crosses above _d_ and _j_. It is stronger when below 20%. Also the SELL action is when _k_ crosses below _d_ and _j_. It is strong when above 80%.

```golang
actions := indicator.KdjStrategy(rPeriod, kPeriod, dPeriod, asset)
```

As the [KdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#KdjStrategy) function does not match the [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction), the [MakeKdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#MakeKdjStrategy) function takes _rPeriod_, _kPeriod_, _dPeriod_, and provides a strategy function.

```golang
strategy := indicator.MakeKdjStrategy(rPeriod, kPeriod, dPeriod)
actions := strategy(asset)
```

By default, _rPeriod_ of 9, _kPeriod_ of 3, and _dPeriod_ of 3 are used. The [DefaultKdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#DefaultKdjStrategy) function can be used with those periods.

```golang
actions := indicator.DefaultKdjStrategy(asset)
```

#### MACD Strategy

The [MacdStrategy](https://pkg.go.dev/github.com/cinar/indicator#MacdStrategy) uses the _macd_, and _signal_ values that are generated by the [Macd](https://pkg.go.dev/github.com/cinar/indicator#Macd) indicator function to provide a BUY action when _macd_ crosses above _signal_, and SELL action when _macd_ crosses below _signal_.
Expand Down

0 comments on commit 0f0e2d5

Please sign in to comment.