Ver en GitHub

Estrategia Good Gbbi

Esta estrategia abre una única posición a una hora específica del día basándose en la diferencia entre precios de apertura históricos.

Lógica

  • Trabaja con velas horarias por defecto.
  • A la hora TradeTime la estrategia compara el precio de apertura de hace T1 barras con el precio de apertura de hace T2 barras.
  • Si la apertura más antigua es mayor que la reciente en DeltaShort puntos, se abre una posición corta.
  • Si la apertura reciente es mayor que la más antigua en DeltaLong puntos, se abre una posición larga.
  • Solo se permite un trade por día. El trading se habilita nuevamente cuando la hora supera TradeTime.
  • Cada posición está protegida por niveles individuales de take-profit y stop-loss y puede cerrarse forzosamente después de MaxOpenTime horas.

Parámetros

Parámetro Descripción
TakeProfitLong Distancia de take profit en puntos para posiciones largas.
StopLossLong Distancia de stop loss en puntos para posiciones largas.
TakeProfitShort Distancia de take profit en puntos para posiciones cortas.
StopLossShort Distancia de stop loss en puntos para posiciones cortas.
TradeTime Hora del día en que se verifican las condiciones de entrada.
T1 Número de barras hacia atrás para el primer precio de apertura.
T2 Número de barras hacia atrás para el segundo precio de apertura.
DeltaLong Diferencia requerida en puntos para abrir una posición larga.
DeltaShort Diferencia requerida en puntos para abrir una posición corta.
MaxOpenTime Tiempo máximo de mantenimiento de posición en horas; 0 desactiva la verificación.
CandleType Tipo de vela a procesar.

Notas

La idea original proviene del asesor experto de MetaTrader GoodG@bi. Este puerto utiliza la API de alto nivel de StockSharp y procesa únicamente velas terminadas. Asegúrese de que el PriceStep del instrumento esté correctamente configurado para interpretar los valores en puntos.

using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Good Gbbi strategy.
/// Opens a position at specified hour based on historical open price
/// differences.
/// </summary>
public class GoodGbbiStrategy : Strategy {
	private readonly StrategyParam<int> _takeProfitLong;
	private readonly StrategyParam<int> _stopLossLong;
	private readonly StrategyParam<int> _takeProfitShort;
	private readonly StrategyParam<int> _stopLossShort;
	private readonly StrategyParam<int> _t1;
	private readonly StrategyParam<int> _t2;
	private readonly StrategyParam<int> _deltaLong;
	private readonly StrategyParam<int> _deltaShort;
	private readonly StrategyParam<int> _maxOpenTime;
	private readonly StrategyParam<DataType> _candleType;

	private readonly decimal[] _openPrices = new decimal[7];
	private int _candlesCount;
	private DateTimeOffset _entryTime;
	private decimal _entryPrice;

	/// <summary>
	/// Take profit in points for long positions.
	/// </summary>
	public int TakeProfitLong {
	get => _takeProfitLong.Value;
	set => _takeProfitLong.Value = value;
	}

	/// <summary>
	/// Stop loss in points for long positions.
	/// </summary>
	public int StopLossLong {
	get => _stopLossLong.Value;
	set => _stopLossLong.Value = value;
	}

	/// <summary>
	/// Take profit in points for short positions.
	/// </summary>
	public int TakeProfitShort {
	get => _takeProfitShort.Value;
	set => _takeProfitShort.Value = value;
	}

	/// <summary>
	/// Stop loss in points for short positions.
	/// </summary>
	public int StopLossShort {
	get => _stopLossShort.Value;
	set => _stopLossShort.Value = value;
	}

	/// <summary>
	/// Bar offset for first open price.
	/// </summary>
	public int T1 {
	get => _t1.Value;
	set => _t1.Value = value;
	}

	/// <summary>
	/// Bar offset for second open price.
	/// </summary>
	public int T2 {
	get => _t2.Value;
	set => _t2.Value = value;
	}

	/// <summary>
	/// Required open difference for long entries in points.
	/// </summary>
	public int DeltaLong {
	get => _deltaLong.Value;
	set => _deltaLong.Value = value;
	}

	/// <summary>
	/// Required open difference for short entries in points.
	/// </summary>
	public int DeltaShort {
	get => _deltaShort.Value;
	set => _deltaShort.Value = value;
	}

	/// <summary>
	/// Maximum position lifetime in hours.
	/// </summary>
	public int MaxOpenTime {
	get => _maxOpenTime.Value;
	set => _maxOpenTime.Value = value;
	}

	/// <summary>
	/// Candle type to process.
	/// </summary>
	public DataType CandleType {
	get => _candleType.Value;
	set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="GoodGbbiStrategy"/>.
	/// </summary>
	public GoodGbbiStrategy() {
	_takeProfitLong =
		Param(nameof(TakeProfitLong), 39)
		.SetGreaterThanZero()
		.SetDisplay("Take Profit Long",
				"Profit target for long positions in points",
				"Risk Management");

	_stopLossLong =
		Param(nameof(StopLossLong), 147)
		.SetGreaterThanZero()
		.SetDisplay("Stop Loss Long",
				"Stop loss for long positions in points",
				"Risk Management");

	_takeProfitShort =
		Param(nameof(TakeProfitShort), 15)
		.SetGreaterThanZero()
		.SetDisplay("Take Profit Short",
				"Profit target for short positions in points",
				"Risk Management");

	_stopLossShort =
		Param(nameof(StopLossShort), 6000)
		.SetGreaterThanZero()
		.SetDisplay("Stop Loss Short",
				"Stop loss for short positions in points",
				"Risk Management");

	_t1 = Param(nameof(T1), 6)
		  .SetGreaterThanZero()
		  .SetDisplay("T1", "First open price offset", "Logic");

	_t2 = Param(nameof(T2), 2)
		  .SetGreaterThanZero()
		  .SetDisplay("T2", "Second open price offset", "Logic");

	_deltaLong =
		Param(nameof(DeltaLong), 6)
		.SetGreaterThanZero()
		.SetDisplay("Delta Long",
				"Open difference for long entries in points",
				"Logic");

	_deltaShort =
		Param(nameof(DeltaShort), 21)
		.SetGreaterThanZero()
		.SetDisplay("Delta Short",
				"Open difference for short entries in points",
				"Logic");

	_maxOpenTime =
		Param(nameof(MaxOpenTime), 504)
		.SetDisplay("Max Open Time",
				"Maximum holding time in hours (0 - unlimited)",
				"Risk Management");

	_candleType =
		Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)>
	GetWorkingSecurities() {
	return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted() {
	base.OnReseted();
	Array.Clear(_openPrices, 0, _openPrices.Length);
	_candlesCount = 0;
	_entryPrice = 0m;
	_entryTime = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time) {
	var subscription = SubscribeCandles(CandleType);
	subscription.Bind(ProcessCandle).Start();

	base.OnStarted2(time);
	}

	private void ProcessCandle(ICandleMessage candle) {
	if (candle.State != CandleStates.Finished)
		return;

	// store open price in circular buffer
	_openPrices[_candlesCount % _openPrices.Length] = candle.OpenPrice;
	_candlesCount++;

	var step = Security.PriceStep ?? 1m;

	// manage existing position and protection
	if (Position > 0) {
		var tp = _entryPrice + TakeProfitLong * step;
		var sl = _entryPrice - StopLossLong * step;
		var expired =
		MaxOpenTime > 0 &&
		(candle.OpenTime - _entryTime).TotalHours >= MaxOpenTime;
		if (candle.ClosePrice >= tp || candle.ClosePrice <= sl || expired)
		SellMarket();
		return;
	} else if (Position < 0) {
		var tp = _entryPrice - TakeProfitShort * step;
		var sl = _entryPrice + StopLossShort * step;
		var expired =
		MaxOpenTime > 0 &&
		(candle.OpenTime - _entryTime).TotalHours >= MaxOpenTime;
		if (candle.ClosePrice <= tp || candle.ClosePrice >= sl || expired)
		BuyMarket();
		return;
	}

	// ensure enough history is collected
	if (_candlesCount <= Math.Max(T1, T2))
		return;

	var openT1 = _openPrices[(_candlesCount - 1 - T1 + _openPrices.Length) %
				 _openPrices.Length];
	var openT2 = _openPrices[(_candlesCount - 1 - T2 + _openPrices.Length) %
				 _openPrices.Length];

	if (openT1 - openT2 > DeltaShort * step && Position >= 0) {
		SellMarket();
		_entryPrice = candle.ClosePrice;
		_entryTime = candle.OpenTime;
	} else if (openT2 - openT1 > DeltaLong * step && Position <= 0) {
		BuyMarket();
		_entryPrice = candle.ClosePrice;
		_entryTime = candle.OpenTime;
	}
	}
}