Открыть на GitHub

Стратегия N Trades Per Set Martingale

Обзор

Стратегия является точной конвертацией советника MetaTrader «N trades per set martingale + Close and reset on equity increase». Торговля ведётся только в длинную сторону, а управление объёмом осуществляется за счёт мартиингейла и сброса по приросту капитала. Новый ордер выставляется сразу после закрытия предыдущей позиции, поэтому стратегия постоянно присутствует в рынке.

Логика торговли

  1. Последовательные входы – как только позиция закрыта, сразу отправляется рыночная покупка. После исполнения автоматически выставляются стоп-лосс и тейк-профит.
  2. Учёт результатов – после закрытия позиции цена выхода сравнивается с ценой входа. Прибыльное закрытие увеличивает счётчик побед, убыточное или безубыточное – счётчик поражений, что полностью соответствует оригиналу.
  3. Завершение серии – параллельно ведётся счётчик сделок в текущем наборе. Когда он достигает Trades Per Set, серия считается завершённой и возможны три варианта:
    • Все сделки прибыльные – объём пересчитывается от текущей стоимости портфеля через параметр Equity Divisor, после чего счётчики обнуляются.
    • Все сделки убыточные – объём умножается на Scale Factor, затем счётчики обнуляются.
    • Смешанный результат – при наличии побед и поражений объём сохраняется, а счётчики просто обнуляются.
  4. Сброс по капиталу – когда эквити портфеля увеличивается минимум на Equity Increase, происходит полный сброс: счётчики обнуляются, базовый объём пересчитывается от нового уровня капитала, целевое значение эквити сдвигается вперёд на ту же величину.

Эта последовательность полностью повторяет схемы блоков fxDreema, использованные в исходном советнике.

Параметры

Параметр Описание
Trades Per Set Количество сделок в одной мартиингейл-серии.
Stop Loss (pips) Размер защитного стопа в ценовых шагах инструмента. Ноль отключает стоп.
Take Profit (pips) Размер целевого тейка в ценовых шагах. Ноль отключает тейк.
Scale Factor Множитель объёма после полностью убыточной серии. Значения меньше 1 автоматически приравниваются к 1.
Equity Divisor Делитель эквити, определяющий базовый объём после выигрышной серии или сброса по капиталу.
Equity Increase Прирост эквити, при котором инициируется глобальный сброс. Ноль отключает проверку по капиталу.

Управление капиталом

  • Объём приводится к требованиям инструмента (VolumeStep, MinVolume, MaxVolume) аналогично функции AlignLots в MQL.
  • Если текущее значение эквити недоступно, используется предыдущий объём, а для первой сделки — шаг объёма инструмента.
  • Стопы и тейки переводятся в шаги цены через PriceStep. При отсутствии шага цены используется округление входного значения до ближайшего целого.

Практические замечания

  • Стратегия строго лонговая, как и исходный советник. Если брокер допускает короткие позиции, их следует отключить вручную.
  • При частичном исполнении остаток позиции получает те же защитные ордера, поскольку они переустанавливаются после каждого заполнения.
  • Проверка условия по эквити выполняется после каждой закрытой позиции. Убедитесь, что подключённый портфель передаёт актуальное значение капитала, иначе сброс не сработает.
namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

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

/// <summary>
/// N Trades Per Set Martingale: RSI-based entry with position tracking.
/// Buys when RSI oversold, sells when RSI overbought.
/// </summary>
public class NTradesPerSetMartingaleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;

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

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public NTradesPerSetMartingaleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI calculation period", "Indicators");
	}

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

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		decimal? prevRsi = null;

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

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevRsi.HasValue)
				{
					var crossBelowOversold = prevRsi.Value >= 30m && rsiVal < 30m;
					var crossAboveOverbought = prevRsi.Value <= 70m && rsiVal > 70m;

					if (crossBelowOversold && Position <= 0)
						BuyMarket();
					else if (crossAboveOverbought && Position >= 0)
						SellMarket();
				}

				prevRsi = rsiVal;
			})
			.Start();

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