Ver no GitHub

Estratégia de Pure Price Action Fractal

Visão geral

A Estratégia de Pure Price Action é um port StockSharp do consultor especialista MetaTrader "Pure Price Action" (MQL id 24291). Combina confirmação de rompimento dos fractais de Bill Williams com um filtro de momentum calculado em um período superior e um filtro de tendência MACD de longo prazo. O algoritmo tenta capturar operações de continuação de tendência imediatamente após o mercado retestar o nível fractal mais recente.

Lógica de trading

  1. Velas de sinal – Decisões de trading são tomadas no período selecionado pelo usuário (15 minutos por padrão).
  2. Confirmação de toque fractal – Uma operação só é permitida se a vela concluída mais recente fechar dentro de um passo de preço do nível fractal confirmado mais recente (fractal superior para vendidos, fractal inferior para comprados).
  3. Padrão de corpo direcional – O tamanho absoluto do corpo da vela mais recente deve ser menor que o corpo da vela anterior, enquanto o corpo anterior deve ser maior que a vela anterior a ele. Isso imita o filtro de retração de momentum do EA original.
  4. Médias móviles – Duas médias móveis lineares ponderadas (LWMA 6 e LWMA 85 por padrão) fornecem a tendência base. Operações compradas exigem que a LWMA rápida esteja acima da lenta; vendidas exigem o oposto.
  5. Filtro de momentum – Um indicador de momentum de 14 períodos avaliado em um período superior (H1 por padrão) deve desviar do nível de equilíbrio (100) pelo menos pelo limiar configurado durante qualquer uma das três últimas leituras de momentum.
  6. Filtro MACD – Um MACD(12, 26, 9) calculado em um período superior (mensal por padrão) deve mostrar a linha principal acima da linha de sinal para comprados e abaixo para vendidos.
  7. Dimensionamento de posição – A estratégia usa a propriedade Volume da classe base Strategy. Se Volume não estiver definido, o padrão é um contrato/lote. O parâmetro MaxPosition limita o tamanho absoluto da posição.

Gerenciamento de posição

  • Proteção inicial – Distâncias opcionais fixas de stop-loss e take-profit são especificadas em passos de preço e aplicadas simetricamente a ambos os lados.
  • Trailing stop – Quando habilitado, a estratégia rastreia o preço mais alto/mais baixo atingido após a entrada pela distância configurada.
  • Bloqueio de break-even – Após o preço percorrer a distância de acionamento, o nível protetor é movido para entrada ± offset para garantir lucros.
  • Saídas manuais – A lógica avalia níveis de stop-loss, take-profit, trailing e break-even em cada vela concluída e fecha toda a posição quando qualquer condição é atendida.

Parâmetros

  • CandleType – Período de sinal principal (padrão: período de 15 minutos).
  • MomentumCandleType – Período para o indicador de momentum (padrão: período de 1 hora).
  • MacdCandleType – Período para o filtro MACD (padrão: período de 30 dias, emulando velas mensais).
  • FastPeriod / SlowPeriod – Períodos da LWMA rápida e lenta.
  • MomentumPeriod – Comprimento do indicador de momentum.
  • MomentumThreshold – Desvio absoluto mínimo do Momentum em relação a 100.
  • MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod – Configuração MACD.
  • StopLossPoints, TakeProfitPoints – Distâncias de proteção de risco em passos de preço.
  • TrailingStopPoints – Distância de trailing em passos de preço.
  • BreakEvenTriggerPoints, BreakEvenOffsetPoints – Distâncias de acionamento e lucro garantido do break-even.
  • MaxPosition – Tamanho máximo absoluto de posição tratado pela estratégia.
  • EnableTrailing, EnableBreakEven, UseStopLoss, UseTakeProfit – Controles para os blocos de gerenciamento de risco.

Notas

  • Todos os comentários no código são escritos em inglês, conforme exigido pelas diretrizes do projeto.
  • A estratégia depende exclusivamente de velas concluídas; sinais intra-barra não são processados.
  • Subscrições multi-período são usadas para emular o comportamento do consultor especialista original (velas de sinal M15, momentum H1, MACD mensal por padrão).
  • Nenhum teste automático é fornecido nesta pasta. A suíte de testes do repositório global deve permanecer intocada, conforme solicitado.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class PurePriceActionFractalStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public PurePriceActionFractalStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}