Files
QuanTAlib/lib/dynamics/amat/Amat.md
T
86fe32a682 SIMD Refactor: Merge simd-dev into dev (#55)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
Co-authored-by: Warp <agent@warp.dev>
2026-01-18 19:02:03 -08:00

196 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AMAT: Archer Moving Averages Trends
> "Markets trend about 30% of the time. The trick isn't just finding trends—it's confirming them before your stops get hit."
AMAT (Archer Moving Averages Trends) is a trend identification system that uses dual EMAs to provide clear directional signals. Unlike simple moving average crossovers that generate signals on any intersection, AMAT requires **alignment** of both fast and slow averages moving in the same direction—reducing false signals during choppy, sideways markets.
## Historical Context
AMAT emerged from concepts developed by Mark Whistler (known as "Archer" in trading circles) and was formalized by Tom Joseph in 2009. The indicator addresses a fundamental problem with traditional crossover systems: they generate excessive whipsaws in ranging markets because a crossover only measures relative position, not directional agreement.
The innovation lies in requiring **three conditions** for a trend signal:
1. Relative position (fast above/below slow)
2. Fast EMA direction (rising/falling)
3. Slow EMA direction (rising/falling)
This triple-confirmation approach filters out the noise inherent in single-condition systems.
## Architecture & Physics
AMAT operates on dual EMA calculations with directional analysis. The computational flow:
```
Input Price
├──► Fast EMA ───► Direction (rising/falling)
│ │
│ ▼
└──► Slow EMA ───► Direction (rising/falling)
Trend Logic (+1, -1, 0)
Strength = |Fast - Slow| / Slow × 100
```
### Trend State Machine
| State | Fast vs Slow | Fast Direction | Slow Direction |
|:------|:------------|:---------------|:---------------|
| **Bullish (+1)** | Fast > Slow | Rising | Rising |
| **Bearish (-1)** | Fast < Slow | Falling | Falling |
| **Neutral (0)** | Any | Mixed | Mixed |
The neutral state captures market indecision: when EMAs disagree on direction or their relative position contradicts their momentum, AMAT stays flat. This is a feature, not a limitation.
### EMA Bias Compensation
QuanTAlib's implementation uses bias-compensated EMAs during the warmup phase. Traditional EMA initialization assumes the first price equals the true average—a convenient fiction. The compensator factor `e` decays exponentially:
$$e_{t} = e_{t-1} \times (1 - \alpha)$$
Until convergence, the EMA is divided by $(1 - e)$ to remove initialization bias.
## Mathematical Foundation
### 1. EMA Calculation
$$\text{EMA}_t = \alpha \times P_t + (1 - \alpha) \times \text{EMA}_{t-1}$$
Where $\alpha = \frac{2}{n + 1}$ and $n$ is the period.
### 2. Direction Detection
$$\text{Direction}_t = \begin{cases} \text{rising} & \text{if } \text{EMA}_t > \text{EMA}_{t-1} \\ \text{falling} & \text{if } \text{EMA}_t < \text{EMA}_{t-1} \\ \text{flat} & \text{otherwise} \end{cases}$$
### 3. Trend Signal
$$\text{Trend}_t = \begin{cases} +1 & \text{if } \text{FastEMA}_t > \text{SlowEMA}_t \land \text{FastRising} \land \text{SlowRising} \\ -1 & \text{if } \text{FastEMA}_t < \text{SlowEMA}_t \land \text{FastFalling} \land \text{SlowFalling} \\ 0 & \text{otherwise} \end{cases}$$
### 4. Trend Strength
$$\text{Strength}_t = \frac{|\text{FastEMA}_t - \text{SlowEMA}_t|}{\text{SlowEMA}_t} \times 100$$
Strength quantifies the separation between EMAs as a percentage of the slow EMA—useful for gauging trend conviction or filtering weak signals.
## Usage
```csharp
// Standard instantiation
var amat = new Amat(fastPeriod: 10, slowPeriod: 50);
// Process streaming data
foreach (var price in prices)
{
amat.Update(new TValue(DateTime.UtcNow, price));
if (amat.Last.Value == 1.0)
Console.WriteLine($"Bullish - Strength: {amat.Strength.Value:F2}%");
else if (amat.Last.Value == -1.0)
Console.WriteLine($"Bearish - Strength: {amat.Strength.Value:F2}%");
else
Console.WriteLine("Neutral");
}
// Access individual EMAs
double fastEma = amat.FastEma.Value;
double slowEma = amat.SlowEma.Value;
// Batch processing
var results = Amat.Batch(priceSeries, fastPeriod: 10, slowPeriod: 50);
// Span-based high-performance
double[] trend = new double[prices.Length];
double[] strength = new double[prices.Length];
Amat.Calculate(prices.AsSpan(), trend, strength, fastPeriod: 10, slowPeriod: 50);
```
### Event-Driven (Chained)
```csharp
var source = new TSeries();
var amat = new Amat(source, fastPeriod: 10, slowPeriod: 50);
// AMAT automatically updates when source publishes
source.Add(new TValue(DateTime.UtcNow, 100.0));
Console.WriteLine($"Trend: {amat.Last.Value}");
```
## Parameters
| Parameter | Type | Default | Description |
|:----------|:-----|:--------|:------------|
| `fastPeriod` | int | 10 | Fast EMA period (must be > 0) |
| `slowPeriod` | int | 50 | Slow EMA period (must be > fastPeriod) |
### Common Period Combinations
| Use Case | Fast | Slow | Notes |
|:---------|:-----|:-----|:------|
| **Scalping** | 5 | 13 | High responsiveness, more signals |
| **Swing** | 10 | 50 | Balanced, classic configuration |
| **Position** | 20 | 100 | Filtered for major trends |
| **Investment** | 50 | 200 | Long-term directional bias |
## Output Properties
| Property | Type | Description |
|:---------|:-----|:------------|
| `Last` | TValue | Trend direction: +1 (bullish), -1 (bearish), 0 (neutral) |
| `Strength` | TValue | Trend strength as percentage |
| `FastEma` | TValue | Current fast EMA value |
| `SlowEma` | TValue | Current slow EMA value |
| `IsHot` | bool | True when both EMAs are fully warmed |
| `WarmupPeriod` | int | Equal to slowPeriod |
## Performance Profile
| Metric | Score | Notes |
|:-------|:------|:------|
| **Throughput** | ~15 ns/bar | Dual EMA + direction check |
| **Allocations** | 0 | Streaming mode is allocation-free |
| **Complexity** | O(1) | Constant time per update |
| **Accuracy** | 9/10 | Bias-compensated EMAs match external libs |
| **Timeliness** | 7/10 | Triple-confirmation adds slight lag |
| **Overshoot** | 8/10 | No overshoot; discrete {-1, 0, +1} output |
| **Smoothness** | 6/10 | State transitions can be abrupt |
## Validation
AMAT is a custom indicator not present in standard TA libraries. Validation confirms:
| Component | Library | Status | Notes |
|:----------|:--------|:-------|:------|
| **Fast EMA** | TA-Lib | ✅ | Matches `TA_EMA` |
| **Fast EMA** | Skender | ✅ | Matches `GetEma` |
| **Slow EMA** | TA-Lib | ✅ | Matches `TA_EMA` |
| **Slow EMA** | Skender | ✅ | Matches `GetEma` |
| **Trend Logic** | Manual | ✅ | Verified against known patterns |
| **Strength** | Manual | ✅ | Formula verification |
## Common Pitfalls
### 1. Expecting Continuous Signals
AMAT returns 0 (neutral) frequently. This is intentional—choppy markets produce neutral signals. Trading systems should respect neutral states rather than forcing a directional bias.
### 2. Period Selection
Fast periods that are too close to slow periods produce excessive neutral readings. A ratio of 1:5 (e.g., 10/50) provides reasonable separation.
### 3. Strength Interpretation
High strength doesn't guarantee trend continuation. It measures current separation, not momentum. A declining strength during a +1 trend may indicate weakening conviction.
### 4. Initialization Phase
Until `IsHot` returns true, trend signals may be unreliable. The indicator needs `slowPeriod` bars to stabilize both EMAs.
## See Also
- [EMA](../trends/ema/Ema.md) - Exponential Moving Average (AMAT's building block)
- [MACD](../momentum/macd/Macd.md) - Another dual-EMA system with different logic
- [ADX](../momentum/adx/Adx.md) - Trend strength without directional bias