La estrategia OpenPendingorderAfterPositionGetStopLoss transfiere los MetaTrader 5 asesores expertos del mismo nombre al API de alto nivel de StockSharp. Evalúa continuamente la pendiente de la línea Stochastic %K en el período de tiempo seleccionado. Cuando %K baja, coloca una orden stop de venta por debajo del mercado, y cuando %K sube, coloca una orden stop de compra por encima del mercado. Cada entrada completa recibe inmediatamente una orden protectora de limitación de pérdidas y toma de ganancias. Si un stop-loss cierra la posición, la estrategia reinstala automáticamente la orden pendiente correspondiente para que la red de operaciones de ruptura se restablezca sin esperar a la siguiente vela.
Reglas comerciales
Suscríbase a las velas terminadas del período de tiempo configurado y calcule un oscilador Stochastic clásico (KPeriod, DPeriod, Slowing).
Compare el valor %K actual con el valor de hace dos barras:
%K(current) < %K(two bars ago) → envíe una parada de venta por debajo de la mejor oferta.
%K(current) > %K(two bars ago) → envíe una parada de compra por encima de la mejor demanda.
Las órdenes pendientes se compensan del mercado mediante el diferencial actual más el buffer MinStopDistancePoints definido por el usuario, que coincide con la lógica MQL original.
Una vez que se completa una orden pendiente, la estrategia envía un stop-loss protector (orden stop) y una toma de ganancias opcional (orden limitada).
Cuando se activa el stop-loss protector, la orden pendiente correspondiente se recrea inmediatamente utilizando los últimos precios del mercado.
Las órdenes de protección se cancelan automáticamente cuando la posición se cierra mediante la toma de ganancias o cuando la estrategia se detiene.
Parámetros
Nombre
Descripción
OrderVolume
Volumen comercial en lotes para cada orden pendiente.
StopLossPoints
Distancia de stop-loss en puntos de símbolo. Establezca en 0 para desactivar.
TakeProfitPoints
Distancia de toma de ganancias en puntos de símbolo. Establezca en 0 para desactivar.
MinStopDistancePoints
Colchón de precio mínimo (en puntos) agregado al diferencial antes de realizar una orden pendiente.
MaxPositions
Número máximo de posiciones simultáneas por dirección (las cuentas de compensación utilizan efectivamente 0 o 1).
KPeriod
Número de barras utilizadas para el cálculo de %K.
DPeriod
Longitud de la línea %D de suavizado.
Slowing
Factor de suavizado adicional aplicado a %K antes de la comparación.
PendingExpiry
Vida útil opcional de las órdenes stop pendientes. Las órdenes vencidas se cancelan en la siguiente vela.
CandleType
Marco de tiempo utilizado para la suscripción de velas y los cálculos de indicadores.
Notas de implementación
Toda la gestión de pedidos depende de ayudantes de alto nivel como BuyStop, SellStop, SellLimit y BuyLimit según lo requiere AGENTS.md.
Los valores del indicador se consumen directamente dentro de la devolución de llamada SubscribeCandles().BindEx(...), evitando cualquier llamada a GetValue.
La estrategia monitorea eventos MyTrade para instalar y eliminar órdenes de protección, emulando la lógica OnTradeTransaction del Asesor Experto original.
Los comentarios dentro del código están escritos en inglés y la sangría se realiza con tabulaciones, de conformidad con las pautas del repositorio.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Trades based on the slope of the Stochastic %K line.
/// Buys when %K is rising, sells when %K is falling.
/// Uses take-profit and stop-loss protection.
/// </summary>
public class OpenPendingorderAfterPositionGetStopLossStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _slowing;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<int> _signalCooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal? _lastK;
private decimal? _prevK;
private decimal _entryPrice;
private int _candlesSinceTrade;
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
public int Slowing
{
get => _slowing.Value;
set => _slowing.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public int SignalCooldownCandles
{
get => _signalCooldownCandles.Value;
set => _signalCooldownCandles.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public OpenPendingorderAfterPositionGetStopLossStrategy()
{
_kPeriod = Param(nameof(KPeriod), 22)
.SetDisplay("%K Period", "Number of bars for %K", "Indicators");
_dPeriod = Param(nameof(DPeriod), 7)
.SetDisplay("%D Period", "Smoothing period for %K", "Indicators");
_slowing = Param(nameof(Slowing), 2)
.SetDisplay("Slowing", "Additional smoothing factor", "Indicators");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop-loss as percentage of entry price", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take-profit as percentage of entry price", "Risk");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastK = null;
_prevK = null;
_entryPrice = 0;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_lastK = null;
_prevK = null;
_entryPrice = 0;
_candlesSinceTrade = SignalCooldownCandles;
var rsi = new RelativeStrengthIndex { Length = KPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal currentK)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
// Check stop-loss / take-profit on existing position
if (Position != 0 && _entryPrice > 0)
{
if (Position > 0)
{
var pnlPct = (close - _entryPrice) / _entryPrice * 100m;
if (pnlPct <= -StopLossPct || pnlPct >= TakeProfitPct)
{
SellMarket();
_entryPrice = 0;
_prevK = currentK;
_lastK = currentK;
return;
}
}
else if (Position < 0)
{
var pnlPct = (_entryPrice - close) / _entryPrice * 100m;
if (pnlPct <= -StopLossPct || pnlPct >= TakeProfitPct)
{
BuyMarket();
_entryPrice = 0;
_prevK = currentK;
_lastK = currentK;
return;
}
}
}
// Need at least 2 values to determine signal transition.
if (_lastK is not decimal prevK)
{
_lastK = currentK;
return;
}
_prevK = _lastK;
_lastK = currentK;
var crossedUp = prevK <= 45m && currentK > 45m;
var crossedDown = prevK >= 55m && currentK < 55m;
if (crossedUp && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_entryPrice = close;
_candlesSinceTrade = 0;
}
else if (crossedDown && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_entryPrice = close;
_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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class open_pendingorder_after_position_get_stop_loss_strategy(Strategy):
def __init__(self):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._k_period = self.Param("KPeriod", 22)
self._stop_loss_pct = self.Param("StopLossPct", 2.0)
self._take_profit_pct = self.Param("TakeProfitPct", 3.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = 4
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def KPeriod(self):
return self._k_period.Value
@KPeriod.setter
def KPeriod(self, value):
self._k_period.Value = value
@property
def StopLossPct(self):
return self._stop_loss_pct.Value
@StopLossPct.setter
def StopLossPct(self, value):
self._stop_loss_pct.Value = value
@property
def TakeProfitPct(self):
return self._take_profit_pct.Value
@TakeProfitPct.setter
def TakeProfitPct(self, value):
self._take_profit_pct.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).OnReseted()
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).OnStarted2(time)
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = self.SignalCooldownCandles
rsi = RelativeStrengthIndex()
rsi.Length = self.KPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, current_k):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
k_val = float(current_k)
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
# Check stop-loss / take-profit on existing position
if self.Position != 0 and self._entry_price > 0:
if self.Position > 0:
pnl_pct = (close - self._entry_price) / self._entry_price * 100.0
if pnl_pct <= -float(self.StopLossPct) or pnl_pct >= float(self.TakeProfitPct):
self.SellMarket()
self._entry_price = 0.0
self._last_k = k_val
return
elif self.Position < 0:
pnl_pct = (self._entry_price - close) / self._entry_price * 100.0
if pnl_pct <= -float(self.StopLossPct) or pnl_pct >= float(self.TakeProfitPct):
self.BuyMarket()
self._entry_price = 0.0
self._last_k = k_val
return
if self._last_k is None:
self._last_k = k_val
return
prev_k = self._last_k
self._last_k = k_val
crossed_up = prev_k <= 45.0 and k_val > 45.0
crossed_down = prev_k >= 55.0 and k_val < 55.0
if crossed_up and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._entry_price = close
self._candles_since_trade = 0
elif crossed_down and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._entry_price = close
self._candles_since_trade = 0
def CreateClone(self):
return open_pendingorder_after_position_get_stop_loss_strategy()