The Geometric Mean computes the nth root of the product of n positive values over a sliding window. Unlike the arithmetic mean, it captures multiplicative relationships and is the correct average for growth rates, ratios, and log-normally distributed data. For financial time series, this means it properly accounts for compounding.
## Historical Context
The geometric mean dates to Euclid's Elements (ca. 300 BCE), where it appeared as the "mean proportional" between two lengths. The concept was well-understood in ancient Greek geometry but found its modern statistical footing in the 19th century. In finance, the geometric mean return became the standard for reporting compounded investment performance after the realization that arithmetic means systematically overstate expected returns for volatile assets. A portfolio returning +50% then -50% has an arithmetic mean of 0% but a geometric mean of approximately -13.4%, which is the actual result. The arithmetic mean lied; the geometric mean told the truth.
## Architecture & Physics
`Geomean` extends `AbstractBase` for single-value input streaming. Instead of computing the nth root of a product directly (which overflows or underflows for even modest windows), it maintains a running sum of logarithms using Kahan-Babuska compensated summation.
### Design Decisions
1.**Log-sum approach**: Converts the product $\prod x_i$ into $\sum \ln(x_i)$, then exponentiates. This avoids catastrophic overflow/underflow that plagues direct multiplication for windows larger than approximately 20 values.
2.**O(1) streaming updates**: Uses a `RingBuffer` to track which log-values are in the window. When a new value enters, its log is added; when an old value exits, its log is subtracted. The Kahan-Babuska compensation preserves numerical accuracy across millions of updates.
3.**Periodic resync**: Every 1000 ticks, the running sum is recomputed from scratch to bound floating-point drift. Without this, sequential add/subtract cycles accumulate error proportional to the number of updates.
4.**Non-positive value handling**: Values <= 0 have undefined logarithms. The indicator substitutes the last valid positive value, matching the PineScript reference behavior. This is conservative but safe.
5.**No SIMD in Update**: The streaming path is inherently sequential (running compensated sum with state). SIMD is used in the static `Batch(Span)` method where applicable.
## Mathematical Foundation
For $n$ positive values $x_1, x_2, \ldots, x_n$, the geometric mean is:
$$ G = \left(\prod_{i=1}^{n} x_i\right)^{1/n} $$
Equivalently, using logarithms:
$$ G = \exp\!\left(\frac{1}{n} \sum_{i=1}^{n} \ln(x_i)\right) $$
Self-validated against mathematical properties and known analytical values. MathNet.Numerics `Statistics.GeometricMean` provides external cross-validation.
1.**Zero or negative values**: The geometric mean is undefined for non-positive values. The indicator substitutes the last valid value, but this is a lossy approximation. Filter your data first if zeros are meaningful.
2.**Overflow with direct multiplication**: Never compute $\prod x_i$ directly for large windows. Even double-precision overflows around $n \approx 20$ for values > 100. The log-sum approach eliminates this entirely.
3.**Confusing with arithmetic mean**: The geometric mean is always smaller (or equal) for positive values. Using the arithmetic mean for compounding returns overstates expected performance.
4.**Small windows**: With period=2, the geometric mean reduces to $\sqrt{x_1 \cdot x_2}$. Mathematically correct but noisy.
5.**Log-normal assumption**: The geometric mean is the natural center for log-normally distributed data (returns). For normally distributed data, the arithmetic mean is more appropriate.