Ver en GitHub

Estrategia universal MA Cross V4

Descripción general

La Estrategia Universal MA Cross V4 es una versión StockSharp de alto nivel del asesor experto MetaTrader 4 "Universal MACross EA v4". El algoritmo sigue la interacción entre una media móvil rápida configurable y una media móvil lenta. Admite varios tipos de medias móviles, fuentes de precios seleccionables, una ventana de negociación horaria y una gestión de posiciones flexible que incluye comportamiento de parada y reversión, objetivos de protección y paradas dinámicas. La estrategia está diseñada para la ejecución basada en barras utilizando API de alto nivel de StockSharp con suscripciones de velas.

Lógica de trading

Procesamiento de indicadores

  • Se evalúan dos medias móviles en cada vela terminada. Cada media móvil puede utilizar su propia longitud, método de suavizado (simple, exponencial, suavizado o ponderado lineal) y fuente de precio (cierre, apertura, máximo, mínimo, mediana, típico o ponderado).
  • El filtro MinCrossDistancePoints requiere que los promedios rápido y lento diverjan al menos en el número especificado de pasos de precio en la barra de cruce. Cuando ConfirmedOnEntry está habilitado, la divergencia se valida en la vela completada anteriormente, reproduciendo el modo "confirmado" del EA original.
  • La configuración de ReverseCondition intercambia señales alcistas y bajistas sin cambiar la configuración del indicador.

Reglas de entrada

  1. Se produce una entrada larga cuando el promedio rápido cruza por encima del promedio lento en al menos MinCrossDistancePoints. Una entrada corta requiere el cruce opuesto.
  2. Cuando StopAndReverse es verdadero, una señal opuesta cierra la posición activa antes de que se consideren nuevas entradas.
  3. OneEntryPerBar evita múltiples entradas dentro de la misma vela al rastrear la marca de tiempo del pedido más reciente.
  4. El tamaño del pedido está controlado por TradeVolume. StockSharp aplica automáticamente este volumen a las órdenes de mercado generadas.

Gestión de posiciones

  • Las distancias de stop-loss y take-profit se definen en puntos a través de StopLossPoints y TakeProfitPoints. Se convierten a precios absolutos utilizando el paso del precio del instrumento. Cuando PureSar está activo, toda la lógica de protección está desactivada, al igual que la opción "Pure SAR" en la versión MQL.
  • La gestión del trailing stop refleja la implementación de MQL: una vez que el precio se mueve más allá de TrailingStopPoints desde el nivel de entrada, el stop se sitúa detrás del mercado a la misma distancia. Las paradas dinámicas se ignoran cuando PureSar está habilitado.
  • Los niveles de protección se controlan en cada vela cerrada. Si el rango de velas viola el stop activo o el objetivo, la estrategia cierra la posición por orden de mercado para mantener un comportamiento determinista en los datos históricos.

Filtro de sesión

  • La marca UseHourTrade restringe el comercio a la ventana inclusiva entre StartHour y EndHour (0–23). Los límites de la sesión finalizan alrededor de la medianoche cuando la hora de finalización es menor que la hora de inicio. La gestión de posiciones, incluidos los trailingstops, permanece activa fuera de la sesión, pero no se permiten nuevas entradas.

Parámetros

Parámetro Descripción
FastMaPeriod, SlowMaPeriod Longitudes de las medias móviles rápida y lenta.
FastMaType, SlowMaType Métodos de media móvil: Simple, Exponencial, Suavizado o Lineal Ponderado.
FastPriceType, SlowPriceType Las fuentes de precios alimentaron cada media móvil.
StopLossPoints, TakeProfitPoints Distancias de protección en los escalones de precios. Establezca en 0 para desactivar.
TrailingStopPoints Distancia del trailing stop en pasos de precio. Establezca en 0 para deshabilitar el seguimiento.
MinCrossDistancePoints Separación mínima entre las medias requeridas para validar un cruce.
ReverseCondition Intercambie reglas alcistas y bajistas sin cambiar los indicadores.
ConfirmedOnEntry Validar señales en la barra cerrada anterior. Desactívelo para confirmación inmediata.
OneEntryPerBar Permita como máximo una nueva posición por vela.
StopAndReverse Cierre e invierta la posición actual cuando aparezca la señal opuesta.
PureSar Deshabilite la lógica de stop-loss, take-profit y trailing stop.
UseHourTrade, StartHour, EndHour Filtro de sesión que restringe las entradas a un rango de horas específico.
TradeVolume Volumen de pedidos utilizado por BuyMarket y SellMarket.
CandleType Serie de velas suscritas para cálculos de indicadores.

Notas de conversión

  • Las distancias basadas en precios se expresan en MetaTrader puntos. El asistente GetPriceOffset convierte esos valores en precios StockSharp utilizando el paso del precio del valor o la precisión decimal. Esto mantiene el comportamiento de la estrategia alineado con el EA original independientemente del instrumento.
  • Los trailingstops se gestionan internamente porque StockSharp estrategias de alto nivel operan en velas terminadas. Este enfoque determinista garantiza que las pruebas retrospectivas que utilizan velas reproduzcan la lógica de seguimiento de MT4 prevista.
  • No se incluye ningún puerto de Python que coincida con la solicitud de conversión. En este paquete solo se proporciona la implementación de C# y la documentación multilingüe.
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 "Universal MACross EA v4" MetaTrader expert advisor.
/// The strategy trades the crossover between configurable fast and slow moving averages
/// with optional session filters, stop-and-reverse behaviour and trailing stop management.
/// </summary>
public class UniversalMaCrossV4Strategy : Strategy
{
	public enum MovingAverageMethods
	{
		Simple,
		Exponential,
		Smoothed,
		LinearWeighted
	}

	public enum AppliedPrices
	{
		Close,
		Open,
		High,
		Low,
		Median,
		Typical,
		Weighted
	}

	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<MovingAverageMethods> _fastMaType;
	private readonly StrategyParam<MovingAverageMethods> _slowMaType;
	private readonly StrategyParam<AppliedPrices> _fastPriceType;
	private readonly StrategyParam<AppliedPrices> _slowPriceType;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _trailingStopPoints;
	private readonly StrategyParam<decimal> _minCrossDistancePoints;
	private readonly StrategyParam<bool> _reverseCondition;
	private readonly StrategyParam<bool> _confirmedOnEntry;
	private readonly StrategyParam<bool> _oneEntryPerBar;
	private readonly StrategyParam<bool> _stopAndReverse;
	private readonly StrategyParam<bool> _pureSar;
	private readonly StrategyParam<bool> _useHourTrade;
	private readonly StrategyParam<int> _startHour;
	private readonly StrategyParam<int> _endHour;
	private readonly StrategyParam<decimal> _volume;
	private readonly StrategyParam<DataType> _candleType;

	private IIndicator _fastMa;
	private IIndicator _slowMa;

	private decimal? _fastPrev;
	private decimal? _fastPrevPrev;
	private decimal? _slowPrev;
	private decimal? _slowPrevPrev;

	private DateTimeOffset? _lastEntryBar;
	private TradeDirections _lastTrade = TradeDirections.None;

	private decimal? _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takeProfitPrice;

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

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

	/// <summary>
	/// Method applied to the fast moving average.
	/// </summary>
	public MovingAverageMethods FastMaType
	{
		get => _fastMaType.Value;
		set => _fastMaType.Value = value;
	}

	/// <summary>
	/// Method applied to the slow moving average.
	/// </summary>
	public MovingAverageMethods SlowMaType
	{
		get => _slowMaType.Value;
		set => _slowMaType.Value = value;
	}

	/// <summary>
	/// Price source for the fast moving average.
	/// </summary>
	public AppliedPrices FastPriceType
	{
		get => _fastPriceType.Value;
		set => _fastPriceType.Value = value;
	}

	/// <summary>
	/// Price source for the slow moving average.
	/// </summary>
	public AppliedPrices SlowPriceType
	{
		get => _slowPriceType.Value;
		set => _slowPriceType.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in points.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Trailing stop distance expressed in points.
	/// </summary>
	public decimal TrailingStopPoints
	{
		get => _trailingStopPoints.Value;
		set => _trailingStopPoints.Value = value;
	}

	/// <summary>
	/// Minimum distance between moving averages to validate a crossover.
	/// </summary>
	public decimal MinCrossDistancePoints
	{
		get => _minCrossDistancePoints.Value;
		set => _minCrossDistancePoints.Value = value;
	}

	/// <summary>
	/// Swap bullish and bearish signals when set to <c>true</c>.
	/// </summary>
	public bool ReverseCondition
	{
		get => _reverseCondition.Value;
		set => _reverseCondition.Value = value;
	}

	/// <summary>
	/// Require the crossover to be confirmed on the previous closed bar.
	/// </summary>
	public bool ConfirmedOnEntry
	{
		get => _confirmedOnEntry.Value;
		set => _confirmedOnEntry.Value = value;
	}

	/// <summary>
	/// Allow only one new position per candle.
	/// </summary>
	public bool OneEntryPerBar
	{
		get => _oneEntryPerBar.Value;
		set => _oneEntryPerBar.Value = value;
	}

	/// <summary>
	/// Close and reverse the active position when the opposite signal appears.
	/// </summary>
	public bool StopAndReverse
	{
		get => _stopAndReverse.Value;
		set => _stopAndReverse.Value = value;
	}

	/// <summary>
	/// Disable stop-loss, take-profit and trailing stop logic.
	/// </summary>
	public bool PureSar
	{
		get => _pureSar.Value;
		set => _pureSar.Value = value;
	}

	/// <summary>
	/// Enable the hour-based trading session filter.
	/// </summary>
	public bool UseHourTrade
	{
		get => _useHourTrade.Value;
		set => _useHourTrade.Value = value;
	}

	/// <summary>
	/// Start hour of the trading window (0-23).
	/// </summary>
	public int StartHour
	{
		get => _startHour.Value;
		set => _startHour.Value = value;
	}

	/// <summary>
	/// End hour of the trading window (0-23).
	/// </summary>
	public int EndHour
	{
		get => _endHour.Value;
		set => _endHour.Value = value;
	}

	/// <summary>
	/// Order volume applied to each market order.
	/// </summary>
	public decimal TradeVolume
	{
		get => _volume.Value;
		set => _volume.Value = value;
	}

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

	/// <summary>
	/// Initializes a new instance of the <see cref="UniversalMaCrossV4Strategy"/> class.
	/// </summary>
	public UniversalMaCrossV4Strategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA Period", "Length of the fast moving average", "Indicators")
			
			.SetOptimize(5, 40, 1);

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 80)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA Period", "Length of the slow moving average", "Indicators")
			
			.SetOptimize(30, 200, 5);

		_fastMaType = Param(nameof(FastMaType), MovingAverageMethods.Exponential)
			.SetDisplay("Fast MA Method", "Smoothing method applied to the fast moving average", "Indicators");

		_slowMaType = Param(nameof(SlowMaType), MovingAverageMethods.Exponential)
			.SetDisplay("Slow MA Method", "Smoothing method applied to the slow moving average", "Indicators");

		_fastPriceType = Param(nameof(FastPriceType), AppliedPrices.Close)
			.SetDisplay("Fast MA Price", "Price source injected into the fast moving average", "Indicators");

		_slowPriceType = Param(nameof(SlowPriceType), AppliedPrices.Close)
			.SetDisplay("Slow MA Price", "Price source injected into the slow moving average", "Indicators");

		_stopLossPoints = Param(nameof(StopLossPoints), 100m)
			.SetNotNegative()
			.SetDisplay("Stop Loss (points)", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
			.SetNotNegative()
			.SetDisplay("Take Profit (points)", "Take-profit distance in price steps", "Risk");

		_trailingStopPoints = Param(nameof(TrailingStopPoints), 40m)
			.SetNotNegative()
			.SetDisplay("Trailing Stop (points)", "Trailing stop distance in price steps", "Risk");

		_minCrossDistancePoints = Param(nameof(MinCrossDistancePoints), 0m)
			.SetNotNegative()
			.SetDisplay("Min Cross Distance (points)", "Minimum separation between the moving averages", "Filters");

		_reverseCondition = Param(nameof(ReverseCondition), false)
			.SetDisplay("Reverse Signals", "Swap bullish and bearish conditions", "General");

		_confirmedOnEntry = Param(nameof(ConfirmedOnEntry), true)
			.SetDisplay("Confirmed On Entry", "Validate signals on the previous closed bar", "General");

		_oneEntryPerBar = Param(nameof(OneEntryPerBar), true)
			.SetDisplay("One Entry Per Bar", "Allow at most one entry per candle", "General");

		_stopAndReverse = Param(nameof(StopAndReverse), true)
			.SetDisplay("Stop And Reverse", "Close and reverse when the opposite signal appears", "Risk");

		_pureSar = Param(nameof(PureSar), false)
			.SetDisplay("Pure SAR", "Disable protective stops and trailing", "Risk");

		_useHourTrade = Param(nameof(UseHourTrade), false)
			.SetDisplay("Use Hour Filter", "Restrict trading to a specific session", "Session");

		_startHour = Param(nameof(StartHour), 10)
			.SetDisplay("Start Hour", "Trading window start hour", "Session");

		_endHour = Param(nameof(EndHour), 11)
			.SetDisplay("End Hour", "Trading window end hour", "Session");

		_volume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order volume for each market entry", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle subscription used by the strategy", "General");
	}

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

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

		_fastMa = null;
		_slowMa = null;
		_fastPrev = null;
		_fastPrevPrev = null;
		_slowPrev = null;
		_slowPrevPrev = null;
		_lastEntryBar = null;
		_lastTrade = TradeDirections.None;
		_entryPrice = null;
		_stopPrice = null;
		_takeProfitPrice = null;

		Volume = TradeVolume;
	}

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

		_fastMa = CreateMovingAverage(FastMaType, FastMaPeriod);
		_slowMa = CreateMovingAverage(SlowMaType, SlowMaPeriod);

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

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _fastMa);
			DrawIndicator(area, _slowMa);
			DrawOwnTrades(area);
		}

		StartProtection(null, null);
	}

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

		ManageExistingPosition(candle);

		if (_fastMa is null || _slowMa is null)
			return;

		var fastPrice = GetPrice(candle, FastPriceType);
		var slowPrice = GetPrice(candle, SlowPriceType);

		var fastResult = _fastMa.Process(new DecimalIndicatorValue(_fastMa, fastPrice, candle.OpenTime) { IsFinal = true });
		if (fastResult.IsEmpty) return;
		var fastValue = fastResult.GetValue<decimal>();
		var slowResult = _slowMa.Process(new DecimalIndicatorValue(_slowMa, slowPrice, candle.OpenTime) { IsFinal = true });
		if (slowResult.IsEmpty) return;
		var slowValue = slowResult.GetValue<decimal>();

		var prevFast = _fastPrev;
		var prevSlow = _slowPrev;
		var prevFastPrev = _fastPrevPrev;
		var prevSlowPrev = _slowPrevPrev;

		_fastPrevPrev = prevFast;
		_slowPrevPrev = prevSlow;
		_fastPrev = fastValue;
		_slowPrev = slowValue;

		

		var minDistance = GetPriceOffset(MinCrossDistancePoints);

		var crossUp = false;
		var crossDown = false;

		if (ConfirmedOnEntry)
		{
			// Confirm signals using the previous completed bar (shift 2 -> 1 in MQL terms).
			if (prevFast.HasValue && prevSlow.HasValue && prevFastPrev.HasValue && prevSlowPrev.HasValue)
			{
				var diff = prevFast.Value - prevSlow.Value;
				crossUp = prevFastPrev.Value < prevSlowPrev.Value && prevFast.Value > prevSlow.Value && diff >= minDistance;
				crossDown = prevFastPrev.Value > prevSlowPrev.Value && prevFast.Value < prevSlow.Value && -diff >= minDistance;
			}
		}
		else
		{
			// Validate crossovers on the current finished bar.
			if (prevFast.HasValue && prevSlow.HasValue)
			{
				var diff = fastValue - slowValue;
				crossUp = prevFast.Value < prevSlow.Value && fastValue > slowValue && diff >= minDistance;
				crossDown = prevFast.Value > prevSlow.Value && fastValue < slowValue && -diff >= minDistance;
			}
		}

		bool buySignal;
		bool sellSignal;

		if (!ReverseCondition)
		{
			buySignal = crossUp;
			sellSignal = crossDown;
		}
		else
		{
			buySignal = crossDown;
			sellSignal = crossUp;
		}

		if (!IsWithinTradingHours(candle))
			return;

		if (StopAndReverse && Position != 0)
		{
			var reverseToShort = _lastTrade == TradeDirections.Long && sellSignal;
			var reverseToLong = _lastTrade == TradeDirections.Short && buySignal;

			if (reverseToLong || reverseToShort)
			{
				ClosePosition();
				ResetProtection();
				_lastTrade = TradeDirections.None;
			}
		}

		if (Position != 0)
			return;

		if (OneEntryPerBar && _lastEntryBar == candle.OpenTime)
			return;

		if (buySignal)
		{
			BuyMarket(TradeVolume);
			SetProtectionLevels(candle.ClosePrice, true);
			_lastTrade = TradeDirections.Long;
			_lastEntryBar = candle.OpenTime;
		}
		else if (sellSignal)
		{
			SellMarket(TradeVolume);
			SetProtectionLevels(candle.ClosePrice, false);
			_lastTrade = TradeDirections.Short;
			_lastEntryBar = candle.OpenTime;
		}
	}

	private void ManageExistingPosition(ICandleMessage candle)
	{
		if (Position == 0)
		{
			ResetProtection();
			return;
		}

		UpdateTrailingStop(candle);

		if (Position > 0)
		{
			if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
			{
				ClosePosition();
				ResetProtection();
				return;
			}

			if (_takeProfitPrice.HasValue && candle.HighPrice >= _takeProfitPrice.Value)
			{
				ClosePosition();
				ResetProtection();
			}
		}
		else if (Position < 0)
		{
			if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
			{
				ClosePosition();
				ResetProtection();
				return;
			}

			if (_takeProfitPrice.HasValue && candle.LowPrice <= _takeProfitPrice.Value)
			{
				ClosePosition();
				ResetProtection();
			}
		}
	}

	private void UpdateTrailingStop(ICandleMessage candle)
	{
		if (PureSar || TrailingStopPoints <= 0m || !_entryPrice.HasValue)
			return;

		var trailingDistance = GetPriceOffset(TrailingStopPoints);
		if (trailingDistance <= 0m)
			return;

		if (Position > 0)
		{
			var move = candle.ClosePrice - _entryPrice.Value;
			if (move > trailingDistance)
			{
				var candidate = candle.ClosePrice - trailingDistance;
				if (!_stopPrice.HasValue || candidate > _stopPrice.Value)
				{
					_stopPrice = candidate;
				}
			}
		}
		else if (Position < 0)
		{
			var move = _entryPrice.Value - candle.ClosePrice;
			if (move > trailingDistance)
			{
				var candidate = candle.ClosePrice + trailingDistance;
				if (!_stopPrice.HasValue || candidate < _stopPrice.Value)
				{
					_stopPrice = candidate;
				}
			}
		}
	}

	private bool IsWithinTradingHours(ICandleMessage candle)
	{
		if (!UseHourTrade)
			return true;

		var hour = candle.OpenTime.Hour;
		var start = StartHour;
		var end = EndHour;

		if (start <= end)
			return hour >= start && hour <= end;

		return hour >= start || hour <= end;
	}

	private static IIndicator CreateMovingAverage(MovingAverageMethods method, int period)
	{
		return method switch
		{
			MovingAverageMethods.Simple => new SimpleMovingAverage { Length = period },
			MovingAverageMethods.Exponential => new ExponentialMovingAverage { Length = period },
			MovingAverageMethods.Smoothed => new SmoothedMovingAverage { Length = period },
			MovingAverageMethods.LinearWeighted => new WeightedMovingAverage { Length = period },
			_ => new SimpleMovingAverage { Length = period }
		};
	}

	private static decimal GetPrice(ICandleMessage candle, AppliedPrices priceType)
	{
		return priceType 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 void SetProtectionLevels(decimal entryPrice, bool isLong)
	{
		_entryPrice = entryPrice;

		if (PureSar)
		{
			_stopPrice = null;
			_takeProfitPrice = null;
			return;
		}

		var stopDistance = GetPriceOffset(StopLossPoints);
		var takeDistance = GetPriceOffset(TakeProfitPoints);

		_stopPrice = stopDistance > 0m ? (isLong ? entryPrice - stopDistance : entryPrice + stopDistance) : null;
		_takeProfitPrice = takeDistance > 0m ? (isLong ? entryPrice + takeDistance : entryPrice - takeDistance) : null;
	}

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

	private decimal GetPriceOffset(decimal points)
	{
		if (points <= 0m)
			return 0m;

		var step = Security?.PriceStep ?? 0m;
		if (step > 0m)
			return points * step;

		var decimals = Security?.Decimals;
		if (decimals.HasValue && decimals.Value > 0)
		{
			decimal scale = 1m;
			for (var i = 0; i < decimals.Value; i++)
				scale /= 10m;

			return points * scale;
		}

		return points;
	}

	private void ClosePosition()
	{
		if (Position > 0)
			SellMarket();
		else if (Position < 0)
			BuyMarket();
	}

	private enum TradeDirections
	{
		None,
		Long,
		Short
	}
}