diff --git a/README.md b/README.md index d7ff656..7da3a0a 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ The following list of indicators are currently supported by this package: - [Chandelier Exit](volatility_indicators.md#chandelier-exit) - [Moving Standard Deviation (Std)](volatility_indicators.md#moving-standard-deviation-std) - [Projection Oscillator (PO)](volatility_indicators.md#projection-oscillator-po) +- [Ulcer Index (UI)](volatility_indicators.md#ulcer-index-ui) ### Volume Indicators diff --git a/helper.go b/helper.go index 9647859..f6418af 100644 --- a/helper.go +++ b/helper.go @@ -233,3 +233,14 @@ func testEquals(t *testing.T, actual, expected []float64) { } } } + +// Sqrt of given values. +func sqrt(values []float64) []float64 { + result := make([]float64, len(values)) + + for i := 0; i < len(values); i++ { + result[i] = math.Sqrt(values[i]) + } + + return result +} diff --git a/volatility_indicators.go b/volatility_indicators.go index bd3f120..191a867 100644 --- a/volatility_indicators.go +++ b/volatility_indicators.go @@ -164,3 +164,26 @@ func ProjectionOscillator(period, smooth int, high, low, closing []float64) ([]f return po, spo } + +// The Ulcer Index (UI) measures downside risk. The index increases in value +// as the price moves farther away from a recent high and falls as the price +// rises to new highs. +// +// High Closings = Max(period, Closings) +// Percentage Drawdown = 100 * ((Closings - High Closings) / High Closings) +// Squared Average = Sma(period, Percent Drawdown * Percent Drawdown) +// Ulcer Index = Sqrt(Squared Average) +// +// Returns ui. +func UlcerIndex(period int, closing []float64) []float64 { + highClosing := Max(period, closing) + percentageDrawdown := multiplyBy(divide(substract(closing, highClosing), highClosing), 100) + squaredAverage := Sma(period, multiply(percentageDrawdown, percentageDrawdown)) + ui := sqrt(squaredAverage) + return ui +} + +// The default ulcer index with the default period of 14. +func DefaultUlcerIndex(closing []float64) []float64 { + return UlcerIndex(14, closing) +} diff --git a/volatility_indicators.md b/volatility_indicators.md index 13c7917..47a70c0 100644 --- a/volatility_indicators.md +++ b/volatility_indicators.md @@ -9,6 +9,7 @@ Volatility indicators measure the rate of movement regardless of its direction. - [Chandelier Exit](#chandelier-exit) - [Moving Standard Deviation (Std)](#moving-standard-deviation-std) - [Projection Oscillator (PO)](#projection-oscillator-po) +- [Ulcer Index (UI)](#ulcer-index-ui) #### Acceleration Bands @@ -103,6 +104,27 @@ SPO = EMA(smooth, PO) po, spo := indicator.ProjectionOscillator(12, 4, high, low, closing) ``` +#### Ulcer Index (UI) + +The [UlcerIndex](https://pkg.go.dev/github.com/cinar/indicator#UlcerIndex) measures downside risk. The index increases in value as the price moves farther away from a recent high and falls as the price rises to new highs. + +``` +High Closings = Max(period, Closings) +Percentage Drawdown = 100 * ((Closings - High Closings) / High Closings) +Squared Average = Sma(period, Percent Drawdown * Percent Drawdown) +Ulcer Index = Sqrt(Squared Average) +``` + +```golang +ui := indicator.UlcerIndex(period, closing) +``` + +The [DefaultUlcerIndex](https://pkg.go.dev/github.com/cinar/indicator#DefaultUlcerIndex) measures the ulcer index with the default period of 14. + +```golang +ui := indicator.DefaultUlcerIndex(closing) +``` + ## Disclaimer The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. diff --git a/volatility_indicators_test.go b/volatility_indicators_test.go index 5d3f8c1..25c306d 100644 --- a/volatility_indicators_test.go +++ b/volatility_indicators_test.go @@ -17,3 +17,12 @@ func TestStd(t *testing.T) { actual := Std(period, values) testEquals(t, roundDigitsAll(actual, 3), expected) } + +func TestUlcerIndex(t *testing.T) { + closing := []float64{9, 11, 7, 10, 8, 7, 7, 8, 10, 9, 5, 4, 6, 7} + expected := []float64{0, 0, 20.99, 18.74, 20.73, 24.05, 26.17, 26.31, + 24.99, 24.39, 28.49, 32.88, 34.02, 34.19} + + actual := DefaultUlcerIndex(closing) + testEquals(t, roundDigitsAll(actual, 2), expected) +}