Открыть на GitHub

Стратегия MA Price Cross

Стратегия MA Price Cross — это конвертация советника MetaTrader 4 «MA Price Cross» на высокоуровневый API StockSharp. Алгоритм ожидает пересечения выбранной скользящей средней с текущей ценой в пределах заданного торгового окна. Когда средняя пробивает цену снизу вверх, стратегия открывает длинную позицию; при пробое сверху вниз — короткую. Уровни стоп-лосса и тейк-профита задаются в пунктах MetaTrader и автоматически переводятся в абсолютные ценовые расстояния через PriceStep инструмента.

Вместо обработки каждого тика, как в исходном MQL-коде, версия на StockSharp работает с завершёнными свечами и использует подписку SubscribeCandles. Это гарантирует единственное решение на свечу и полную совместимость с конвейером индикаторов. Скользящая средняя настраивается в соответствии со всеми четырьмя режимами MetaTrader и поддерживает различные источники цены (close, open, high, low, median, typical, weighted).

Логика работы

  1. Ожидать, пока текущее время попадёт в торговое окно [StartTime, StopTime). Окна, пересекающие полночь, поддерживаются.
  2. Обрабатывать только закрытые свечи. Передавать в индикатор выбранный тип цены.
  3. Сохранять предыдущее значение скользящей средней, имитируя использование сдвигов iMA в MetaTrader.
  4. Если предыдущая средняя была ниже последней цены, а новое значение стало выше, открыть (или развернуть в) длинную позицию.
  5. Если предыдущая средняя была выше, а текущее значение стало ниже цены, открыть (или развернуть в) короткую позицию.
  6. Перед открытием позиции противоположного направления закрыть текущую, повторяя условие OrdersTotal() == 0 из оригинала.
  7. Запустить виртуальные стоп-уровни и тейк-профит, умножив заданные в пунктах расстояния на PriceStep инструмента.

Значения по умолчанию

Параметр Значение Описание
CandleType TimeFrame(1m) Ряд свечей, используемый в расчётах.
MaPeriod 160 Количество баров в окне скользящей средней.
MaMethod Simple Тип скользящей средней: Simple, Exponential, Smoothed или LinearWeighted.
PriceType Close Источник цены для индикатора (close/open/high/low/median/typical/weighted).
StartTime 01:00 Время начала торговли.
StopTime 22:00 Время окончания приёма новых сигналов.
StopLossPoints 200 Дистанция стоп-лосса в пунктах MetaTrader, пересчитанная в цену.
TakeProfitPoints 600 Дистанция тейк-профита в пунктах MetaTrader, пересчитанная в цену.
OrderVolume 0.1 Объём рыночных заявок.

Особенности

  • При равных StartTime и StopTime фильтр времени отключается.
  • Если StopLossPoints или TakeProfitPoints равны нулю, соответствующий защитный уровень не создаётся.
  • Для фильтра времени используется candle.CloseTime.TimeOfDay, поэтому стратегия учитывает часовой пояс коннектора.
  • При отсутствии PriceStep пункты применяются напрямую без масштабирования.

Источник оригинала

  • Файл: MQL/44133/MA Price Cross.mq4
  • Автор: JBlanked (2023)
using System;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "MA Price Cross" MetaTrader expert.
/// Enters when SMA crosses above/below the current close price.
/// </summary>
public class MaPriceCrossStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maPeriod;

	private ExponentialMovingAverage _sma;
	private decimal? _prevAverage;
	private decimal? _prevClose;

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

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	public MaPriceCrossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for MA cross detection", "General");

		_maPeriod = Param(nameof(MaPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "SMA period", "Indicators");
	}

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

		_sma = new ExponentialMovingAverage { Length = MaPeriod };
		_prevAverage = null;
		_prevClose = null;

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

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

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

		if (!_sma.IsFormed)
		{
			_prevAverage = smaValue;
			_prevClose = candle.ClosePrice;
			return;
		}

		if (_prevAverage is null || _prevClose is null)
		{
			_prevAverage = smaValue;
			_prevClose = candle.ClosePrice;
			return;
		}

		var close = candle.ClosePrice;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// MA was below price, now crosses above -> sell signal (price goes under MA)
		var sellSignal = _prevClose.Value >= _prevAverage.Value && close < smaValue;
		// MA was above price, now crosses below -> buy signal (price goes above MA)
		var buySignal = _prevClose.Value <= _prevAverage.Value && close > smaValue;

		if (buySignal)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (sellSignal)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevAverage = smaValue;
		_prevClose = close;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_sma = null;
		_prevAverage = null;
		_prevClose = null;

		base.OnReseted();
	}
}