Открыть на GitHub

Стратегия MA RSI EA

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

MA RSI EA Strategy переносит логику оригинального советника MetaTrader, который сочетает быструю скользящую среднюю и RSI малого периода. Стратегия торгует на выбранной серии свечей, анализирует сигналы только после закрытия бара и рассчитывает объём сделок на основе баланса или капитала счёта. Как только плавающий результат открытых позиций становится положительным, все позиции закрываются для фиксации прибыли.

Используемые индикаторы

  • Moving Average — скользящая средняя с выбором метода расчёта (простая, экспоненциальная, сглаженная, линейно-взвешенная), источника цены и сдвига.
  • Relative Strength Index (RSI) — быстрый осциллятор, использующий те же типы цен свечи, что и версия на MQL.

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

  1. На каждой завершённой свече рассчитываются значения скользящей средней и RSI согласно выбранным источникам цены.
  2. Последнее значение скользящей может быть сдвинуто на заданное число баров, чтобы повторить поведение MQL.
  3. Оценивается плавающий результат текущей позиции:
    • Если суммарный плавающий результат больше нуля, стратегия закрывает всю позицию и фиксирует прибыль.
    • Если результат отрицательный, открывается дополнительная сделка в ту сторону, где убыток меньше (long против short).
  4. Если условия усреднения не выполняются, срабатывает фильтр RSI + MA:
    • Продажа — RSI ≥ RsiOverbought и цена открытия свечи ниже сдвинутой скользящей средней.
    • Покупка — RSI ≤ RsiOversold и цена открытия свечи выше сдвинутой скользящей средней.

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

  • Положительный плавающий результат вызывает CloseAllPositions, полностью закрывая позицию.
  • Сигналы усреднения автоматически уменьшают или переворачивают позицию, поскольку StockSharp работает с нетто-позициями.

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

LotSizingModes повторяет настройку OptLot из советника:

  • Fixed — всегда отправляется объём LotSize.
  • BalancePercentOfBalance от стоимости портфеля переводится в объём через цену свечи.
  • EquityPercentOfEquity от текущего капитала портфеля конвертируется в объём.

Полученный объём округляется к ближайшему Security.VolumeStep (если параметр доступен у инструмента), чтобы заявки соответствовали минимальному шагу лота.

Параметры

Параметр Описание Значение по умолчанию
LotOption Режим расчёта объёма (Fixed, Balance, Equity). Balance
LotSize Фиксированный объём в режиме Fixed. 0.01
PercentOfBalance Доля баланса в режиме Balance. 2
PercentOfEquity Доля капитала в режиме Equity. 3
FastMaPeriod Период скользящей средней. 4
FastMaShift Сдвиг результата скользящей средней. 0
FastMaMethod Метод расчёта (Simple, Exponential, Smoothed, LinearWeighted). LinearWeighted
FastMaPrice Источник цены для скользящей. Open
RsiPeriod Период RSI. 4
RsiPrice Источник цены для RSI. Open
RsiOverbought Уровень перекупленности RSI. 80
RsiOversold Уровень перепроданности RSI. 20
CandleType Тип свечей, используемых стратегией. Таймфрейм 15 минут

Источники цены свечи

Перечисление CandlePriceSources соответствует списку applied price в MQL:

  • Open, High, Low, Close
  • Median = (High + Low) / 2
  • Typical = (High + Low + Close) / 3
  • Weighted = (High + Low + Close + Close) / 4

Примечания

  • Сигналы формируются только при онлайне стратегии и закрытии свечи, что повторяет запуск советника по новым барам.
  • Из-за неттинга в StockSharp усреднение фактически сокращает или разворачивает позицию вместо открытия хеджирующих ордеров.
  • Версия на Python по требованию не создавалась.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MA + RSI strategy. Uses EMA trend direction with RSI momentum confirmation.
/// </summary>
public class MaRsiEaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

	private decimal? _prevRsi;

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

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

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

	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

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

		_maPeriod = Param(nameof(MaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "EMA period for trend", "Indicators");

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

		_rsiOverbought = Param(nameof(RsiOverbought), 65m)
			.SetDisplay("Overbought", "RSI overbought level", "Levels");

		_rsiOversold = Param(nameof(RsiOversold), 35m)
			.SetDisplay("Oversold", "RSI oversold level", "Levels");
	}

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

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

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

		_prevRsi = null;

		var ema = new ExponentialMovingAverage { Length = MaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevRsi = rsiValue;
			return;
		}

		var close = candle.ClosePrice;

		if (_prevRsi == null)
		{
			_prevRsi = rsiValue;
			return;
		}

		// Buy: price above EMA and RSI crosses above oversold
		if (close > emaValue && _prevRsi.Value <= RsiOversold && rsiValue > RsiOversold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Sell: price below EMA and RSI crosses below overbought
		else if (close < emaValue && _prevRsi.Value >= RsiOverbought && rsiValue < RsiOverbought && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevRsi = rsiValue;
	}
}