Ver no GitHub

Fechar estratégia de agente

Visão geral

Close Agent Strategy é um assistente de gerenciamento de risco que reflete o consultor especialista MQL CloseAgent. A estratégia não abre novas negociações. Em vez disso, ele monitora as posições existentes e as fecha quando o preço ultrapassa as bandas Bollinger enquanto o Índice de Força Relativa (RSI) atinge zonas extremas. A ferramenta pode observar posições criadas manualmente ou por outras estratégias automatizadas e, opcionalmente, liquidar tudo assim que uma meta de lucro global for alcançada.

Indicadores e Dados

  • Velas: intervalo de tempo configurável (padrão: 5 minutos) usado para calcular os indicadores.
  • Bollinger Bandas (comprimento 21, largura 2): detecta variações de preço acima da banda superior ou abaixo da banda inferior.
  • Índice de Força Relativa (comprimento 13): confirma se o mercado está sobrecomprado (>70) ou sobrevendido (<30).
  • Dados de nível 1: capturam as cotações de lance e solicitação mais recentes para avaliar as condições de saída com a maior precisão possível.

Parâmetros

  • Modo Fechar (CloseMode): seleciona quais posições são elegíveis para fechamento.
    • Manual – apenas posições sem este identificador de estratégia (negociações manuais ou outros bots).
    • Auto – apenas posições abertas por esta instância de estratégia.
    • Both – monitore todas as posições no símbolo de estratégia.
  • Tipo de vela (CandleType): intervalo de tempo usado para calcular Bollinger Bandas e RSI.
  • Modo de operação (OperationMode):
    • LiveBar – use a última vela em formação; reage mais rápido, mas pode usar dados inacabados.
    • NewBar – espera o fechamento de uma vela antes de gerar um sinal (mais seguro, porém mais lento).
  • Close All Target (CloseAllTarget): se o lucro flutuante (PnL) atingir esse valor absoluto, cada posição monitorada é fechada imediatamente.
  • Ativar alertas (EnableAlerts): quando verdadeiro, registra uma mensagem sempre que uma saída é acionada, incluindo a estimativa de lucro realizado.

Lógica de negociação

  1. Assina as cotações do Nível 1 e a série de velas configurada. Bollinger Bandas e RSI são atualizadas para cada vela recebida.
  2. Mantém um buffer de histórico compacto para que a estratégia possa fazer referência à vela fechada mais recente quando OperationMode estiver definido como NewBar.
  3. Verifica se a meta de lucro global foi atingida. Quando CloseAllTarget > 0 e PnL excede o limite, todas as posições elegíveis são niveladas a preços de mercado.
  4. Para cada posição monitorada no símbolo de estratégia:
    • Posições longas: fechadas quando o melhor lance está acima da faixa superior Bollinger, RSI está acima de 70 e o preço permanece acima do preço médio de entrada.
    • Posições curtas: fechadas quando a melhor venda está abaixo da faixa inferior Bollinger, RSI está abaixo de 30 e o preço permanece abaixo do preço médio de entrada.
  5. Usa cotações de compra/venda quando disponíveis; caso contrário, volta para o fechamento da última vela processada para evitar saídas perdidas.

Notas de uso

  • A estratégia é concebida como uma camada protetora e pressupõe que as posições podem ser abertas externamente.
  • Como a lógica atua apenas nas saídas do mercado, a estratégia deve funcionar em conjunto com outros sistemas de negociação para gerir a exposição ao risco.
  • Os alertas aparecem no log do Designer quando EnableAlerts está ativo, correspondendo aos alertas originais do MQL.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Closes open positions when price stretches beyond Bollinger Bands and RSI reaches extreme values.
/// Adds SMA crossover entries for backtesting.
/// </summary>
public class CloseAgentStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _bollingerLength;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

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

	/// <summary>
	/// Length of the RSI indicator.
	/// </summary>
	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	/// <summary>
	/// Length of the Bollinger Bands indicator.
	/// </summary>
	public int BollingerLength
	{
		get => _bollingerLength.Value;
		set => _bollingerLength.Value = value;
	}

	/// <summary>
	/// RSI threshold for overbought.
	/// </summary>
	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	/// <summary>
	/// RSI threshold for oversold.
	/// </summary>
	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public CloseAgentStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for indicators", "General");

		_rsiLength = Param(nameof(RsiLength), 13)
			.SetGreaterThanZero()
			.SetDisplay("RSI Length", "RSI period", "Indicators");

		_bollingerLength = Param(nameof(BollingerLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Length", "Bollinger Bands period", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetRange(0m, 100m)
			.SetDisplay("RSI Overbought", "Overbought threshold", "Signals");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetRange(0m, 100m)
			.SetDisplay("RSI Oversold", "Oversold threshold", "Signals");
	}

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

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var smaFast = new SimpleMovingAverage { Length = 10 };
		var smaSlow = new SimpleMovingAverage { Length = 30 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(smaFast, smaSlow, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		var crossUp = _prevFast <= _prevSlow && fast > slow;
		var crossDown = _prevFast >= _prevSlow && fast < slow;

		if (crossUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}