Открыть на GitHub

EMA WMA RSI

Обзор

EMA WMA RSI — порт эксперта MetaTrader 4 «EMA WMA RSI» автора cmillion. В оригинале экспоненциальная и линейно-взвешенная средние строятся по ценам открытия свечей, а фильтрация направлений выполняется индикатором RSI, рассчитанным по тому же ряду. Реализация для StockSharp сохраняет эту логику, обрабатывает только завершённые свечи и воспроизводит опции управления позицией: принудительное закрытие противоположной позиции, защитные уровни в пунктах и гибкий трейлинг-стоп (фиксированное расстояние, ближайший фрактал или экстремумы свечей).

Стратегия работает с одним инструментом и таймфреймом, выбираемым параметром Candle Type. Все расстояния измеряются в пунктах MetaTrader, поэтому важно задать корректные Security.Step, Security.PriceStep и Security.StepPrice, чтобы преобразование в абсолютные цены выполнялось правильно.

Логика стратегии

Индикаторы

  • EMA — период задаётся параметром EMA Period, расчёт по ценам открытия.
  • WMA — период WMA Period, тоже по открытиям.
  • RSI — период RSI Period, источник данных — цены открытия.

Индикаторы обновляются при закрытии свечи. Для имитации «старта на новой свече» сохраняются значения EMA/WMA предыдущего бара и сравниваются с текущими.

Правила входа

  • Покупка
    1. Текущее значение EMA ниже WMA, а на предыдущей свече EMA была выше WMA (нисходящее пересечение).
    2. RSI превышает 50.
    3. Если открыта короткая позиция, при включённом Close Counter Trades она закрывается; иначе сигнал игнорируется до выхода в ноль.
    4. При выполнении условий отправляется рыночная заявка на покупку с фиксированным объёмом или рассчитанная по риску.
  • Продажа — зеркальные условия: EMA пересекает WMA снизу вверх, RSI ниже 50, и стратегия либо закрывает длинную позицию, либо пропускает сигнал.

Правила выхода

  • Базовая защитаStop Loss (points) и Take Profit (points) переводятся в абсолютные цены через шаг. Нулевые значения отключают уровни.
  • Трейлинг-стоп
    • При Trailing Stop (points) > 0 стоп подтягивается на фиксированном расстоянии от последней цены закрытия (только в сторону уменьшения риска).
    • Если параметр равен нулю, используется адаптивный режим:
      • Trailing Source = CandleExtremes — поиск ближайших минимумов/максимумов прошлых свечей с буфером не менее пяти пунктов.
      • Trailing Source = Fractals — поиск подтверждённых фракталов Билла Уильямса (по две свечи по обе стороны) с тем же буфером.
    • Перенос стопа начинается только после выхода цены за точку входа, как и в MQL4-функции SlLastBar.
  • Закрытие позиции — если в пределах свечи достигнут трейлинг или тейк-профит, позиция закрывается рыночной заявкой, внутреннее состояние сбрасывается.

Управление объёмом

  • Fixed Volume задаёт жёсткий размер заявки (аналог параметра Lot).
  • При значении 0 активируется расчёт по риску: стратегия оценивает денежный риск на единицу объёма с помощью доступного защитного расстояния (стоп-лосс или активный трейлинг) и Security.StepPrice, затем применяет долю капитала Risk %. Если и объём, и процент равны нулю, сигнал игнорируется.

Параметры

Параметр Описание Значение по умолчанию
EMA Period Период EMA по ценам открытия. 28
WMA Period Период WMA по ценам открытия. 8
RSI Period Период RSI. 14
Stop Loss (points) Защитный стоп в пунктах. 0 — без стопа. 0
Take Profit (points) Тейк-профит в пунктах. 0 — без цели. 500
Trailing Stop (points) Фиксированное расстояние трейлинга; 0 включает адаптивный режим. 70
Trailing Source Источник адаптивного трейлинга: CandleExtremes — экстремумы свечей, Fractals — подтверждённые фракталы. CandleExtremes
Close Counter Trades Закрывать ли противоположную позицию перед входом. false
Fixed Volume Фиксированный объём заявки. 0 — рассчитывать по риску. 0.1
Risk % Доля капитала при расчёте по риску, требуется ненулевое защитное расстояние. 10
Candle Type Таймфрейм, на котором работают индикаторы и сигналы. 30-минутные свечи

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

  • Перевод пунктов в цены использует Security.Step/Security.PriceStep и Security.StepPrice, поэтому заполните свойства инструмента перед тестированием.
  • Расчёт ведётся только по закрытым свечам, индикаторы получают их цены открытия — полностью соответствует логике MQL4.
  • Трейлинг-стоп держит не менее пяти пунктов запаса от текущей цены, как и функция SlLastBar в оригинале.
  • При отключенном закрытии контр-позиций стратегия не хеджируется и держит лишь один нетто-объём.
  • Python-версии для данной стратегии нет — доступна только реализация на C#.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EMA WMA RSI: Dual EMA crossover with RSI filter and ATR stops.
/// </summary>
public class EmaWmaRsiStrategy : 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 EmaWmaRsiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 8)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 28)
			.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;
	}
}