La Estrategia MA RSI EA reproduce la lógica del asesor experto original de MetaTrader que combina una media móvil rápida con un filtro RSI de período corto. La estrategia opera en la serie de velas seleccionada, evalúa nuevas órdenes solo en barras terminadas y utiliza dimensionamiento de posición dinámico basado en el saldo o patrimonio de la cuenta. Cuando el beneficio flotante de todas las posiciones abiertas se vuelve positivo, cada posición se cierra inmediatamente para asegurar la ganancia.
Indicadores
Moving Average – método configurable (simple, exponencial, suavizado, ponderado linealmente) con selección de fuente de precio y desplazamiento opcional.
Relative Strength Index (RSI) – oscilador a corto plazo que lee de la misma familia de precios de velas que en la versión MQL.
Lógica de trading
Para cada vela completada, la estrategia calcula los valores de media móvil y RSI usando las fuentes de precio configuradas.
El valor de media móvil más reciente puede desplazarse por un número de barras definido por el usuario para coincidir con el comportamiento MQL.
Evalúa el PnL flotante de la posición neta actual:
Si el resultado flotante de todas las posiciones abiertas es mayor que cero, la estrategia cierra la posición completa para realizar la ganancia.
Si el resultado flotante es negativo, el lado con la pérdida menor (lado comprador vs. lado vendedor) se refuerza abriendo una operación adicional en esa dirección.
Si no hay señal de promediado, se aplica el filtro RSI + MA:
Entrada corto – RSI ≥ RsiOverbought y el precio de apertura de la vela está por debajo de la media móvil desplazada.
Entrada largo – RSI ≤ RsiOversold y el precio de apertura de la vela está por encima de la media móvil desplazada.
Lógica de salida
El PnL flotante positivo activa CloseAllPositions, aplanando la estrategia inmediatamente.
Las señales de reversión manual desde la lógica de promediado cierran la exposición opuesta porque StockSharp trabaja con posiciones netas.
Dimensionamiento de posición
LotSizingModes refleja la selección OptLot del EA:
Fixed – siempre envía el volumen LotSize.
Balance – convierte PercentOfBalance del valor de la cartera en volumen usando el precio de cierre de la vela.
Equity – convierte PercentOfEquity del patrimonio actual de la cartera en volumen.
El volumen calculado se redondea al Security.VolumeStep más cercano (cuando esté disponible) para que las órdenes cumplan con el tamaño de lote del instrumento.
Parámetros
Parámetro
Descripción
Valor predeterminado
LotOption
Modo de cálculo de volumen (Fixed, Balance, Equity).
Balance
LotSize
Valor de lote fijo para el modo Fixed.
0.01
PercentOfBalance
Porcentaje de saldo usado en el modo Balance.
2
PercentOfEquity
Porcentaje de patrimonio usado en el modo Equity.
3
FastMaPeriod
Longitud de la media móvil.
4
FastMaShift
Desplazamiento aplicado al resultado de la media móvil.
0
FastMaMethod
Método de cálculo de la media móvil (Simple, Exponential, Smoothed, LinearWeighted).
LinearWeighted
FastMaPrice
Fuente de precio de vela para la media móvil.
Open
RsiPeriod
Longitud del RSI.
4
RsiPrice
Fuente de precio de vela para el RSI.
Open
RsiOverbought
Nivel RSI que define un mercado sobrecomprado.
80
RsiOversold
Nivel RSI que define un mercado sobrevendido.
20
CandleType
Serie de velas usada por la estrategia.
Marco temporal de 15 minutos
Fuentes de precio de vela
CandlePriceSources replica la lista de precios aplicados de MQL:
Open, High, Low, Close
Median = (High + Low) / 2
Typical = (High + Low + Close) / 3
Weighted = (High + Low + Close + Close) / 4
Notas
Las órdenes se generan solo cuando la estrategia está en línea y la vela ha terminado, coincidiendo con el EA original que se activa en nuevas barras.
Dado que StockSharp mantiene una posición neta, las señales de promediado automáticamente reducen o invierten la exposición actual en lugar de crear posiciones de cobertura.
La implementación en Python se omite intencionalmente según se solicitó.
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()