Ver en GitHub

Estrategia Brandy (C#)

Descripción General

La estrategia Brandy es una traducción directa del Expert Advisor de MetaTrader 5 Brandy (edición de barabashkakvn). Combina dos medias móviles configurables y evalúa sus posiciones relativas en velas cerradas para decidir si abrir una posición larga o corta. La lógica original también aplica controles opcionales de stop loss, take profit y trailing stop expresados en pips. Esta versión en C# reproduce fielmente esos comportamientos sobre la API de estrategia de alto nivel de StockSharp.

La estrategia calcula una media móvil "rápida" sobre el flujo de precios de apertura y una media móvil "lenta" sobre el flujo de precios de cierre. Ambos indicadores tienen parámetros independientes de período, método de suavizado, fuente de precio, referencia de barra de señal y desplazamiento. Las señales se generan cuando los valores de MA de la barra anterior están en el mismo lado de los valores de señal respectivos. La lógica protectora verifica la media móvil basada en la apertura en cada vela y sale inmediatamente de la operación si la condición de tendencia ya no se cumple. La gestión de riesgo adicional se implementa con distancias opcionales de stop loss, take profit y trailing stop, todas medidas en pips y convertidas a precios absolutos mediante el tamaño del tick del instrumento con un ajuste de pip de cinco dígitos.

Lógica de Trading

  1. En cada vela terminada, la estrategia actualiza las medias móviles de precio de apertura y cierre usando el método de suavizado configurado y el precio aplicado. Los valores históricos de MA se almacenan en búfer para que el código pueda emular el comportamiento de desplazamiento de iMA del Expert Advisor original.
  2. Cuando no hay posición activa, se abre una operación larga si:
    • El valor de MA basado en apertura de la barra anterior es mayor que el valor de señal configurado (posiblemente desplazado);
    • El valor de MA basado en cierre de la barra anterior también es mayor que su referencia de señal (nótese que el EA original compara contra el indicador basado en apertura para esta verificación, y la traducción mantiene esa peculiaridad por compatibilidad).
  3. Se abre una operación corta cuando ambas medias móviles están por debajo de sus referencias de señal respectivas.
  4. Mientras hay una posición activa, la estrategia evalúa las salidas en cada vela terminada en el siguiente orden:
    • Reversión de tendencia: si la MA basada en apertura cae por debajo del valor de señal (para largos) o sube por encima (para cortos), la posición se cierra inmediatamente a mercado.
    • Actualización del trailing stop: cuando está habilitado y el movimiento a favor de la operación excede trailing stop + trailing step (convertido a precios absolutos), el nivel de stop se ajusta para mantener una distancia de trailing stop desde el último cierre.
    • Take profit: si el rango de la vela toca el objetivo de beneficio, la operación se cierra a mercado.
    • Stop loss: si el rango de la vela viola el nivel de stop protector, la operación se cierra.
  5. Todo el volumen es fijo y está determinado por el parámetro TradeVolume. El valor por defecto replica la configuración de 0.1 lotes de la versión MT5.

Referencia de Parámetros

Parámetro Descripción
TradeVolume Tamaño de la orden de mercado en lotes.
StopLossPips Distancia del stop protector, medida en pips (0 lo deshabilita).
TakeProfitPips Distancia del objetivo de beneficio en pips (0 lo deshabilita).
TrailingStopPips Distancia del trailing stop en pips. Requiere que TrailingStepPips sea positivo.
TrailingStepPips Movimiento adicional de pips requerido antes de avanzar el trailing stop. Debe ser distinto de cero cuando el trailing stop está activo.
MaClosePeriod, MaOpenPeriod Longitudes de media móvil para las series de cierre y apertura respectivamente.
MaCloseShift, MaOpenShift Desplazamientos aplicados a los búferes de MA (número de barras).
MaCloseSignalBar, MaOpenSignalBar Índices de barras usados como referencias de comparación. Cero coincide con el valor más reciente, uno se refiere a la barra anterior, y así sucesivamente.
MaCloseMethod, MaOpenMethod Métodos de suavizado de media móvil (SMA, EMA, SMMA, LWMA).
MaCloseAppliedPrice, MaOpenAppliedPrice Fuente de precio de vela para cada indicador (cierre, apertura, máximo, mínimo, mediana, típico, ponderado).
CandleType Marco temporal de las velas solicitadas desde la fuente de datos.

Notas de Implementación

  • El tamaño del pip se calcula desde Security.PriceStep y se multiplica por 10 cuando el instrumento expone 3 o 5 decimales, reflejando el ajuste de MetaTrader entre puntos y pips.
  • El historial del indicador se retiene usando colas acotadas para que la estrategia pueda reproducir llamadas a iMA con índices de barra de señal arbitrarios y desplazamientos positivos sin depender de accesores de indicadores prohibidos.
  • La condición de cierre para la media móvil basada en cierre compara intencionalmente contra el búfer de MA de apertura porque el código fuente original invocaba iMAGet(handle_iMAOpen, MaClose_SignalBar). Esta traducción mantiene el comportamiento para preservar la compatibilidad con configuraciones heredadas.
  • Los stops y la lógica de trailing se ejecutan en velas terminadas y aproximan las modificaciones de órdenes realizadas por el Expert Advisor mientras respetan la API de alto nivel de StockSharp.

Consejos de Uso

  • Configure el parámetro CandleType para que coincida con el marco temporal usado por el EA original (típicamente un único marco temporal de instrumento).
  • Mantenga TrailingStopPips en cero si no se desea comportamiento de trailing; de lo contrario, asegúrese de que TrailingStepPips sea estrictamente positivo para evitar el error de inicialización aplicado por la estrategia.
  • Al hacer backtesting en StockSharp, asegúrese de que PriceStep y Decimals del instrumento reflejen la definición de pip prevista para que las distancias de riesgo se conviertan correctamente.
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>
/// Conversion of the Brandy Expert Advisor from MetaTrader 5.
/// Combines two configurable moving averages to generate entries and manages positions with trailing exits.
/// </summary>
public class BrandyStrategy : Strategy
{
	/// <summary>
	/// Supported moving average smoothing methods.
	/// </summary>
	public enum MovingAverageMethods
	{
		/// <summary>
		/// Simple moving average.
		/// </summary>
		Sma,

		/// <summary>
		/// Exponential moving average.
		/// </summary>
		Ema,

		/// <summary>
		/// Smoothed moving average.
		/// </summary>
		Smma,

		/// <summary>
		/// Linear weighted moving average.
		/// </summary>
		Lwma
	}

	/// <summary>
	/// Price sources that can be fed into the moving averages.
	/// </summary>
	public enum AppliedPriceTypes
	{
		/// <summary>
		/// Candle close price.
		/// </summary>
		Close,

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

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

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

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

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

		/// <summary>
		/// Weighted price (high + low + 2 * close) / 4.
		/// </summary>
		Weighted
	}
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<decimal> _stopLossPips;
	private readonly StrategyParam<decimal> _takeProfitPips;
	private readonly StrategyParam<decimal> _trailingStopPips;
	private readonly StrategyParam<decimal> _trailingStepPips;
	private readonly StrategyParam<int> _maClosePeriod;
	private readonly StrategyParam<int> _maCloseShift;
	private readonly StrategyParam<MovingAverageMethods> _maCloseMethod;
	private readonly StrategyParam<AppliedPriceTypes> _maCloseAppliedPrice;
	private readonly StrategyParam<int> _maCloseSignalBar;
	private readonly StrategyParam<int> _maOpenPeriod;
	private readonly StrategyParam<int> _maOpenShift;
	private readonly StrategyParam<MovingAverageMethods> _maOpenMethod;
	private readonly StrategyParam<AppliedPriceTypes> _maOpenAppliedPrice;
	private readonly StrategyParam<int> _maOpenSignalBar;
	private readonly StrategyParam<DataType> _candleType;

	private DecimalLengthIndicator _maOpenIndicator;
	private DecimalLengthIndicator _maCloseIndicator;
	private decimal _pipSize;
	private readonly List<decimal> _maOpenValues = [];
	private readonly List<decimal> _maCloseValues = [];
	private int _maxOpenQueueSize;
	private int _maxCloseQueueSize;
	private decimal? _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;

	/// <summary>
	/// Trading volume per order.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set
		{
			_tradeVolume.Value = value;
			Volume = value;
		}
	}

	/// <summary>
	/// Stop loss distance expressed in pips.
	/// </summary>
	public decimal StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take profit distance expressed in pips.
	/// </summary>
	public decimal TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Trailing stop distance expressed in pips.
	/// </summary>
	public decimal TrailingStopPips
	{
		get => _trailingStopPips.Value;
		set => _trailingStopPips.Value = value;
	}

	/// <summary>
	/// Step that defines how far the price must move before the trailing stop is advanced.
	/// </summary>
	public decimal TrailingStepPips
	{
		get => _trailingStepPips.Value;
		set => _trailingStepPips.Value = value;
	}

	/// <summary>
	/// Period of the moving average calculated on the close series.
	/// </summary>
	public int MaClosePeriod
	{
		get => _maClosePeriod.Value;
		set => _maClosePeriod.Value = value;
	}

	/// <summary>
	/// Displacement applied to the moving average calculated on closes.
	/// </summary>
	public int MaCloseShift
	{
		get => _maCloseShift.Value;
		set => _maCloseShift.Value = value;
	}

	/// <summary>
	/// Moving average smoothing method for the close series.
	/// </summary>
	public MovingAverageMethods MaCloseMethod
	{
		get => _maCloseMethod.Value;
		set => _maCloseMethod.Value = value;
	}

	/// <summary>
	/// Price source used by the close moving average.
	/// </summary>
	public AppliedPriceTypes MaCloseAppliedPrice
	{
		get => _maCloseAppliedPrice.Value;
		set => _maCloseAppliedPrice.Value = value;
	}

	/// <summary>
	/// Bar index used as a signal reference for the close moving average.
	/// </summary>
	public int MaCloseSignalBar
	{
		get => _maCloseSignalBar.Value;
		set => _maCloseSignalBar.Value = value;
	}

	/// <summary>
	/// Period of the moving average calculated on the open series.
	/// </summary>
	public int MaOpenPeriod
	{
		get => _maOpenPeriod.Value;
		set => _maOpenPeriod.Value = value;
	}

	/// <summary>
	/// Displacement applied to the moving average calculated on opens.
	/// </summary>
	public int MaOpenShift
	{
		get => _maOpenShift.Value;
		set => _maOpenShift.Value = value;
	}

	/// <summary>
	/// Moving average smoothing method for the open series.
	/// </summary>
	public MovingAverageMethods MaOpenMethod
	{
		get => _maOpenMethod.Value;
		set => _maOpenMethod.Value = value;
	}

	/// <summary>
	/// Price source used by the open moving average.
	/// </summary>
	public AppliedPriceTypes MaOpenAppliedPrice
	{
		get => _maOpenAppliedPrice.Value;
		set => _maOpenAppliedPrice.Value = value;
	}

	/// <summary>
	/// Bar index used as a signal reference for the open moving average.
	/// </summary>
	public int MaOpenSignalBar
	{
		get => _maOpenSignalBar.Value;
		set => _maOpenSignalBar.Value = value;
	}

	/// <summary>
	/// Candle type used to feed the indicators.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="BrandyStrategy"/> class.
	/// </summary>
	public BrandyStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
		.SetGreaterThanZero()
		.SetDisplay("Trade Volume", "Order size in lots", "General");

		_stopLossPips = Param(nameof(StopLossPips), 50m)
		.SetNotNegative()
		.SetDisplay("Stop Loss (pips)", "Protective stop distance", "Risk");

		_takeProfitPips = Param(nameof(TakeProfitPips), 150m)
		.SetNotNegative()
		.SetDisplay("Take Profit (pips)", "Profit target distance", "Risk");

		_trailingStopPips = Param(nameof(TrailingStopPips), 5m)
		.SetNotNegative()
		.SetDisplay("Trailing Stop (pips)", "Distance for trailing stop", "Risk");

		_trailingStepPips = Param(nameof(TrailingStepPips), 5m)
		.SetNotNegative()
		.SetDisplay("Trailing Step (pips)", "Additional move required before trailing", "Risk");

		_maClosePeriod = Param(nameof(MaClosePeriod), 20)
		.SetGreaterThanZero()
		.SetDisplay("MA Close Period", "Length of MA calculated on close", "Indicators");

		_maCloseShift = Param(nameof(MaCloseShift), 0)
		.SetNotNegative()
		.SetDisplay("MA Close Shift", "Forward shift applied to close MA", "Indicators");

		_maCloseMethod = Param(nameof(MaCloseMethod), MovingAverageMethods.Ema)
		.SetDisplay("MA Close Method", "Smoothing method for close MA", "Indicators");

		_maCloseAppliedPrice = Param(nameof(MaCloseAppliedPrice), AppliedPriceTypes.Close)
		.SetDisplay("MA Close Price", "Price source for close MA", "Indicators");

		_maCloseSignalBar = Param(nameof(MaCloseSignalBar), 0)
		.SetNotNegative()
		.SetDisplay("MA Close Signal Bar", "Reference bar index for close MA", "Indicators");

		_maOpenPeriod = Param(nameof(MaOpenPeriod), 70)
		.SetGreaterThanZero()
		.SetDisplay("MA Open Period", "Length of MA calculated on open", "Indicators");

		_maOpenShift = Param(nameof(MaOpenShift), 0)
		.SetNotNegative()
		.SetDisplay("MA Open Shift", "Forward shift applied to open MA", "Indicators");

		_maOpenMethod = Param(nameof(MaOpenMethod), MovingAverageMethods.Ema)
		.SetDisplay("MA Open Method", "Smoothing method for open MA", "Indicators");

		_maOpenAppliedPrice = Param(nameof(MaOpenAppliedPrice), AppliedPriceTypes.Close)
		.SetDisplay("MA Open Price", "Price source for open MA", "Indicators");

		_maOpenSignalBar = Param(nameof(MaOpenSignalBar), 0)
		.SetNotNegative()
		.SetDisplay("MA Open Signal Bar", "Reference bar index for open MA", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
		.SetDisplay("Candle Type", "Time frame of input candles", "General");

		Volume = _tradeVolume.Value;
	}

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

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

		_maOpenIndicator = null;
		_maCloseIndicator = null;
		_maOpenValues.Clear();
		_maCloseValues.Clear();
		_pipSize = 0m;
		_maxOpenQueueSize = 0;
		_maxCloseQueueSize = 0;
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
	}

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

		if (TrailingStopPips > 0m && TrailingStepPips <= 0m)
		throw new InvalidOperationException("Trailing step must be greater than zero when trailing stop is enabled.");

		_maOpenIndicator = CreateMovingAverage(MaOpenMethod, MaOpenPeriod);
		_maCloseIndicator = CreateMovingAverage(MaCloseMethod, MaClosePeriod);

		UpdatePipSize();
		UpdateQueueSizes();

		Volume = _tradeVolume.Value;

		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;

		if (_maOpenIndicator == null || _maCloseIndicator == null)
		return;

		var openSource = GetAppliedPrice(candle, MaOpenAppliedPrice);
		var closeSource = GetAppliedPrice(candle, MaCloseAppliedPrice);

		var maOpenResult = _maOpenIndicator!.Process(new DecimalIndicatorValue(_maOpenIndicator, openSource, candle.OpenTime) { IsFinal = true });
		var maCloseResult = _maCloseIndicator!.Process(new DecimalIndicatorValue(_maCloseIndicator, closeSource, candle.OpenTime) { IsFinal = true });

		if (maOpenResult.IsEmpty || maCloseResult.IsEmpty || !_maOpenIndicator.IsFormed || !_maCloseIndicator.IsFormed)
			return;

		var maOpen = maOpenResult.ToDecimal();
		var maClose = maCloseResult.ToDecimal();

		EnqueueValue(_maOpenValues, maOpen, _maxOpenQueueSize);
		EnqueueValue(_maCloseValues, maClose, _maxCloseQueueSize);

		var maOpenPrev = GetQueueValue(_maOpenValues, 1 + MaOpenShift);
		var maOpenSignal = GetQueueValue(_maOpenValues, MaOpenSignalBar + MaOpenShift);
		var maClosePrev = GetQueueValue(_maCloseValues, 1 + MaCloseShift);
		var maCloseSignal = GetQueueValue(_maCloseValues, MaCloseSignalBar + MaCloseShift);

		if (maOpenPrev is null || maOpenSignal is null || maClosePrev is null || maCloseSignal is null)
		return;

		var longSignal = maOpenPrev > maOpenSignal && maClosePrev > maCloseSignal;
		var shortSignal = maOpenPrev < maOpenSignal && maClosePrev < maCloseSignal;

		if (Position == 0)
		{
			if (longSignal)
			{
				OpenLong(candle.ClosePrice);
			}
			else if (shortSignal)
			{
				OpenShort(candle.ClosePrice);
			}
		}
		else
		{
			ManageOpenPosition(candle, maOpenPrev.Value, maOpenSignal.Value);
		}
	}

	private void OpenLong(decimal price)
	{
		var volume = Volume;
		if (volume <= 0m)
		return;

		_entryPrice = price;
		_stopPrice = StopLossPips > 0m ? price - StopLossPips * _pipSize : null;
		_takePrice = TakeProfitPips > 0m ? price + TakeProfitPips * _pipSize : null;

		BuyMarket(volume);
	}

	private void OpenShort(decimal price)
	{
		var volume = Volume;
		if (volume <= 0m)
		return;

		_entryPrice = price;
		_stopPrice = StopLossPips > 0m ? price + StopLossPips * _pipSize : null;
		_takePrice = TakeProfitPips > 0m ? price - TakeProfitPips * _pipSize : null;

		SellMarket(volume);
	}

	private void ManageOpenPosition(ICandleMessage candle, decimal maOpenPrev, decimal maOpenSignal)
	{
		var position = Position;

		if (position > 0)
		{
			if (maOpenPrev < maOpenSignal)
			{
				SellMarket(position);
				ResetPositionState();
				return;
			}

			UpdateTrailingForLong(candle);

			if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
			{
				SellMarket(position);
				ResetPositionState();
				return;
			}

			if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
			{
				SellMarket(position);
				ResetPositionState();
			}
		}
		else if (position < 0)
		{
			if (maOpenPrev > maOpenSignal)
			{
				BuyMarket(-position);
				ResetPositionState();
				return;
			}

			UpdateTrailingForShort(candle);

			if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
			{
				BuyMarket(-position);
				ResetPositionState();
				return;
			}

			if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
			{
				BuyMarket(-position);
				ResetPositionState();
			}
		}
		else
		{
			ResetPositionState();
		}
	}

	private void UpdateTrailingForLong(ICandleMessage candle)
	{
		if (TrailingStopPips <= 0m || TrailingStepPips <= 0m || _entryPrice is null)
		return;

		var trailingStop = TrailingStopPips * _pipSize;
		var trailingStep = TrailingStepPips * _pipSize;

		if (trailingStop <= 0m)
		return;

		var currentPrice = candle.ClosePrice;
		var entryPrice = _entryPrice.Value;

		if (currentPrice - entryPrice <= trailingStop + trailingStep)
		return;

		var threshold = currentPrice - (trailingStop + trailingStep);

		if (_stopPrice.HasValue && _stopPrice.Value >= threshold)
		return;

		var newStop = currentPrice - trailingStop;
		if (!_stopPrice.HasValue || newStop > _stopPrice.Value)
		_stopPrice = newStop;
	}

	private void UpdateTrailingForShort(ICandleMessage candle)
	{
		if (TrailingStopPips <= 0m || TrailingStepPips <= 0m || _entryPrice is null)
		return;

		var trailingStop = TrailingStopPips * _pipSize;
		var trailingStep = TrailingStepPips * _pipSize;

		if (trailingStop <= 0m)
		return;

		var currentPrice = candle.ClosePrice;
		var entryPrice = _entryPrice.Value;

		if (entryPrice - currentPrice <= trailingStop + trailingStep)
		return;

		var threshold = currentPrice + trailingStop + trailingStep;

		if (_stopPrice.HasValue && _stopPrice.Value <= threshold)
		return;

		var newStop = currentPrice + trailingStop;
		if (!_stopPrice.HasValue || newStop < _stopPrice.Value)
		_stopPrice = newStop;
	}

	private void ResetPositionState()
	{
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
	}

	private void UpdatePipSize()
	{
		var step = Security?.PriceStep ?? 1m;
		_pipSize = step;
	}

	private void UpdateQueueSizes()
	{
		var shiftOpen = Math.Max(0, MaOpenShift);
		var shiftClose = Math.Max(0, MaCloseShift);
		var openDepth = Math.Max(Math.Max(1 + shiftOpen, MaOpenSignalBar + shiftOpen), MaCloseSignalBar + shiftOpen);
		var closeDepth = Math.Max(1 + shiftClose, 1);

		_maxOpenQueueSize = Math.Max(2, openDepth + 2);
		_maxCloseQueueSize = Math.Max(2, closeDepth + 2);
	}

	private static void EnqueueValue(List<decimal> queue, decimal value, int maxSize)
	{
		queue.Add(value);

		while (queue.Count > maxSize)
			queue.RemoveAt(0);
	}

	private static decimal? GetQueueValue(List<decimal> queue, int indexFromCurrent)
	{
		if (indexFromCurrent < 0)
			return null;

		if (queue.Count <= indexFromCurrent)
			return null;

		var targetIndex = queue.Count - 1 - indexFromCurrent;

		return targetIndex >= 0 && targetIndex < queue.Count
			? queue[targetIndex]
			: null;
	}

	private static DecimalLengthIndicator CreateMovingAverage(MovingAverageMethods method, int length)
	{
		return method switch
		{
		MovingAverageMethods.Sma => new SimpleMovingAverage { Length = length },
		MovingAverageMethods.Ema => new ExponentialMovingAverage { Length = length },
		MovingAverageMethods.Smma => new SmoothedMovingAverage { Length = length },
		MovingAverageMethods.Lwma => new WeightedMovingAverage { Length = length },
		_ => new ExponentialMovingAverage { Length = length }
		};
	}

	private static decimal GetAppliedPrice(ICandleMessage candle, AppliedPriceTypes priceType)
	{
		return priceType switch
		{
			AppliedPriceTypes.Close => candle.ClosePrice,
			AppliedPriceTypes.Open => candle.OpenPrice,
			AppliedPriceTypes.High => candle.HighPrice,
			AppliedPriceTypes.Low => candle.LowPrice,
			AppliedPriceTypes.Median => (candle.HighPrice + candle.LowPrice) / 2m,
			AppliedPriceTypes.Typical => (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m,
			AppliedPriceTypes.Weighted => (candle.HighPrice + candle.LowPrice + candle.ClosePrice + candle.ClosePrice) / 4m,
			_ => candle.ClosePrice
		};
	}
}