Открыть на GitHub

Стратегия SuperForexV2

Обзор

SuperForexV2 — это перенос советника MetaTrader 4 SuperForexV2.mq4 на платформу StockSharp. Исходный робот торгует по краткосро- чному индикатору RSI и использует фиксированные расстояния до тейк-профита, стоп-лосса и трейлинг-стопа. Реализация на C# повторяет эти решения через высокоуровневый API StockSharp: стратегия анализирует только закрывшиеся свечи, реагирует на пересечения порогов RSI и управляет единственной позицией с помощью ценовых уровней, выраженных в пунктах.

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

  1. Поток данных и индикатор
    • Подписывается на настраиваемый поток свечей (по умолчанию 15-минутные бары) и передаёт каждую закрытую свечу в индикатор RSI.
    • Период RSI задаётся параметром и равен 4, как в оригинале.
  2. Динамический объём сделки
    • Перед входом рассчитывается рабочий объём как отношение текущей стоимости портфеля к BalanceToVolumeDivider.
    • Полученное значение ограничивается параметрами InitialVolume и MaxVolume, после чего приводится к шагу объёма инструмента.
  3. Условия входа
    • Если позиции нет и RSI опускается ниже RsiLowerLevel, отправляется рыночная покупка.
    • Если RSI поднимается выше RsiUpperLevel, открывается короткая позиция.
  4. Управление позицией
    • При открытии сделки фиксируются абсолютные уровни стоп-лосса и тейк-профита, рассчитанные из пунктов.
    • На каждой закрытой свече проверяется, достигались ли эти уровни; при срабатывании позиция закрывается по рынку.
    • Трейлинг-стоп переносит уровень стопа после движения цены в прибыльную сторону минимум на TrailingStopPips пунктов.
    • Дополнительно позиция закрывается при достижении противоположного порога RSI (например, лонг ликвидируется при RSI выше верхней границы).
  5. Контроль количества сделок
    • Подобно MT4-версии, стратегия поддерживает не более одной чистой позиции: пока есть активная сделка, новые входы не рассматри- ваются.

Параметры

Название Описание Значение по умолчанию Примечания
CandleType Тип свечей для расчётов. Таймфрейм 15m Можно выбрать любой DataType, поддерживаемый коннектором.
RsiPeriod Длина RSI. 4 Значение должно быть больше нуля.
RsiUpperLevel Верхний порог RSI для шортов и выхода из лонгов. 62 Соответствует параметру Pos в MT4.
RsiLowerLevel Нижний порог RSI для лонгов и выхода из шортов. 42 Соответствует параметру Neg в MT4.
TakeProfitPips Расстояние до тейк-профита в пунктах. 109 При 0 тейк-профит отключается.
StopLossPips Расстояние до стоп-лосса в пунктах. 9 При 0 стоп-лосс отключается.
TrailingStopPips Длина трейлинг-стопа в пунктах. 6 При 0 трейлинг не используется.
InitialVolume Минимальный объём при отсутствии данных о портфеле. 0.1 Используется и тогда, когда расчётный объём ≤ 0.
MaxVolume Максимальный допустимый объём входа. 100 Ограничивает рост позиции при большом балансе.
BalanceToVolumeDivider Делитель баланса при расчёте объёма. 10000 Повторяет формулу MT4 Lots = AccountBalance()/10000.

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

  • Анализ выполняется только после закрытия свечи (CandleStates.Finished), что эквивалентно обработке тиков MT4 по окончании бара и одновременно защищает от неполных данных.
  • Перевод пунктов в цену основан на PriceStep инструмента. Для трёх- и пятизнаковых валютных пар шаг дополнительно умножается на 10, чтобы привести понятие пункта к метатрейдеровскому.
  • Уровни стоп-лосса, тейк-профита и трейлинга хранятся в самой стратегии и сравниваются с экстремумами свечей, поскольку высокоуровне- вые ордера StockSharp не управляют биржевыми стопами автоматически.
  • Рассчитанный объём корректируется по VolumeStep, MinVolume и MaxVolume, чтобы заявка соответствовала торговым ограничениям.
  • При наличии позиции метод входа немедленно завершается, гарантируюя отсутствие параллельных сделок.

Отличия от версии для MT4

  • StockSharp-реализация работает по закрытым свечам, поэтому факты достижения стопов/тейков внутри бара фиксируются на следующей свече.
  • Проверка AccountFreeMargin() заменена вычислением объёма через стоимость портфеля; если данные недоступны, используется InitialVolume.
  • Стратегия не выставляет стоп-ордеры брокеру, а закрывает позиции рыночными приказами при достижении уровня — это соответствует возможностям высокоуровневого API StockSharp.
  • Параметр NumeroMagico, применявшийся для фильтрации сделок в MT4, не требуется и опущен.
  • Текстовые сообщения Print из оригинального советника не перенесены; при необходимости используйте систему логирования StockSharp.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// SuperForexV2: RSI threshold reversal with ATR trailing stop.
/// Buys when RSI below lower level, sells when above upper level.
/// </summary>
public class SuperForexV2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _upperLevel;
	private readonly StrategyParam<decimal> _lowerLevel;

	private decimal _entryPrice;
	private decimal _trailStop;

	public SuperForexV2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

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

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for trailing.", "Indicators");

		_upperLevel = Param(nameof(UpperLevel), 62m)
			.SetDisplay("RSI Upper", "Overbought for shorts.", "Signals");

		_lowerLevel = Param(nameof(LowerLevel), 42m)
			.SetDisplay("RSI Lower", "Oversold for longs.", "Signals");
	}

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

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

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal UpperLevel
	{
		get => _upperLevel.Value;
		set => _upperLevel.Value = value;
	}

	public decimal LowerLevel
	{
		get => _lowerLevel.Value;
		set => _lowerLevel.Value = value;
	}

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

		_entryPrice = 0;
		_trailStop = 0;
	}

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

		_entryPrice = 0;
		_trailStop = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (atrVal <= 0)
			return;

		var close = candle.ClosePrice;

		// Trailing stop + opposite RSI exit
		if (Position > 0)
		{
			var newTrail = close - atrVal * 1.5m;
			if (newTrail > _trailStop)
				_trailStop = newTrail;

			if (close <= _trailStop || rsiVal > UpperLevel)
			{
				SellMarket();
				_entryPrice = 0;
				_trailStop = 0;
			}
		}
		else if (Position < 0)
		{
			var newTrail = close + atrVal * 1.5m;
			if (_trailStop == 0 || newTrail < _trailStop)
				_trailStop = newTrail;

			if (close >= _trailStop || rsiVal < LowerLevel)
			{
				BuyMarket();
				_entryPrice = 0;
				_trailStop = 0;
			}
		}

		// Entry on RSI levels
		if (Position == 0)
		{
			if (rsiVal < LowerLevel)
			{
				_entryPrice = close;
				_trailStop = close - atrVal * 2m;
				BuyMarket();
			}
			else if (rsiVal > UpperLevel)
			{
				_entryPrice = close;
				_trailStop = close + atrVal * 2m;
				SellMarket();
			}
		}
	}
}