Ver en GitHub

Estrategia Blau Ergodic MDI

Descripción general

La estrategia Blau Ergodic Market Directional Indicator (MDI) reproduce el comportamiento del asesor experto de MetaTrader Exp_BlauErgodicMDI. El algoritmo opera sobre un flujo de velas de un marco temporal superior (por defecto 4H) y aplica un pipeline de triple suavizado al precio de entrada seleccionado para construir un histograma de momentum y una línea de señal. Las decisiones de trading se derivan de ese histograma usando uno de tres modos de entrada configurables:

  1. Breakdown – opera cuando el histograma cruza la línea cero.
  2. Twist – reacciona a reversiones en la pendiente del histograma (momentum cambiando de dirección).
  3. CloudTwist – actúa en cruces del histograma/línea de señal.

Cada señal puede opcionalmente cerrar posiciones opuestas y/o abrir nuevas operaciones dependiendo de los indicadores de permiso proporcionados por el usuario.

Lógica del indicador

  1. Suavizar el precio aplicado elegido con el tipo de media móvil configurado y PrimaryLength para obtener el precio base.
  2. Calcular la diferencia de momentum (price - baseline) / point_value.
  3. Suavizar ese momentum con FirstSmoothingLength y SecondSmoothingLength para construir el histograma.
  4. Suavizar el histograma una vez más con SignalLength para obtener la línea de señal.
  5. Almacenar valores históricos según SignalBarShift para que las señales puedan confirmarse en velas cerradas.

Las familias de suavizado soportadas son EMA, SMA, SMMA/RMA y WMA. La selección del precio aplicado refleja la implementación de MetaTrader (cierre, apertura, máximo, mínimo, mediana, típico, ponderado, simple, cuarto, variantes de seguimiento de tendencia).

Parámetros

Nombre Descripción
Volume Tamaño de orden usado al abrir posiciones.
StopLossPoints Distancia del stop-loss en puntos del instrumento (0 deshabilita).
TakeProfitPoints Distancia del take-profit en puntos del instrumento (0 deshabilita).
SlippagePoints Deslizamiento de precio máximo en puntos aplicado a órdenes de mercado.
AllowLongEntries / AllowShortEntries Permitir abrir posiciones en la dirección respectiva.
AllowLongExits / AllowShortExits Permitir cerrar posiciones existentes en señales opuestas.
Mode Modo de entrada (Breakdown / Twist / CloudTwist).
CandleType Marco temporal de velas usadas para cálculos (por defecto 4H).
SmoothingMethods Familia de media móvil usada en todos los pasos de suavizado.
PrimaryLength Longitud de suavizado base para el precio aplicado.
FirstSmoothingLength Primera longitud de suavizado aplicada al momentum.
SecondSmoothingLength Segunda longitud de suavizado que forma el histograma.
SignalLength Longitud de suavizado del histograma para crear la línea de señal.
AppliedPrices Fuente de precio usada en los cálculos del indicador.
SignalBarShift Número de barras cerradas a mirar atrás al evaluar señales.
Phase Parámetro reservado para compatibilidad (no usado en la implementación actual).

Condiciones de señal

  • Breakdown
    • Largo: el histograma en SignalBarShift es positivo mientras la barra anterior no lo es.
    • Corto: el histograma en SignalBarShift es negativo mientras la barra anterior no lo es.
  • Twist
    • Largo: el histograma en SignalBarShift está subiendo después de un período bajista (anterior < último y dos barras atrás > anterior).
    • Corto: el histograma en SignalBarShift está bajando después de un período alcista (anterior > último y dos barras atrás < anterior).
  • CloudTwist
    • Largo: el histograma cruza por encima de la línea de señal (último histograma > última señal, histograma anterior <= señal anterior).
    • Corto: el histograma cruza por debajo de la línea de señal.

Cada señal puede tanto aplanar la exposición opuesta (si se permiten salidas) como abrir una nueva operación con el volumen configurado.

Gestión de riesgos

StartProtection se inicializa con las distancias de stop-loss y take-profit especificadas (convertidas de puntos a unidades de precio usando el tamaño de tick del instrumento). Si alguna distancia es cero, la protección respectiva se omite. El deslizamiento también se convierte a unidades de precio usando el mismo tamaño de tick.

Notas

  • Las señales se procesan solo en velas terminadas para reflejar el comportamiento original de MetaTrader.
  • SignalBarShift permite retrasar la confirmación de operaciones para evitar actuar en la barra más reciente.
  • El parámetro Phase se mantiene por completitud pero no tiene efecto al usar los métodos de suavizado soportados.
  • Todos los comentarios de código se proporcionan en inglés para simplificar el mantenimiento futuro.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Blau Ergodic Market Directional Indicator strategy converted from MetaTrader.
/// Uses a triple-smoothed momentum histogram with configurable entry confirmation modes.
/// </summary>
public class BlauErgodicMdiStrategy : Strategy
{
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<int> _slippagePoints;
	private readonly StrategyParam<bool> _allowLongEntries;
	private readonly StrategyParam<bool> _allowShortEntries;
	private readonly StrategyParam<bool> _allowLongExits;
	private readonly StrategyParam<bool> _allowShortExits;
	private readonly StrategyParam<EntryModes> _entryMode;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<SmoothingMethods> _smoothingMethod;
	private readonly StrategyParam<int> _primaryLength;
	private readonly StrategyParam<int> _firstSmoothingLength;
	private readonly StrategyParam<int> _secondSmoothingLength;
	private readonly StrategyParam<int> _signalLength;
	private readonly StrategyParam<AppliedPrices> _appliedPrice;
	private readonly StrategyParam<int> _signalBarShift;
	private readonly StrategyParam<int> _phase;

	private IIndicator _priceAverage = null!;
	private IIndicator _firstSmoothing = null!;
	private IIndicator _secondSmoothing = null!;
	private IIndicator _signalSmoothing = null!;

	private decimal[] _histogramBuffer = Array.Empty<decimal>();
	private decimal[] _signalBuffer = Array.Empty<decimal>();
	private int _bufferIndex;
	private int _bufferFilled;
	private decimal _pointValue = 1m;
	private decimal _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;

	/// <summary>
	/// Initializes a new instance of <see cref="BlauErgodicMdiStrategy"/>.
	/// </summary>
	public BlauErgodicMdiStrategy()
	{
		_stopLossPoints = Param(nameof(StopLossPoints), 1000)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop loss in points", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take profit in points", "Risk");

		_slippagePoints = Param(nameof(SlippagePoints), 10)
			.SetNotNegative()
			.SetDisplay("Slippage", "Maximum slippage in points", "Risk");

		_allowLongEntries = Param(nameof(AllowLongEntries), true)
			.SetDisplay("Allow Long Entries", "Enable opening long positions", "Permissions");

		_allowShortEntries = Param(nameof(AllowShortEntries), true)
			.SetDisplay("Allow Short Entries", "Enable opening short positions", "Permissions");

		_allowLongExits = Param(nameof(AllowLongExits), true)
			.SetDisplay("Allow Long Exits", "Enable closing long positions", "Permissions");

		_allowShortExits = Param(nameof(AllowShortExits), true)
			.SetDisplay("Allow Short Exits", "Enable closing short positions", "Permissions");

		_entryMode = Param(nameof(Mode), EntryModes.Twist)
			.SetDisplay("Entry Mode", "Signal interpretation mode", "Strategy");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Indicator Timeframe", "Timeframe used for calculations", "Data");

		_smoothingMethod = Param(nameof(SmoothingMethod), SmoothingMethods.Exponential)
			.SetDisplay("Smoothing Method", "Type of moving average", "Indicator");

		_primaryLength = Param(nameof(PrimaryLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("Primary Length", "Base smoothing length", "Indicator")
			
			.SetOptimize(5, 60, 1);

		_firstSmoothingLength = Param(nameof(FirstSmoothingLength), 5)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Smoothing", "First smoothing length", "Indicator")
			
			.SetOptimize(2, 20, 1);

		_secondSmoothingLength = Param(nameof(SecondSmoothingLength), 3)
			.SetGreaterThanZero()
			.SetDisplay("Histogram Smoothing", "Second smoothing length", "Indicator")
			
			.SetOptimize(2, 20, 1);

		_signalLength = Param(nameof(SignalLength), 8)
			.SetGreaterThanZero()
			.SetDisplay("Signal Length", "Signal line smoothing", "Indicator")
			
			.SetOptimize(2, 30, 1);

		_appliedPrice = Param(nameof(AppliedPrice), AppliedPrices.Close)
			.SetDisplay("Applied Price", "Price source for calculations", "Indicator");

		_signalBarShift = Param(nameof(SignalBarShift), 1)
			.SetNotNegative()
			.SetDisplay("Signal Bar", "Shift of the bar used for signals", "Strategy");

		_phase = Param(nameof(Phase), 15)
			.SetDisplay("Phase", "Reserved smoothing phase parameter", "Indicator");
	}

	/// <summary>
	/// Stop loss distance expressed in instrument points.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take profit distance expressed in instrument points.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Allowed price slippage in points.
	/// </summary>
	public int SlippagePoints
	{
		get => _slippagePoints.Value;
		set => _slippagePoints.Value = value;
	}

	/// <summary>
	/// Enables opening long positions.
	/// </summary>
	public bool AllowLongEntries
	{
		get => _allowLongEntries.Value;
		set => _allowLongEntries.Value = value;
	}

	/// <summary>
	/// Enables opening short positions.
	/// </summary>
	public bool AllowShortEntries
	{
		get => _allowShortEntries.Value;
		set => _allowShortEntries.Value = value;
	}

	/// <summary>
	/// Enables closing existing long positions on opposite signals.
	/// </summary>
	public bool AllowLongExits
	{
		get => _allowLongExits.Value;
		set => _allowLongExits.Value = value;
	}

	/// <summary>
	/// Enables closing existing short positions on opposite signals.
	/// </summary>
	public bool AllowShortExits
	{
		get => _allowShortExits.Value;
		set => _allowShortExits.Value = value;
	}

	/// <summary>
	/// Selected entry confirmation mode.
	/// </summary>
	public EntryModes Mode
	{
		get => _entryMode.Value;
		set => _entryMode.Value = value;
	}

	/// <summary>
	/// Candle type used for indicator calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Moving average family used for smoothing steps.
	/// </summary>
	public SmoothingMethods SmoothingMethod
	{
		get => _smoothingMethod.Value;
		set => _smoothingMethod.Value = value;
	}

	/// <summary>
	/// Length for the initial smoothing of price.
	/// </summary>
	public int PrimaryLength
	{
		get => _primaryLength.Value;
		set => _primaryLength.Value = value;
	}

	/// <summary>
	/// Length of the first smoothing applied to momentum.
	/// </summary>
	public int FirstSmoothingLength
	{
		get => _firstSmoothingLength.Value;
		set => _firstSmoothingLength.Value = value;
	}

	/// <summary>
	/// Length of the second smoothing forming the histogram.
	/// </summary>
	public int SecondSmoothingLength
	{
		get => _secondSmoothingLength.Value;
		set => _secondSmoothingLength.Value = value;
	}

	/// <summary>
	/// Length of the signal line smoothing.
	/// </summary>
	public int SignalLength
	{
		get => _signalLength.Value;
		set => _signalLength.Value = value;
	}

	/// <summary>
	/// Applied price selection for calculations.
	/// </summary>
	public AppliedPrices AppliedPrice
	{
		get => _appliedPrice.Value;
		set => _appliedPrice.Value = value;
	}

	/// <summary>
	/// Offset of the bar used for signal confirmation.
	/// </summary>
	public int SignalBarShift
	{
		get => _signalBarShift.Value;
		set => _signalBarShift.Value = value;
	}

	/// <summary>
	/// Reserved phase parameter kept for compatibility with the original script.
	/// </summary>
	public int Phase
	{
		get => _phase.Value;
		set => _phase.Value = value;
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_histogramBuffer = Array.Empty<decimal>();
		_signalBuffer = Array.Empty<decimal>();
		_bufferIndex = 0;
		_bufferFilled = 0;
		_entryPrice = 0m;
		_stopPrice = null;
		_takePrice = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_pointValue = Security?.PriceStep ?? 1m;
		if (_pointValue <= 0m)
			_pointValue = 1m;

		_priceAverage = CreateMovingAverage(SmoothingMethod, PrimaryLength);
		_firstSmoothing = CreateMovingAverage(SmoothingMethod, FirstSmoothingLength);
		_secondSmoothing = CreateMovingAverage(SmoothingMethod, SecondSmoothingLength);
		_signalSmoothing = CreateMovingAverage(SmoothingMethod, SignalLength);

		InitializeBuffers();

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		ApplyRiskManagement(candle);

		var price = SelectPrice(candle);
		var time = candle.CloseTime;

		// Smooth the selected price to match the indicator baseline.
		var baseValue = _priceAverage.Process(new DecimalIndicatorValue(_priceAverage, price, time) { IsFinal = true });
		if (!baseValue.IsFormed)
			return;

		var basePrice = baseValue.ToDecimal();
		var momentum = _pointValue != 0m ? (price - basePrice) / _pointValue : 0m;

		// Apply the first momentum smoothing stage.
		var firstValue = _firstSmoothing.Process(new DecimalIndicatorValue(_firstSmoothing, momentum, time) { IsFinal = true });
		if (!firstValue.IsFormed)
			return;

		var first = firstValue.ToDecimal();

		// Build the histogram with the second smoothing stage.
		var secondValue = _secondSmoothing.Process(new DecimalIndicatorValue(_secondSmoothing, first, time) { IsFinal = true });
		if (!secondValue.IsFormed)
			return;

		var histogram = secondValue.ToDecimal();

		// Smooth the histogram to generate the signal line.
		var signalValue = _signalSmoothing.Process(new DecimalIndicatorValue(_signalSmoothing, histogram, time) { IsFinal = true });
		if (!signalValue.IsFormed)
			return;

		var signal = signalValue.ToDecimal();

		// Store values so that shifted comparisons work like in the MQL version.
		AddToBuffer(histogram, signal);

		if (!TryGetHist(SignalBarShift, out var latestHist) || !TryGetHist(SignalBarShift + 1, out var previousHist))
			return;

		var currentPosition = Position;
		var buySignal = false;
		var sellSignal = false;

		switch (Mode)
		{
			case EntryModes.Breakdown:
			{
				buySignal = latestHist > 0m && previousHist <= 0m;
				sellSignal = latestHist < 0m && previousHist >= 0m;
				break;
			}

			case EntryModes.Twist:
			{
				if (!TryGetHist(SignalBarShift + 2, out var olderHist))
					return;

				buySignal = previousHist < latestHist && olderHist > previousHist;
				sellSignal = previousHist > latestHist && olderHist < previousHist;
				break;
			}

			case EntryModes.CloudTwist:
			{
				if (!TryGetSignal(SignalBarShift, out var latestSignal) || !TryGetSignal(SignalBarShift + 1, out var previousSignal))
					return;

				buySignal = latestHist > latestSignal && previousHist <= previousSignal;
				sellSignal = latestHist < latestSignal && previousHist >= previousSignal;
				break;
			}
		}

		if (buySignal)
		{
			ExecuteBuy(currentPosition, candle.ClosePrice);
		}
		else if (sellSignal)
		{
			ExecuteSell(currentPosition, candle.ClosePrice);
		}
	}

	private void ExecuteBuy(decimal currentPosition, decimal price)
	{
		var volume = 0m;

		if (AllowShortExits && currentPosition < 0m)
			volume += Math.Abs(currentPosition);

		if (AllowLongEntries && (currentPosition <= 0m || (AllowShortExits && currentPosition < 0m)))
			volume += Volume;

		if (volume > 0m)
		{
			BuyMarket(volume);
			_entryPrice = price;
			var slDist = StopLossPoints > 0 ? StopLossPoints * _pointValue : 0m;
			var tpDist = TakeProfitPoints > 0 ? TakeProfitPoints * _pointValue : 0m;
			_stopPrice = slDist > 0m ? price - slDist : null;
			_takePrice = tpDist > 0m ? price + tpDist : null;
		}
	}

	private void ExecuteSell(decimal currentPosition, decimal price)
	{
		var volume = 0m;

		if (AllowLongExits && currentPosition > 0m)
			volume += Math.Abs(currentPosition);

		if (AllowShortEntries && (currentPosition >= 0m || (AllowLongExits && currentPosition > 0m)))
			volume += Volume;

		if (volume > 0m)
		{
			SellMarket(volume);
			_entryPrice = price;
			var slDist = StopLossPoints > 0 ? StopLossPoints * _pointValue : 0m;
			var tpDist = TakeProfitPoints > 0 ? TakeProfitPoints * _pointValue : 0m;
			_stopPrice = slDist > 0m ? price + slDist : null;
			_takePrice = tpDist > 0m ? price - tpDist : null;
		}
	}

	private void ApplyRiskManagement(ICandleMessage candle)
	{
		if (Position > 0)
		{
			if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
			{
				SellMarket(Position);
				ResetTargets();
				return;
			}
			if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
			{
				SellMarket(Position);
				ResetTargets();
			}
		}
		else if (Position < 0)
		{
			if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
			{
				BuyMarket(Math.Abs(Position));
				ResetTargets();
				return;
			}
			if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
			{
				BuyMarket(Math.Abs(Position));
				ResetTargets();
			}
		}
	}

	private void ResetTargets()
	{
		_entryPrice = 0m;
		_stopPrice = null;
		_takePrice = null;
	}

	private void InitializeBuffers()
	{
		var size = Math.Max(3, SignalBarShift + 3);
		_histogramBuffer = new decimal[size];
		_signalBuffer = new decimal[size];
		_bufferIndex = 0;
		_bufferFilled = 0;
	}

	private void AddToBuffer(decimal histogram, decimal signal)
	{
		if (_histogramBuffer.Length == 0)
			return;

		_histogramBuffer[_bufferIndex] = histogram;
		_signalBuffer[_bufferIndex] = signal;
		_bufferIndex = (_bufferIndex + 1) % _histogramBuffer.Length;
		if (_bufferFilled < _histogramBuffer.Length)
			_bufferFilled++;
	}

	private bool TryGetHist(int shift, out decimal value)
	{
		return TryGetBufferedValue(_histogramBuffer, shift, out value);
	}

	private bool TryGetSignal(int shift, out decimal value)
	{
		return TryGetBufferedValue(_signalBuffer, shift, out value);
	}

	private bool TryGetBufferedValue(decimal[] buffer, int shift, out decimal value)
	{
		value = default;

		if (shift < 0 || shift >= _bufferFilled)
			return false;

		var index = _bufferIndex - 1 - shift;
		if (index < 0)
			index += buffer.Length;

		value = buffer[index];
		return true;
	}

	private decimal SelectPrice(ICandleMessage candle)
	{
		return AppliedPrice switch
		{
			AppliedPrices.Open => candle.OpenPrice,
			AppliedPrices.High => candle.HighPrice,
			AppliedPrices.Low => candle.LowPrice,
			AppliedPrices.Median => (candle.HighPrice + candle.LowPrice) / 2m,
			AppliedPrices.Typical => (candle.ClosePrice + candle.HighPrice + candle.LowPrice) / 3m,
			AppliedPrices.Weighted => (2m * candle.ClosePrice + candle.HighPrice + candle.LowPrice) / 4m,
			AppliedPrices.Simple => (candle.OpenPrice + candle.ClosePrice) / 2m,
			AppliedPrices.Quarter => (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m,
			AppliedPrices.TrendFollow0 => candle.ClosePrice > candle.OpenPrice ? candle.HighPrice : candle.ClosePrice < candle.OpenPrice ? candle.LowPrice : candle.ClosePrice,
			AppliedPrices.TrendFollow1 => candle.ClosePrice > candle.OpenPrice ? (candle.HighPrice + candle.ClosePrice) / 2m : candle.ClosePrice < candle.OpenPrice ? (candle.LowPrice + candle.ClosePrice) / 2m : candle.ClosePrice,
			_ => candle.ClosePrice,
		};
	}

	private static IIndicator CreateMovingAverage(SmoothingMethods method, int length)
	{
		return method switch
		{
			SmoothingMethods.Simple => new SimpleMovingAverage { Length = length },
			SmoothingMethods.Smoothed => new SmoothedMovingAverage { Length = length },
			SmoothingMethods.Weighted => new WeightedMovingAverage { Length = length },
			_ => new ExponentialMovingAverage { Length = length },
		};
	}

	/// <summary>
	/// Entry confirmation modes replicated from the original expert advisor.
	/// </summary>
	public enum EntryModes
	{
		/// <summary>
		/// Histogram breaks above or below the zero line.
		/// </summary>
		Breakdown,

		/// <summary>
		/// Histogram changes slope direction.
		/// </summary>
		Twist,

		/// <summary>
		/// Histogram crosses the signal line.
		/// </summary>
		CloudTwist
	}

	/// <summary>
	/// Supported smoothing families.
	/// </summary>
	public enum SmoothingMethods
	{
		/// <summary>
		/// Exponential moving average.
		/// </summary>
		Exponential,

		/// <summary>
		/// Simple moving average.
		/// </summary>
		Simple,

		/// <summary>
		/// Smoothed (RMA) moving average.
		/// </summary>
		Smoothed,

		/// <summary>
		/// Weighted moving average.
		/// </summary>
		Weighted
	}

	/// <summary>
	/// Applied price sources identical to the MetaTrader version.
	/// </summary>
	public enum AppliedPrices
	{
		/// <summary>
		/// Close price.
		/// </summary>
		Close,

		/// <summary>
		/// Open price.
		/// </summary>
		Open,

		/// <summary>
		/// High price.
		/// </summary>
		High,

		/// <summary>
		/// Low price.
		/// </summary>
		Low,

		/// <summary>
		/// Median price (high + low) / 2.
		/// </summary>
		Median,

		/// <summary>
		/// Typical price (close + high + low) / 3.
		/// </summary>
		Typical,

		/// <summary>
		/// Weighted price (2 * close + high + low) / 4.
		/// </summary>
		Weighted,

		/// <summary>
		/// Simple price (open + close) / 2.
		/// </summary>
		Simple,

		/// <summary>
		/// Quarted price (open + high + low + close) / 4.
		/// </summary>
		Quarter,

		/// <summary>
		/// Trend-following price using candle extremes.
		/// </summary>
		TrendFollow0,

		/// <summary>
		/// Half-trend-following price.
		/// </summary>
		TrendFollow1
	}
}