Открыть на GitHub

Стратегия Expert RSI Stochastic MA

Обзор

Expert RSI Stochastic MA — перенос советника MetaTrader 5 Expert_RSI_Stochastic_MA.mq5 на инфраструктуру StockSharp. Стратегия сочетает трендовый фильтр на основе настраиваемой скользящей средней, подтверждение импульса через RSI и точку входа по двум линиям стохастика. Логика управления позицией повторяет оригинал: допускается ограниченный убыток и реализован скользящий стоп, который активируется по сигналу стохастика.

Индикаторы и параметры

Все параметры совпадают с MQL5-версией и имеют те же значения по умолчанию. Их можно оптимизировать средствами StockSharp.

Категория Параметр Значение по умолчанию Описание
Общее CandleType Таймфрейм 15 минут Тип свечей, который используется для расчётов.
Торговля TradeVolume 0.01 Базовый объём позиции (лоты/контракты).
RSI RsiPeriod 3 Количество свечей для расчёта RSI.
RSI RsiPriceType Close Источник цены для RSI (close, open, high, low, median, typical, weighted).
RSI RsiUpperLevel 80 Уровень перекупленности, инициирует короткие сигналы.
RSI RsiLowerLevel 20 Уровень перепроданности, инициирует длинные сигналы.
Стохастик StochKPeriod 6 Период линии %K.
Стохастик StochDPeriod 3 Период сглаживания линии %D.
Стохастик StochSlowing 3 Дополнительное сглаживание %K.
Стохастик StochUpperLevel 70 Уровень перекупленности для обеих линий.
Стохастик StochLowerLevel 30 Уровень перепроданности для обеих линий.
Скользящая средняя MaMethod Simple Тип средней (simple, exponential, smoothed, weighted).
Скользящая средняя MaPriceType Close Источник цены для средней.
Скользящая средняя MaPeriod 150 Длина скользящей средней.
Скользящая средняя MaShift 0 Сдвиг значения на указанное число завершённых баров.
Риск AllowLossPoints 30 Максимальный допустимый убыток в пунктах (0 — отключить).
Риск TrailingStopPoints 30 Размер стохастического трейлинг-стопа в пунктах (0 — закрывать по стохастику без трейлинга).

Пересчёт пунктов. Значения AllowLossPoints и TrailingStopPoints переводятся в абсолютную цену через Security.PriceStep. Для инструментов с 3 или 5 знаками после запятой используется множитель ×10, чтобы получить значение «пипса» как в MetaTrader.

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

Условия для длинных позиций

  1. Тренд — цена закрытия выше сдвинутой скользящей средней.
  2. Импульс — RSI ниже RsiLowerLevel.
  3. Тайминг — линии стохастика %K и %D ниже StochLowerLevel.
  4. Позиция — новые покупки разрешены только при отсутствии лонгов (Position <= 0). Объём заявки равен TradeVolume плюс величина, необходимая для переворота из шорта.

Условия для коротких позиций

  1. Тренд — цена закрытия ниже скользящей средней.
  2. Импульс — RSI выше RsiUpperLevel.
  3. Тайминг — обе линии стохастика выше StochUpperLevel.
  4. Позиция — продажи открываются только при отсутствии шортов (Position >= 0); при необходимости выполняется разворот из лонга.

Управление позицией

  • Ограничение убытков
    • AllowLossPoints = 0: отрицательная позиция удерживается до тех пор, пока стохастик не достигнет противоположной экстремальной зоны (StochUpperLevel для лонга и StochLowerLevel для шорта).
    • AllowLossPoints > 0: позиция закрывается, как только убыток превышает порог (в абсолютной цене) и стохастик возвращается в нейтральную область (stochMain > StochLowerLevel для лонга, < StochUpperLevel для шорта).
  • Трейлинг-стоп
    • При TrailingStopPoints > 0 стратегия устанавливает стоп после перехода сделки в прибыль и входа стохастика в экстремум. Стоп обновляется на каждой завершённой свече.
    • При TrailingStopPoints = 0 прибыльные сделки закрываются сразу при попадании стохастика в экстремальную зону.
  • Частота обновления — трейлинг пересчитывается один раз на свечу, что повторяет поведение оригинального советника.

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

  • Сдвиг MaShift реализован через хранение истории скользящей средней и обращение к значению N баров назад — полностью соответствует MQL5.
  • Для RSI и скользящей средней доступны все типы цен, использованные в MetaTrader. Стохастик построен на стандартном индикаторе StockSharp (режим High/Low) и использует те же периоды сглаживания.
  • Значения в пунктах зависят от PriceStep; если шаг цены неизвестен, применяется значение 1.
  • В комплекте строится график со свечами, скользящей средней, RSI и стохастиком для визуального анализа.
  • По требованию задачи Python-версия не создавалась — доступна только реализация на C#.

Рекомендации

  • Перед запуском убедитесь, что для инструмента заданы PriceStep и Decimals, чтобы корректно перевести пункты в цену.
  • При необходимости комбинируйте стратегию с StartProtection или собственными модулями управления рисками.
  • При оптимизации целесообразно совместно варьировать периоды индикаторов и параметры риска — их баланс сильно влияет на профиль доходности.
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>
/// Combines an SMA trend filter with RSI and Stochastic oscillators.
/// Enters long when price is above SMA, RSI is oversold and Stochastic is oversold.
/// Enters short when price is below SMA, RSI is overbought and Stochastic is overbought.
/// </summary>
public class ExpertRsiStochasticMaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiUpperLevel;
	private readonly StrategyParam<decimal> _rsiLowerLevel;
	private readonly StrategyParam<int> _stochKPeriod;
	private readonly StrategyParam<int> _stochDPeriod;
	private readonly StrategyParam<decimal> _stochUpperLevel;
	private readonly StrategyParam<decimal> _stochLowerLevel;
	private readonly StrategyParam<int> _maPeriod;

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

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

	public decimal RsiUpperLevel
	{
		get => _rsiUpperLevel.Value;
		set => _rsiUpperLevel.Value = value;
	}

	public decimal RsiLowerLevel
	{
		get => _rsiLowerLevel.Value;
		set => _rsiLowerLevel.Value = value;
	}

	public int StochKPeriod
	{
		get => _stochKPeriod.Value;
		set => _stochKPeriod.Value = value;
	}

	public int StochDPeriod
	{
		get => _stochDPeriod.Value;
		set => _stochDPeriod.Value = value;
	}

	public decimal StochUpperLevel
	{
		get => _stochUpperLevel.Value;
		set => _stochUpperLevel.Value = value;
	}

	public decimal StochLowerLevel
	{
		get => _stochLowerLevel.Value;
		set => _stochLowerLevel.Value = value;
	}

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	public ExpertRsiStochasticMaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame used for calculations", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Number of bars for RSI", "RSI");

		_rsiUpperLevel = Param(nameof(RsiUpperLevel), 80m)
			.SetDisplay("RSI Overbought", "Upper RSI threshold", "RSI");

		_rsiLowerLevel = Param(nameof(RsiLowerLevel), 20m)
			.SetDisplay("RSI Oversold", "Lower RSI threshold", "RSI");

		_stochKPeriod = Param(nameof(StochKPeriod), 6)
			.SetGreaterThanZero()
			.SetDisplay("%K Period", "Length of Stochastic %K", "Stochastic");

		_stochDPeriod = Param(nameof(StochDPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("%D Period", "Length of Stochastic %D", "Stochastic");

		_stochUpperLevel = Param(nameof(StochUpperLevel), 70m)
			.SetDisplay("Stoch Overbought", "Upper Stochastic threshold", "Stochastic");

		_stochLowerLevel = Param(nameof(StochLowerLevel), 30m)
			.SetDisplay("Stoch Oversold", "Lower Stochastic threshold", "Stochastic");

		_maPeriod = Param(nameof(MaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Length of moving average", "Moving Average");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var sma = new SMA { Length = MaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var stoch = new StochasticOscillator();
		stoch.K.Length = StochKPeriod;
		stoch.D.Length = StochDPeriod;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(sma, rsi, stoch, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue rsiValue, IIndicatorValue stochValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!smaValue.IsFinal || !rsiValue.IsFinal || !stochValue.IsFinal)
			return;

		var sma = smaValue.IsEmpty ? (decimal?)null : smaValue.GetValue<decimal>();
		var rsiVal = rsiValue.IsEmpty ? (decimal?)null : rsiValue.GetValue<decimal>();

		if (sma is not decimal smaDecimal || rsiVal is not decimal rsiDecimal)
			return;

		var stoch = (StochasticOscillatorValue)stochValue;
		if (stoch.K is not decimal stochK || stoch.D is not decimal stochD)
			return;

		var price = candle.ClosePrice;

		// Long entry: price above SMA, RSI oversold, Stochastic oversold
		if (price > smaDecimal && rsiDecimal < RsiLowerLevel && stochK < StochLowerLevel && stochD < StochLowerLevel)
		{
			if (Position <= 0)
				BuyMarket();
		}
		// Short entry: price below SMA, RSI overbought, Stochastic overbought
		else if (price < smaDecimal && rsiDecimal > RsiUpperLevel && stochK > StochUpperLevel && stochD > StochUpperLevel)
		{
			if (Position >= 0)
				SellMarket();
		}
		// Exit long: Stochastic overbought
		else if (Position > 0 && stochK > StochUpperLevel)
		{
			SellMarket();
		}
		// Exit short: Stochastic oversold
		else if (Position < 0 && stochK < StochLowerLevel)
		{
			BuyMarket();
		}
	}
}