Открыть на GitHub

Стратегия MA Reverse

Описание

MA Reverse — это порт на StockSharp простого советника MetaTrader 4 «MA_Reverse». Исходный робот отслеживает, как долго цена Bid находится выше или ниже простой скользящей средней (SMA) с периодом 14. После достаточно долгой серии баров в одну сторону он открывает позицию в расчёте на возврат цены к среднему значению. В версии для StockSharp та же идея реализована через подсчёт количества завершённых свечей, закрывающихся выше или ниже SMA, и немедленное исполнение рыночного ордера, когда срабатывает заданный порог.

Торговая логика

  • Стратегия подписывается на свечи выбранного таймфрейма и рассчитывает простую скользящую среднюю с периодом SmaPeriod.
  • Поддерживается целочисленный счётчик (его целевое значение задаётся параметром StreakThreshold), который увеличивается, пока цена закрытия остаётся выше средней, и уменьшается, пока цена закрытия ниже. При касании средней счётчик сбрасывается.
  • Когда счётчик достигает StreakThreshold, а цена закрытия минимум на MinimumDeviation выше SMA, отправляется рыночный ордер на продажу. Предполагается, что затянувшееся движение вверх относительно средней скоро развернётся.
  • При достижении -StreakThreshold и отклонении закрытия на MinimumDeviation ниже SMA логика зеркально открывает длинную позицию.
  • После совершения сделки счётчик не обнуляется автоматически — он продолжает отслеживать следующую серию, как и в исходном советнике.

Управление ордерами

  • Рыночные входы используют объём TradeVolume. Если на момент сигнала существует противоположная позиция, стратегия сначала закрывает её и в том же ордере открывает новую позицию, что повторяет поведение MetaTrader при переводе из лонга в шорт или наоборот.
  • Глобальный тейк-профит настраивается через вспомогательный метод StartProtection. Расстояние вычисляется как TakeProfitPoints умноженное на шаг цены инструмента и повторяет выражение «30 * Point» из MQL-кода. При достижении цели позиция закрывается рыночным ордером.
  • В оригинальном советнике не предусмотрен стоп-лосс, поэтому в порте он также отсутствует. Управление рисками полностью лежит на тейк-профите и настройках риск-менеджмента пользователя.

Параметры

Параметр Описание
TradeVolume Лот для каждого рыночного входа. Это же значение используется при развороте позиции для закрытия старых сделок и открытия новых.
SmaPeriod Количество свечей в расчёте простой скользящей средней. По умолчанию соответствует 14, как в советнике.
StreakThreshold Сколько последовательных закрытий должно оказаться по одну сторону от SMA, прежде чем разрешается открытие позиции.
MinimumDeviation Минимальное абсолютное расстояние между ценой закрытия и SMA, подтверждающее экстремум.
TakeProfitPoints Дистанция тейк-профита в шагах цены. Умножается на PriceStep, чтобы получить абсолютное значение.
CandleType Тип свечей (таймфрейм), используемый для расчёта SMA и оценки серий.

Примечания

  • Счётчик работает на завершённых свечах, полученных через SubscribeCandles, что делает реализацию устойчивой и совместимой с прогоном по историческим данным. Поведение совпадает с тиковым вариантом MetaTrader при достаточно мелком таймфрейме.
  • StockSharp агрегирует позиции, поэтому несколько последовательных сделок рассматриваются как одна позиция с единым плавающим тейк-профитом. Это аналогично постановке одинакового тейк-профита на каждую заявку в MetaTrader.
  • Индикатор не добавляется в Strategy.Indicators, поскольку привязка через Bind сама управляет жизненным циклом индикатора.
  • Перед запуском стратегии уточняйте шаг цены и торговый объём для конкретных инструментов у брокера, чтобы TakeProfitPoints давал ожидаемое абсолютное смещение.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Moving average reversal strategy converted from the MetaTrader MA_Reverse expert advisor.
/// Counts how many consecutive closes remain on one side of the SMA and opens a trade once the streak is long enough.
/// </summary>
public class MaReverseStrategy : Strategy
{
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<int> _streakThreshold;
	private readonly StrategyParam<decimal> _minimumDeviation;
	private readonly StrategyParam<DataType> _candleType;

	private int _streak;

	public MaReverseStrategy()
	{
		_smaPeriod = Param(nameof(SmaPeriod), 14)
			.SetDisplay("SMA Period", "Number of candles used by the moving average.", "Indicator");

		_streakThreshold = Param(nameof(StreakThreshold), 3)
			.SetDisplay("Streak Threshold", "Number of consecutive closes required before reversing.", "Logic");

		_minimumDeviation = Param(nameof(MinimumDeviation), 0.0001m)
			.SetDisplay("Minimum Deviation", "Minimum distance between price and SMA to confirm the reversal.", "Logic");

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

	public int SmaPeriod
	{
		get => _smaPeriod.Value;
		set => _smaPeriod.Value = value;
	}

	public int StreakThreshold
	{
		get => _streakThreshold.Value;
		set => _streakThreshold.Value = value;
	}

	public decimal MinimumDeviation
	{
		get => _minimumDeviation.Value;
		set => _minimumDeviation.Value = value;
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_streak = 0;
	}

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

		_streak = 0;

		var sma = new SimpleMovingAverage { Length = SmaPeriod };

		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;

		var closePrice = candle.ClosePrice;
		var deviation = closePrice - smaValue;

		if (deviation == 0m)
		{
			_streak = 0;
			return;
		}

		if (deviation > 0m)
		{
			// Price above SMA
			if (_streak < 0)
				_streak = 0;
			_streak++;

			if (_streak >= StreakThreshold && deviation > MinimumDeviation)
			{
				// Long streak above SMA => sell (reversal)
				if (Position >= 0)
				{
					SellMarket();
					_streak = 0;
				}
			}
		}
		else
		{
			// Price below SMA
			if (_streak > 0)
				_streak = 0;
			_streak--;

			if (-_streak >= StreakThreshold && -deviation > MinimumDeviation)
			{
				// Long streak below SMA => buy (reversal)
				if (Position <= 0)
				{
					BuyMarket();
					_streak = 0;
				}
			}
		}
	}
}