Ver no GitHub

Estratégia Good Gbbi

Esta estratégia abre uma única posição a uma hora específica do dia com base na diferença entre preços de abertura históricos.

Lógica

  • Trabalha com velas horárias por padrão.
  • Na hora TradeTime a estratégia compara o preço de abertura de T1 barras atrás com o preço de abertura de T2 barras atrás.
  • Se a abertura mais antiga for maior que a recente em DeltaShort pontos, uma posição vendida é aberta.
  • Se a abertura recente for maior que a mais antiga em DeltaLong pontos, uma posição comprada é aberta.
  • Apenas uma operação por dia é permitida. O trading é habilitado novamente após a hora ser maior que TradeTime.
  • Cada posição é protegida por níveis individuais de take-profit e stop-loss e pode ser fechada forçosamente após MaxOpenTime horas.

Parâmetros

Parâmetro Descrição
TakeProfitLong Distância de take profit em pontos para posições compradas.
StopLossLong Distância de stop loss em pontos para posições compradas.
TakeProfitShort Distância de take profit em pontos para posições vendidas.
StopLossShort Distância de stop loss em pontos para posições vendidas.
TradeTime Hora do dia em que as condições de entrada são verificadas.
T1 Número de barras para trás para o primeiro preço de abertura.
T2 Número de barras para trás para o segundo preço de abertura.
DeltaLong Diferença necessária em pontos para abrir uma posição comprada.
DeltaShort Diferença necessária em pontos para abrir uma posição vendida.
MaxOpenTime Tempo máximo de manutenção de posição em horas; 0 desativa a verificação.
CandleType Tipo de vela a processar.

Notas

A ideia original vem do consultor especializado do MetaTrader GoodG@bi. Esta portabilidade usa a API de alto nível do StockSharp e processa apenas velas concluídas. Certifique-se de que o PriceStep do instrumento esteja configurado corretamente para interpretar os valores em pontos.

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;
	}
	}
}