Skip to content

Commit

Permalink
Volatility: Ulcer Index (UI) (#60)
Browse files Browse the repository at this point in the history
Fixes #43
  • Loading branch information
cinar authored Jan 26, 2022
1 parent cef5249 commit 4ff0442
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 11 additions & 0 deletions helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
23 changes: 23 additions & 0 deletions volatility_indicators.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
22 changes: 22 additions & 0 deletions volatility_indicators.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
9 changes: 9 additions & 0 deletions volatility_indicators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

0 comments on commit 4ff0442

Please sign in to comment.