Files
Miha Kralj 329b0657bc Add "Ehlers" prefix to 5 Ehlers indicators: SAM, PMA, ILRS, CTI, RVGI
Standardize naming convention so all Ehlers-originated indicators
have "Ehlers" in their display name across all documentation and
code surfaces:

- SAM: Smoothed Adaptive Momentum → Ehlers Smoothed Adaptive Momentum
- PMA: Predictive Moving Average → Ehlers Predictive Moving Average
- ILRS: Integral of LinReg Slope → Ehlers Integral of LinReg Slope
- CTI: Correlation Trend Indicator → Ehlers Correlation Trend Indicator
- RVGI: Relative Vigor Index → Ehlers Relative Vigor Index

Updated across: .md H1 titles, XML doc summaries, Quantower Name
properties, Quantower test assertions, _sidebar.md, lib/_index.md,
category _index.md files, docs/indicators.md, docs/validation.md.

Build: 0 warnings, 0 errors. All tests pass.
2026-03-17 10:51:24 -07:00

89 lines
2.6 KiB
C#

using System.Drawing;
using TradingPlatform.BusinessLayer;
using static QuanTAlib.IndicatorExtensions;
namespace QuanTAlib;
/// <summary>
/// SAM (Smoothed Adaptive Momentum) Quantower indicator.
/// Ehlers adaptive momentum oscillator that measures price change over the
/// dominant cycle period, then smooths with a 2-pole Super Smoother filter.
/// </summary>
public class SamIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Alpha", 0, 0.01, 1.0, 0.01, 2)]
public double Alpha { get; set; } = 0.07;
[InputParameter("Cutoff", 1, 2, 100, 1, 0)]
public int Cutoff { get; set; } = 8;
[DataSourceInput]
public SourceType Source { get; set; } = SourceType.Close;
[InputParameter("Show Cold Values", sortIndex: 100)]
public bool ShowColdValues { get; set; } = true;
private Sam? _sam;
private Func<IHistoryItem, double>? _selector;
public int MinHistoryDepths => 100; // WarmupPeriod = MaxCyclePeriod * 2
public override string ShortName => $"SAM({Alpha},{Cutoff})";
public SamIndicator()
{
Name = "SAM - Ehlers Smoothed Adaptive Momentum";
Description = "Ehlers adaptive momentum oscillator using Hilbert Transform cycle detection and Super Smoother";
SeparateWindow = true;
OnBackGround = false;
}
protected override void OnInit()
{
_sam = new Sam(Alpha, Cutoff);
_selector = Source.GetPriceSelector();
AddLineSeries(new LineSeries("SAM", IndicatorExtensions.Momentum, 2, LineStyle.Histogramm));
AddLineSeries(new LineSeries("Zero", Color.Gray, 1, LineStyle.Dot));
}
protected override void OnUpdate(UpdateArgs args)
{
if (_sam == null || _selector == null)
{
return;
}
var item = HistoricalData[0, SeekOriginHistory.End];
double value = _selector(item);
bool isNew = args.IsNewBar();
TValue input = new(item.TimeLeft, value);
_sam.Update(input, isNew);
bool isHot = _sam.IsHot;
LinesSeries[0].SetValue(_sam.Last.Value, isHot, ShowColdValues);
LinesSeries[1].SetValue(0);
if (isHot || ShowColdValues)
{
double sam = _sam.Last.Value;
Color color;
if (sam > 0)
{
color = Color.Green;
}
else if (sam < 0)
{
color = Color.Red;
}
else
{
color = Color.Gray;
}
LinesSeries[0].SetMarker(0, new IndicatorLineMarker(color));
}
}
}