GitHub で見る

Brandy戦略 (C#)

概要

Brandy戦略はMetaTrader 5エキスパートアドバイザー*Brandy(barabashkakvn版)*の直接ポートです。2つの設定可能な移動平均を組み合わせ、閉じたローソク足でのそれらの相対的な位置を評価してロングまたはショートポジションを建てるかどうかを決定します。オリジナルのロジックはpipで表されたオプションのストップロス、テイクプロフィット、トレーリングストップコントロールも強制します。このC#バージョンはStockSharpの高レベル戦略APIの上にこれらの動作を忠実に再現します。

戦略は始値ストリームで「速い」移動平均を、終値ストリームで「遅い」移動平均を計算します。両方のインジケーターは独立した期間、平滑化メソッド、価格ソース、シグナルバー参照、シフトパラメーターを持ちます。前のバーのMA値がそれぞれのシグナル値の同じ側にある場合にシグナルが生成されます。保護ロジックは各ローソク足で始値ベースの移動平均を確認し、トレンド条件がもはや満たされなければ即座に取引を終了します。追加のリスク管理はオプションのストップロス、テイクプロフィット、トレーリングストップ距離で実装され、すべてpipで測定され、5桁pip調整付きの銘柄ティックサイズを使用して絶対価格に変換されます。

取引ロジック

  1. 各完成したローソク足で、戦略は設定された平滑化メソッドと適用価格を使用して始値と終値の移動平均を更新します。オリジナルのエキスパートアドバイザーのiMAシフト動作をエミュレートできるよう、MAの履歴値がバッファリングされます。
  2. アクティブなポジションがない場合、以下の条件でロングトレードが開かれます:
    • 前の始値ベースのMA値が設定されたシグナル値(おそらくシフトされた)より大きい;
    • 前の終値ベースのMA値もそのシグナル参照より大きい(元のEAはこのチェックに始値ベースのインジケーターと比較することに注意してください。互換性のためにポートはその特異性を保持しています)。
  3. 両方の移動平均がそれぞれのシグナル参照を下回る場合にショートトレードが開かれます。
  4. ポジションがアクティブな間、戦略は以下の順序で各完成したローソク足でエグジットを評価します:
    • トレンド反転:始値ベースのMAがシグナル値を下回る(ロングの場合)またはその上に上昇する(ショートの場合)場合、ポジションはすぐに市場価格でクローズされます。
    • トレーリングストップ更新:有効で取引に有利な動きがトレーリングストップ + トレーリングステップ(絶対価格に変換)を超えた場合、ストップレベルが最新の終値からトレーリングストップ距離を維持するように締め付けられます。
    • テイクプロフィット:ローソク足の範囲が利益目標に触れた場合、取引は市場価格でクローズされます。
    • ストップロス:ローソク足の範囲が保護ストップレベルを破った場合、取引はクローズされます。
  5. すべてのボリュームは固定でTradeVolumeパラメーターによって決定されます。デフォルト値はMT5バージョンの0.1ロット設定を複製します。

パラメータ参照

パラメータ 説明
TradeVolume ロット単位の成行注文サイズ。
StopLossPips 保護ストップの距離、pip単位で測定(0で無効)。
TakeProfitPips 利益目標の距離(pip)(0で無効)。
TrailingStopPips pip単位のトレーリングストップ距離。TrailingStepPipsが正であることが必要。
TrailingStepPips トレーリングストップを進める前に必要な追加pip移動。トレーリングストップがアクティブな場合はゼロ以外である必要があります。
MaClosePeriod, MaOpenPeriod それぞれ終値と始値シリーズの移動平均長。
MaCloseShift, MaOpenShift MAバッファに適用された前向きシフト(バーの数)。
MaCloseSignalBar, MaOpenSignalBar 比較参照として使用されるバーインデックス。ゼロは最も最近の値に一致し、1つは前のバーを参照し、以下同様。
MaCloseMethod, MaOpenMethod 移動平均平滑化メソッド(SMA、EMA、SMMA、LWMA)。
MaCloseAppliedPrice, MaOpenAppliedPrice 各インジケーターのローソク足価格ソース(終値、始値、高値、安値、中央値、典型値、加重)。
CandleType データソースからリクエストされたローソク足の時間軸。

実装ノート

  • pip サイズはSecurity.PriceStepから計算され、インジケーターが3または5の小数点を公開する場合に10を乗算します。これはポイントとpipの間のMetaTrader調整を反映します。
  • 元のiMAの任意のシグナルバーインデックスと正のシフトを持つ呼び出しを禁止されたインジケーターアクセサーに頼ることなく再現できるよう、インジケーター履歴は有界キューを使用して保持されます。
  • 終値ベースの移動平均のクローズ条件は、元のソースコードがiMAGet(handle_iMAOpen, MaClose_SignalBar)を呼び出したため、始値MAバッファと意図的に比較します。このポートはレガシー設定との互換性を維持するためにその動作を保持します。
  • ストップとトレーリングロジックは完成したローソク足で実行され、StockSharpの高レベルAPIを尊重しながらエキスパートアドバイザーが実行したオーダー変更を近似します。

使用のヒント

  • CandleTypeパラメーターをオリジナルEAが使用した時間軸に合わせて設定してください(通常は単一の銘柄時間軸)。
  • トレーリング動作が不要な場合はTrailingStopPipsをゼロに保ってください。それ以外の場合は戦略によって強制される初期化エラーを避けるためにTrailingStepPipsが厳密に正であることを確認してください。
  • StockSharpでのバックテスト時は、リスク距離が正しく変換されるよう、銘柄のPriceStepDecimalsが意図したpip定義を反映していることを確認してください。
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
		};
	}
}