diff --git a/README.md b/README.md index 253dd15..81dc83f 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ The following list of indicators are currently supported by this package: - [Ease of Movement (EMV)](volume_indicators.md#ease-of-movement-emv) - [Force Index (FI)](volume_indicators.md#force-index-fi) - [Money Flow Index (MFI)](volume_indicators.md#money-flow-index-mfi) +- [Negative Volume Index (NVI)](volume_indicators.md#negative-volume-index-nvi) - [On-Balance Volume (OBV)](volume_indicators.md#on-balance-volume-obv) - [Volume Price Trend (VPT)](volume_indicators.md#volume-price-trend-vpt) - [Volume Weighted Average Price (VWAP)](volume_indicators.md#volume-weighted-average-price-vwap) @@ -97,6 +98,7 @@ The following list of strategies are currently supported by this package: - [Ease of Movement Strategy](volume_strategies.md#ease-of-movement-strategy) - [Force Index Strategy](volume_strategies.md#force-index-strategy) - [Money Flow Index Strategy](volume_strategies.md#money-flow-index-strategy) +- [Negative Volume Index Strategy](volume_strategies.md#negative-volume-index-strategy) - [Volume Weighted Average Price Strategy](volume_strategies.md#volume-weighted-average-price-strategy) ### Compound Strategies diff --git a/volume_indicators.go b/volume_indicators.go index fd68c44..88d762f 100644 --- a/volume_indicators.go +++ b/volume_indicators.go @@ -156,3 +156,35 @@ func VolumeWeightedAveragePrice(period int, closing []float64, volume []int64) [ func DefaultVolumeWeightedAveragePrice(closing []float64, volume []int64) []float64 { return VolumeWeightedAveragePrice(14, closing, volume) } + +// The Negative Volume Index (NVI) is a cumulative indicator using +// the change in volume to decide when the smart money is active. +// +// If Volume is greather than Previous Volume: +// +// NVI = Previous NVI +// +// Otherwise: +// +// NVI = Previous NVI + (((Closing - Previous Closing) / Previous Closing) * Previous NVI) +// +// Returns nvi values. +func NegativeVolumeIndex(closing []float64, volume []int64) []float64 { + if len(closing) != len(volume) { + panic("not all same size") + } + + nvi := make([]float64, len(closing)) + + for i := 0; i < len(nvi); i++ { + if i == 0 { + nvi[i] = 1000 + } else if volume[i-1] < volume[i] { + nvi[i] = nvi[i-1] + } else { + nvi[i] = nvi[i-1] + (((closing[i] - closing[i-1]) / closing[i-1]) * nvi[i-1]) + } + } + + return nvi +} diff --git a/volume_indicators.md b/volume_indicators.md index 686d7d9..28394a1 100644 --- a/volume_indicators.md +++ b/volume_indicators.md @@ -6,6 +6,7 @@ Volumne indicators measure the strength of a trend based the volume. - [Ease of Movement (EMV)](#ease-of-movement-emv) - [Force Index (FI)](#force-index-fi) - [Money Flow Index (MFI)](#money-flow-index-mfi) +- [Negative Volume Index (NVI)](#negative-volume-index-nvi) - [On-Balance Volume (OBV)](#on-balance-volume-obv) - [Volume Price Trend (VPT)](#volume-price-trend-vpt) - [Volume Weighted Average Price (VWAP)](#volume-weighted-average-price-vwap) @@ -83,6 +84,24 @@ result := indicator.MoneyFlowIndex(period, high, low, closing, volume) The [DefaultMoneyFlowIndex](https://pkg.go.dev/github.com/cinar/indicator#DefaultMoneyFlowIndex) function uses the default period of 14. +#### Negative Volume Index (NVI) + +The [NegativeVolumeIndex](https://pkg.go.dev/github.com/cinar/indicator#NegativeVolumeIndex) function calculates a cumulative indicator using the change in volume to decide when the smart money is active. + +``` +If Volume is greather than Previous Volume: + + NVI = Previous NVI + +Otherwise: + + NVI = Previous NVI + (((Closing - Previous Closing) / Previous Closing) * Previous NVI) +``` + +```Golang +result := indicator.NegativeVolumeIndex(closing, volume) +``` + #### On-Balance Volume (OBV) The [Obv](https://pkg.go.dev/github.com/cinar/indicator#Obv) function calculates a technical trading momentum indicator that uses volume flow to predict changes in stock price. diff --git a/volume_indicators_test.go b/volume_indicators_test.go index 2245226..defcb60 100644 --- a/volume_indicators_test.go +++ b/volume_indicators_test.go @@ -75,3 +75,12 @@ func TestDefaultVolumeWeightedAveragePrice(t *testing.T) { actual := roundDigitsAll(DefaultVolumeWeightedAveragePrice(closing, volume), 2) testEquals(t, actual, expected) } + +func TestNegativeVolumeIndex(t *testing.T) { + closing := []float64{9, 11, 7, 10, 8} + volume := []int64{100, 110, 80, 120, 90} + expected := []float64{1000, 1000, 636.36, 636.36, 509.09} + + actual := roundDigitsAll(NegativeVolumeIndex(closing, volume), 2) + testEquals(t, actual, expected) +} diff --git a/volume_strategies.go b/volume_strategies.go index d03e42f..10f9c84 100644 --- a/volume_strategies.go +++ b/volume_strategies.go @@ -82,3 +82,23 @@ func VolumeWeightedAveragePriceStrategy(asset Asset) []Action { return actions } + +// Negative volume index strategy. +func NegativeVolumeIndexStrategy(asset Asset) []Action { + actions := make([]Action, len(asset.Date)) + + nvi := NegativeVolumeIndex(asset.Closing, asset.Volume) + nvi255 := Ema(255, nvi) + + for i := 0; i < len(actions); i++ { + if nvi[i] < nvi255[i] { + actions[i] = BUY + } else if nvi[i] > nvi255[i] { + actions[i] = SELL + } else { + actions[i] = HOLD + } + } + + return actions +} diff --git a/volume_strategies.md b/volume_strategies.md index 3979745..6c66297 100644 --- a/volume_strategies.md +++ b/volume_strategies.md @@ -5,6 +5,7 @@ Volume strategies generate signals based on a volume indicator. - [Ease of Movement Strategy](#ease-of-movement-strategy) - [Force Index Strategy](#force-index-strategy) - [Money Flow Index Strategy](#money-flow-index-strategy) +- [Negative Volume Index Strategy](#negative-volume-index-strategy) - [Volume Weighted Average Price Strategy](#volume-weighted-average-price-strategy) #### Ease of Movement Strategy @@ -31,6 +32,14 @@ The [MoneyFlowIndexStrategy](https://pkg.go.dev/github.com/cinar/indicator#Money actions := indicator.MoneyFlowIndexStrategy(asset) ``` +#### Negative Volume Index Strategy + +The [NegativeVolumeIndexStrategy](https://pkg.go.dev/github.com/cinar/indicator#NegativeVolumeIndexStrategy) uses the _nvi_ values that are generated by the [Negative Volume Index (NVI)](volume_indicators.md#negative-volume-index-nvi) indicator function to provide a _BUY_ action when _nvi_ is less than its 255-period EMA, and a _SELL_ action when it is greather than its 255-period EMA, otherwise a _HOLD_ action. + +```Golang +actions := indicator.NegativeVolumeIndexStrategy(asset) +``` + #### Volume Weighted Average Price Strategy The [VolumeWeightedAveragePriceStrategy]([./volumeWeightedAveragePriceStrategy.ts](https://pkg.go.dev/github.com/cinar/indicator#VolumeWeightedAveragePriceStrategy)) uses the values that are generated by the [Volume Weighted Average Price (VWAP)](volume_indicators.md#volume-weighted-average-price-vwap) indicator function to provide a _BUY_ action when the _closing_ is below the _VWAP_, and a _SELL_ action when the _closing_ is below the _VWAP_, a _HOLD_ action otherwise.