La Estrategia EA manual es una conversión uno a uno API de alto nivel de StockSharp del MetaTrader 4 asesor experto Manual_EA.mq4 (carpeta MQL/8159). El sistema original emite órdenes discrecionales de compra o venta cada vez que el oscilador Stochastic sale de zonas extremas. El puerto StockSharp mantiene la misma configuración del oscilador 5-3-3, neta automáticamente la exposición existente antes de realizar la siguiente orden de mercado y expone las opciones comunes de administración de dinero a través de parámetros de estrategia.
Lógica comercial
La estrategia se suscribe a la serie CandleType (predeterminado: velas de 15 minutos) e introduce los precios de cierre en un oscilador Stochastic configurado con:
Las señales se evalúan según el valor final de la línea %D (señal) de cada vela terminada. Se comparan dos lecturas consecutivas para detectar pasos a nivel.
Entrada larga: cuando el valor %D anterior era inferior o igual a OversoldLevel (predeterminado 10) y el último valor supera ese umbral. La estrategia primero neutraliza cualquier exposición corta y luego compra Volume + |short position| por orden de mercado.
Entrada corta: cuando el valor %D anterior era superior o igual a OverboughtLevel (predeterminado 90) y el último valor cae por debajo de ese umbral. Cualquier posición larga existente se cierra antes de vender Volume + |long position| en el mercado.
Las órdenes de protección se manejan a través de StartProtection. Un StopLoss y/o TakeProfit positivo (medido en puntos de precio) activa la gestión automática de riesgos. Establecer un parámetro en 0 deshabilita la protección correspondiente.
El puerto evita deliberadamente los patrones de acceso al búfer del indicador y la lógica de vela inacabada, cumpliendo con las mejores prácticas de API de alto nivel de StockSharp.
Parámetros
Parámetro
Descripción
Predeterminado
CandleType
Marco de tiempo (como DataType) utilizado para construir velas e impulsar el oscilador.
plazo de 15 minutos
KPeriod
Longitud retrospectiva de la línea Stochastic %K.
5
DPeriod
Longitud de suavizado de la línea de señal Stochastic %D.
3
Slowing
Se aplica un suavizado adicional a %K antes de que se calcule %D.
3
OverboughtLevel
Límite superior que activa entradas cortas cuando %D lo cruza hacia abajo.
90
OversoldLevel
Límite inferior que activa entradas largas cuando %D lo cruza hacia arriba.
10
StopLoss
Distancia en puntos de precio para el stop-loss protector (0 = deshabilitado).
100
TakeProfit
Distancia en puntos de precio para el objetivo de obtención de beneficios (0 = deshabilitado).
100
Volume
Tamaño del pedido enviado con cada nueva señal (lotes). Las posiciones opuestas existentes se compensan primero.
0.1
Notas adicionales
La estrategia utiliza SubscribeCandles junto con BindEx para transmitir StochasticOscillatorValue actualizaciones, lo que garantiza que los valores de los indicadores sean definitivos antes de que se tomen decisiones comerciales.
La visualización del gráfico traza automáticamente la serie de velas seleccionada, el oscilador Stochastic y las operaciones propias cuando hay un área del gráfico disponible.
Debido a que los cruces %D se evalúan en velas terminadas consecutivas, el comportamiento coincide con la implementación MQL que comparó los valores MODE_SIGNAL en los turnos 1 y 2.
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;
public class ManualEaStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRsi;
private bool _hasPrev;
private int _cooldown;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ManualEaStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 9).SetDisplay("RSI Period", "RSI lookback", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
}
private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevRsi = rsi; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevRsi = rsi;
return;
}
if (_prevRsi <= 20 && rsi > 20 && close > ema && Position == 0)
{
BuyMarket();
_cooldown = 2;
}
else if (_prevRsi >= 80 && rsi < 80 && close < ema && Position == 0)
{
SellMarket();
_cooldown = 2;
}
_prevRsi = rsi;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class manual_ea_strategy(Strategy):
def __init__(self):
super(manual_ea_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 9).SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_rsi = 0.0; self._has_prev = False; self._cooldown = 0
@property
def rsi_period(self): return self._rsi_period.Value
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(manual_ea_strategy, self).OnReseted()
self._prev_rsi = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(manual_ea_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
rsi = RelativeStrengthIndex(); rsi.Length = self.rsi_period
ema = ExponentialMovingAverage(); ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, ema, self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent)
)
def process_candle(self, candle, rsi, ema):
if candle.State != CandleStates.Finished: return
close = float(candle.ClosePrice)
rsi_val = float(rsi); ema_val = float(ema)
if not self._has_prev:
self._prev_rsi = rsi_val; self._has_prev = True; return
if self._cooldown > 0:
self._cooldown -= 1; self._prev_rsi = rsi_val; return
if self._prev_rsi <= 20 and rsi_val > 20 and close > ema_val and self.Position == 0:
self.BuyMarket(); self._cooldown = 2
elif self._prev_rsi >= 80 and rsi_val < 80 and close < ema_val and self.Position == 0:
self.SellMarket(); self._cooldown = 2
self._prev_rsi = rsi_val
def CreateClone(self): return manual_ea_strategy()