Открыть на GitHub

Стратегия Martingail Expert

Общее описание

  • Портирование советника MetaTrader 5 MartingailExpert.mq5.
  • Использует пересечение стохастика с настраиваемыми параметрами %K, %D и замедлением для генерации сигналов.
  • Реализует мартингейловую сетку: добавляет позиции как по направлению прибыли, так и при усреднении против движения.
  • Рассчитана на неттинг – стратегия удерживает только одну суммарную длинную или короткую позицию.

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

Условия входа

  1. Обработка ведётся по закрытым свечам таймфрейма CandleType.
  2. Значения стохастика берутся с предыдущей завершённой свечи, что соответствует вызову MQL iStochastic(..., 1).
  3. Вход в лонг выполняется, если:
    • Предыдущее значение %K выше %D.
    • Предыдущее %D превышает BuyLevel.
    • Открытых позиций нет.
  4. Вход в шорт выполняется, если:
    • Предыдущее значение %K ниже %D.
    • Предыдущее %D ниже SellLevel.
    • Открытых позиций нет.
  5. Все рыночные ордера используют объём Volume, нормализованный по Security.VolumeStep.

Масштабирование позиции

  • ProfitPips задаёт дистанцию в пипсах, по достижении которой добавляется ещё один базовый объём по тренду.
    • Для лонга: если максимум свечи достигает lastEntryPrice + ProfitPips * positionCount, отправляется ордер с базовым Volume.
    • Для шорта: если минимум свечи достигает lastEntryPrice - ProfitPips * positionCount, отправляется базовый ордер на продажу.
  • StepPips определяет расстояние для мартингейлового усреднения против движения.
    • Для лонга: при снижении минимума до lastEntryPrice - StepPips следующий объём равен lastVolume * Multiplier.
    • Для шорта: при росте максимума до lastEntryPrice + StepPips используется тот же множитель.
  • После каждой сделки обновляются lastEntryPrice, lastVolume и счётчик открытых позиций.

Выход из позиции

  • Хранится цена последнего входа по текущему направлению.
  • При достижении lastEntryPrice ± ProfitPips (по максимумам для лонга и минимумам для шорта) все позиции закрываются рыночным ордером.
  • После обнуления позиции внутреннее состояние мартингейла сбрасывается.

Параметры

Параметр Значение по умолчанию Описание
Volume 0.03 Базовый лот для стартового входа и добавлений по прибыли.
Multiplier 1.6 Множитель для мартингейлового усреднения.
StepPips 25 Расстояние в пипсах для усредняющих заявок против тренда.
ProfitPips 9 Дистанция в пипсах для фиксации прибыли и добавления по тренду.
KPeriod 5 Период расчёта %K стохастика.
DPeriod 3 Период сглаживания линии %D.
Slowing 3 Дополнительное сглаживание %K (slow stochastic).
BuyLevel 20 Минимальное значение %D для разрешения лонга.
SellLevel 55 Максимальное значение %D для разрешения шорта.
CandleType таймфрейм 5 минут Таймфрейм для построения свечей и индикатора.

Особенности реализации

  • Размер пипса вычисляется из Security.PriceStep. Для инструментов с 3 или 5 знаками цена шага дополнительно умножается на 10, что повторяет логику MQL.
  • Объёмы округляются вниз до ближайшего Security.VolumeStep. Если результат меньше минимального шага объёма, ордер не отправляется.
  • Проверки условий выполняются по максимумам и минимумам свечи, чтобы приблизить внутрисвечное поведение исходного советника при работе с высокоуровневым API.
  • Метод OnOwnTradeReceived фиксирует фактические цены и объёмы, поддерживая корректную последовательность мартингейловых шагов.

Рекомендации по использованию

  • Выставляйте CandleType в соответствии с таймфреймом исходного шаблона MetaTrader (обычно M5), чтобы получить схожую динамику.
  • Убедитесь, что у инструмента корректно заданы шаг цены и объёма, иначе подстройте Volume, StepPips и ProfitPips вручную.
  • Дополнительно применяйте внешние лимиты по риску: мартингейл увеличивает позицию при неблагоприятном движении.

Отличия от оригинала

  • В StockSharp стратегия работает по закрытым свечам, а не по каждому тику; проверки выполняются по high/low свечи.
  • Проверка свободной маржи, доступная в MetaTrader, в данной реализации отсутствует и должна контролироваться отдельно.
  • Используется неттинговая модель StockSharp; режим хеджирования не поддерживается.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Martingale Expert strategy (simplified). Uses RSI oscillator with
/// martingale-style position scaling and profit targets.
/// </summary>
public class MartingailExpertStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<decimal> _buyLevel;
	private readonly StrategyParam<decimal> _sellLevel;

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

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public decimal BuyLevel
	{
		get => _buyLevel.Value;
		set => _buyLevel.Value = value;
	}

	public decimal SellLevel
	{
		get => _sellLevel.Value;
		set => _sellLevel.Value = value;
	}

	public MartingailExpertStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Length", "RSI period", "Indicators");

		_buyLevel = Param(nameof(BuyLevel), 35m)
			.SetDisplay("Buy Level", "RSI level for longs", "Logic");

		_sellLevel = Param(nameof(SellLevel), 65m)
			.SetDisplay("Sell Level", "RSI level for shorts", "Logic");
	}

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

		var rsi = new RelativeStrengthIndex { Length = RsiLength };

		decimal prevRsi = 50;
		var hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, (ICandleMessage candle, decimal rsiVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!hasPrev)
				{
					prevRsi = rsiVal;
					hasPrev = true;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevRsi = rsiVal;
					return;
				}

				// RSI crosses up from oversold
				if (prevRsi < BuyLevel && rsiVal >= BuyLevel && Position <= 0)
					BuyMarket();
				// RSI crosses down from overbought
				else if (prevRsi > SellLevel && rsiVal <= SellLevel && Position >= 0)
					SellMarket();

				prevRsi = rsiVal;
			})
			.Start();

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

			var rsiArea = CreateChartArea();
			if (rsiArea != null)
				DrawIndicator(rsiArea, rsi);
		}
	}
}