La Estrategia PricerEA recrea el comportamiento del MetaTrader 4 experto "PricerEA v1.0" usando la API de alto nivel de StockSharp.
Coloca hasta cuatro órdenes pendientes (stop de compra, stop de venta, límite de compra y límite de venta) a niveles de precios definidos manualmente. Una vez cualquiera
orden pendiente se completa, la estrategia adjunta órdenes protectoras de stop-loss y take-profit, habilitando opcionalmente un trailing stop y
ajuste de equilibrio para seguir el Asesor Experto original.
como funciona
Órdenes pendientes: la estrategia lee los niveles de precios absolutos de las entradas y envía únicamente las órdenes pendientes correspondientes.
una vez al inicio. La caducidad opcional se puede configurar en minutos.
Selección de volumen: los usuarios pueden mantener el tamaño de lote manual fijo o cambiar al modo automático desde donde se deriva el volumen.
el saldo de la cartera y el análogo del factor de riesgo MT4.
Protección: después de completar una orden de entrada, la estrategia crea órdenes de limitación de pérdidas y toma de ganancias a la distancia configurada.
(expresado en puntos de precio). Cuando se habilitan tanto el seguimiento como el punto de equilibrio, la parada sigue las condiciones originales MQL: es
se mueve solo después de que el precio cubre la distancia de equilibrio más la parada inicial.
Mantenimiento de pedidos: los pedidos pendientes se cancelan automáticamente cuando expira su vida útil o cuando se detiene la estrategia.
Precios absolutos de las correspondientes órdenes pendientes. Un valor de 0 deshabilita el pedido.
TakeProfitPoints
Distancia desde el precio de entrada hasta la orden de toma de ganancias, medida en puntos de precio (Security.PriceStep).
StopLossPoints
Distancia desde el precio de entrada hasta la orden de stop-loss, también medida en puntos de precio.
EnableTrailingStop
Habilita la lógica del trailing stop.
TrailingStepPoints
Se requiere un movimiento mínimo (en puntos) antes de mover el tope móvil.
EnableBreakEven
Habilita la regla de equilibrio que eleva el stop por encima/por debajo de la entrada después de obtener ganancias suficientes.
BreakEvenTriggerPoints
Se requiere ganancia adicional (puntos) antes de que se mueva el tope para alcanzar el punto de equilibrio.
PendingExpiryMinutes
Vida útil de las órdenes pendientes en minutos. 0 los mantiene vivos hasta que se llenen o se cancelen manualmente.
VolumeMode
Elige entre volumen manual y dimensionamiento automático.
RiskFactor
Multiplicador de riesgo utilizado por el dimensionamiento automático (refleja la entrada MQL).
ManualVolume
Tamaño de lote fijo utilizado cuando VolumeMode se establece en Manual.
Diferencias vs. la versión MT4
El cálculo automático del volumen utiliza el saldo de la cartera StockSharp y el multiplicador del contrato de valores. Diferentes corredores
Puede utilizar fórmulas distintas, por lo que el valor resultante puede diferir ligeramente de MetaTrader.
Las órdenes de protección se realizan a través de StockSharp ayudantes y respetan el paso de volumen del lugar, el volumen mínimo y máximo.
La caducidad se implementa dentro de la estrategia (MetaTrader depende de la caducidad de la orden del lado del servidor).
Notas de uso
Configura los niveles de precios antes de iniciar la estrategia. Valores iguales a cero dejan inhabilitado el orden correspondiente.
Para imitar la lógica de "Dígitos" de MT4, los parámetros basados en puntos operan en Security.PriceStep unidades.
Combine la estrategia con la cartera y las herramientas de registro de StockSharp para monitorear las órdenes pendientes y las paradas de protección.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// PricerEA strategy: Bollinger Bands mean reversion with RSI filter.
/// Buys at lower band when RSI is oversold, sells at upper band when overbought.
/// </summary>
public class PricerEaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _signalCooldownCandles;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public PricerEaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_bbPeriod = Param(nameof(BbPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for adaptive band distance", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 8)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candlesSinceTrade = SignalCooldownCandles;
var sma = new SimpleMovingAverage { Length = BbPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, rsi, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sma, decimal rsi, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var bandDistance = atr * 2.5m;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (Position > 0 && (close >= sma || rsi >= 50))
{
SellMarket();
_candlesSinceTrade = 0;
}
else if (Position < 0 && (close <= sma || rsi <= 50))
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (Position == 0 && _candlesSinceTrade >= SignalCooldownCandles && close <= sma - bandDistance && rsi < 30)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (Position == 0 && _candlesSinceTrade >= SignalCooldownCandles && close >= sma + bandDistance && rsi > 70)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
}
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 SimpleMovingAverage, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class pricer_ea_strategy(Strategy):
def __init__(self):
super(pricer_ea_strategy, self).__init__()
self._bb_period = self.Param("BbPeriod", 20) \
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period", "Indicators")
self._signal_cooldown = self.Param("SignalCooldownCandles", 8) \
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading")
self._sma = None
self._rsi = None
self._atr = None
self._candles_since_trade = 0
@property
def bb_period(self):
return self._bb_period.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(pricer_ea_strategy, self).OnReseted()
self._sma = None
self._rsi = None
self._atr = None
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(pricer_ea_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.bb_period
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.Bind(self._sma, self._rsi, self._atr, self._process_candle)
subscription.Start()
def _process_candle(self, candle, sma_value, rsi_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self._sma.IsFormed or not self._rsi.IsFormed or not self._atr.IsFormed:
return
close = float(candle.ClosePrice)
sma_val = float(sma_value)
rsi_val = float(rsi_value)
atr_val = float(atr_value)
band_distance = atr_val * 2.5
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
if self.Position > 0 and (close >= sma_val or rsi_val >= 50.0):
self.SellMarket()
self._candles_since_trade = 0
elif self.Position < 0 and (close <= sma_val or rsi_val <= 50.0):
self.BuyMarket()
self._candles_since_trade = 0
elif self.Position == 0 and self._candles_since_trade >= self.signal_cooldown and close <= sma_val - band_distance and rsi_val < 30.0:
self.BuyMarket()
self._candles_since_trade = 0
elif self.Position == 0 and self._candles_since_trade >= self.signal_cooldown and close >= sma_val + band_distance and rsi_val > 70.0:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return pricer_ea_strategy()