Ver en GitHub

Estrategia de Buy Sell Stop Buttons

Descripción general

  • Recrea el asesor experto "Buy Sell Stop Buttons" de MetaTrader 4 dentro de StockSharp.
  • Proporciona tres parámetros manuales (BuyRequest, SellRequest, CloseRequest) que emulan los botones del gráfico.
  • Implementa los mismos asistentes de gestión monetaria: take-profit de dinero fijo, take-profit porcentual, bloqueo de trailing de capital, break-even y trailing stops en pips.
  • Usa una suscripción de vela de un minuto puramente como latido para evaluar las reglas de gestión en barras terminadas.

Parámetros

Nombre Descripción
OrderLots Tamaño de lote base utilizado cuando se solicita una entrada manual. Refleja la entrada extern Lots (0.01 por defecto).
NumberOfTrades Número de tickets despachados por solicitud. El puerto C# consolida el volumen en una sola orden a mercado.
UseTakeProfitInMoney / TakeProfitInMoney Habilitar y configurar el objetivo monetario directo que cierra todas las operaciones al alcanzarse.
UseTakeProfitPercent / TakeProfitPercent Habilitar y configurar el objetivo de porcentaje de capital. La estrategia usa Portfolio.CurrentValue para aproximar el saldo de la cuenta.
EnableTrailing, TrailingProfitMoney, TrailingLossMoney Configurar el bloque de trailing de capital: una vez que el beneficio supera TrailingProfitMoney, se rastrea el máximo y todas las operaciones se cierran si el beneficio retrocede TrailingLossMoney.
UseBreakEven, BreakEvenTriggerPips, BreakEvenOffsetPips Mover el stop a break-even más offset después de que la posición gane la distancia de pips configurada.
StopLossPips, TakeProfitPips, TrailingStopPips Configuración de gestión de tickets convertida a distancias en pips en StockSharp.
CandleType Serie de velas que impulsa el latido (por defecto velas de un minuto).
BuyRequest, SellRequest, CloseRequest Comandos manuales que reemplazan los botones originales del gráfico. Las banderas se restablecen automáticamente después de que la acción se realiza correctamente.

Lógica de trading

  1. OnStarted se suscribe a la serie de velas configurada, establece el Volume base y habilita la protección de posición integrada.
  2. Cada vela terminada activa el siguiente flujo de trabajo:
    • Los comandos manuales se evalúan: compra y venta envían una orden a mercado con volumen OrderLots * NumberOfTrades, opcionalmente compensando una posición opuesta; las solicitudes de cierre aplanan la estrategia.
    • Los objetivos monetarios se verifican en orden: importe fijo, porcentaje de capital, luego el bloqueo de trailing de capital.
    • Los stops de break-even y pip trailing ajustan los niveles de stop internos basándose en el precio de entrada promedio.
    • Se aplican las distancias estáticas de stop-loss/take-profit.
    • La salida opcional de Bandas de Bollinger cierra posiciones largas que tocan la banda superior o posiciones cortas que tocan la banda inferior (20 períodos, ancho 2).
  3. El beneficio abierto se calcula con Security.PriceStep/Security.StepPrice cuando está disponible; de lo contrario, se usa una diferencia de precio de respaldo.

Diferencias de la versión MQL

  • MetaTrader permitía posiciones con cobertura; StockSharp consolida la exposición, por lo que las solicitudes de compra/venta primero neutralizan las posiciones opuestas.
  • Las salidas basadas en MACD mensual (Close_BUY/Close_SELL) no están presentes porque nunca fueron llamadas en el script original.
  • El escalado automático de volumen mediante MaximumRisk/DecreaseFactor se reemplaza por el parámetro explícito OrderLots. El asistente MQL dependía del historial de cuenta que no está disponible en este puerto.
  • Los ajustes de stop se conducen por velas terminadas en lugar de ticks sin procesar, siguiendo las directrices del repositorio.
  • Los valores de los indicadores se procesan a través de Bind, evitando colecciones directas o buffers de historial manuales.

Notas de uso

  • Mantener BuyRequest, SellRequest y CloseRequest bajo el grupo "Controles manuales" deshabilitados al ejecutar optimizaciones.
  • La lógica de trailing de capital y take-profit monetario requieren Security.StepPrice para traducir el beneficio en moneda. Cuando no está disponible, el respaldo usa diferencias de precio puras.
  • Break-even y trailing stops usan el tamaño de pip del instrumento inferido de MinPriceStep/PriceStep y dígitos decimales.
  • No hay traducción a Python, como se solicitó.

Pruebas

  • No se modificaron pruebas automatizadas; la estrategia se integra con la estructura de solución existente y se basa en alternancia manual de parámetros para verificación.
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 BuySellStopButtonsStrategy : 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 BuySellStopButtonsStrategy()
	{
		_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;
	}
}