MA RSI EA Strategy переносит логику оригинального советника MetaTrader, который сочетает быструю скользящую среднюю и RSI малого периода. Стратегия торгует на выбранной серии свечей, анализирует сигналы только после закрытия бара и рассчитывает объём сделок на основе баланса или капитала счёта. Как только плавающий результат открытых позиций становится положительным, все позиции закрываются для фиксации прибыли.
Используемые индикаторы
Moving Average — скользящая средняя с выбором метода расчёта (простая, экспоненциальная, сглаженная, линейно-взвешенная), источника цены и сдвига.
Relative Strength Index (RSI) — быстрый осциллятор, использующий те же типы цен свечи, что и версия на MQL.
Логика торговли
На каждой завершённой свече рассчитываются значения скользящей средней и RSI согласно выбранным источникам цены.
Последнее значение скользящей может быть сдвинуто на заданное число баров, чтобы повторить поведение MQL.
Оценивается плавающий результат текущей позиции:
Если суммарный плавающий результат больше нуля, стратегия закрывает всю позицию и фиксирует прибыль.
Если результат отрицательный, открывается дополнительная сделка в ту сторону, где убыток меньше (long против short).
Если условия усреднения не выполняются, срабатывает фильтр RSI + MA:
Продажа — RSI ≥ RsiOverbought и цена открытия свечи ниже сдвинутой скользящей средней.
Покупка — RSI ≤ RsiOversold и цена открытия свечи выше сдвинутой скользящей средней.
Выход из позиции
Положительный плавающий результат вызывает CloseAllPositions, полностью закрывая позицию.
Сигналы усреднения автоматически уменьшают или переворачивают позицию, поскольку StockSharp работает с нетто-позициями.
Управление объёмом
LotSizingModes повторяет настройку OptLot из советника:
Fixed — всегда отправляется объём LotSize.
Balance — PercentOfBalance от стоимости портфеля переводится в объём через цену свечи.
Equity — PercentOfEquity от текущего капитала портфеля конвертируется в объём.
Полученный объём округляется к ближайшему 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class ma_rsi_ea_strategy(Strategy):
def __init__(self):
super(ma_rsi_ea_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ma_period = self.Param("MaPeriod", 20) \
.SetDisplay("MA Period", "EMA period for trend", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._rsi_overbought = self.Param("RsiOverbought", 65.0) \
.SetDisplay("Overbought", "RSI overbought level", "Levels")
self._rsi_oversold = self.Param("RsiOversold", 35.0) \
.SetDisplay("Oversold", "RSI oversold level", "Levels")
self._prev_rsi = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def MaPeriod(self):
return self._ma_period.Value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@property
def RsiOverbought(self):
return self._rsi_overbought.Value
@property
def RsiOversold(self):
return self._rsi_oversold.Value
def OnReseted(self):
super(ma_rsi_ea_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(ma_rsi_ea_strategy, self).OnStarted2(time)
self._prev_rsi = None
ema = ExponentialMovingAverage()
ema.Length = self.MaPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
ev = float(ema_value)
if self._prev_rsi is None:
self._prev_rsi = rv
return
ob = float(self.RsiOverbought)
os_level = float(self.RsiOversold)
# Buy: price above EMA and RSI crosses above oversold
if close > ev and self._prev_rsi <= os_level and rv > os_level and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Sell: price below EMA and RSI crosses below overbought
elif close < ev and self._prev_rsi >= ob and rv < ob and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_rsi = rv
def CreateClone(self):
return ma_rsi_ea_strategy()