Ver no GitHub

Estratégia Exp Sistema Aberto de Pivôs de Fuso Horário Tm Plus

Esta estratégia é um port de alto nível do StockSharp do expert advisor Exp_TimeZonePivotsOpenSystem_Tm_Plus. Recria o indicador proprietário TimeZonePivotsOpenSystem que projeta duas zonas de rompimento em torno da abertura da sessão diária e opera os recuos que seguem um rompimento. Cada componente do script original—atraso de sinal, filtro de tempo, lógica de saída assimétrica e os presets de gestão de dinheiro—foi mapeado para parâmetros explícitos para que o comportamento permaneça consistente com a implementação MQL5.

Lógica de trading

  1. No StartHour configurado, a estratégia registra o preço de abertura da sessão. Dois níveis dinâmicos são então traçados a OffsetPoints (em pontos) acima e abaixo dessa âncora.
  2. Sempre que uma vela finalizada fecha acima do nível superior, a estratégia:
    • Agenda uma entrada comprada para ser executada na próxima vela (respeitando o atraso SignalBar) somente se a barra atual não está mais acima da faixa.
    • Fecha qualquer posição vendida aberta imediatamente se SellPosClose estiver habilitado.
  3. Sempre que uma vela finalizada fecha abaixo do nível inferior, a estratégia:
    • Agenda uma entrada vendida para a próxima vela desde que a barra atual não esteja mais abaixo da faixa.
    • Fecha qualquer posição comprada aberta imediatamente se BuyPosClose estiver habilitado.
  4. As entradas são executadas na primeira atualização da próxima vela graças ao TryExecutePendingEntries. Isso corresponde ao expert original que atrasa a ordem até que a nova barra comece.

O parâmetro de atraso de sinal SignalBar reproduz o deslocamento original CopyBuffer. Um valor de 0 reage à barra fechada mais recente, enquanto 1 espera uma barra extra antes de agir, dando confirmação adicional.

Gerenciamento de ordens

  • Stop-loss / take-profit – As distâncias são definidas em pontos (StopLossPoints, TakeProfitPoints) e convertidas em preço usando o passo do instrumento. Ambos os níveis são monitorados usando os extremos das velas para que toques intrabarra acionem uma saída.
  • Saída baseada em tempo – Quando TimeTrade é verdadeiro, a posição é forçosamente fechada após HoldingMinutes minutos, espelhando o temporizador nTime do código MQL5.
  • Fechamentos manuais – Sinais de rompimento na direção oposta fecham a operação em andamento se o indicador BuyPosClose ou SellPosClose correspondente estiver habilitado.

Gestão de dinheiro

O parâmetro MoneyMode reproduz a enumeração MarginMode:

  • Lot – volume fixo igual a MoneyManagement.
  • Balance e FreeMargin – usam múltiplos de capital da conta ou margem livre (MoneyManagement * capital / preço).
  • LossBalance e LossFreeMargin – dimensionamento baseado em risco que divide a fração de capital desejada pela distância do stop.

Se StopLossPoints estiver em zero, os modos de risco recorrem graciosamente ao dimensionamento baseado em preço.

Parâmetros

Parâmetro Descrição Padrão
MoneyManagement Coeficiente base usado para dimensionar a posição dependendo de MoneyMode. 0.1
MoneyMode Modelo de dimensionamento de posição (Lot, Balance, FreeMargin, LossBalance, LossFreeMargin). Lot
StopLossPoints Distância do stop-loss expressa em pontos a partir do preço de execução. 1000
TakeProfitPoints Distância do take-profit expressa em pontos a partir do preço de execução. 2000
DeviationPoints Parâmetro informativo mantido do expert (configuração de slippage em pontos). 10
BuyPosOpen / SellPosOpen Habilitar ou desabilitar entradas compradas e vendidas. true
BuyPosClose / SellPosClose Permitir que o rompimento oposto feche posições forçosamente. true
TimeTrade Habilitar o filtro de tempo máximo de manutenção. true
HoldingMinutes Vida útil máxima da posição em minutos. 720
OffsetPoints Distância das faixas de pivô a partir da abertura da sessão em pontos. 200
SignalBar Número de barras para atrasar a avaliação de sinal (0 = última barra fechada). 1
CandleType Período principal usado para calcular o indicador. TimeSpan.FromHours(1).TimeFrame()
StartHour Hora do dia (0-23) que define o preço de abertura da sessão. 0

Notas de uso

  • A estratégia assume que o ativo fornece um PriceStep válido. Se o instrumento não tiver esses metadados, um fallback de 0.0001 é usado.
  • Como as entradas são acionadas na primeira atualização de uma nova vela, o preço real de execução seguirá o mercado naquele momento, assim como o expert, o que pode diferir do preço teórico de abertura em mercados rápidos.
  • Para replicar a sobreposição do indicador original, mantenha o período de backtest em H1 ou abaixo, pois o script MQL5 só opera em períodos horários ou menores.
  • Defina SignalBar como 0 para comportamento mais responsivo ou como 1 (padrão) para esperar uma barra extra após um rompimento.
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>
/// Strategy that replicates the Time Zone Pivots Open System expert from MetaTrader.
/// It follows the session open price and reacts when candles close above or below the
/// upper and lower offset bands while respecting the original money management rules.
/// </summary>
public class ExpTimeZonePivotsOpenSystemTmPlusStrategy : Strategy
{
	// Parameters from the original expert controlling size, stops and permissions.
	private readonly StrategyParam<decimal> _moneyManagement;
	private readonly StrategyParam<MoneyManagementModes> _moneyMode;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _deviationPoints;
	private readonly StrategyParam<bool> _allowBuyOpen;
	private readonly StrategyParam<bool> _allowSellOpen;
	private readonly StrategyParam<bool> _allowBuyClose;
	private readonly StrategyParam<bool> _allowSellClose;
	private readonly StrategyParam<bool> _useTimeExit;
	private readonly StrategyParam<int> _holdingMinutes;
	private readonly StrategyParam<decimal> _offsetPoints;
	private readonly StrategyParam<int> _signalBar;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _startHour;

	// Rolling buffer that stores recent candle states for the indicator recreation.
	private readonly List<ZoneSnapshot> _zoneHistory = new();

	// Session level tracking.
	private DateTime? _lastSessionDate;
	private decimal? _sessionOpenPrice;
	private decimal? _upperBand;
	private decimal? _lowerBand;
	private bool _sessionTradeTaken;
	private DateTimeOffset? _lastEntryDate;

	// Pending entry scheduling.
	private bool _pendingLongEntry;
	private bool _pendingShortEntry;
	private DateTimeOffset? _longSignalTime;
	private DateTimeOffset? _shortSignalTime;
	private DateTimeOffset? _lastLongSignalOrigin;
	private DateTimeOffset? _lastShortSignalOrigin;
	private DateTimeOffset? _currentCandleOpen;

	// Position bookkeeping for exit controls.
	private DateTimeOffset? _longEntryTime;
	private DateTimeOffset? _shortEntryTime;
	private decimal? _longEntryPrice;
	private decimal? _shortEntryPrice;
	private decimal? _longStopPrice;
	private decimal? _longTakePrice;
	private decimal? _shortStopPrice;
	private decimal? _shortTakePrice;

	// Cached timeframe of the selected candle series.
	private TimeSpan? _timeFrame;

	public decimal MoneyManagement
	{
		get => _moneyManagement.Value;
		set => _moneyManagement.Value = value;
	}

	public MoneyManagementModes MoneyMode
	{
		get => _moneyMode.Value;
		set => _moneyMode.Value = value;
	}

	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	public decimal DeviationPoints
	{
		get => _deviationPoints.Value;
		set => _deviationPoints.Value = value;
	}

	public bool BuyPosOpen
	{
		get => _allowBuyOpen.Value;
		set => _allowBuyOpen.Value = value;
	}

	public bool SellPosOpen
	{
		get => _allowSellOpen.Value;
		set => _allowSellOpen.Value = value;
	}

	public bool BuyPosClose
	{
		get => _allowBuyClose.Value;
		set => _allowBuyClose.Value = value;
	}

	public bool SellPosClose
	{
		get => _allowSellClose.Value;
		set => _allowSellClose.Value = value;
	}

	public bool TimeTrade
	{
		get => _useTimeExit.Value;
		set => _useTimeExit.Value = value;
	}

	public int HoldingMinutes
	{
		get => _holdingMinutes.Value;
		set => _holdingMinutes.Value = value;
	}

	public decimal OffsetPoints
	{
		get => _offsetPoints.Value;
		set => _offsetPoints.Value = value;
	}

	public int SignalBar
	{
		get => _signalBar.Value;
		set => _signalBar.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int StartHour
	{
		get => _startHour.Value;
		set => _startHour.Value = value;
	}

	public ExpTimeZonePivotsOpenSystemTmPlusStrategy()
	{
		_moneyManagement = Param(nameof(MoneyManagement), 0.1m)
			.SetDisplay("Money Management", "Base value used for position sizing", "Trading")
			.SetGreaterThanZero();

		_moneyMode = Param(nameof(MoneyMode), MoneyManagementModes.Lot)
			.SetDisplay("Money Mode", "Position sizing model", "Trading");

		_stopLossPoints = Param(nameof(StopLossPoints), 1000m)
			.SetDisplay("Stop Loss (points)", "Distance from entry to stop loss expressed in points", "Risk")
			.SetNotNegative();

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000m)
			.SetDisplay("Take Profit (points)", "Distance from entry to take profit expressed in points", "Risk")
			.SetNotNegative();

		_deviationPoints = Param(nameof(DeviationPoints), 10m)
			.SetDisplay("Allowed Deviation", "Maximum acceptable price deviation for entries", "Risk")
			.SetNotNegative();

		_allowBuyOpen = Param(nameof(BuyPosOpen), true)
			.SetDisplay("Enable Long Entries", "Allow opening long positions", "Trading");

		_allowSellOpen = Param(nameof(SellPosOpen), true)
			.SetDisplay("Enable Short Entries", "Allow opening short positions", "Trading");

		_allowBuyClose = Param(nameof(BuyPosClose), true)
			.SetDisplay("Enable Long Exits", "Allow closing long positions on opposite signals", "Trading");

		_allowSellClose = Param(nameof(SellPosClose), true)
			.SetDisplay("Enable Short Exits", "Allow closing short positions on opposite signals", "Trading");

		_useTimeExit = Param(nameof(TimeTrade), true)
			.SetDisplay("Use Time Exit", "Close positions after a fixed holding time", "Risk");

		_holdingMinutes = Param(nameof(HoldingMinutes), 720)
			.SetDisplay("Holding Minutes", "Maximum position lifetime in minutes", "Risk")
			.SetNotNegative();

		_offsetPoints = Param(nameof(OffsetPoints), 200m)
			.SetDisplay("Offset (points)", "Distance from session open that defines the pivot zones", "Indicator")
			.SetNotNegative();

		_signalBar = Param(nameof(SignalBar), 1)
			.SetDisplay("Signal Bar", "Number of bars to delay the signal evaluation", "Indicator")
			.SetNotNegative();

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe for calculations", "Indicator");

		_startHour = Param(nameof(StartHour), 0)
			.SetDisplay("Session Start Hour", "Hour of day used to anchor the session open price", "Indicator")
			.SetNotNegative()
			;
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();

		_zoneHistory.Clear();
		_lastSessionDate = null;
		_sessionOpenPrice = null;
		_upperBand = null;
		_lowerBand = null;
		_pendingLongEntry = false;
		_pendingShortEntry = false;
		_longSignalTime = null;
		_shortSignalTime = null;
		_lastLongSignalOrigin = null;
		_lastShortSignalOrigin = null;
		_currentCandleOpen = null;
		_longEntryTime = null;
		_shortEntryTime = null;
		_longEntryPrice = null;
		_shortEntryPrice = null;
		_longStopPrice = null;
		_longTakePrice = null;
		_shortStopPrice = null;
		_shortTakePrice = null;
		_timeFrame = CandleType.Arg as TimeSpan?;
		_sessionTradeTaken = false;
		_lastEntryDate = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_timeFrame = CandleType.Arg as TimeSpan?;

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

		// Enable loss protection guard as required by the framework.
		StartProtection(null, null);
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		// Detect new candle openings to trigger delayed entries on the first update.
		if (_currentCandleOpen != candle.OpenTime)
		{
			_currentCandleOpen = candle.OpenTime;
			TryExecutePendingEntries(candle);
		}

		// Only finished candles contribute to the indicator logic and trade decisions.
		if (candle.State != CandleStates.Finished)
			return;

		// Refresh the session reference and pre-compute band levels.
		UpdateSessionReference(candle);

		// Memorise the current candle classification for delayed evaluations.
		var snapshot = new ZoneSnapshot
		{
			State = DetermineState(candle),
			OpenTime = candle.OpenTime,
			CloseTime = candle.CloseTime
		};

		_zoneHistory.Insert(0, snapshot);

		var maxHistory = Math.Max(5, SignalBar + 3);
		while (_zoneHistory.Count > maxHistory)
			_zoneHistory.RemoveAt(_zoneHistory.Count - 1);

		// Ensure we have enough candles to evaluate the signal and confirmation offsets.
		if (_zoneHistory.Count <= SignalBar + 1)
		{
			ManageStops(candle);
			HandleTimeExit(candle.CloseTime);
			return;
		}

		var signalSnapshot = _zoneHistory[SignalBar];
		var confirmSnapshot = _zoneHistory[SignalBar + 1];

		if (signalSnapshot == null || confirmSnapshot == null)
		{
			ManageStops(candle);
			HandleTimeExit(candle.CloseTime);
			return;
		}

		var closeLong = false;
		var closeShort = false;

		// Previous candle closed above the upper band – schedule long entry and close shorts.
		if (confirmSnapshot.State == ZoneSignals.Above)
		{
			if (SellPosClose)
				closeShort = true;

			if (BuyPosOpen && signalSnapshot.State != ZoneSignals.Above && (_lastLongSignalOrigin != confirmSnapshot.CloseTime))
			{
				_pendingLongEntry = true;
				_longSignalTime = confirmSnapshot.CloseTime + (_timeFrame ?? TimeSpan.Zero);
				_lastLongSignalOrigin = confirmSnapshot.CloseTime;
			}
		}
		// Previous candle closed below the lower band – schedule short entry and close longs.
		else if (confirmSnapshot.State == ZoneSignals.Below)
		{
			if (BuyPosClose)
				closeLong = true;

			if (SellPosOpen && signalSnapshot.State != ZoneSignals.Below && (_lastShortSignalOrigin != confirmSnapshot.CloseTime))
			{
				_pendingShortEntry = true;
				_shortSignalTime = confirmSnapshot.CloseTime + (_timeFrame ?? TimeSpan.Zero);
				_lastShortSignalOrigin = confirmSnapshot.CloseTime;
			}
		}

		if (closeLong && Position > 0m)
		{
			SellMarket(Position);
			_longEntryTime = null;
			_longEntryPrice = null;
			_longStopPrice = null;
			_longTakePrice = null;
		}

		if (closeShort && Position < 0m)
		{
			BuyMarket(Math.Abs(Position));
			_shortEntryTime = null;
			_shortEntryPrice = null;
			_shortStopPrice = null;
			_shortTakePrice = null;
		}

		ManageStops(candle);
		HandleTimeExit(candle.CloseTime);
	}

	// Execute pending entries once the new candle that should host the trade begins.
	private void TryExecutePendingEntries(ICandleMessage candle)
	{
		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (Position != 0m)
			return;

		if (_sessionTradeTaken)
			return;

		if (_lastEntryDate.HasValue && candle.OpenTime.Date <= _lastEntryDate.Value.Date.AddDays(4))
			return;

		var opened = false;

		if (_pendingLongEntry && BuyPosOpen)
		{
			if (!_longSignalTime.HasValue || candle.OpenTime >= _longSignalTime.Value)
			{
				var entryPrice = candle.OpenPrice;
				var volume = GetEntryVolume(true, entryPrice);

				if (volume > 0m)
				{
					_longEntryPrice = entryPrice;
					BuyMarket(volume);
					_pendingLongEntry = false;
					_longSignalTime = null;
					_sessionTradeTaken = true;
					_lastEntryDate = candle.OpenTime;
					opened = true;
				}
			}
		}

		if (!opened && _pendingShortEntry && SellPosOpen)
		{
			if (!_shortSignalTime.HasValue || candle.OpenTime >= _shortSignalTime.Value)
			{
				var entryPrice = candle.OpenPrice;
				var volume = GetEntryVolume(false, entryPrice);

				if (volume > 0m)
				{
					_shortEntryPrice = entryPrice;
					SellMarket(volume);
					_pendingShortEntry = false;
					_shortSignalTime = null;
					_sessionTradeTaken = true;
					_lastEntryDate = candle.OpenTime;
				}
			}
		}
	}

	// Monitor stop-loss and take-profit levels intrabar using candle extremes.
	private void ManageStops(ICandleMessage candle)
	{
		var volume = Math.Abs(Position);

		if (volume <= 0m)
			return;

		if (Position > 0m)
		{
			if (_longStopPrice.HasValue && candle.LowPrice <= _longStopPrice.Value)
			{
				SellMarket(volume);
				_longEntryTime = null;
				_longEntryPrice = null;
				_longStopPrice = null;
				_longTakePrice = null;
			}
			else if (_longTakePrice.HasValue && candle.HighPrice >= _longTakePrice.Value)
			{
				SellMarket(volume);
				_longEntryTime = null;
				_longEntryPrice = null;
				_longStopPrice = null;
				_longTakePrice = null;
			}
		}
		else if (Position < 0m)
		{
			if (_shortStopPrice.HasValue && candle.HighPrice >= _shortStopPrice.Value)
			{
				BuyMarket(volume);
				_shortEntryTime = null;
				_shortEntryPrice = null;
				_shortStopPrice = null;
				_shortTakePrice = null;
			}
			else if (_shortTakePrice.HasValue && candle.LowPrice <= _shortTakePrice.Value)
			{
				BuyMarket(volume);
				_shortEntryTime = null;
				_shortEntryPrice = null;
				_shortStopPrice = null;
				_shortTakePrice = null;
			}
		}
	}

	// Implement the time based exit from the MQL5 code.
	private void HandleTimeExit(DateTimeOffset time)
	{
		if (!TimeTrade)
			return;

		var holdMinutes = HoldingMinutes;
		if (holdMinutes <= 0)
			return;

		var threshold = TimeSpan.FromMinutes(holdMinutes);
		var volume = Math.Abs(Position);

		if (volume <= 0m)
			return;

		if (Position > 0m && _longEntryTime.HasValue && time - _longEntryTime.Value >= threshold)
		{
			SellMarket(volume);
			_longEntryTime = null;
			_longEntryPrice = null;
			_longStopPrice = null;
			_longTakePrice = null;
		}
		else if (Position < 0m && _shortEntryTime.HasValue && time - _shortEntryTime.Value >= threshold)
		{
			BuyMarket(volume);
			_shortEntryTime = null;
			_shortEntryPrice = null;
			_shortStopPrice = null;
			_shortTakePrice = null;
		}
	}

	// Update the session open reference when the configured hour is reached.
	private void UpdateSessionReference(ICandleMessage candle)
	{
		var openTime = candle.OpenTime;
		var currentDate = openTime.Date;

		if ((!_lastSessionDate.HasValue || _lastSessionDate.Value != currentDate) && openTime.Hour == StartHour)
		{
			_sessionOpenPrice = candle.OpenPrice;
			_lastSessionDate = currentDate;
			_zoneHistory.Clear();
			_pendingLongEntry = false;
			_pendingShortEntry = false;
			_longSignalTime = null;
			_shortSignalTime = null;
			_lastLongSignalOrigin = null;
			_lastShortSignalOrigin = null;
			_sessionTradeTaken = false;
		}

		if (_sessionOpenPrice.HasValue)
		{
			var step = GetPriceStep();
			var offset = OffsetPoints * step;

			_upperBand = _sessionOpenPrice + offset;
			_lowerBand = _sessionOpenPrice - offset;
		}
		else
		{
			_upperBand = null;
			_lowerBand = null;
		}
	}

	// Classify the candle relative to the offset bands.
	private ZoneSignals DetermineState(ICandleMessage candle)
	{
		if (!_sessionOpenPrice.HasValue || !_upperBand.HasValue || !_lowerBand.HasValue)
			return ZoneSignals.Inside;

		if (candle.ClosePrice > _upperBand.Value)
			return ZoneSignals.Above;

		if (candle.ClosePrice < _lowerBand.Value)
			return ZoneSignals.Below;

		return ZoneSignals.Inside;
	}

	// Translate the money management mode into an executable volume.
	private decimal GetEntryVolume(bool isLong, decimal price)
	{
		if (price <= 0m)
			return 0m;

		var step = GetPriceStep();
		var stopDistance = StopLossPoints > 0m ? StopLossPoints * step : 0m;
		var capital = Portfolio?.CurrentValue ?? 0m;
		var mmValue = MoneyManagement;

		switch (MoneyMode)
		{
			case MoneyManagementModes.Lot:
				return mmValue;
			case MoneyManagementModes.Balance:
			case MoneyManagementModes.FreeMargin:
				return capital > 0m ? capital * mmValue / price : 0m;
			case MoneyManagementModes.LossBalance:
			case MoneyManagementModes.LossFreeMargin:
				if (stopDistance > 0m)
					return capital > 0m ? capital * mmValue / stopDistance : 0m;

				return capital > 0m ? capital * mmValue / price : 0m;
			default:
				return mmValue;
		}
	}

	// Retrieve the minimum price step for the configured security.
	private decimal GetPriceStep()
	{
		var security = Security;
		if (security == null)
			return 0.0001m;

		if (security.PriceStep > 0m)
			return security.PriceStep.Value;

		return 0.0001m;
	}

	// Helper to compute stop-loss and take-profit levels around the fill price.
	private decimal? CalculateStopPrice(bool isLong, decimal? entryPrice)
	{
		if (!entryPrice.HasValue || StopLossPoints <= 0m)
			return null;

		var distance = StopLossPoints * GetPriceStep();
		return isLong ? entryPrice - distance : entryPrice + distance;
	}

	private decimal? CalculateTakePrice(bool isLong, decimal? entryPrice)
	{
		if (!entryPrice.HasValue || TakeProfitPoints <= 0m)
			return null;

		var distance = TakeProfitPoints * GetPriceStep();
		return isLong ? entryPrice + distance : entryPrice - distance;
	}

	protected override void OnOwnTradeReceived(MyTrade trade)
	{
		base.OnOwnTradeReceived(trade);

		if (Position > 0m && trade.Order.Side == Sides.Buy)
		{
			_longEntryTime = trade.Trade.ServerTime;
			_longEntryPrice = trade.Trade.Price;
			_longStopPrice = CalculateStopPrice(true, _longEntryPrice);
			_longTakePrice = CalculateTakePrice(true, _longEntryPrice);
		}
		else if (Position < 0m && trade.Order.Side == Sides.Sell)
		{
			_shortEntryTime = trade.Trade.ServerTime;
			_shortEntryPrice = trade.Trade.Price;
			_shortStopPrice = CalculateStopPrice(false, _shortEntryPrice);
			_shortTakePrice = CalculateTakePrice(false, _shortEntryPrice);
		}

		if (Position == 0m)
		{
			_longEntryTime = null;
			_shortEntryTime = null;
			_longEntryPrice = null;
			_shortEntryPrice = null;
			_longStopPrice = null;
			_shortStopPrice = null;
			_longTakePrice = null;
			_shortTakePrice = null;
		}
	}

	public enum MoneyManagementModes
	{
		FreeMargin,
		Balance,
		LossFreeMargin,
		LossBalance,
		Lot
	}

	private enum ZoneSignals
	{
		Inside,
		Above,
		Below
	}

	private sealed class ZoneSnapshot
	{
		public ZoneSignals State { get; init; }
		public DateTimeOffset OpenTime { get; init; }
		public DateTimeOffset CloseTime { get; init; }
	}
}