Esta estrategia reproduce el comportamiento del experto MetaTrader original stochSell. Escucha un único flujo de velas y espera una confirmación estocástica triple combinada con un filtro de volatilidad antes de enviar una orden de venta de mercado inicial. Inmediatamente después de la entrada corta, despliega una escalera de sell stops pendientes para escalar en el movimiento si el precio continúa bajando.
Lógica de trading
Filtro de volatilidad – un Rango Verdadero Promedio (ATR) con longitud configurable debe mantenerse por debajo del umbral especificado.
Confirmación estocástica lenta – el oscilador estocástico de mayor período debe mantenerse por debajo del nivel de sobrevendido a largo plazo antes de que se permita cualquier operación.
Confirmación de cruce – tanto el oscilador estocástico medio como el rápido deben cruzar hacia abajo a través del disparador de sobrevendido durante la misma vela finalizada.
Verificación de posición – las nuevas entradas se colocan solo cuando la estrategia no tiene órdenes activas y la posición es plana.
Una vez que se cumplen todas las condiciones, la estrategia envía una orden de venta de mercado usando el volumen configurado e inmediatamente programa un conjunto de órdenes de venta stop según la configuración de la cuadrícula. Las órdenes pendientes son opcionales y pueden desactivarse estableciendo el conteo de órdenes de cuadrícula en cero.
Reglas de salida
Objetivo de beneficio – cuando la cesta corta acumula el beneficio deseado en pips (calculado desde el precio de entrada ponderado por volumen), la estrategia recompra toda la posición y elimina cada orden pendiente restante.
Stop manual – las órdenes de cuadrícula respetan un tiempo de vida configurable. Cuando una orden de stop expira sin ejecutarse, se cancela automáticamente.
Cierre completo – cualquier operación de compra que devuelva la posición a cero borra las estadísticas de entrada internas y cancela la cuadrícula pendiente.
Gestión de la cuadrícula
Las órdenes pendientes se colocan por debajo del precio de referencia usando el offset inicial y el paso expresado en pips.
Cada orden pendiente usa el multiplicador de volumen de la cuadrícula, permitiendo que el tamaño de la cesta difiera de la entrada de mercado inicial.
La expiración (en minutos) se aplica a cada orden pendiente; cero desactiva el tiempo de espera.
Parámetros
Nombre
Descripción
CandleType
Marco temporal principal para cada indicador y decisión de trading.
AtrPeriod / AtrThreshold
Filtro de volatilidad que controla cuándo la estrategia puede operar.
FastKPeriod, FastDPeriod, FastSlowing
Configuración del oscilador estocástico rápido.
MediumKPeriod, MediumDPeriod, MediumSlowing
Configuración del oscilador estocástico medio.
SlowKPeriod, SlowDPeriod, SlowSlowing
Configuración del oscilador estocástico lento.
OversoldLevel
Nivel que los valores estocásticos rápido y medio deben cruzar hacia abajo.
LongTermOversoldLevel
Límite superior para el estocástico lento durante la entrada.
ProfitTargetPips
Beneficio neto en pips requerido para cerrar la cesta corta.
GridOrdersCount
Número de sell stops pendientes creados después de la entrada.
GridStartOffsetPips
Offset en pips entre el precio de entrada y la primera orden pendiente.
GridStepPips
Distancia en pips entre órdenes pendientes consecutivas.
GridVolume
Volumen aplicado a cada orden pendiente.
GridExpirationMinutes
Tiempo de vida de las órdenes pendientes en minutos.
MarketVolume
Volumen usado para la venta de mercado inicial.
Notas
Los valores de los indicadores se procesan a través de la API BindEx de alto nivel y solo las velas finalizadas desencadenan decisiones de trading.
La lógica de seguimiento de posición mantiene un precio de entrada ponderado por volumen para traducir el objetivo de beneficio bruto en pips.
Para deshabilitar el escalado, simplemente establezca el conteo de órdenes de cuadrícula en cero; la estrategia seguirá dependiendo de la confirmación estocástica y el filtro ATR para operaciones de disparo único.
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 StochSellStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public StochSellStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class stoch_sell_strategy(Strategy):
def __init__(self):
super(stoch_sell_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(stoch_sell_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(stoch_sell_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return stoch_sell_strategy()