feat: add volatility and volume indicator wrappers via purego
- add TA-Lib function pointer signatures for ATR/NATR/TRANGE and AD/ADOSC/CMF/NVI/OBV/PVI/PVO in functions.go - register new native symbols in loader.go - add new wrapper sources: volatility_indicators.go and volume_indicators.go - update README function matrix with new volatility and volume categories - add ta-lib upstream as a git submodule (.gitmodules + ta-lib gitlink)
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "ta-lib"]
|
||||||
|
path = ta-lib
|
||||||
|
url = https://github.com/TA-Lib/ta-lib.git
|
||||||
+116
@@ -1024,4 +1024,120 @@ var (
|
|||||||
outNBElement *int32,
|
outNBElement *int32,
|
||||||
outReal []float64,
|
outReal []float64,
|
||||||
) int32
|
) int32
|
||||||
|
|
||||||
|
atr func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inHigh []float64,
|
||||||
|
inLow []float64,
|
||||||
|
inClose []float64,
|
||||||
|
optInTimePeriod int32,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
natr func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inHigh []float64,
|
||||||
|
inLow []float64,
|
||||||
|
inClose []float64,
|
||||||
|
optInTimePeriod int32,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
trange func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inHigh []float64,
|
||||||
|
inLow []float64,
|
||||||
|
inClose []float64,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
ad func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inHigh []float64,
|
||||||
|
inLow []float64,
|
||||||
|
inClose []float64,
|
||||||
|
inVolume []float64,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
adosc func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inHigh []float64,
|
||||||
|
inLow []float64,
|
||||||
|
inClose []float64,
|
||||||
|
inVolume []float64,
|
||||||
|
optInFastPeriod int32,
|
||||||
|
optInSlowPeriod int32,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
cmf func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inHigh []float64,
|
||||||
|
inLow []float64,
|
||||||
|
inClose []float64,
|
||||||
|
inVolume []float64,
|
||||||
|
optInTimePeriod int32,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
nvi func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inReal []float64,
|
||||||
|
inVolume []float64,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
obv func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inReal []float64,
|
||||||
|
inVolume []float64,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
pvi func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inReal []float64,
|
||||||
|
inVolume []float64,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
|
|
||||||
|
pvo func(
|
||||||
|
startIdx int32,
|
||||||
|
endIdx int32,
|
||||||
|
inVolume []float64,
|
||||||
|
optInFastPeriod int32,
|
||||||
|
optInSlowPeriod int32,
|
||||||
|
optInMAType int32,
|
||||||
|
outBegIdx *int32,
|
||||||
|
outNBElement *int32,
|
||||||
|
outReal []float64,
|
||||||
|
) int32
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -156,6 +156,18 @@ func internalLoad() (uintptr, error) {
|
|||||||
purego.RegisterLibFunc(&tsf, ptr, "TA_TSF")
|
purego.RegisterLibFunc(&tsf, ptr, "TA_TSF")
|
||||||
purego.RegisterLibFunc(&variance, ptr, "TA_VAR")
|
purego.RegisterLibFunc(&variance, ptr, "TA_VAR")
|
||||||
|
|
||||||
|
purego.RegisterLibFunc(&atr, ptr, "TA_ATR")
|
||||||
|
purego.RegisterLibFunc(&natr, ptr, "TA_NATR")
|
||||||
|
purego.RegisterLibFunc(&trange, ptr, "TA_TRANGE")
|
||||||
|
|
||||||
|
purego.RegisterLibFunc(&ad, ptr, "TA_AD")
|
||||||
|
purego.RegisterLibFunc(&adosc, ptr, "TA_ADOSC")
|
||||||
|
purego.RegisterLibFunc(&cmf, ptr, "TA_CMF")
|
||||||
|
purego.RegisterLibFunc(&nvi, ptr, "TA_NVI")
|
||||||
|
purego.RegisterLibFunc(&obv, ptr, "TA_OBV")
|
||||||
|
purego.RegisterLibFunc(&pvi, ptr, "TA_PVI")
|
||||||
|
purego.RegisterLibFunc(&pvo, ptr, "TA_PVO")
|
||||||
|
|
||||||
return ptr, nil
|
return ptr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Submodule
+1
Submodule ta-lib added at 144c635bb4
@@ -0,0 +1,96 @@
|
|||||||
|
package talib
|
||||||
|
|
||||||
|
// ATR - Wilder-smoothed average of the True Range over a period, measuring price volatility regardless of direction.
|
||||||
|
// Higher ATR means greater volatility; no directional bias.
|
||||||
|
//
|
||||||
|
// TR_t = max(high-low, |prevClose-high|, |prevClose-low|)
|
||||||
|
// ATR seed = simple average of first period TR values
|
||||||
|
// ATR_t = (ATR_{t-1} * (period-1) + TR_t) / period
|
||||||
|
func ATR(inHigh, inLow, inClose []float64, optInTimePeriod int) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inClose) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inClose))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := atr(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inHigh,
|
||||||
|
inLow,
|
||||||
|
inClose,
|
||||||
|
int32(optInTimePeriod),
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// NATR - Average True Range expressed as a percentage of the current close, making volatility comparable across price levels and securities.
|
||||||
|
// Same computation as ATR, then normalized by close. Higher values mean greater relative volatility; unit is percent of price.
|
||||||
|
//
|
||||||
|
// NATR = (ATR / Close) * 100
|
||||||
|
// ATR: first value = SMA of TRANGE over period; then Wilder smoothing ATR_t = (ATR_{t-1}*(period-1) + TR_t) / period
|
||||||
|
func NATR(inHigh, inLow, inClose []float64, optInTimePeriod int) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inClose) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inClose))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := natr(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inHigh,
|
||||||
|
inLow,
|
||||||
|
inClose,
|
||||||
|
int32(optInTimePeriod),
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// TRANGE - True Range: the greatest of today's high-low span and the two gaps between yesterday's close and today's high/low.
|
||||||
|
// Base volatility measure used to build ATR/NATR. Larger values mean wider or gappier bars (higher volatility).
|
||||||
|
//
|
||||||
|
// TR = max( high - low, |prevClose - high|, |prevClose - low| )
|
||||||
|
//
|
||||||
|
// Note: The first bar produces no value because it has no prior close;
|
||||||
|
// unlike some definitions, it does not fall back to the high-low range for that bar.
|
||||||
|
func TRANGE(inHigh, inLow, inClose []float64) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inClose) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inClose))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := trange(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inHigh,
|
||||||
|
inLow,
|
||||||
|
inClose,
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
package talib
|
||||||
|
|
||||||
|
// AD - Chaikin Accumulation/Distribution Line, a cumulative volume-flow indicator.
|
||||||
|
// Sums a volume-weighted money-flow multiplier per bar to gauge buying vs. selling pressure.
|
||||||
|
// Rising line = accumulation (buying pressure); falling = distribution.
|
||||||
|
//
|
||||||
|
// MFM = ((close-low) - (high-close)) / (high-low);
|
||||||
|
// AD_t = AD_{t-1} + MFM_t * volume_t (running sum, seeded at 0)
|
||||||
|
func AD(inHigh, inLow, inClose, inVolume []float64) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inClose) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inClose))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := ad(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inHigh,
|
||||||
|
inLow,
|
||||||
|
inClose,
|
||||||
|
inVolume,
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADOsc - Chaikin A/D Oscillator: the difference between a fast and a slow EMA of the Accumulation/Distribution line.
|
||||||
|
// Highlights momentum in accumulation/distribution volume flow.
|
||||||
|
// Positive/rising suggests accumulation; negative/falling suggests distribution.
|
||||||
|
//
|
||||||
|
// ad += ((close-low)-(high-close))/(high-low) * volume (only when high>low)
|
||||||
|
// fastEMA = fastk*ad + (1-fastk)fastEMA, fastk = 2/(optInFastPeriod+1)
|
||||||
|
// slowEMA = slowkad + (1-slowk)*slowEMA, slowk = 2/(optInSlowPeriod+1)
|
||||||
|
// ADOSC = fastEMA - slowEMA
|
||||||
|
//
|
||||||
|
// @param inHigh []float64
|
||||||
|
// @param inLow []float64
|
||||||
|
// @param inClose []float64
|
||||||
|
// @param inVolume []float64
|
||||||
|
// @param fastPeriod int Period of the fast A/D EMA; default is 3 (2–100000)
|
||||||
|
// @param slowPeriod int Period of the slow A/D EMA; default is 10 (2–100000)
|
||||||
|
// @return []float64 Fast-EMA minus slow-EMA of the A/D line
|
||||||
|
func ADOsc(inHigh, inLow, inClose, inVolume []float64, fastPeriod, slowPeriod int) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inClose) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inClose))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := adosc(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inHigh,
|
||||||
|
inLow,
|
||||||
|
inClose,
|
||||||
|
inVolume,
|
||||||
|
int32(fastPeriod),
|
||||||
|
int32(slowPeriod),
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMF - Chaikin Money Flow: over a trailing window of optInTimePeriod bars, the sum of each bar's money flow volume divided by the sum of its volume.
|
||||||
|
// The result is a ratio in [-1, +1].
|
||||||
|
// A bar's money flow volume is its volume scaled by where the close sat inside the bar's range: a close at the high contributes the full volume,
|
||||||
|
// a close at the low contributes minus the full volume, and a close at the midpoint contributes nothing.
|
||||||
|
// Summing that over a window and dividing by the window's volume answers "over these N bars, what share of the traded volume closed near the top of its range?"
|
||||||
|
//
|
||||||
|
// Above zero is accumulation, below zero is distribution, and the distance from zero measures conviction.
|
||||||
|
// Because the divisor is the window's own volume, the output is comparable across instruments and across time in a way a raw accumulation total is not.
|
||||||
|
//
|
||||||
|
// Created by Marc Chaikin, who also authored the AD line this shares its per-bar multiplier with.
|
||||||
|
// CMF is that same multiplier summed over a fixed window and normalised, where AD accumulates it from the start of the series without bound.
|
||||||
|
//
|
||||||
|
// t = high[i] - low[i]
|
||||||
|
// mfv[i] = ((close[i] - low[i]) - (high[i] - close[i])) / t * volume[i], or 0 when t is not positive
|
||||||
|
// CMF[i] = ( sum_{k=i-N+1..i} mfv[k] ) / ( sum_{k=i-N+1..i} volume[k] ), N = optInTimePeriod
|
||||||
|
// There is no seeding and no recursion, hence no unstable period. Each output depends only on the N bars in its own window.
|
||||||
|
//
|
||||||
|
// Note: The output is the raw ratio in [-1, +1], matching every published definition.
|
||||||
|
// Some retail platforms display it multiplied by 100; that is a presentation choice, not a different indicator.
|
||||||
|
//
|
||||||
|
// Each bar's close is expected to lie within its own [low, high], and its volume to be finite and non-negative.
|
||||||
|
// A close outside its bar makes the multiplier exceed ±1 and is passed through unclamped, exactly as AD does.
|
||||||
|
//
|
||||||
|
// A bar whose high equals its low has no range for the close to sit inside, so it contributes exactly zero money flow volume rather than dividing by zero.
|
||||||
|
// Its volume still counts toward the divisor.
|
||||||
|
//
|
||||||
|
// A window whose volume is entirely zero has no money flow to distribute and reports 0.0.
|
||||||
|
// Published references are silent here and other implementations divide by zero; TA-Lib does not return NaN from a successful call.
|
||||||
|
//
|
||||||
|
// Bars where the low exceeds the high are malformed rather than degenerate, and also contribute zero.
|
||||||
|
// The default period of 20 follows the original write-up, which describes 20 or 21 bars.
|
||||||
|
//
|
||||||
|
// @param inHigh []float64
|
||||||
|
// @param inLow []float64
|
||||||
|
// @param inClose []float64
|
||||||
|
// @param inVolume []float64
|
||||||
|
// @param period int Lookback window; default is 20 (2–100000)
|
||||||
|
// @return []float64
|
||||||
|
//
|
||||||
|
// since TA-Lib 0.8.1
|
||||||
|
func CMF(inHigh, inLow, inClose, inVolume []float64, period int) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inClose) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inClose))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := cmf(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inHigh,
|
||||||
|
inLow,
|
||||||
|
inClose,
|
||||||
|
inVolume,
|
||||||
|
int32(period),
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// NVI - Negative Volume Index: a running cumulative index that changes only on days when
|
||||||
|
// volume falls versus the prior day, compounding that day's percentage price change.
|
||||||
|
// The premise is that quiet, low-volume days reflect the actions of well-informed
|
||||||
|
// "smart money", so NVI is read as a proxy for that cohort's positioning.
|
||||||
|
//
|
||||||
|
// NVI[startIdx] = 1000
|
||||||
|
//
|
||||||
|
// For each subsequent bar i:
|
||||||
|
// NVI[i] = NVI[i-1] + ( inVolume[i] < inVolume[i-1] ? ((inClose[i] - inClose[i-1]) / inClose[i-1]) * NVI[i-1] : 0 )
|
||||||
|
//
|
||||||
|
// The index carries forward unchanged on bars whose volume did not fall (and on the
|
||||||
|
// degenerate case of a zero previous close, which would otherwise divide by zero).
|
||||||
|
//
|
||||||
|
// since TA-Lib 0.8.1
|
||||||
|
func NVI(inReal, inVolume []float64) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inReal) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inReal))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := nvi(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inReal,
|
||||||
|
inVolume,
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// OBV - On Balance Volume: a running cumulative total of volume, added on up-price bars and subtracted on down-price bars.
|
||||||
|
// Relates volume flow to price direction.
|
||||||
|
func OBV(inReal, inVolume []float64) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inReal) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inReal))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := obv(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inReal,
|
||||||
|
inVolume,
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// PVI - Positive Volume Index: a running cumulative index that changes only on days when
|
||||||
|
// volume rises versus the prior day, compounding that day's percentage price change.
|
||||||
|
// The premise is that active, high-volume days reflect the actions of the
|
||||||
|
// less-informed "crowd", so PVI is read as a proxy for that cohort's positioning.
|
||||||
|
//
|
||||||
|
// PVI[startIdx] = 1000
|
||||||
|
//
|
||||||
|
// For each subsequent bar i:
|
||||||
|
// PVI[i] = PVI[i-1] + ( inVolume[i] > inVolume[i-1] ? ((inClose[i] - inClose[i-1]) / inClose[i-1]) * PVI[i-1] : 0 )
|
||||||
|
//
|
||||||
|
// The index carries forward unchanged on bars whose volume did not rise (and on the
|
||||||
|
// degenerate case of a zero previous close, which would otherwise divide by zero).
|
||||||
|
//
|
||||||
|
// since TA-Lib 0.8.1
|
||||||
|
func PVI(inReal, inVolume []float64) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inReal) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inReal))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := pvi(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inReal,
|
||||||
|
inVolume,
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
|
|
||||||
|
// PVO - Percentage Volume Oscillator: a variation of the Percentage Price Oscillator (PPO, created by Gerald Appel)
|
||||||
|
// applied to the volume series instead of price. It is the difference between a fast and slow moving average of volume,
|
||||||
|
// expressed as a percentage of the slow talib.MAType.
|
||||||
|
// Positive when short-term volume is above its longer-term average (rising participation), negative when below.
|
||||||
|
// The default periods (12, 26) match MACD and PPO.
|
||||||
|
//
|
||||||
|
// PVO = ((fastMA(inVolume) - slowMA(inVolume)) / slowMA(inVolume)) * 100, both MAs of type optInMAType; output = 0 when slowMA == 0
|
||||||
|
//
|
||||||
|
// The standard form is exponential with periods 12 and 26 — ((12-day EMA of Volume - 26-day EMA of Volume) / 26-day EMA of Volume) * 100,
|
||||||
|
// i.e. the talib.PPO/talib.MACD oscillator computed on volume.
|
||||||
|
// optInMAType therefore defaults to talib.EMA — the moving average Gerald Appel used for the original talib.PPO/talib.MACD;
|
||||||
|
// pass another type (e.g. talib.MA_SMA) to override.
|
||||||
|
//
|
||||||
|
// @param inVolume []float64
|
||||||
|
// @param fastPeriod int Period of the fast moving average; default is 12 (2–100000)
|
||||||
|
// @param slowPeriod int Period of the slow moving average; default is 26 (2–100000)
|
||||||
|
// @param ma MAType Type of moving average to use; default is talib.EMA
|
||||||
|
// @return []float64 Percentage Volume Oscillator
|
||||||
|
//
|
||||||
|
// since TA-Lib 0.8.1
|
||||||
|
func PVO(inVolume []float64, fastPeriod, slowPeriod int, ma MAType) []float64 {
|
||||||
|
var (
|
||||||
|
startIdx int32
|
||||||
|
endIdx = int32(len(inVolume) - 1)
|
||||||
|
outBegIdx int32
|
||||||
|
outNBElement int32
|
||||||
|
outReal = make([]float64, len(inVolume))
|
||||||
|
)
|
||||||
|
|
||||||
|
if retCode := pvo(
|
||||||
|
startIdx,
|
||||||
|
endIdx,
|
||||||
|
inVolume,
|
||||||
|
int32(fastPeriod),
|
||||||
|
int32(slowPeriod),
|
||||||
|
int32(ma),
|
||||||
|
&outBegIdx,
|
||||||
|
&outNBElement,
|
||||||
|
outReal,
|
||||||
|
); retCode != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return outReal
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user