Ver no GitHub

Estratégia de Bug Psar

Visão geral

A Psar Bug Strategy é uma versão direta do MetaTrader 4 consultor especialista pSAR_bug.mq4. Ele reage ao primeiro Parabolic SAR ponto que aparece no lado oposto do preço e inverte imediatamente a posição. A implementação StockSharp assina velas, avalia apenas barras concluídas e usa o API de alto nível para colocar ordens de mercado e gerenciar stops de proteção.

Lógica de negociação

  • Calcule o Parabolic SAR com um passo de aceleração de 0.02 e uma aceleração máxima de 0.2 (ambos configuráveis).
  • Aguarde por uma vela finalizada onde o valor Parabolic SAR muda em relação ao fechamento:
    • Entrada comprada: o valor atual de SAR está abaixo do preço de fechamento, enquanto o valor anterior de SAR estava acima do preço de fechamento anterior.
    • Entrada curta: o valor atual de SAR está acima do preço de fechamento, enquanto o valor anterior de SAR estava abaixo do preço de fechamento anterior.
  • Inverta a exposição existente em cada sinal. Quando um sinal de compra aparece, qualquer posição curta aberta é achatada e substituída por uma posição longa do tamanho configurado. O oposto se aplica aos sinais de venda.
  • Aplique distâncias fixas de stop-loss e take-profit expressas em etapas de preço do instrumento. A proteção é implementada com StartProtection para que os parâmetros de risco sejam automaticamente anexados a cada nova posição.

Parâmetros

Nome Descrição
TradeVolume Volume de pedidos em lotes utilizados para lançamentos. O valor padrão é 0.1 lotes.
StopLossPoints Distância do preço de entrada ao stop loss expresso em etapas de preço. Espelha a entrada MetaTrader StopLoss.
TakeProfitPoints Distância do preço de entrada ao take-profit expresso em etapas de preço. Espelha a entrada MetaTrader TakeProfit.
SarAccelerationStep Fator de aceleração inicial do indicador Parabolic SAR.
SarAccelerationMax Fator de aceleração máximo para o cálculo Parabolic SAR.
CandleType Tipo de dados de vela (período de tempo) usado para os cálculos do indicador. Por padrão, a estratégia funciona em velas de 15 minutos.

Notas sobre a conversão

  • O especialista original faz referência direta ao símbolo e ao período de tempo do gráfico atual. A versão StockSharp expõe o tipo de vela como um parâmetro para que o período de tempo possa ser alterado sem recompilar.
  • As paradas de proteção são representadas como compensações de preços absolutos. Eles são inicializados uma vez na inicialização e gerenciados automaticamente pela plataforma.
  • O gerenciamento de pedidos depende da lógica de compensação: comprar Volume + |Position| lotes fecha a posição curta anterior e abre a nova posição longa, reproduzindo o comportamento MetaTrader de fechar antes de abrir na direção oposta.

Uso

  1. Configure os parâmetros de segurança, prazo (CandleType) e risco desejados no StockSharp Designer ou Backtester.
  2. Certifique-se de que os dados de mercado estejam disponíveis e inicie a estratégia. Os sinais são avaliados apenas em velas finalizadas.
  3. Monitore posições e desempenho por meio das ferramentas padrão StockSharp. Os gráficos exibem velas, o indicador Parabolic SAR e negociações executadas para validação visual dos sinais de reversão.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// PsarBug: EMA trend with RSI confirmation and ATR stops.
/// </summary>
public class PsarBugStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevClose;
	private decimal _entryPrice;

	public PsarBugStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_emaLength = Param(nameof(EmaLength), 30)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

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

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevClose = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevClose = 0;
		_entryPrice = 0;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, rsi, atr, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;

		if (_prevClose == 0 || atrVal <= 0)
		{
			_prevClose = close;
			return;
		}

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || close < emaVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || close > emaVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (close > emaVal && _prevClose <= emaVal && rsiVal > 50)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < emaVal && _prevClose >= emaVal && rsiVal < 50)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevClose = close;
	}
}