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
Velas de sinal – Decisões de trading são tomadas no período selecionado pelo usuário (15 minutos por padrão).
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).
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.
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.
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.
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.
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.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class pure_price_action_fractal_strategy(Strategy):
def __init__(self):
super(pure_price_action_fractal_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(pure_price_action_fractal_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(pure_price_action_fractal_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return pure_price_action_fractal_strategy()