Ver no GitHub

Estratégia TwoPerBar Ron

Visão geral

O especialista original MetaTrader "TwoPerBar" de Ron Thompson abre duas ordens de mercado no início de cada nova barra – uma longa e uma curta. Sempre que uma perna atinge uma meta de caixa fixa (ProfitMade * Point no código MQL), ela é fechada e, na abertura da próxima barra, qualquer exposição restante é liquidada antes que um novo par coberto seja criado. Se a barra anterior terminou com posições abertas, o tamanho do lote é duplicado até um limite de segurança (LotLimit). A porta StockSharp reproduz esse comportamento usando a estratégia de alto nível API, cotações de nível 1 para monitoramento de oferta/venda e rastreamento explícito das duas pernas protegidas.

Fluxo de trabalho de negociação

  1. Detecção de barraSubscribeCandles(CandleType) notifica a estratégia quando a série de velas configurada termina. Uma vela concluída marca o início de uma nova barra, assim como a mudança Time[0] de MetaTrader.
  2. Inspeção de lucro – Instantâneos de nível 1 (oferta/venda) são monitorados continuamente. Assim que o melhor lance ou venda se afastar o suficiente do preço de entrada registrado, a perna correspondente é fechada com SellMarket ou BuyMarket.
  3. Liquidação forçada – no início de uma nova barra, quaisquer pernas sobreviventes são fechadas no mercado. Isso reflete o loop OrderClose no script MQL.
  4. Escalonamento de volume – quando o ciclo anterior tinha negociações ativas, o tamanho do lote é multiplicado por VolumeMultiplier (padrão 2). Caso contrário, ele será redefinido para BaseVolume. O valor é normalizado em relação à etapa de volume do instrumento e fixado por MaxVolume e pela troca Security.MaxVolume.
  5. Criação de hedge – duas ordens de mercado são enviadas via BuyMarket e SellMarket. Cada perna lembra seu volume alvo, o tamanho real preenchido e o preço médio ponderado de preenchimento para que as verificações de lucro operem com informações precisas.

Gestão de risco e dinheiro

  • Martingale escalonamento de estilo – dobrar o lote após um ciclo inacabado imita o dimensionamento original semelhante ao martingale. Quando ambas as pernas fecham durante a barra, a sequência é redefinida para o lote base.
  • Metas de lucro por trechoProfitTargetPoints traduz a entrada MetaTrader ProfitMade. O valor é multiplicado pelo tamanho do ponto do instrumento e comparado com o bid/ask para decidir quando sair de uma perna.
  • Conformidade cambialNormalizeVolume garante que os lotes gerados respeitem o instrumento VolumeStep e MinVolume. Valores superdimensionados acionam uma redefinição para uma quantidade negociável.
  • Contabilidade coberta – a estratégia mantém sua própria lista de pernas, porque StockSharp carteiras normalmente expõem apenas posições líquidas. Isto permite que os ambientes que suportam contas cobertas sigam o mesmo comportamento.

Parâmetros

Nome Tipo Padrão Descrição
CandleType DataType Velas de 1 minuto Período primário que sinaliza quando uma nova barra foi iniciada.
BaseVolume decimal 0.1 Tamanho inicial do lote para um ciclo totalmente novo.
VolumeMultiplier decimal 2 Multiplicador aplicado após uma barra terminar com posições abertas.
MaxVolume decimal 12.8 Teto rígido para o tamanho do lote martingale.
ProfitTargetPoints decimal 19 Meta de lucro expressa em pontos; multiplicado pelo tamanho do ponto do instrumento e comparado às cotações de compra/venda.

Diferenças da versão MQL

  • Usa SubscribeLevel1() em vez de globais Bid/Ask tick-by-tick, mas mantém a mesma lógica com base nas melhores cotações.
  • Os pedidos são enviados por meio de métodos auxiliares StockSharp (BuyMarket, SellMarket) para que todos os arredondamentos específicos da bolsa ocorram automaticamente.
  • O tratamento de volume respeita VolumeStep, MinVolume e MaxVolume, enquanto o script original funcionava com valores duplos brutos.
  • A porta StockSharp armazena informações de trecho internamente; os conectores executados no modo de compensação ainda podem nivelar os hedges, portanto, confirme se o seu corretor oferece suporte a posições opostas.

Dicas de uso

  • Combine BaseVolume com um tamanho de lote válido para o instrumento selecionado; caso contrário, a etapa de normalização irá ignorar a negociação.
  • Mantenha ProfitTargetPoints alinhado com o tamanho do ponto do símbolo – valores excessivamente grandes raramente serão atingidos dentro de uma única barra.
  • Como a estratégia envia ordens de mercado opostas, execute-a em fontes de dados de demonstração ou contas de hedge antes de passar para ambientes de produção.
  • Anexe a estratégia a um gráfico: OnStarted adiciona velas e negociações executadas ao gráfico visual para facilitar o monitoramento.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Two Per Bar Ron strategy - momentum-based direction with EMA confirmation.
/// Buys when momentum crosses above zero and close is above EMA.
/// Sells when momentum crosses below zero and close is below EMA.
/// </summary>
public class TwoPerBarRonStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevMom;
	private bool _hasPrev;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TwoPerBarRonStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA trend filter", "Indicators");

		_momentumPeriod = Param(nameof(MomentumPeriod), 10)
			.SetDisplay("Momentum Period", "Momentum lookback", "Indicators");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevMom = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var mom = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, mom, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevMom = mom;
			_hasPrev = true;
			return;
		}

		if (_prevMom <= 0 && mom > 0 && close > ema && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevMom >= 0 && mom < 0 && close < ema && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevMom = mom;
	}
}