在 GitHub 上查看

SV日内突破策略

概览

SV日内突破策略 是对 MetaTrader 5 专家顾问“SV v.4.2.5”的完整 C# 迁移版本。策略在每根完成的K线后进行评估,每个交易日最多只允许一次入场。只有当服务器时间晚于设定的开始时间后才会考虑信号。系统跳过最近的 Shift 根K线,利用之后 Interval 根历史K线的高低点与两条平滑移动平均线之间的关系来判断是否出现极端区间,从而捕捉趋势反转的机会。

交易规则

入场条件

  • 日内闸门:在当前服务器时间早于 Start Hour/Start Minute 时不进行任何评估。每天仅允许一次入场。
  • 数据窗口:忽略最近 Shift 根K线,分析紧随其后的 Interval 根K线,并计算这段时间的最高价和最低价。
  • 做多:若分析区间内的最高价严格低于慢速均线,同时最低价严格低于快速均线,则视为超卖反弹信号,平掉空头并开多。
  • 做空:若分析区间内的最低价严格高于慢速均线,同时最高价严格高于快速均线,则视为超买回落信号,平掉多头并开空。

离场管理

  • 初始止损:按 Stop Loss (pips) 设置距离入场价的固定止损,一旦触发立即平仓。
  • 止盈:按 Take Profit (pips) 设置固定盈利目标,触发后退出仓位。
  • 追踪止损:当 Trailing StopTrailing Step 均大于零时启用。多头在价格上涨超过 Trailing Stop + Trailing Step 后,将止损上移至 收盘价 − Trailing Stop;空头逻辑相反。
  • 日内锁定:无论仓位如何退出,当天不再寻找新的入场机会。

仓位规模

  • 手数模式:当 Use Manual Volumetrue 时,按照 Volume 参数(自动对齐合约最小交易量)直接下单。
  • 风险模式:当 Use Manual Volumefalse 时,根据账户权益和 Risk % 估算下单数量。系统会利用标的的价格步长及每步价值,计算覆盖止损所需的合约数量。

参数

参数 默认值 说明
Use Manual Volume false 是否使用固定交易量而非风险仓位。
Volume 0.1 手数模式下的下单量。
Risk % 5 风险模式下的账户权益百分比。
Stop Loss (pips) 50 以点数表示的止损距离,设为 0 表示关闭。
Take Profit (pips) 50 以点数表示的止盈距离,设为 0 表示关闭。
Trailing Stop (pips) 5 追踪止损的距离,必须与 Trailing Step 配合使用。
Trailing Step (pips) 5 每次移动追踪止损所需的最小盈利增量。
Start Hour 19 开始评估信号的小时(交易所时间)。
Start Minute 0 开始评估信号的分钟(交易所时间)。
Shift 6 计算区间时跳过的最新K线数量。
Interval 27 用于计算区间高低点的K线数量。
Fast MA Period 14 快速移动平均的周期。
Fast MA Shift 0 获取快速均线数值时向左偏移的K线数量。
Fast MA Method Smma 快速均线的计算方式。
Fast Applied Price Median 快速均线使用的价格类型。
Slow MA Period 41 慢速移动平均的周期。
Slow MA Shift 0 获取慢速均线数值时向左偏移的K线数量。
Slow MA Method Smma 慢速均线的计算方式。
Slow Applied Price Median 慢速均线使用的价格类型。
Candle Type 1 hour 用于分析的K线周期。

其他说明

  • 策略保留了原EA“跳过最新K线后再分析区间”的特点,有助于避免最近几根K线的噪音。
  • 追踪止损基于K线收盘价来模拟MetaTrader的逐笔调整,如交易品种点值不同请相应调整参数。
  • 风险仓位计算依赖 Security.PriceStepSecurity.StepPriceSecurity.VolumeStep。请确保交易品种的这些属性已经设置。
  • 策略调用 StartProtection(),便于叠加全局风控或保护规则。
  • 若要完全复现原策略的行为,请保证行情数据与交易账户的服务器时区与 Start Hour/Start Minute 所使用的时区一致。
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>
/// Daily breakout strategy converted from the "SV v.4.2.5" MetaTrader 5 expert advisor.
/// Evaluates one trade per day after a configurable start time using moving average filters.
/// </summary>
public class SvDailyBreakoutStrategy : Strategy
{
	private readonly StrategyParam<bool> _useManualVolume;
	private readonly StrategyParam<decimal> _riskPercent;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<int> _trailingStopPips;
	private readonly StrategyParam<int> _trailingStepPips;
	private readonly StrategyParam<int> _startHour;
	private readonly StrategyParam<int> _startMinute;
	private readonly StrategyParam<int> _shift;
	private readonly StrategyParam<int> _interval;
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _fastMaShift;
	private readonly StrategyParam<MovingAverageMethods> _fastMaMethod;
	private readonly StrategyParam<AppliedPrices> _fastAppliedPrice;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _slowMaShift;
	private readonly StrategyParam<MovingAverageMethods> _slowMaMethod;
	private readonly StrategyParam<AppliedPrices> _slowAppliedPrice;
	private readonly StrategyParam<DataType> _candleType;

	private IIndicator _fastMa;
	private IIndicator _slowMa;

	private readonly List<decimal> _fastMaValues = new();
	private readonly List<decimal> _slowMaValues = new();
	private readonly List<decimal> _highHistory = new();
	private readonly List<decimal> _lowHistory = new();

	private decimal? _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takeProfitPrice;
	private decimal? _trailingStopPrice;
	private DateTime? _currentDay;
	private decimal _pipSize;

	/// <summary>
	/// Use manual volume instead of the risk-based sizing model.
	/// </summary>
	public bool UseManualVolume
	{
		get => _useManualVolume.Value;
		set => _useManualVolume.Value = value;
	}


	/// <summary>
	/// Risk percentage of account equity used when calculating the dynamic position size.
	/// </summary>
	public decimal RiskPercent
	{
		get => _riskPercent.Value;
		set => _riskPercent.Value = value;
	}

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

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

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

	/// <summary>
	/// Trailing step distance expressed in pips.
	/// </summary>
	public int TrailingStepPips
	{
		get => _trailingStepPips.Value;
		set => _trailingStepPips.Value = value;
	}

	/// <summary>
	/// Hour of the day (exchange time) when the strategy starts searching for entries.
	/// </summary>
	public int StartHour
	{
		get => _startHour.Value;
		set => _startHour.Value = value;
	}

	/// <summary>
	/// Minute of the hour when the strategy starts searching for entries.
	/// </summary>
	public int StartMinute
	{
		get => _startMinute.Value;
		set => _startMinute.Value = value;
	}

	/// <summary>
	/// Number of recent bars excluded from the high/low analysis window.
	/// </summary>
	public int Shift
	{
		get => _shift.Value;
		set => _shift.Value = value;
	}

	/// <summary>
	/// Number of bars that are analysed when computing the breakout range.
	/// </summary>
	public int Interval
	{
		get => _interval.Value;
		set => _interval.Value = value;
	}

	/// <summary>
	/// Fast moving average period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Fast moving average horizontal shift.
	/// </summary>
	public int FastMaShift
	{
		get => _fastMaShift.Value;
		set => _fastMaShift.Value = value;
	}

	/// <summary>
	/// Fast moving average calculation method.
	/// </summary>
	public MovingAverageMethods FastMaMethod
	{
		get => _fastMaMethod.Value;
		set => _fastMaMethod.Value = value;
	}

	/// <summary>
	/// Applied price used for the fast moving average.
	/// </summary>
	public AppliedPrices FastAppliedPrice
	{
		get => _fastAppliedPrice.Value;
		set => _fastAppliedPrice.Value = value;
	}

	/// <summary>
	/// Slow moving average period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow moving average horizontal shift.
	/// </summary>
	public int SlowMaShift
	{
		get => _slowMaShift.Value;
		set => _slowMaShift.Value = value;
	}

	/// <summary>
	/// Slow moving average calculation method.
	/// </summary>
	public MovingAverageMethods SlowMaMethod
	{
		get => _slowMaMethod.Value;
		set => _slowMaMethod.Value = value;
	}

	/// <summary>
	/// Applied price used for the slow moving average.
	/// </summary>
	public AppliedPrices SlowAppliedPrice
	{
		get => _slowAppliedPrice.Value;
		set => _slowAppliedPrice.Value = value;
	}

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

	/// <summary>
	/// Initializes strategy parameters with defaults that match the original expert advisor.
	/// </summary>
	public SvDailyBreakoutStrategy()
	{
		_useManualVolume = Param(nameof(UseManualVolume), false)
			.SetDisplay("Use Manual Volume", "Use fixed volume instead of risk percentage", "Risk");


		_riskPercent = Param(nameof(RiskPercent), 5m)
			.SetGreaterThanZero()
			.SetDisplay("Risk %", "Risk percentage of account equity", "Risk");

		_stopLossPips = Param(nameof(StopLossPips), 50)
			.SetNotNegative()
			.SetDisplay("Stop Loss (pips)", "Stop loss distance in pips", "Risk");

		_takeProfitPips = Param(nameof(TakeProfitPips), 50)
			.SetNotNegative()
			.SetDisplay("Take Profit (pips)", "Take profit distance in pips", "Risk");

		_trailingStopPips = Param(nameof(TrailingStopPips), 5)
			.SetNotNegative()
			.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk");

		_trailingStepPips = Param(nameof(TrailingStepPips), 5)
			.SetNotNegative()
			.SetDisplay("Trailing Step (pips)", "Trailing step increment in pips", "Risk");

		_startHour = Param(nameof(StartHour), 0)
			.SetDisplay("Start Hour", "Hour when trading may begin", "Trading Window");

		_startMinute = Param(nameof(StartMinute), 0)
			.SetDisplay("Start Minute", "Minute when trading may begin", "Trading Window");

		_shift = Param(nameof(Shift), 2)
			.SetNotNegative()
			.SetDisplay("Shift", "Number of newest bars excluded from range analysis", "Logic");

		_interval = Param(nameof(Interval), 10)
			.SetGreaterThanZero()
			.SetDisplay("Interval", "Number of historical bars analysed", "Logic");

		_fastMaPeriod = Param(nameof(FastMaPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA Period", "Fast moving average length", "Indicators");

		_fastMaShift = Param(nameof(FastMaShift), 0)
			.SetNotNegative()
			.SetDisplay("Fast MA Shift", "Horizontal shift for the fast moving average", "Indicators");

		_fastMaMethod = Param(nameof(FastMaMethod), MovingAverageMethods.Ema)
			.SetDisplay("Fast MA Method", "Calculation method for the fast moving average", "Indicators");

		_fastAppliedPrice = Param(nameof(FastAppliedPrice), AppliedPrices.Median)
			.SetDisplay("Fast Applied Price", "Price type used for the fast moving average", "Indicators");

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA Period", "Slow moving average length", "Indicators");

		_slowMaShift = Param(nameof(SlowMaShift), 0)
			.SetNotNegative()
			.SetDisplay("Slow MA Shift", "Horizontal shift for the slow moving average", "Indicators");

		_slowMaMethod = Param(nameof(SlowMaMethod), MovingAverageMethods.Ema)
			.SetDisplay("Slow MA Method", "Calculation method for the slow moving average", "Indicators");

		_slowAppliedPrice = Param(nameof(SlowAppliedPrice), AppliedPrices.Median)
			.SetDisplay("Slow Applied Price", "Price type used for the slow moving average", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle series used for calculations", "General");
	}

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

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

		_fastMaValues.Clear();
		_slowMaValues.Clear();
		_highHistory.Clear();
		_lowHistory.Clear();
		_entryPrice = null;
		_pipSize = 0m;
		_stopPrice = null;
		_takeProfitPrice = null;
		_trailingStopPrice = null;
		_currentDay = null;
	}

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

		// validation removed for flexibility

		_fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
		_slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };

		var decimals = Security?.Decimals ?? 2;
		var step = Security?.PriceStep ?? 0.01m;
		var factor = decimals is 3 or 5 ? 10m : 1m;
		_pipSize = step * factor;
		if (_pipSize <= 0m)
			_pipSize = step > 0m ? step : 0.01m;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_fastMa, _slowMa, ProcessCandleWithMa)
			.Start();
	}

	private void ProcessCandleWithMa(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		UpdateDailyState(candle.CloseTime);

		UpdateRangeHistory(candle);

		UpdateTrailing(candle);

		if (CheckProtectiveExits(candle))
			return;

		if (Position != 0)
			return;

		if (!TryGetRangeExtremes(out var lowest, out var highest))
			return;

		if (highest < slowValue && lowest < fastValue)
		{
			EnterPosition(true, candle);
			return;
		}

		if (lowest > slowValue && highest > fastValue)
		{
			EnterPosition(false, candle);
		}
	}

	private void EnterPosition(bool isLong, ICandleMessage candle)
	{
		var entryPrice = candle.ClosePrice;
		var stopDistance = StopLossPips > 0 ? StopLossPips * _pipSize : 0m;

		var volume = UseManualVolume
			? NormalizeVolume(Volume)
			: CalculateRiskBasedVolume(stopDistance);

		if (volume <= 0m)
			return;

		if (isLong)
		{
			var totalVolume = volume + (Position < 0 ? Math.Abs(Position) : 0m);
			if (totalVolume <= 0m)
				return;

			BuyMarket(totalVolume);
			_entryPrice = entryPrice;
			_stopPrice = StopLossPips > 0 ? entryPrice - stopDistance : null;
			_takeProfitPrice = TakeProfitPips > 0 ? entryPrice + TakeProfitPips * _pipSize : null;
		}
		else
		{
			var totalVolume = volume + (Position > 0 ? Position : 0m);
			if (totalVolume <= 0m)
				return;

			SellMarket(totalVolume);
			_entryPrice = entryPrice;
			_stopPrice = StopLossPips > 0 ? entryPrice + stopDistance : null;
			_takeProfitPrice = TakeProfitPips > 0 ? entryPrice - TakeProfitPips * _pipSize : null;
		}

		_trailingStopPrice = TrailingStopPips > 0 ? _stopPrice : null;
	}

	private void UpdateDailyState(DateTimeOffset time)
	{
		var day = time.Date;
		if (_currentDay != day)
		{
			_currentDay = day;
		}
	}

	private void UpdateRangeHistory(ICandleMessage candle)
	{
		_highHistory.Add(candle.HighPrice);
		_lowHistory.Add(candle.LowPrice);

		var maxCount = Math.Max(Shift + Interval + 5, 50);
		if (_highHistory.Count > maxCount)
		{
			var remove = _highHistory.Count - maxCount;
			_highHistory.RemoveRange(0, remove);
			_lowHistory.RemoveRange(0, remove);
		}
	}

	private decimal? ProcessMovingAverage(IIndicator indicator, AppliedPrices priceMode, List<decimal> buffer, int shift, ICandleMessage candle)
	{
		var price = GetAppliedPrice(candle, priceMode);
		var result = indicator.Process(new DecimalIndicatorValue(indicator, price, candle.OpenTime));

		if (!result.IsFormed)
			return null;

		var value = result.GetValue<decimal>();
		buffer.Add(value);

		var maxSize = Math.Max(shift + 1, 100);
		if (buffer.Count > maxSize)
			buffer.RemoveAt(0);

		var index = buffer.Count - 1 - shift;
		if (index < 0 || index >= buffer.Count)
			return null;

		return buffer[index];
	}

	private bool TryGetRangeExtremes(out decimal lowest, out decimal highest)
	{
		lowest = 0m;
		highest = 0m;

		var required = Shift + Interval;
		if (required <= 0)
			return false;

		if (_lowHistory.Count < required || _highHistory.Count < required)
			return false;

		var low = decimal.MaxValue;
		var high = decimal.MinValue;
		var total = _lowHistory.Count;

		for (var offset = Shift; offset < Shift + Interval; offset++)
		{
			var index = total - 1 - offset;
			if (index < 0)
				return false;

			var lowValue = _lowHistory[index];
			var highValue = _highHistory[index];

			if (lowValue < low)
				low = lowValue;

			if (highValue > high)
				high = highValue;
		}

		if (low == decimal.MaxValue || high == decimal.MinValue)
			return false;

		lowest = low;
		highest = high;
		return true;
	}

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

		var trailDistance = TrailingStopPips * _pipSize;
		var stepDistance = TrailingStepPips * _pipSize;

		if (Position > 0)
		{
			var current = candle.ClosePrice;
			var entry = _entryPrice.Value;
			if (current - entry > trailDistance + stepDistance)
			{
				var threshold = current - (trailDistance + stepDistance);
				if (_stopPrice is null || _stopPrice < threshold)
				{
					var newStop = current - trailDistance;
					if (_stopPrice is null || newStop > _stopPrice)
					{
						_stopPrice = newStop;
						_trailingStopPrice = newStop;
					}
				}
			}
		}
		else if (Position < 0)
		{
			var current = candle.ClosePrice;
			var entry = _entryPrice.Value;
			if (entry - current > trailDistance + stepDistance)
			{
				var threshold = current + trailDistance + stepDistance;
				if (_stopPrice is null || _stopPrice > threshold)
				{
					var newStop = current + trailDistance;
					if (_stopPrice is null || newStop < _stopPrice)
					{
						_stopPrice = newStop;
						_trailingStopPrice = newStop;
					}
				}
			}
		}
	}

	private bool CheckProtectiveExits(ICandleMessage candle)
	{
		if (Position > 0)
		{
			if (_stopPrice is decimal stop && candle.LowPrice <= stop)
			{
				SellMarket(Position);
				ResetTradeState();
				return true;
			}

			if (_takeProfitPrice is decimal take && candle.HighPrice >= take)
			{
				SellMarket(Position);
				ResetTradeState();
				return true;
			}
		}
		else if (Position < 0)
		{
			var volume = Math.Abs(Position);
			if (_stopPrice is decimal stop && candle.HighPrice >= stop)
			{
				BuyMarket(volume);
				ResetTradeState();
				return true;
			}

			if (_takeProfitPrice is decimal take && candle.LowPrice <= take)
			{
				BuyMarket(volume);
				ResetTradeState();
				return true;
			}
		}
		else if (_entryPrice is not null)
		{
			ResetTradeState();
		}

		return false;
	}

	private decimal CalculateRiskBasedVolume(decimal stopDistance)
	{
		if (UseManualVolume || stopDistance <= 0m)
			return NormalizeVolume(Volume);

		var portfolioValue = Portfolio?.CurrentValue ?? 0m;
		if (portfolioValue <= 0m)
			return NormalizeVolume(Volume);

		var riskAmount = portfolioValue * RiskPercent / 100m;
		if (riskAmount <= 0m)
			return NormalizeVolume(Volume);

		var step = Security?.PriceStep ?? _pipSize;
		if (step <= 0m)
			step = _pipSize > 0m ? _pipSize : 1m;

		var stepValue = GetSecurityValue<decimal?>(Level1Fields.StepPrice) ?? step;
		if (stepValue <= 0m)
			stepValue = step;

		var steps = stopDistance / step;
		if (steps <= 0m)
			return NormalizeVolume(Volume);

		var riskPerUnit = steps * stepValue;
		if (riskPerUnit <= 0m)
			return NormalizeVolume(Volume);

		var rawVolume = riskAmount / riskPerUnit;
		return NormalizeVolume(rawVolume);
	}

	private decimal NormalizeVolume(decimal volume)
	{
		if (Security is null)
			return volume;

		var step = Security.VolumeStep ?? 1m;
		if (step > 0m)
			volume = Math.Floor(volume / step) * step;

		if (volume < step)
			volume = step;

		return volume;
	}

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

	private static IIndicator 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 SimpleMovingAverage { Length = length },
		};
	}

	private void ResetTradeState()
	{
		_entryPrice = null;
		_stopPrice = null;
		_takeProfitPrice = null;
		_trailingStopPrice = null;
	}

	/// <summary>
	/// Available moving average calculation methods.
	/// </summary>
	public enum MovingAverageMethods
	{
		/// <summary>
		/// Simple moving average.
		/// </summary>
		Sma,

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

		/// <summary>
		/// Smoothed moving average (SMMA).
		/// </summary>
		Smma,

		/// <summary>
		/// Linear weighted moving average (LWMA).
		/// </summary>
		Lwma
	}

	/// <summary>
	/// Price sources supported by the moving averages.
	/// </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 (high + low + close) / 3.
		/// </summary>
		Typical,

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