feat: add overlap studies wrappers and align indicator API names

- add TA-Lib bindings and loader registrations for overlap studies:
  TA_ACCBANDS, TA_BBANDS, TA_DEMA, TA_EMA, TA_HT_TRENDLINE, TA_KAMA
- add new public wrappers in overlap_studies.go:
  AccBands, BBands, DEMA, EMA, HT_TRENDLINE, KAMA
- refactor momentum wrapper names to consistent Go-style exports
  (e.g. Aroon/AroonOsc, MACDExt/MACDFix, Stoch/StochF/StochRSI,
  MinusDI/MinusDM, PlusDI/PlusDM, Trix, UltOsc, WillR)
- rename MAType constants to MA_* for clearer namespacing
- update README to reflect the current exported API and overlap coverage
This commit is contained in:
2026-07-24 18:27:36 +09:00
parent 4b07e7c741
commit 89d990ff1b
6 changed files with 350 additions and 63 deletions
+206
View File
@@ -0,0 +1,206 @@
package talib
import "log/slog"
// AccBands - Acceleration Bands: three overlap lines around price.
// The middle band is an SMA of the close; the upper/lower bands are SMAs of the high/low scaled by an intraday-range factor.
//
// factor = 4*(H-L)/(H+L)
// upperRaw = H*(1+factor), lowerRaw = L*(1-factor)
// Upper = SMA(upperRaw, N), Middle = SMA(Close, N), Lower = SMA(lowerRaw, N)
func AccBands(inHigh, inLow, inClose []float64, inTimePeriod int) ([]float64, []float64, []float64) {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outRealUpperBand = make([]float64, len(inClose))
outRealMiddleBand = make([]float64, len(inClose))
outRealLowerBand = make([]float64, len(inClose))
)
if retCode := accbands(
startIdx,
endIdx,
inHigh,
inLow,
inClose,
int32(inTimePeriod),
&outBegIdx,
&outNBElement,
outRealUpperBand,
outRealMiddleBand,
outRealLowerBand,
); retCode != 0 {
slog.Debug("AccBands", "result", retCode)
return nil, nil, nil
}
return outRealUpperBand, outRealMiddleBand, outRealLowerBand
}
// BBands - Bollinger Bands: a moving-average middle band with upper and lower bands offset by a multiple of the standard deviation.
// Used to gauge relative price volatility.
//
// middle = MA(inReal, period); sd = stddev(inReal, period);
// upper = middle + nbDevUpsd;
// lower = middle - nbDevDnsd
//
// Note: The standard deviation uses the population form (dividing by the period), not the sample form.
// The standard deviation is always computed with a simple moving average regardless of the selected MA type.
func BBands(inReal []float64, inTimePeriod int, inNbDevUp, inNbDevDn float64, inMAType MAType) ([]float64, []float64, []float64) {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outRealUpperBand = make([]float64, len(inReal))
outRealMiddleBand = make([]float64, len(inReal))
outRealLowerBand = make([]float64, len(inReal))
)
if retCode := bbands(
startIdx,
endIdx,
inReal,
int32(inTimePeriod),
inNbDevUp,
inNbDevDn,
int32(inMAType),
&outBegIdx,
&outNBElement,
outRealUpperBand,
outRealMiddleBand,
outRealLowerBand,
); retCode != 0 {
slog.Debug("BBands", "result", retCode)
return nil, nil, nil
}
return outRealUpperBand, outRealMiddleBand, outRealLowerBand
}
// DEMA - Double Exponential Moving Average: an EMA combined with an EMA-of-EMA to reduce lag versus a plain EMA.
//
// EMA1 = EMA(inReal, period); EMA2 = EMA(EMA1, period); DEMA = 2*EMA1 - EMA2
//
// Note: A period of 1 performs no smoothing: the output is a copy of the input. Allowed since 0.6.5 (issues #48/#59).
func DEMA(inReal []float64, inTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := dema(
startIdx,
endIdx,
inReal,
int32(inTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
slog.Debug("DEMA", "result", retCode)
return nil
}
return outReal
}
// EMA - Exponential moving average that weights recent prices more heavily via a recursive smoothing factor.
// A core building block seeding or composing many other indicators. Reacts faster than SMA; price above/below EMA suggests up/down trend.
//
// k = 2 / (period + 1);
// EMA_t = (price_t - EMA_{t-1}) * k + EMA_{t-1}.
// Seed: EMA = SMA of first period bars.
//
// Note: A period of 1 performs no smoothing: the output is a copy of the input. Allowed since 0.6.5 (issues #48/#59).
func EMA(inReal []float64, inTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := ema(
startIdx,
endIdx,
inReal,
int32(inTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
slog.Debug("EMA", "result", retCode)
return nil
}
return outReal
}
// HT_TRENDLINE - Ehlers' Hilbert Transform Instantaneous Trendline:
// a smoothed, low-lag overlay whose averaging window adapts to the dominant cycle period measured via Hilbert-transform quadrature (I/Q) analysis of price.
func HT_TRENDLINE(inReal []float64) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := ht_trendline(
startIdx,
endIdx,
inReal,
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
slog.Debug("HT_TRENDLINE", "result", retCode)
return nil
}
return outReal
}
// KAMA - Kaufman Adaptive Moving Average: an EMA whose smoothing factor adapts each bar to an efficiency ratio (directional move vs. total volatility).
// Reacts fast in trends and smooths in ranging markets. Flat KAMA = non-trending/ranging market.
// KAMA tracking price closely = efficient trend.
//
// ER = |price[t] - price[t-period]| / sum(|price[i]-price[i-1]|, last period bars)
// SC = (ER*(2/3 - 2/31) + 2/31)^2
// KAMA[t] = KAMA[t-1] + SC*(price[t] - KAMA[t-1])
//
// Note: A period of 1 performs no smoothing: the output is a copy of the input, consistent with MA(period=1) for every MAType.
// (The natural KAMA math at period 1 would degenerate to a fixed-alpha EMA because the efficiency ratio is always 1, so the copy is made explicit.)
// Allowed since 0.6.5.
func KAMA(inReal []float64, inTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := kama(
startIdx,
endIdx,
inReal,
int32(inTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
slog.Debug("KAMA", "result", retCode)
return nil
}
return outReal
}