# SGF: Savitzky-Golay Filter

> "SMA smoothes. Savitzky-Golay understands."

SGF (Savitzky-Golay Filter) is a digital signal processing technique that smoothes data by fitting successive sub-sets of adjacent data points with a low-degree polynomial by the method of linear least squares. Unlike standard moving averages that simply average the points, SGF preserves higher moments of the data distribution, such as the area, center of gravity, and line width. This makes it exceptionally good at preserving features of the distribution such as relative maxima and minima and width, which are usually flattened by other smoothing techniques.

## Historical Context

Introduced by Abraham Savitzky and Marcel J. E. Golay in 1964, this filter revolutionized analytical chemistry (spectroscopy) by allowing noise reduction without distorting the signal shape. In quantitative trading, it is prized for its ability to smooth price data while preserving significant peaks and valleys that are crucial for identifying support and resistance levels or turning points.

## Architecture & Physics

The implementation uses a Finite Impulse Response (FIR) architecture where the convolution coefficients are determined by the polynomial order and window size.

* **Kernel Construction:** Weights are derived ensuring the least-squares fit of a polynomial of Degree $order$ over a window of Size $N$.
* **Polynomial Order:** Determines the complexity of the curve fitting. Order 2 is a quadratic fit (preserves curvature), Order 4 is quartic (preserves more detail).
* **Convolution:** The filter is applied as a weighted moving average using these calculated coefficients.
* **Boundary Handling:** During startup (warmup) or when initialized, the filter uses partial window convolution with normalization to provide valid outputs immediately.

### Characteristics

* **Preservation:** Excellent at preserving peak heights and widths.
* **Lag:** Introduces minimal lag compared to moving averages of similar length, as the polynomial fit can better track changes in direction.
* **Smoothness:** Produces a differentiable curve if the polynomial order is sufficient.

## Mathematical Foundation

### 1. Coefficient Calculation

For a polynomial of order $p$ and window size $2m+1$ (where $N=2m+1$), the coefficients $c_k$ at position $k$ (where $-m \le k \le m$) are derived from the least squares solution.

For a 2nd order polynomial ($p=2$), the weights are proportional to:
$$ w_k = 3(3N^2 - 7 - 20k^2) $$

For a 4th order polynomial ($p=4$), the weights are proportional to:
$$ w_k = 15 - 20k^2 + 6k^4 $$

### 2. Normalization

The weights are normalized so their sum equals 1:
$$ W_k = \frac{w_k}{\sum_{j=-m}^{m} w_j} $$

### 3. Convolution

The smoothed value $y_t$ is obtained by convolving the input sequence $x$ with the normalized weights $W$:
$$ y_t = \sum_{k=-m}^{m} W_k \cdot x_{t+k} $$

Note: In a causal (real-time) implementation, the kernel is shifted to operate on past data points ($t-N+1$ to $t$).

## Performance Profile

| Metric | Score | Notes |
| :--- | :--- | :--- |
| **Throughput** | 10 ns/bar | SIMD-optimized static calculation; RingBuffer-optimized streaming. |
| **Allocations** | 0 | Core update loop is allocation-free. |
| **Complexity** | O(N) | Linear with respect to window size. |
| **Accuracy** | 10/10 | Exact polynomial least-squares fit. |
| **Timeliness** | 9/10 | Low lag; tracks turning points extremely well. |
| **Overshoot** | 2/10 | Can exhibit slight overshoot/undershoot at sharp transitions (Gibbs phenomenon-like behavior) due to polynomial fitting, which is sometimes desirable for peak detection. |
| **Smoothness** | 8/10 | Very smooth, derivative-preserving. |

## Validation

The implementation is validated against a reference polynomial fitting algorithm to ensure mathematical correctness.

| Library | Status | Notes |
| :--- | :--- | :--- |
| **Reference** | ✅ | Matches independent least-squares calculation with $10^{-9}$ precision. |
| **Quantower** | ✅ | Verified visual alignment. |

### Common Pitfalls

* **Polynomial Order vs Window Size:** The window size must be significantly larger than the polynomial order. A rule of thumb is $N \ge p + 3$. If $N \approx p$, the filter performs little smoothing (overfitting).
* **Overshoot:** Unlike Gaussian or SMA, SGF can produce values outside the range of the input data (overshoot) at sharp corners. This is mathematically correct behavior for a polynomial fit but can be surprising.

## C# Usage

```csharp
// Initialize with period=11, polynomial order=2
var sgf = new Sgf(period: 11, polyOrder: 2);

// Update with a new value
var result = sgf.Update(new TValue(DateTime.UtcNow, 100.0));

// Static calculation on a span
Sgf.Calculate(inputSpan, outputSpan, period: 21, polyOrder: 4);

// Chainable using TValuePublisher
var sgf = new Sgf(source, period: 14);
