Ver no GitHub

Estratégia de Puria Method

Visão geral

A estratégia Puria Method é um sistema seguidor de tendência originalmente projetado para o MetaTrader. Combina três médias móveis com um filtro de tendência MACD para detectar rompimentos de Momentum. A conversão para StockSharp mantém a lógica de entrada original e adiciona controles de risco modernos, como tomada parcial de lucros e stops de trailing automatizados.

Lógica de trading

  • Calcular três médias móveis usando métodos de suavização e fontes de preço configuráveis.
  • Avaliar a diferença entre a MA de referência mais lenta e as duas MAs mais rápidas na barra anterior. Um sinal de alta requer que ambas as MAs rápidas estejam pelo menos 0,5 pontos acima da referência; um sinal de baixa requer que a referência lidere pelo mesmo margem.
  • Confirmar a direção do mercado com a linha principal do MACD. Operações longas requerem que o valor anterior do MACD seja positivo e que o histórico recente do MACD seja não-decrescente pelo número configurado de barras. Operações curtas requerem as condições opostas.
  • Quando uma entrada é acionada, a estratégia fecha uma posição oposta (se houver) e abre uma nova posição líquida na direção do sinal.

Gestão de risco

  • Stop Loss / Take Profit: Os preços são calculados a partir da entrada usando distâncias em pips e normalizados para o passo de preço do instrumento.
  • Trailing Stop: Uma vez que a posição avance além do limiar de trailing mais o passo, o stop é avançado a cada passo de trailing adicional.
  • Saída parcial: Após o preço percorrer uma distância mínima de lucro, uma fração configurável da posição é fechada para garantir ganhos.
  • Gestão de posição: O algoritmo acompanha o preço mais alto (longo) ou mais baixo (curto) após a entrada para acionar regras de stop ou lucro quando as velas ultrapassam esses níveis.

Parâmetros

Nome Descrição
StopLossPips Distância do stop loss em pips.
TakeProfitPips Distância do take profit em pips.
TrailingStopPips Distância do trailing stop em pips.
TrailingStepPips Avanço mínimo de lucro antes de atualizar o trailing stop.
MinProfitStepPips Distância mínima em pips antes de tomar lucro parcial.
MinProfitFraction Fração da posição a fechar quando o passo mínimo de lucro é atingido.
CandleType Série de velas primária usada pela estratégia.
Ma0Period, Ma1Period, Ma2Period Períodos para as três médias móveis.
Ma0Shift, Ma1Shift, Ma2Shift Deslocamentos de barra opcionais aplicados a cada média móvel.
Ma0Method, Ma1Method, Ma2Method Métodos de suavização de médias móveis (simples, exponencial, suavizado, linear ponderado).
Ma0Price, Ma1Price, Ma2Price Fontes de preço de vela para as médias móveis.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod Configuração do MACD.
MacdTrendBars Número de barras para verificar a tendência monotônica do MACD (mínimo 3).
MacdPrice Fonte de preço de vela para o cálculo do MACD.

Notas

  • A estratégia usa a barra concluída anterior para comparações de MA e MACD para evitar depender de dados de velas incompletos.
  • O tamanho do pip é derivado automaticamente do passo de preço do instrumento e da precisão decimal.
  • As funções de trailing e saída parcial requerem valores de configuração diferentes de zero; caso contrário, os blocos correspondentes permanecem inativos.
  • A versão convertida depende exclusivamente de velas concluídas (CandleStates.Finished) e deve ser usada com uma série de velas que corresponda ao período de tempo do gráfico original.
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 PuriaMethodStrategy : 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 PuriaMethodStrategy()
	{
		_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;
	}
}