Auf GitHub ansehen

YenTrader051-Strategie (C#)

Überblick

Die YenTrader051-Strategie repliziert den ursprünglichen MetaTrader Expert Advisor, der die Beziehung zwischen drei Währungspaaren arbitriert:

  • Handeltes Kreuzpaar – das Instrument, das die Strategieinstanz hostet (z.B. GBPJPY).
  • Hauptpaar – typischerweise die Basiswährung des Kreuzes gegen USD (z.B. GBPUSD).
  • USDJPY – wird zur Bestätigung des Yen-Beins des Dreiecks verwendet.

Ein Ausbruch beim Hauptpaar kombiniert mit Bestätigung von USDJPY erzeugt die Handelssignale. Optionale RSI-, CCI-, RVI- und gleitende Durchschnittsfilter verfeinern die Einstiege. Das Positionsmanagement unterstützt sowohl Averaging als auch Pyramiding, während das Risikomanagement die pip/ATR-basierte Stop-Behandlung des EA reproduziert.

Handelslogik

  1. Ausbruchserkennung
    • LoopBackBars steuert das Lookback-Fenster. Wenn es größer als 1 ist, prüft die Strategie entweder:
      • aktuelle Hochs/Tiefs (PriceReference = HighLow), oder
      • Schlusskurse von vor LoopBackBars Bars (PriceReference = Close).
    • MajorDirection definiert, wie sich das Hauptpaar und das Yen-Bein relativ zueinander bewegen sollen, wenn das Kreuz als Haupt/Yen (Left) oder Yen/Haupt (Right) notiert ist.
  2. Einstiegsfilter
    • UseRsiFilter erfordert RSI über/unter 50 je nach erwartetem Trendabgleich.
    • UseCciFilter erzwingt positiven/negativen CCI.
    • UseRviFilter wartet auf das Kreuzen des RVI mit seiner Signallinie. Die Signallinie ist ein 4-Perioden-SMA der RVI-Werte, genau wie in der MT4-Implementierung.
    • UseMovingAverageFilter hält Einstiege mit einem konfigurierbaren gleitenden Durchschnitt ausgerichtet (MaMode, MaPeriod).
  3. Einsstiegsstil
    • EntryMode = Both erlaubt jeden Ausbruch.
    • EntryMode = Pyramiding fügt nur bei bullischen/bärischen Kerzen in Handelsrichtung hinzu.
    • EntryMode = Averaging fügt nur hinzu, wenn die vorherige Kerze gegen die Position schloss, um zu mitteln.
  4. Order-Sizing
    • FixedLotSize platziert ein konstantes Volumen.
    • Wenn das feste Los null ist, verwendet die Strategie BalancePercentLotSize und den aktuellen Portfoliowert für das Sizing.
    • MaxOpenPositions begrenzt die kumulative Größe (Anzahl der additiven Einstiege).
  5. Risikomanagement
    • Pip-Abstände (StopLossPips, TakeProfitPips, BreakEvenPips, ProfitLockPips, TrailingStopPips, TrailingStepPips) werden über Security.MinPriceStep übersetzt.
    • Wenn EnableAtrLevels aktiv ist, ersetzen ATR-Abstände Pips unter Verwendung des täglichen ATR (AtrCandleType, AtrPeriod) und der jeweiligen Multiplikatoren.
    • Stops, Take-Profits, Break-Even, Gewinnsperre und Trailing-Levels werden von abgeschlossenen Kerzen aktualisiert, genau wie in der MQL-Implementierung.
    • CloseOnOpposite schließt bestehende Positionen, anstatt neue zu stapeln, wenn ein entgegengesetzter Ausbruch erscheint.
    • AllowHedging lässt die Strategie zu einer Position hinzufügen, auch wenn noch eine entgegengesetzte Position offen ist. Beachten Sie, dass StockSharp-Strategien Netto-Positionen verwenden, sodass simultane Long/Short-Positionen nicht unterstützt werden; das Flag steuert effektiv, ob die Strategie die Exposition erhöhen darf, wenn die aktuelle Netto-Position in die andere Richtung zeigt.

Parameter

Gruppe Name Beschreibung
Instrumente MajorSecurity Hauptpaar für Ausbruchsbestätigung.
UsdJpySecurity USDJPY-Wertpapier für Yen-Bein-Bestätigung.
Daten CandleType Signal-Zeitrahmen für alle drei Paare.
Filter MajorDirection Ausrichtung zwischen Hauptpaar und gehandeltem Kreuz (Left = Haupt/Yen, Right = Yen/Haupt).
PriceReference Hoch/Tief-Ausbruch oder verzögerter Schlusskursvergleich.
LoopBackBars Anzahl historischer Bars zur Ausbruchsauswertung.
EntryMode Averaging, Pyramiding oder beides.
Indikatoren UseRsiFilter, UseCciFilter, UseRviFilter, UseMovingAverageFilter Zusätzliche Bestätigungsfilter aktivieren/deaktivieren.
MaPeriod, MaMode Konfiguration des gleitenden Durchschnitts.
Risiko FixedLotSize, BalancePercentLotSize Volumensteuerungen.
MaxOpenPositions Maximale Anzahl additiver Einstiege.
StopLossPips, TakeProfitPips, BreakEvenPips, ProfitLockPips, TrailingStopPips, TrailingStepPips Pip-basierte Risikoabstände.
EnableAtrLevels, AtrCandleType, AtrPeriod, AtrStopLossMultiplier, AtrTakeProfitMultiplier, AtrTrailingMultiplier, AtrBreakEvenMultiplier, AtrProfitLockMultiplier ATR-basierte Risikokonfiguration.
Verhalten CloseOnOpposite Positionen bei entgegengesetzten Signalen schließen oder umkehren.
AllowHedging Einstiege erlauben, wenn eine entgegengesetzte Netto-Position besteht.

Verwendungshinweise

  • Weisen Sie das gehandelte Kreuzinstrument der Security-Eigenschaft der Strategie zu, dann setzen Sie MajorSecurity und UsdJpySecurity für die Supportinstrumente.
  • Stellen Sie sicher, dass das Portfolio verbunden ist; variables Los-Sizing benötigt Portfolio.CurrentValue.
  • Die Strategie erwartet synchronisierte Kerzendaten für alle drei Instrumente. Wenn verschiedene Börsen Daten mit unterschiedlichen Sitzungskalendern liefern, erwägen Sie ein Resampling auf einen gemeinsamen Zeitrahmen.
  • ATR-Berechnungen abonnieren das konfigurierte AtrCandleType. Halten Sie es an den ursprünglichen EA-Standardwerten (täglich, 21 Perioden) ausgerichtet für vergleichbares Verhalten.
  • Die RisikoLogik operiert auf geschlossenen Kerzen, sodass Schutzorders durch Market-Exits ausgeführt werden, wenn die Schwellenwerte während der folgenden Kerze durchbrochen werden.

Unterschiede zur MT4-Version

  • StockSharp verwendet aggregierte Netto-Positionen; echtes Hedging (gleichzeitig Long und Short halten) ist nicht verfügbar. AllowHedging steuert lediglich, ob die Strategie Positionen automatisch umkehren kann, wenn ein neues Signal erscheint.
  • Stop-/Limit-Management wird mit Market-Exits implementiert, nachdem die Schwellenwerte auf Kerzendaten ausgelöst wurden. Der ursprüngliche EA modifiziert Order-Stops direkt, weil er auf Tick-Ebene operiert.
  • Die RVI-Signallinie wird als ein Vier-Perioden-SMA der RVI-Werte implementiert, was dem Verhalten von MODE_SIGNAL in MT4 entspricht.
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>
/// Port of the Yen Trader expert advisor that trades a JPY cross with confirmation from a major pair and USDJPY.
/// </summary>
public class YenTrader051Strategy : Strategy
{
	private readonly StrategyParam<Security> _majorSecurity;
	private readonly StrategyParam<Security> _usdJpySecurity;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<YenTraderMajorDirections> _majorDirection;
	private readonly StrategyParam<YenTraderEntryModes> _entryMode;
	private readonly StrategyParam<YenTraderPriceReferences> _priceReference;
	private readonly StrategyParam<int> _loopBackBars;
	private readonly StrategyParam<bool> _useRsiFilter;
	private readonly StrategyParam<bool> _useCciFilter;
	private readonly StrategyParam<bool> _useRviFilter;
	private readonly StrategyParam<bool> _useMovingAverageFilter;
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<MovingAverageModes> _maMode;
	private readonly StrategyParam<decimal> _fixedLotSize;
	private readonly StrategyParam<decimal> _balancePercentLotSize;
	private readonly StrategyParam<int> _maxOpenPositions;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<int> _breakEvenPips;
	private readonly StrategyParam<int> _profitLockPips;
	private readonly StrategyParam<int> _trailingStopPips;
	private readonly StrategyParam<int> _trailingStepPips;
	private readonly StrategyParam<bool> _closeOnOpposite;
	private readonly StrategyParam<bool> _allowHedging;
	private readonly StrategyParam<bool> _enableAtrLevels;
	private readonly StrategyParam<DataType> _atrCandleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrStopLossMultiplier;
	private readonly StrategyParam<decimal> _atrTakeProfitMultiplier;
	private readonly StrategyParam<decimal> _atrTrailingMultiplier;
	private readonly StrategyParam<decimal> _atrBreakEvenMultiplier;
	private readonly StrategyParam<decimal> _atrProfitLockMultiplier;

	private Highest _majorHighest = null!;
	private Lowest _majorLowest = null!;
	private RelativeStrengthIndex _majorRsi = null!;
	private CommodityChannelIndex _majorCci = null!;
	private RelativeVigorIndex _majorRvi = null!;
	private SimpleMovingAverage _majorRviSignal = null!;
	private IIndicator _majorMa = null!;

	private Highest _usdJpyHighest = null!;
	private Lowest _usdJpyLowest = null!;
	private RelativeStrengthIndex _usdJpyRsi = null!;
	private CommodityChannelIndex _usdJpyCci = null!;
	private RelativeVigorIndex _usdJpyRvi = null!;
	private SimpleMovingAverage _usdJpyRviSignal = null!;
	private IIndicator _usdJpyMa = null!;

	private AverageTrueRange _atr;

	private readonly Queue<decimal> _majorCloses = new();
	private readonly Queue<decimal> _usdJpyCloses = new();

	private decimal? _majorLastClose;
	private decimal? _majorLookbackClose;
	private decimal? _majorHighestValue;
	private decimal? _majorLowestValue;
	private decimal? _majorRsiValue;
	private decimal? _majorCciValue;
	private decimal? _majorRviValue;
	private decimal? _majorRviSignalValue;
	private decimal? _majorMaValue;

	private decimal? _usdJpyLastClose;
	private decimal? _usdJpyLookbackClose;
	private decimal? _usdJpyHighestValue;
	private decimal? _usdJpyLowestValue;
	private decimal? _usdJpyRsiValue;
	private decimal? _usdJpyCciValue;
	private decimal? _usdJpyRviValue;
	private decimal? _usdJpyRviSignalValue;
	private decimal? _usdJpyMaValue;

	private decimal? _atrValue;

	private decimal? _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takeProfitPrice;
	private bool _breakEvenActivated;
	private bool _profitLockActivated;
	private decimal _highestSinceEntry;
	private decimal _lowestSinceEntry;

	/// <summary>
	/// Initializes a new instance of the <see cref="YenTrader051Strategy"/> class.
	/// </summary>
	public YenTrader051Strategy()
	{
		_majorSecurity = Param(nameof(MajorSecurity), default(Security))
			.SetDisplay("Major Security", "Major currency pair used for confirmation", "Instruments");
		_usdJpySecurity = Param(nameof(UsdJpySecurity), default(Security))
			.SetDisplay("USDJPY Security", "USDJPY pair used for confirmation", "Instruments");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Signal Candles", "Primary timeframe for signals", "Data");
		_majorDirection = Param(nameof(MajorDirection), YenTraderMajorDirections.Left)
			.SetDisplay("Major Direction", "Alignment between major and cross", "Filters");
		_entryMode = Param(nameof(EntryMode), YenTraderEntryModes.Both)
			.SetDisplay("Entry Mode", "Control averaging or pyramiding behaviour", "Filters");
		_priceReference = Param(nameof(PriceReference), YenTraderPriceReferences.Close)
			.SetDisplay("Price Reference", "Breakout reference for loop back bars", "Filters");
		_loopBackBars = Param(nameof(LoopBackBars), 40)
			.SetDisplay("Loop Back Bars", "Number of historical bars for breakout logic", "Filters");
		_useRsiFilter = Param(nameof(UseRsiFilter), true)
			.SetDisplay("Use RSI", "Enable RSI confirmation filter", "Indicators");
		_useCciFilter = Param(nameof(UseCciFilter), false)
			.SetDisplay("Use CCI", "Enable CCI confirmation filter", "Indicators");
		_useRviFilter = Param(nameof(UseRviFilter), false)
			.SetDisplay("Use RVI", "Enable RVI confirmation filter", "Indicators");
		_useMovingAverageFilter = Param(nameof(UseMovingAverageFilter), true)
			.SetDisplay("Use Moving Average", "Enable moving average confirmation filter", "Indicators");
		_maPeriod = Param(nameof(MaPeriod), 34)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Moving average period", "Indicators");
		_maMode = Param(nameof(MaMode), MovingAverageModes.Smoothed)
			.SetDisplay("MA Mode", "Moving average calculation mode", "Indicators");
		_fixedLotSize = Param(nameof(FixedLotSize), 0m)
			.SetDisplay("Fixed Volume", "Fixed volume per trade (0 = disabled)", "Risk");
		_balancePercentLotSize = Param(nameof(BalancePercentLotSize), 1m)
			.SetDisplay("Balance Percent Volume", "Portfolio percent used to size trades when fixed volume is disabled", "Risk");
		_maxOpenPositions = Param(nameof(MaxOpenPositions), 1)
			.SetDisplay("Max Positions", "Maximum number of additive entries", "Risk");
		_stopLossPips = Param(nameof(StopLossPips), 1000)
			.SetDisplay("Stop Loss (pips)", "Stop loss distance in pips", "Risk");
		_takeProfitPips = Param(nameof(TakeProfitPips), 5000)
			.SetDisplay("Take Profit (pips)", "Take profit distance in pips", "Risk");
		_breakEvenPips = Param(nameof(BreakEvenPips), 200)
			.SetDisplay("Break Even (pips)", "Distance before moving stop to break even", "Risk");
		_profitLockPips = Param(nameof(ProfitLockPips), 200)
			.SetDisplay("Profit Lock (pips)", "Distance before locking additional profit", "Risk");
		_trailingStopPips = Param(nameof(TrailingStopPips), 200)
			.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk");
		_trailingStepPips = Param(nameof(TrailingStepPips), 10)
			.SetDisplay("Trailing Step (pips)", "Minimum trailing stop step in pips", "Risk");
		_closeOnOpposite = Param(nameof(CloseOnOpposite), false)
			.SetDisplay("Close On Opposite", "Close current position when opposite signal appears", "Risk");
		_allowHedging = Param(nameof(AllowHedging), true)
			.SetDisplay("Allow Hedging", "Allow simultaneous trades without closing existing ones", "Risk");
		_enableAtrLevels = Param(nameof(EnableAtrLevels), false)
			.SetDisplay("Use ATR Levels", "Use ATR based distances instead of pips", "Risk");
		_atrCandleType = Param(nameof(AtrCandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("ATR Candles", "Timeframe for ATR calculations", "Risk");
		_atrPeriod = Param(nameof(AtrPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR lookback period", "Risk");
		_atrStopLossMultiplier = Param(nameof(AtrStopLossMultiplier), 2m)
			.SetDisplay("ATR SL Multiplier", "ATR multiplier for stop loss", "Risk");
		_atrTakeProfitMultiplier = Param(nameof(AtrTakeProfitMultiplier), 4m)
			.SetDisplay("ATR TP Multiplier", "ATR multiplier for take profit", "Risk");
		_atrTrailingMultiplier = Param(nameof(AtrTrailingMultiplier), 1m)
			.SetDisplay("ATR Trail Multiplier", "ATR multiplier for trailing stop", "Risk");
		_atrBreakEvenMultiplier = Param(nameof(AtrBreakEvenMultiplier), 0.5m)
			.SetDisplay("ATR BE Multiplier", "ATR multiplier for break even distance", "Risk");
		_atrProfitLockMultiplier = Param(nameof(AtrProfitLockMultiplier), 2m)
			.SetDisplay("ATR PL Multiplier", "ATR multiplier for profit lock distance", "Risk");
	}

	/// <summary>
	/// Major pair used for confirmation.
	/// </summary>
	public Security MajorSecurity
	{
		get => _majorSecurity.Value;
		set => _majorSecurity.Value = value;
	}

	/// <summary>
	/// USDJPY pair used for confirmation.
	/// </summary>
	public Security UsdJpySecurity
	{
		get => _usdJpySecurity.Value;
		set => _usdJpySecurity.Value = value;
	}

	/// <summary>
	/// Main candle type used for trading signals.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Relationship between the major pair and the traded cross.
	/// </summary>
	public YenTraderMajorDirections MajorDirection
	{
		get => _majorDirection.Value;
		set => _majorDirection.Value = value;
	}

	/// <summary>
	/// Entry behaviour when stacking orders.
	/// </summary>
	public YenTraderEntryModes EntryMode
	{
		get => _entryMode.Value;
		set => _entryMode.Value = value;
	}

	/// <summary>
	/// Price reference used for breakout detection.
	/// </summary>
	public YenTraderPriceReferences PriceReference
	{
		get => _priceReference.Value;
		set => _priceReference.Value = value;
	}

	/// <summary>
	/// Number of bars used for breakout checks.
	/// </summary>
	public int LoopBackBars
	{
		get => _loopBackBars.Value;
		set => _loopBackBars.Value = value;
	}

	/// <summary>
	/// Enable RSI confirmation.
	/// </summary>
	public bool UseRsiFilter
	{
		get => _useRsiFilter.Value;
		set => _useRsiFilter.Value = value;
	}

	/// <summary>
	/// Enable CCI confirmation.
	/// </summary>
	public bool UseCciFilter
	{
		get => _useCciFilter.Value;
		set => _useCciFilter.Value = value;
	}

	/// <summary>
	/// Enable RVI confirmation.
	/// </summary>
	public bool UseRviFilter
	{
		get => _useRviFilter.Value;
		set => _useRviFilter.Value = value;
	}

	/// <summary>
	/// Enable moving average confirmation.
	/// </summary>
	public bool UseMovingAverageFilter
	{
		get => _useMovingAverageFilter.Value;
		set => _useMovingAverageFilter.Value = value;
	}

	/// <summary>
	/// Moving average period.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Moving average calculation mode.
	/// </summary>
	public MovingAverageModes MaMode
	{
		get => _maMode.Value;
		set => _maMode.Value = value;
	}

	/// <summary>
	/// Fixed volume per trade.
	/// </summary>
	public decimal FixedLotSize
	{
		get => _fixedLotSize.Value;
		set => _fixedLotSize.Value = value;
	}

	/// <summary>
	/// Percentage of portfolio balance used when variable sizing is active.
	/// </summary>
	public decimal BalancePercentLotSize
	{
		get => _balancePercentLotSize.Value;
		set => _balancePercentLotSize.Value = value;
	}

	/// <summary>
	/// Maximum number of additive entries.
	/// </summary>
	public int MaxOpenPositions
	{
		get => _maxOpenPositions.Value;
		set => _maxOpenPositions.Value = value;
	}

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

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

	/// <summary>
	/// Break even trigger distance in pips.
	/// </summary>
	public int BreakEvenPips
	{
		get => _breakEvenPips.Value;
		set => _breakEvenPips.Value = value;
	}

	/// <summary>
	/// Profit lock trigger distance in pips.
	/// </summary>
	public int ProfitLockPips
	{
		get => _profitLockPips.Value;
		set => _profitLockPips.Value = value;
	}

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

	/// <summary>
	/// Minimum trailing stop update step in pips.
	/// </summary>
	public int TrailingStepPips
	{
		get => _trailingStepPips.Value;
		set => _trailingStepPips.Value = value;
	}

	/// <summary>
	/// Close current position when an opposite signal is generated.
	/// </summary>
	public bool CloseOnOpposite
	{
		get => _closeOnOpposite.Value;
		set => _closeOnOpposite.Value = value;
	}

	/// <summary>
	/// Allow adding positions even if an opposite trade is still open.
	/// </summary>
	public bool AllowHedging
	{
		get => _allowHedging.Value;
		set => _allowHedging.Value = value;
	}

	/// <summary>
	/// Use ATR based levels instead of pip distances.
	/// </summary>
	public bool EnableAtrLevels
	{
		get => _enableAtrLevels.Value;
		set => _enableAtrLevels.Value = value;
	}

	/// <summary>
	/// Candle type used for ATR calculations.
	/// </summary>
	public DataType AtrCandleType
	{
		get => _atrCandleType.Value;
		set => _atrCandleType.Value = value;
	}

	/// <summary>
	/// ATR lookback period.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// ATR multiplier for stop loss.
	/// </summary>
	public decimal AtrStopLossMultiplier
	{
		get => _atrStopLossMultiplier.Value;
		set => _atrStopLossMultiplier.Value = value;
	}

	/// <summary>
	/// ATR multiplier for take profit.
	/// </summary>
	public decimal AtrTakeProfitMultiplier
	{
		get => _atrTakeProfitMultiplier.Value;
		set => _atrTakeProfitMultiplier.Value = value;
	}

	/// <summary>
	/// ATR multiplier for trailing stop distance.
	/// </summary>
	public decimal AtrTrailingMultiplier
	{
		get => _atrTrailingMultiplier.Value;
		set => _atrTrailingMultiplier.Value = value;
	}

	/// <summary>
	/// ATR multiplier for break even activation.
	/// </summary>
	public decimal AtrBreakEvenMultiplier
	{
		get => _atrBreakEvenMultiplier.Value;
		set => _atrBreakEvenMultiplier.Value = value;
	}

	/// <summary>
	/// ATR multiplier for profit lock activation.
	/// </summary>
	public decimal AtrProfitLockMultiplier
	{
		get => _atrProfitLockMultiplier.Value;
		set => _atrProfitLockMultiplier.Value = value;
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		if (Security != null)
			yield return (Security, CandleType);

		if (MajorSecurity != null)
			yield return (MajorSecurity, CandleType);

		if (UsdJpySecurity != null)
			yield return (UsdJpySecurity, CandleType);

		if (EnableAtrLevels && Security != null)
			yield return (Security, AtrCandleType);
	}

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

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

		InitializeIndicators();

		var useSingleSecurity = MajorSecurity == null && UsdJpySecurity == null;

		var tradingSubscription = SubscribeCandles(CandleType);

		if (useSingleSecurity)
		{
			// When no separate securities are configured, use the primary security data for all streams.
			tradingSubscription.Bind(c =>
			{
				ProcessMajorCandle(c);
				ProcessUsdJpyCandle(c);
				ProcessTradingCandle(c);
			}).Start();
		}
		else
		{
			tradingSubscription.Bind(ProcessTradingCandle).Start();

			if (MajorSecurity != null)
			{
				var majorSubscription = SubscribeCandles(CandleType, true, MajorSecurity);
				majorSubscription.Bind(ProcessMajorCandle).Start();
			}

			if (UsdJpySecurity != null)
			{
				var usdJpySubscription = SubscribeCandles(CandleType, true, UsdJpySecurity);
				usdJpySubscription.Bind(ProcessUsdJpyCandle).Start();
			}
		}

		if (EnableAtrLevels)
		{
			_atr = new AverageTrueRange { Length = AtrPeriod };
			var atrSubscription = SubscribeCandles(AtrCandleType, true, Security);
			atrSubscription.Bind(ProcessAtrCandle).Start();
		}
		else
		{
			_atr = null;
		}

		StartProtection(null, null);

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

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

		UpdateBreakoutIndicators(candle, _majorHighest, _majorLowest, ref _majorHighestValue, ref _majorLowestValue);
		UpdateOscillators(candle, _majorRsi, ref _majorRsiValue, _majorCci, ref _majorCciValue, _majorRvi, _majorRviSignal, ref _majorRviValue, ref _majorRviSignalValue);
		_majorMaValue = UpdateMovingAverage(_majorMa, candle);

		_majorLastClose = candle.ClosePrice;
		UpdateLookbackQueue(_majorCloses, LoopBackBars, candle.ClosePrice, ref _majorLookbackClose);
	}

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

		UpdateBreakoutIndicators(candle, _usdJpyHighest, _usdJpyLowest, ref _usdJpyHighestValue, ref _usdJpyLowestValue);
		UpdateOscillators(candle, _usdJpyRsi, ref _usdJpyRsiValue, _usdJpyCci, ref _usdJpyCciValue, _usdJpyRvi, _usdJpyRviSignal, ref _usdJpyRviValue, ref _usdJpyRviSignalValue);
		_usdJpyMaValue = UpdateMovingAverage(_usdJpyMa, candle);

		_usdJpyLastClose = candle.ClosePrice;
		UpdateLookbackQueue(_usdJpyCloses, LoopBackBars, candle.ClosePrice, ref _usdJpyLookbackClose);
	}

	private void ProcessAtrCandle(ICandleMessage candle)
	{
		if (!EnableAtrLevels || _atr == null)
			return;

		if (candle.State != CandleStates.Finished)
			return;

		var atrValue = _atr.Process(candle);
		if (atrValue.IsFinal)
		{
			var v = TryGetDecimal(atrValue);
			if (v.HasValue)
				_atrValue = v.Value;
		}
	}

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

		UpdateRiskManagement(candle);

		if (!IsFormed)
			return;

		if (!IsSignalReady())
			return;

		var longSignal = CalculateBreakoutSignal(true);
		var shortSignal = CalculateBreakoutSignal(false);

		ApplyEntryMode(candle, ref longSignal, ref shortSignal);
		ApplyIndicatorFilters(ref longSignal, ref shortSignal);

		if (longSignal)
			TryEnterLong(candle);

		if (shortSignal)
			TryEnterShort(candle);
	}

	private void ApplyEntryMode(ICandleMessage candle, ref bool longSignal, ref bool shortSignal)
	{
		if (EntryMode == YenTraderEntryModes.Averaging)
		{
			longSignal &= candle.ClosePrice < candle.OpenPrice;
			shortSignal &= candle.ClosePrice > candle.OpenPrice;
		}
		else if (EntryMode == YenTraderEntryModes.Pyramiding)
		{
			longSignal &= candle.ClosePrice > candle.OpenPrice;
			shortSignal &= candle.ClosePrice < candle.OpenPrice;
		}
	}

	private void ApplyIndicatorFilters(ref bool longSignal, ref bool shortSignal)
	{
		if (!longSignal && !shortSignal)
			return;

		if (UseRsiFilter)
		{
			if (!_majorRsiValue.HasValue || !_usdJpyRsiValue.HasValue)
			{
				longSignal = false;
				shortSignal = false;
			}
			else if (MajorDirection == YenTraderMajorDirections.Left)
			{
				longSignal &= _majorRsiValue > 50m && _usdJpyRsiValue > 50m;
				shortSignal &= _majorRsiValue < 50m && _usdJpyRsiValue < 50m;
			}
			else
			{
				longSignal &= _majorRsiValue < 50m && _usdJpyRsiValue > 50m;
				shortSignal &= _majorRsiValue > 50m && _usdJpyRsiValue < 50m;
			}
		}

		if (UseCciFilter && (longSignal || shortSignal))
		{
			if (!_majorCciValue.HasValue || !_usdJpyCciValue.HasValue)
			{
				longSignal = false;
				shortSignal = false;
			}
			else if (MajorDirection == YenTraderMajorDirections.Left)
			{
				longSignal &= _majorCciValue > 0m && _usdJpyCciValue > 0m;
				shortSignal &= _majorCciValue < 0m && _usdJpyCciValue < 0m;
			}
			else
			{
				longSignal &= _majorCciValue < 0m && _usdJpyCciValue > 0m;
				shortSignal &= _majorCciValue > 0m && _usdJpyCciValue < 0m;
			}
		}

		if (UseRviFilter && (longSignal || shortSignal))
		{
			if (!_majorRviValue.HasValue || !_usdJpyRviValue.HasValue || !_majorRviSignalValue.HasValue || !_usdJpyRviSignalValue.HasValue)
			{
				longSignal = false;
				shortSignal = false;
			}
			else if (MajorDirection == YenTraderMajorDirections.Left)
			{
				longSignal &= _majorRviValue > _majorRviSignalValue && _usdJpyRviValue > _usdJpyRviSignalValue;
				shortSignal &= _majorRviValue < _majorRviSignalValue && _usdJpyRviValue < _usdJpyRviSignalValue;
			}
			else
			{
				longSignal &= _majorRviValue < _majorRviSignalValue && _usdJpyRviValue > _usdJpyRviSignalValue;
				shortSignal &= _majorRviValue > _majorRviSignalValue && _usdJpyRviValue < _usdJpyRviSignalValue;
			}
		}

		if (UseMovingAverageFilter && (longSignal || shortSignal))
		{
			if (!_majorMaValue.HasValue || !_usdJpyMaValue.HasValue || !_majorLastClose.HasValue || !_usdJpyLastClose.HasValue)
			{
				longSignal = false;
				shortSignal = false;
			}
			else if (MajorDirection == YenTraderMajorDirections.Left)
			{
				longSignal &= _majorLastClose > _majorMaValue && _usdJpyLastClose > _usdJpyMaValue;
				shortSignal &= _majorLastClose < _majorMaValue && _usdJpyLastClose < _usdJpyMaValue;
			}
			else
			{
				longSignal &= _majorLastClose < _majorMaValue && _usdJpyLastClose > _usdJpyMaValue;
				shortSignal &= _majorLastClose > _majorMaValue && _usdJpyLastClose < _usdJpyMaValue;
			}
		}
	}

	private bool CalculateBreakoutSignal(bool isLong)
	{
		if (_majorLastClose == null || _usdJpyLastClose == null)
			return false;

		if (LoopBackBars <= 1)
			return true;

		if (PriceReference == YenTraderPriceReferences.HighLow)
		{
			if (_majorHighestValue == null || _majorLowestValue == null || _usdJpyHighestValue == null || _usdJpyLowestValue == null)
				return false;

			return MajorDirection == YenTraderMajorDirections.Left
				? isLong
					? _majorLastClose > _majorHighestValue && _usdJpyLastClose > _usdJpyHighestValue
					: _majorLastClose < _majorLowestValue && _usdJpyLastClose < _usdJpyLowestValue
				: isLong
					? _majorLastClose < _majorLowestValue && _usdJpyLastClose > _usdJpyHighestValue
					: _majorLastClose > _majorHighestValue && _usdJpyLastClose < _usdJpyLowestValue;
		}

		if (_majorLookbackClose == null || _usdJpyLookbackClose == null)
			return false;

		return MajorDirection == YenTraderMajorDirections.Left
			? isLong
				? _majorLastClose > _majorLookbackClose && _usdJpyLastClose > _usdJpyLookbackClose
				: _majorLastClose < _majorLookbackClose && _usdJpyLastClose < _usdJpyLookbackClose
			: isLong
				? _majorLastClose < _majorLookbackClose && _usdJpyLastClose > _usdJpyLookbackClose
				: _majorLastClose > _majorLookbackClose && _usdJpyLastClose < _usdJpyLookbackClose;
	}

	private void TryEnterLong(ICandleMessage candle)
	{
		if (Position > 0 && MaxOpenPositions <= 1)
			return;

		if (!AllowHedging && Position < 0)
			return;

		var orderVolume = GetOrderVolume(candle.ClosePrice, Sides.Buy);
		if (orderVolume <= 0m)
			return;

		var totalVolume = orderVolume;
		if (CloseOnOpposite && Position < 0)
			totalVolume += Math.Abs(Position);

		if (totalVolume <= 0m)
			return;

		BuyMarket(totalVolume);
		InitializePositionState(candle, Sides.Buy);
	}

	private void TryEnterShort(ICandleMessage candle)
	{
		if (Position < 0 && MaxOpenPositions <= 1)
			return;

		if (!AllowHedging && Position > 0)
			return;

		var orderVolume = GetOrderVolume(candle.ClosePrice, Sides.Sell);
		if (orderVolume <= 0m)
			return;

		var totalVolume = orderVolume;
		if (CloseOnOpposite && Position > 0)
			totalVolume += Math.Abs(Position);

		if (totalVolume <= 0m)
			return;

		SellMarket(totalVolume);
		InitializePositionState(candle, Sides.Sell);
	}

	private void InitializePositionState(ICandleMessage candle, Sides side)
	{
		_entryPrice = candle.ClosePrice;
		_stopPrice = null;
		_takeProfitPrice = null;
		_breakEvenActivated = false;
		_profitLockActivated = false;
		_highestSinceEntry = candle.HighPrice;
		_lowestSinceEntry = candle.LowPrice;

		var atrDistance = EnableAtrLevels ? _atrValue : null;
		var stopDistance = GetDistance(StopLossPips, AtrStopLossMultiplier, atrDistance);
		var takeDistance = GetDistance(TakeProfitPips, AtrTakeProfitMultiplier, atrDistance);

		if (side == Sides.Buy)
		{
			if (stopDistance > 0m)
				_stopPrice = _entryPrice - stopDistance;

			if (takeDistance > 0m)
				_takeProfitPrice = _entryPrice + takeDistance;
		}
		else
		{
			if (stopDistance > 0m)
				_stopPrice = _entryPrice + stopDistance;

			if (takeDistance > 0m)
				_takeProfitPrice = _entryPrice - takeDistance;
		}
	}

	private void UpdateRiskManagement(ICandleMessage candle)
	{
		if (Position == 0)
		{
			ResetPositionState();
			return;
		}

		if (_entryPrice == null)
			return;

		if (Position > 0)
		{
			_highestSinceEntry = Math.Max(_highestSinceEntry, candle.HighPrice);

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

			if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
			{
				SellMarket(Position);
				ResetPositionState();
				return;
			}

			ApplyTrailingRules(candle, Sides.Buy);
		}
		else
		{
			_lowestSinceEntry = Math.Min(_lowestSinceEntry, candle.LowPrice);

			if (_takeProfitPrice.HasValue && candle.LowPrice <= _takeProfitPrice.Value)
			{
				BuyMarket(Math.Abs(Position));
				ResetPositionState();
				return;
			}

			if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
			{
				BuyMarket(Math.Abs(Position));
				ResetPositionState();
				return;
			}

			ApplyTrailingRules(candle, Sides.Sell);
		}
	}

	private void ApplyTrailingRules(ICandleMessage candle, Sides side)
	{
		if (_entryPrice == null)
			return;

		var atrDistance = EnableAtrLevels ? _atrValue : null;
		var breakEvenDistance = GetDistance(BreakEvenPips, AtrBreakEvenMultiplier, atrDistance);
		var profitLockDistance = GetDistance(ProfitLockPips, AtrProfitLockMultiplier, atrDistance);
		var trailingDistance = GetDistance(TrailingStopPips, AtrTrailingMultiplier, atrDistance);
		var trailingStep = ConvertPipsToPrice(TrailingStepPips);

		if (side == Sides.Buy)
		{
			if (!_breakEvenActivated && breakEvenDistance > 0m && candle.HighPrice >= _entryPrice + breakEvenDistance)
			{
				var newStop = (_entryPrice + breakEvenDistance).Value;
				_stopPrice = _stopPrice.HasValue ? Math.Max(_stopPrice.Value, newStop) : newStop;
				_breakEvenActivated = true;
			}

			if (!_profitLockActivated && profitLockDistance > 0m && candle.HighPrice >= _entryPrice + profitLockDistance)
			{
				var newStop = (_entryPrice + profitLockDistance).Value;
				_stopPrice = _stopPrice.HasValue ? Math.Max(_stopPrice.Value, newStop) : newStop;
				_profitLockActivated = true;
			}

			if (trailingDistance > 0m)
			{
				var desiredStop = Math.Max(_entryPrice.Value, candle.HighPrice - trailingDistance);
				if (!_stopPrice.HasValue || desiredStop > _stopPrice.Value + trailingStep)
					_stopPrice = desiredStop;
			}
		}
		else
		{
			if (!_breakEvenActivated && breakEvenDistance > 0m && candle.LowPrice <= _entryPrice - breakEvenDistance)
			{
				var newStop = (_entryPrice - breakEvenDistance).Value;
				_stopPrice = _stopPrice.HasValue ? Math.Min(_stopPrice.Value, newStop) : newStop;
				_breakEvenActivated = true;
			}

			if (!_profitLockActivated && profitLockDistance > 0m && candle.LowPrice <= _entryPrice - profitLockDistance)
			{
				var newStop = (_entryPrice - profitLockDistance).Value;
				_stopPrice = _stopPrice.HasValue ? Math.Min(_stopPrice.Value, newStop) : newStop;
				_profitLockActivated = true;
			}

			if (trailingDistance > 0m)
			{
				var desiredStop = Math.Min(_entryPrice.Value, candle.LowPrice + trailingDistance);
				if (!_stopPrice.HasValue || desiredStop < _stopPrice.Value - trailingStep)
					_stopPrice = desiredStop;
			}
		}
	}

	private decimal GetOrderVolume(decimal price, Sides side)
	{
		var baseVolume = FixedLotSize > 0m ? FixedLotSize : Volume;

		if (FixedLotSize <= 0m && BalancePercentLotSize > 0m && Portfolio != null && price > 0m)
		{
			var portfolioValue = Portfolio.CurrentValue ?? 0m;
			if (portfolioValue > 0m)
				baseVolume = portfolioValue * BalancePercentLotSize / 100m / price;
		}

		var step = Security?.VolumeStep ?? 0m;
		if (step > 0m && baseVolume > 0m)
			baseVolume = Math.Max(step, Math.Round(baseVolume / step) * step);

		if (MaxOpenPositions > 0 && Security != null)
		{
			var current = side == Sides.Buy ? Math.Max(0m, Position) : Math.Max(0m, -Position);
			var maxVolume = baseVolume * MaxOpenPositions;
			var available = maxVolume - current;
			if (available <= 0m)
				return 0m;

			baseVolume = Math.Min(baseVolume, available);
		}

		return Math.Max(0m, baseVolume);
	}

	private decimal GetDistance(int pips, decimal multiplier, decimal? atrValue)
	{
		if (EnableAtrLevels && atrValue.HasValue)
			return atrValue.Value * multiplier;

		if (pips <= 0)
			return 0m;

		return ConvertPipsToPrice(pips);
	}

	private decimal ConvertPipsToPrice(int pips)
	{
		var step = Security?.PriceStep ?? 0m;
		return step > 0m ? pips * step : pips;
	}

	private void ResetPositionState()
	{
		_entryPrice = null;
		_stopPrice = null;
		_takeProfitPrice = null;
		_breakEvenActivated = false;
		_profitLockActivated = false;
		_highestSinceEntry = 0m;
		_lowestSinceEntry = 0m;
	}

	private void ResetState()
	{
		_majorCloses.Clear();
		_usdJpyCloses.Clear();

		_majorLastClose = null;
		_majorLookbackClose = null;
		_majorHighestValue = null;
		_majorLowestValue = null;
		_majorRsiValue = null;
		_majorCciValue = null;
		_majorRviValue = null;
		_majorRviSignalValue = null;
		_majorMaValue = null;

		_usdJpyLastClose = null;
		_usdJpyLookbackClose = null;
		_usdJpyHighestValue = null;
		_usdJpyLowestValue = null;
		_usdJpyRsiValue = null;
		_usdJpyCciValue = null;
		_usdJpyRviValue = null;
		_usdJpyRviSignalValue = null;
		_usdJpyMaValue = null;

		_atrValue = null;

		ResetPositionState();
	}

	private bool IsSignalReady()
	{
		if (_majorLastClose == null || _usdJpyLastClose == null)
			return false;

		if (LoopBackBars > 1)
		{
			if (PriceReference == YenTraderPriceReferences.HighLow)
			{
				if (_majorHighestValue == null || _majorLowestValue == null || _usdJpyHighestValue == null || _usdJpyLowestValue == null)
					return false;
			}
			else
			{
				if (_majorLookbackClose == null || _usdJpyLookbackClose == null)
					return false;
			}
		}

		if (UseRsiFilter && (!_majorRsiValue.HasValue || !_usdJpyRsiValue.HasValue))
			return false;

		if (UseCciFilter && (!_majorCciValue.HasValue || !_usdJpyCciValue.HasValue))
			return false;

		if (UseRviFilter && (!_majorRviValue.HasValue || !_majorRviSignalValue.HasValue || !_usdJpyRviValue.HasValue || !_usdJpyRviSignalValue.HasValue))
			return false;

		if (UseMovingAverageFilter && (!_majorMaValue.HasValue || !_usdJpyMaValue.HasValue))
			return false;

		return true;
	}

	private void InitializeIndicators()
	{
		var breakoutLength = Math.Max(LoopBackBars, 2);

		_majorHighest = new Highest { Length = breakoutLength };
		_majorLowest = new Lowest { Length = breakoutLength };
		_majorRsi = new RelativeStrengthIndex { Length = 14 };
		_majorCci = new CommodityChannelIndex { Length = 14 };
		_majorRvi = new RelativeVigorIndex();
		_majorRviSignal = new SimpleMovingAverage { Length = 4 };
		_majorMa = CreateMovingAverage(MaMode, MaPeriod);

		_usdJpyHighest = new Highest { Length = breakoutLength };
		_usdJpyLowest = new Lowest { Length = breakoutLength };
		_usdJpyRsi = new RelativeStrengthIndex { Length = 14 };
		_usdJpyCci = new CommodityChannelIndex { Length = 14 };
		_usdJpyRvi = new RelativeVigorIndex();
		_usdJpyRviSignal = new SimpleMovingAverage { Length = 4 };
		_usdJpyMa = CreateMovingAverage(MaMode, MaPeriod);
	}

	private static void UpdateBreakoutIndicators(ICandleMessage candle, Highest highest, Lowest lowest, ref decimal? highValue, ref decimal? lowValue)
	{
		var highVal = highest.Process(candle);
		if (highVal.IsFinal)
		{
			var v = TryGetDecimal(highVal);
			if (v.HasValue)
				highValue = v.Value;
		}

		var lowVal = lowest.Process(candle);
		if (lowVal.IsFinal)
		{
			var v = TryGetDecimal(lowVal);
			if (v.HasValue)
				lowValue = v.Value;
		}
	}

	private static decimal? TryGetDecimal(IIndicatorValue value)
	{
		if (value == null || value.IsEmpty)
			return null;

		try
		{
			return value.ToDecimal();
		}
		catch
		{
			return null;
		}
	}

	private static void UpdateOscillators(
		ICandleMessage candle,
		RelativeStrengthIndex rsi,
		ref decimal? rsiValue,
		CommodityChannelIndex cci,
		ref decimal? cciValue,
		RelativeVigorIndex rvi,
		SimpleMovingAverage rviSignal,
		ref decimal? rviMain,
		ref decimal? rviSignalValue)
	{
		var rsiInput = new DecimalIndicatorValue(rsi, candle.ClosePrice, candle.CloseTime) { IsFinal = true };
		var rsiVal = rsi.Process(rsiInput);
		if (rsiVal.IsFinal)
		{
			var v = TryGetDecimal(rsiVal);
			if (v.HasValue)
				rsiValue = v.Value;
		}

		var cciVal = cci.Process(candle);
		if (cciVal.IsFinal)
		{
			var v = TryGetDecimal(cciVal);
			if (v.HasValue)
				cciValue = v.Value;
		}

		var rviVal = rvi.Process(candle);
		if (rviVal.IsFinal)
		{
			var v = TryGetDecimal(rviVal);
			if (v.HasValue)
			{
				rviMain = v.Value;

				var signalVal = rviSignal.Process(new DecimalIndicatorValue(rviSignal, v.Value, candle.CloseTime) { IsFinal = true });
				if (signalVal.IsFinal)
				{
					var sv = TryGetDecimal(signalVal);
					if (sv.HasValue)
						rviSignalValue = sv.Value;
				}
			}
		}
	}

	private static decimal? UpdateMovingAverage(IIndicator indicator, ICandleMessage candle)
	{
		var input = new DecimalIndicatorValue(indicator, candle.ClosePrice, candle.CloseTime) { IsFinal = true };
		var value = indicator.Process(input);
		return value.IsFinal ? TryGetDecimal(value) : null;
	}

	private static void UpdateLookbackQueue(Queue<decimal> queue, int loopBackBars, decimal close, ref decimal? lookback)
	{
		queue.Enqueue(close);

		var maxCount = Math.Max(loopBackBars + 1, 2);
		while (queue.Count > maxCount)
			queue.Dequeue();

		if (loopBackBars > 0 && queue.Count > loopBackBars)
		{
			var values = queue.ToArray();
			var index = values.Length - 1 - loopBackBars;
			if (index >= 0)
				lookback = values[index];
		}
		else
		{
			lookback = null;
		}
	}

	private static IIndicator CreateMovingAverage(MovingAverageModes mode, int period)
	{
		var length = Math.Max(1, period);

		return mode switch
		{
			MovingAverageModes.Simple => new SimpleMovingAverage { Length = length },
			MovingAverageModes.Exponential => new ExponentialMovingAverage { Length = length },
			MovingAverageModes.Smoothed => new SmoothedMovingAverage { Length = length },
			MovingAverageModes.LinearWeighted => new WeightedMovingAverage { Length = length },
			_ => new SimpleMovingAverage { Length = length },
		};
	}

	/// <summary>
	/// Entry stacking behaviour.
	/// </summary>
	public enum YenTraderEntryModes
	{
		/// <summary>Allow both averaging and pyramiding entries.</summary>
		Both,
		/// <summary>Only add to profitable trades.</summary>
		Pyramiding,
		/// <summary>Only add to losing trades.</summary>
		Averaging
	}

	/// <summary>
	/// Mapping between major pair and traded cross.
	/// </summary>
	public enum YenTraderMajorDirections
	{
		/// <summary>Major pair acts as the left component.</summary>
		Left,
		/// <summary>Major pair acts as the right component.</summary>
		Right
	}

	/// <summary>
	/// Breakout reference type.
	/// </summary>
	public enum YenTraderPriceReferences
	{
		/// <summary>Use delayed close values.</summary>
		Close,
		/// <summary>Use highest highs and lowest lows.</summary>
		HighLow
	}

	/// <summary>
	/// Moving average calculation modes supported by the strategy.
	/// </summary>
	public enum MovingAverageModes
	{
		/// <summary>Simple moving average.</summary>
		Simple,
		/// <summary>Exponential moving average.</summary>
		Exponential,
		/// <summary>Smoothed moving average.</summary>
		Smoothed,
		/// <summary>Linear weighted moving average.</summary>
		LinearWeighted
	}
}