Ver en GitHub

Estrategia Killer Sell 2.0 (C#)

Descripción general

Killer Sell 2.0 es un asesor experto de MetaTrader 4 solo-corto que cronometra entradas después de lecturas extendidas de sobrecompra y asegura beneficios cuando el impulso oscila hacia territorio de sobreventa. Este port reescribe la lógica original sobre la API de estrategia de alto nivel de StockSharp. Todo el procesamiento de indicadores es controlado por eventos a través de SubscribeCandles().BindEx(...), y las reglas de gestión de dinero están encapsuladas dentro de la clase de estrategia.

Lógica de trading

La lógica convertida sigue la cadena de señales original mientras usa el modelo de posición neta de StockSharp. Cada vela completada del marco temporal configurado ejecuta los siguientes pasos:

  1. Preparación de datos. La estrategia actualiza un MACD (12/120/9), Williams %R (período 350 para ambos filtros) y dos osciladores Estocásticos (10/1/3 para entrada, 90/7/1 para salidas). Los valores de los indicadores se consumen solo cuando la nueva barra está terminada y las entradas están completamente formadas.
  2. Filtro de entrada. Un setup corto es válido cuando se cumplen todas las condiciones siguientes:
    • Williams %R sube por encima de −10, señalando un mercado sobrecomprado.
    • La línea principal del MACD es mayor que 0.0014.
    • El %K del Estocástico de entrada cruza por debajo del nivel de entrada configurable (predeterminado 90). La detección de cruce se realiza en lecturas consecutivas de %K.
  3. Colocación de orden. Una vez que los filtros se alinean, la estrategia envía una venta de mercado usando el tamaño de lote martingala actual. Las órdenes heredan un take-profit configurado N pips más allá (predeterminado 100 pips) a través de StartProtection.
  4. Gestión de salida. Mientras existe exposición corta, la estrategia calcula la media aritmética del beneficio en pips de las tickets abiertas. Dependiendo del impulso:
    • Si el beneficio promedio está por debajo de 10 pips y Williams %R cae por debajo de −80, todos los cortos se cierran inmediatamente.
    • Si el beneficio promedio está por encima de 15 pips y el %K del Estocástico de salida cae por debajo de 12, la posición se cierra para asegurar la ganancia.

Gestión de dinero

Killer Sell 2.0 usa una escalera martingala similar al EA original. La implementación de StockSharp mantiene una lista interna de lotes cortos abiertos para imitar los cálculos por ticket de MetaTrader:

  • La primera operación usa InitialVolume (predeterminado 0.05 lotes).
  • Después de un ciclo rentable o en break-even, el volumen se restablece al tamaño de lote inicial.
  • Después de un ciclo perdedor, la siguiente orden se multiplica por MartingaleMultiplier (predeterminado ×1.2). Un límite de seguridad MaxVolume previene el crecimiento descontrolado.

El asistente también rastrea el PnL realizado en los fills para decidir si el ciclo anterior fue rentable.

Parámetros

Parámetro Descripción
CandleType Marco temporal principal que alimenta cada indicador.
EntryWprPeriod / ExitWprPeriod Longitudes de Williams %R para confirmaciones de entrada y salida.
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod Configuración del MACD.
MacdThreshold Valor mínimo de la línea principal del MACD requerido para una venta.
StochasticEntryKPeriod, StochasticEntryDPeriod, StochasticEntrySlow Parámetros del Estocástico de entrada.
EntryStochasticLevel Nivel que %K debe cruzar desde arriba para validar una señal.
StochasticExitKPeriod, StochasticExitDPeriod, StochasticExitSlow Parámetros del Estocástico de salida.
ExitStochasticLevel Límite de sobreventa verificado antes de asegurar beneficios.
EntryWprThreshold / ExitWprThreshold Umbrales de Williams %R para entradas/salidas.
LossExitPips / ProfitExitPips Límites de beneficio promedio (en pips) que controlan las salidas defensivas y objetivo.
TakeProfitPips Take-profit protector asignado a cada orden de venta.
InitialVolume Volumen del primer paso martingala.
MartingaleMultiplier Factor aplicado después de pérdidas.
MaxVolume Límite absoluto aplicado al próximo tamaño de lote.

Notas de conversión

  • MetaTrader mantiene tickets individuales; StockSharp trabaja con una posición neta. La estrategia por lo tanto almacena cada corto llenado (volumen + precio) para reproducir los cálculos de beneficio promedio y para evaluar los resets martingala.
  • El bloque "martingala" de MT4 exponía muchos modos adicionales (fijo, riesgo porcentual, 1326, Fibonacci, etc.). La configuración original usaba la rama martingala simple; solo ese comportamiento se replica aquí.
  • El stop-loss de emergencia estaba deshabilitado en el proyecto fuente. El port refleja esa configuración adjuntando solo un take-profit y gestionando otras salidas internamente.

Consejos de uso

  1. Adjunte la estrategia a un portfolio y valor, luego configure el mismo marco temporal usado en los backtests de MT4 (los valores predeterminados asumen H1).
  2. Asegúrese de que los datos de mercado entreguen velas completadas; los indicadores dependen de eventos CandleStates.Finished.
  3. Revise el apalancamiento de la cuenta y los tamaños de lote permisibles. El límite martingala predeterminado (5 lotes) debe ajustarse a los requisitos de su bróker.
  4. Realice backtests exhaustivos — las estrategias martingala amplían el riesgo cuando los mercados tienen tendencia fuerte contra el sesgo corto.
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 KillerSell20Strategy : 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 KillerSell20Strategy()
	{
		_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;
	}
}