Открыть на GitHub

Стратегия TenPointThree MACD Grid

Обзор

Стратегия представляет собой порт MetaTrader-советника 10p3v003 (10point3.mq4) на C#. Торговая логика объединяет сигналы MACD с мартингейл-сеткой и реализована на высокоуровневом API StockSharp:

  • Сигналы MACD. Направление выбирается при пересечении основной линии MACD со строкой сигнала на свечe, заданной SignalShift. Для длинных позиций требуется, чтобы предыдущий сигнал был ниже -TradingRangePips, а текущий MACD находился ниже нуля (для коротких — зеркальные условия). Параметр ReverseSignal позволяет инвертировать направление.
  • Сеточное наращивание. После первой сделки новые входы в том же направлении разрешаются только когда цена прошла против последней сделки минимум GridStepPips. Объём каждой следующей сделки умножается на LotMultiplier (или на 1.5, если MaxTrades > 12), что повторяет оригинальную мартингейл-логику.
  • Защита прибыли. Когда открыто OrdersToProtect и более сделок и плавающая прибыль превышает порог, закрывается последняя сделка и новые входы временно блокируются. Порог рассчитывается по доле капитала (при включённом манименеджменте) либо через оценку контрактного размера (при фиксированном объёме).
  • Выходы для каждой ноги. Для каждой позиции рассчитываются собственные тейк-профит, виртуальный стоп и трейлинг-стоп. Дистанция стопа повторяет формулу MQL4: InitialStopPips + (MaxTrades - existingOrders) * GridStepPips. Трейлинг активируется только после прохождения TrailingStopPips + GridStepPips и закрывает ногу при откате на TrailingStopPips.
  • Фильтр времени. При активном UseTimeFilter новые сетки не запускаются, если время свечи строго между StopHour и StartHour, что соответствует «опасному часу» в исходном советнике.

Все денежные расчёты используют PriceStep и StepPrice. Если биржа не предоставляет размер контракта, применяется запасное значение 100000, соответствующее стандартному форекс-лоту.

Параметры

Параметр Описание
CandleType Тип свечей для расчёта MACD (по умолчанию 30-минутные).
Volume Базовый объём первой сделки сетки.
TakeProfitPips Дистанция тейк-профита в пунктах (0 — выключено).
InitialStopPips Базовый стоп в пунктах; фактический стоп увеличивается пропорционально оставшимся уровням сетки.
TrailingStopPips Дистанция трейлинг-стопа (0 — отключить).
MaxTrades Максимальное число одновременных сделок сетки.
LotMultiplier Множитель объёма при каждом догрузе (если MaxTrades > 12, используется 1.5).
GridStepPips Минимальное встречное движение цены для открытия новой сделки.
OrdersToProtect Минимальное число ног для запуска защиты прибыли.
UseMoneyManagement Включает расчёт объёма от капитала.
AccountType Формула риска: 0 — стандартный счёт (equity / 10 000), 1 — обычный (equity / 100 000), 2 — nano (equity / 1 000).
RiskPercent Процент капитала, используемый при динамическом расчёте объёма.
ReverseSignal Инвертирует сигналы MACD.
FastEmaLength, SlowEmaLength, SignalLength Периоды MACD (12/26/9 по умолчанию).
SignalShift Количество закрытых свечей, отступ для проверки пересечения (по умолчанию 1).
TradingRangePips Диапазон сигнала MACD, который должен быть пробит перед входом.
UseTimeFilter Включает временной фильтр.
StopHour, StartHour Границы запрещённого интервала (исключая сами границы).

Манименеджмент

При выключенном UseMoneyManagement используется фиксированный объём Volume. Если параметр включён, объём рассчитывается как в MQL4-версии:

  • Тип 0: Ceil(risk% * equity / 10 000) / 10
  • Тип 1: risk% * equity / 100 000
  • Тип 2: risk% * equity / 1 000

Полученный объём нормализуется по Security.VolumeStep и ограничивается диапазоном MinVolume / MaxVolume инструмента.

Алгоритм работы

  1. Подписаться на свечи указанного таймфрейма и связать MACD через BindEx.
  2. На каждой закрытой свече обновлять стопы/трейлинги у всех открытых ног.
  3. При выполнении условий пересечения MACD проверить временной фильтр, направление и смещение цены на GridStepPips, затем открыть новую ногу сетки.
  4. Следить за плавающей прибылью; при достижении порога закрыть последнюю сделку и ждать следующей свечи.

Примечания по конверсии

  • Комментарии переведены на английский язык согласно требованиям репозитория.
  • Используется высокоуровневый API StockSharp (подписка на свечи и BindEx).
  • Для корректных денежных расчётов обязательно задать PriceStep, StepPrice и по возможности размер контракта инструмента.
  • Для эмуляции MQL4-управления несколькими ордерами стратегия хранит состояние каждой ноги самостоятельно, так как StockSharp агрегирует позицию по инструменту.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// 10point3 MACD Grid: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class TenPointThreeMacdGridStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public TenPointThreeMacdGridStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 12)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 26)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
	public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

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

		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
		}
		_prevFast = fastVal; _prevSlow = slowVal;
	}
}