feat(stats): add regression and volatility wrappers

- add native TA-Lib bindings in functions.go for beta, correl, linear regression, stddev, tsf, and variance
- register the new TA-Lib symbols in loader.go
- add public wrappers in statistic_functions.go for Beta, Correl, LinearReg, LinearRegAngle, LinearRegIntercept, LinearRegSlope, StdDev, TSF, and Var
- update README.md to document Price Transforms and Statistic Functions
This commit is contained in:
2026-07-27 18:10:46 +09:00
parent 7cf9ab6e24
commit fc504d0c01
4 changed files with 393 additions and 1 deletions
+288
View File
@@ -0,0 +1,288 @@
package talib
// Beta - Beta: the slope of a least-squares linear regression of one series' percentage returns (y, from inReal1) against another's (x, from inReal0) over a rolling window.
// Measures how much a security moves relative to a market index.
// Beta = 1 moves with the index; < 1 less volatile, > 1 more volatile.
//
// @param inReal0: Series whose returns are the regression x (market/index)
// @param inReal1: Series whose returns are the regression y (security)
// @param optInTimePeriod: Rolling window length (number of returns) for the regression sums; default is 5 (1-100000)
//
// @return: regression slope of inReal1-returns on inReal0-returns
func Beta(inReal0, inReal1 []float64, optInTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal0) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal0))
)
if retCode := beta(
startIdx,
endIdx,
inReal0,
inReal1,
int32(optInTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// Correl - Pearson's correlation coefficient (r) between two input series over a rolling window of optInTimePeriod bars.
// Measures how linearly the two series move together. r near +1: strong positive co-movement; near -1: strong inverse; near 0: no linear relationship.
//
// r = (sumXY - sumX*sumY/n) / sqrt((sumX2 - sumX^2/n) * (sumY2 - sumY^2/n)), n = optInTimePeriod, sums over the window
//
// Note: When the correlation is undefined for a window (for example a constant series), the output is 0 rather than an error or NaN.
//
// @param inReal0: First data series (X)
// @param inReal1: Second data series (Y)
// @param optInTimePeriod: Rolling window length (number of bars) for the correlation sums; default is 30 (2-100000)
//
// @return: Correlation coefficient r in [-1, 1]
func Correl(inReal0, inReal1 []float64, optInTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal0) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal0))
)
if retCode := correl(
startIdx,
endIdx,
inReal0,
inReal1,
int32(optInTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// LinearReg - Least-squares straight-line fit over the last optInTimePeriod bars, reported as the fitted line value at the window endpoint (b + m*(period-1)).
//
// @param inReal: Input series
// @param optInTimePeriod: Number of bars (period) for the regression; default is 14 (2-100000)
//
// @return: Fitted line value at the window endpoint
func LinearReg(inReal []float64, optInTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := linearreg(
startIdx,
endIdx,
inReal,
int32(optInTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// LinearRegAngle - The angle, in degrees, of the least-squares best-fit line over the last N points.
// It is the LINEARREG_SLOPE value passed through atan and converted to degrees.
// Positive angle = rising fit line, negative = falling; magnitude reflects steepness.
//
// m = (N·SumXY SumX·SumY) / (SumX² N·SumXSqr), with SumX=N(N1)/2, SumXSqr=N(N1)(2N1)/6; angle = atan(m)·(180/π)
func LinearRegAngle(inReal []float64, optInTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := linearreg_angle(
startIdx,
endIdx,
inReal,
int32(optInTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// LinearRegIntercept - Returns the y-intercept (b) of the least-squares regression line fitted over the last optInTimePeriod values.
// Part of the linear-regression family (LINEARREG, SLOPE, ANGLE, TSF).
//
// Fit y = b + m·x over the window with x = bars-ago (x=0 is the current bar, x=period-1 the oldest).
// With SumX = period(period-1)/2, SumXSqr = period(period-1)(2·period-1)/6, Divisor = SumX² period·SumXSqr:
// m = (period·SumXY SumX·SumY) / Divisor
// b = (SumY m·SumX) / period ← output
func LinearRegIntercept(inReal []float64, optInTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := linearreg_intercept(
startIdx,
endIdx,
inReal,
int32(optInTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// LinearRegSlope - Slope 'm' of the least-squares best-fit line (y = b + m*x) over the last optInTimePeriod bars.
// Reports the per-bar rate of change of the fitted trend line.
// Positive slope = rising trend, negative = falling; magnitude is price change per bar.
//
// m = (n·SumXY SumX·SumY) / Divisor
// SumX = n(n1)/2, SumXSqr = n(n1)(2n1)/6, Divisor = SumX² n·SumXSqr
// SumXY = Σ i·y[todayi], SumY = Σ y[todayi], i=0..n1, n=period, y=inReal
func LinearRegSlope(inReal []float64, optInTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := linearreg_slope(
startIdx,
endIdx,
inReal,
int32(optInTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// StdDev - Rolling standard deviation of a series over a window, scaled by a deviations multiplier.
// Delegates to VAR, then takes the square root.
//
// Note: Uses population variance (divides by the period, not period minus one), so results differ slightly from the sample standard deviation used by some tools.
// @param inReal: Input series
// @param optInTimePeriod: Number of bars (period) for the rolling window; default is 5 (2-100000)
// @param optInNbDev: Multiplier for the standard deviation; default is 1.0 (0.0-500.0)
//
// @return: Rolling standard deviation scaled by optInNbDev
func StdDev(inReal []float64, optInTimePeriod int, optInNbDev float64) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := stddev(
startIdx,
endIdx,
inReal,
int32(optInTimePeriod),
optInNbDev,
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// TSF - Time Series Forecast: fits a least-squares linear regression line over the last N bars and projects it one x-step beyond talib.LinearReg.
// Same regression as talib.LinearReg but evaluated at x=period instead of x=period-1.
//
// Fit y=b+mx over window (x=0..N-1): m = (NSumXY - SumXSumY)/(SumX^2 - NSumXSqr), b = (SumY - mSumX)/N; output = b + mN.
// With SumX=N(N-1)/2, SumXSqr=N(N-1)(2N-1)/6.
func TSF(inReal []float64, optInTimePeriod int) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := tsf(
startIdx,
endIdx,
inReal,
int32(optInTimePeriod),
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}
// Var - Rolling population variance of a real series over a given period.
// Measures dispersion of values around their mean. Higher values indicate greater dispersion; 0 means constant input.
//
// Var = (SumX2 - SumX^2/n) / n, n = optInTimePeriod, sums over the window
//
// Note: Computes population variance (divides by the period), not the sample variance (n-1) used by some definitions.
// The deviation-count parameter is accepted but has no effect on the result.
func Var(inReal []float64, optInTimePeriod int, optInNbDev float64) []float64 {
var (
startIdx int32
endIdx = int32(len(inReal) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]float64, len(inReal))
)
if retCode := variance(
startIdx,
endIdx,
inReal,
int32(optInTimePeriod),
optInNbDev,
&outBegIdx,
&outNBElement,
outReal,
); retCode != 0 {
return nil
}
return outReal
}