mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-23 04:58:08 +00:00
feat: Enhance volume indicators with ADOSC and SSF implementation and validation
This commit is contained in:
+34
-108
@@ -1,136 +1,62 @@
|
||||
# SMA: Simple Moving Average
|
||||
|
||||
## What It Does
|
||||
> "The vanilla ice cream of technical analysis. Boring, ubiquitous, and the only thing your grandfather and your high-frequency trading bot agree on."
|
||||
|
||||
The Simple Moving Average (SMA) is the most fundamental indicator in technical analysis. It calculates the unweighted mean of the previous $N$ data points. By smoothing out price fluctuations, it helps traders identify the direction of the trend and potential support/resistance levels.
|
||||
The Simple Moving Average (SMA) is the unweighted arithmetic mean of the last $N$ data points. It acts as a low-pass filter, smoothing out high-frequency noise to reveal the underlying trend. While conceptually simple, efficient implementation on modern hardware requires careful attention to memory access patterns and vectorization.
|
||||
|
||||
## Historical Context
|
||||
|
||||
The concept of a moving average dates back to the early 20th century, used by statisticians to smooth time series data. In financial markets, it became a cornerstone of technical analysis with the advent of computing, allowing traders to filter out "noise" and focus on the underlying trend.
|
||||
The concept of a moving average dates back to 1901 (R.H. Hooker) for smoothing weather data, but it became a staple of financial analysis in the mid-20th century. It is the baseline against which all other averages are compared.
|
||||
|
||||
## How It Works
|
||||
## Architecture & Physics
|
||||
|
||||
### The Core Idea
|
||||
The naive implementation of SMA sums $N$ numbers at every step, resulting in $O(N)$ complexity. QuanTAlib uses an optimized $O(1)$ approach.
|
||||
|
||||
The SMA treats every price in the lookback window equally. A price from 10 days ago has the same influence on the average as the price from today. This "democracy" of data points makes it stable but slow to react to recent changes compared to weighted averages like EMA or WMA.
|
||||
### O(1) Running Sum
|
||||
|
||||
### Mathematical Foundation
|
||||
We maintain a running `Sum` and a `RingBuffer` of history.
|
||||
$$ Sum_{new} = Sum_{old} - Value_{oldest} + Value_{new} $$
|
||||
$$ SMA = \frac{Sum_{new}}{N} $$
|
||||
|
||||
$$ SMA_t = \frac{P_t + P_{t-1} + \dots + P_{t-n+1}}{n} $$
|
||||
This ensures that calculating an SMA(200) takes the exact same time as an SMA(10).
|
||||
|
||||
Where:
|
||||
### Drift Correction
|
||||
|
||||
- $P$ = Price
|
||||
- $n$ = Period length
|
||||
Floating-point addition is not associative. Repeatedly adding and subtracting values from a running sum introduces cumulative error (drift) over millions of ticks. QuanTAlib implements a periodic **Resync** mechanism (every 1000 ticks) that recalculates the sum from scratch to ensure precision remains within `1e-9` of the true mean.
|
||||
|
||||
### Implementation Details: O(1) Streaming
|
||||
### SIMD Optimization
|
||||
|
||||
A naive implementation sums all $N$ prices every bar, resulting in $O(N)$ complexity. We optimize this to **O(1)** using a sliding window algorithm:
|
||||
For batch processing of large datasets, `Sma.Batch` utilizes `System.Runtime.Intrinsics` (AVX2/AVX-512) to process multiple data points in parallel, significantly outperforming scalar loops.
|
||||
|
||||
$$ Sum_{new} = Sum_{old} - P_{leaving} + P_{entering} $$
|
||||
$$ SMA_{new} = \frac{Sum_{new}}{n} $$
|
||||
## Mathematical Foundation
|
||||
|
||||
This ensures that calculating an SMA(200) takes the exact same amount of CPU time as an SMA(10).
|
||||
### 1. The Mean
|
||||
|
||||
## Configuration
|
||||
|
||||
| Parameter | Default | Purpose | Adjustment Guidelines |
|
||||
|-----------|---------|---------|----------------------|
|
||||
| Period | 10 | Lookback window | Short (10-20) for short-term trends; Medium (50) for intermediate; Long (200) for major trends. |
|
||||
$$ SMA_t = \frac{1}{N} \sum_{i=0}^{N-1} P_{t-i} $$
|
||||
|
||||
## Performance Profile
|
||||
|
||||
| Operation | Complexity | Description |
|
||||
|-----------|------------|-------------------|
|
||||
| Streaming update | O(1) | Sliding window sum |
|
||||
| Bar correction | O(1) | Efficient state rollback |
|
||||
| Batch processing | O(N) | Single pass through data |
|
||||
| Memory footprint | O(period) | RingBuffer for lookback window |
|
||||
The implementation is optimized for both streaming (latency) and batch (throughput) scenarios.
|
||||
|
||||
## Interpretation
|
||||
### Zero-Allocation Design
|
||||
|
||||
### Trading Signals
|
||||
The `RingBuffer` is pre-allocated at initialization. All updates are performed in-place using scalar operations or SIMD intrinsics, ensuring no heap allocations occur during the hot path.
|
||||
|
||||
#### Trend Direction
|
||||
| Metric | Score | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| **Throughput** | High | Optimized running sum |
|
||||
| **Complexity** | O(1) | Constant time update |
|
||||
| **Accuracy** | 5/10 | Baseline accuracy, unweighted |
|
||||
| **Timeliness** | 4/10 | Significant lag (N/2) |
|
||||
| **Overshoot** | 8/10 | Generally stable, no projection |
|
||||
| **Smoothness** | 6/10 | Susceptible to "drop-off" effect |
|
||||
|
||||
- **Uptrend:** Price > SMA and SMA slope is positive.
|
||||
- **Downtrend:** Price < SMA and SMA slope is negative.
|
||||
## Validation
|
||||
|
||||
#### Crossovers
|
||||
Validated against TA-Lib (`TA_SMA`) and Skender.Stock.Indicators.
|
||||
|
||||
- **Golden Cross:** Short-term SMA (e.g., 50) crosses above Long-term SMA (e.g., 200). Bullish.
|
||||
- **Death Cross:** Short-term SMA crosses below Long-term SMA. Bearish.
|
||||
### Common Pitfalls
|
||||
|
||||
#### Support/Resistance
|
||||
|
||||
- The 50-day and 200-day SMAs are widely watched by institutions and often act as self-fulfilling support or resistance levels.
|
||||
|
||||
### When It Works Best
|
||||
|
||||
- **Strong Trends:** In clearly trending markets, SMA keeps you on the right side of the move.
|
||||
|
||||
### When It Struggles
|
||||
|
||||
- **Sideways Markets:** In ranging markets, price will constantly cross the SMA, generating false signals (whipsaws).
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
This implementation makes specific trade-offs:
|
||||
|
||||
### Choice: RingBuffer for History
|
||||
|
||||
- **Implementation:** Uses a circular buffer to store the last $N$ prices.
|
||||
- **Rationale:** Necessary to know which value is leaving the window ($P_{leaving}$) for the O(1) update.
|
||||
|
||||
### Choice: Periodic Resync
|
||||
|
||||
- **Implementation:** Recalculates the full sum every few thousand ticks.
|
||||
- **Rationale:** Prevents floating-point errors from accumulating in the running sum over very long data streams.
|
||||
|
||||
## References
|
||||
|
||||
- Murphy, John J. "Technical Analysis of the Financial Markets." New York Institute of Finance, 1999.
|
||||
|
||||
## C# Usage
|
||||
|
||||
### Streaming Updates (Single Instance)
|
||||
|
||||
```csharp
|
||||
using QuanTAlib;
|
||||
|
||||
var sma = new Sma(period: 20);
|
||||
|
||||
// Process each new bar
|
||||
TValue result = sma.Update(new TValue(timestamp, closePrice));
|
||||
Console.WriteLine($"SMA: {result.Value:F2}");
|
||||
|
||||
// Check if buffer is full
|
||||
if (sma.IsHot)
|
||||
{
|
||||
// Indicator is fully initialized
|
||||
}
|
||||
```
|
||||
|
||||
### Batch Processing (Historical Data)
|
||||
|
||||
```csharp
|
||||
// TSeries API
|
||||
TSeries prices = ...;
|
||||
TSeries smaValues = Sma.Batch(prices, period: 20);
|
||||
|
||||
// Span API (High Performance)
|
||||
double[] prices = new double[1000];
|
||||
double[] output = new double[1000];
|
||||
Sma.Calculate(prices.AsSpan(), output.AsSpan(), period: 20);
|
||||
```
|
||||
|
||||
### Bar Correction (isNew Parameter)
|
||||
|
||||
```csharp
|
||||
var sma = new Sma(20);
|
||||
|
||||
// New bar
|
||||
sma.Update(new TValue(time, 100), isNew: true);
|
||||
|
||||
// Intra-bar update
|
||||
sma.Update(new TValue(time, 101), isNew: false); // Replaces 100 with 101
|
||||
1. **Lag**: SMA has the most lag of all moving averages (Lag $\approx N/2$).
|
||||
2. **Drop-off Effect**: An old, large outlier dropping out of the window causes the SMA to jump, even if the current price is flat. This "Barker effect" is why EMAs are often preferred.
|
||||
3. **NaN Handling**: A single `NaN` in the history window corrupts the entire SMA. QuanTAlib handles this by substituting the last valid value.
|
||||
|
||||
Reference in New Issue
Block a user