Ver no GitHub

Estratégia Candle Trader

Visão geral

A Estratégia Candle Trader analisa a direção (de alta ou de baixa) das últimas quatro velas completadas para identificar oportunidades de reversão de curto prazo. Opera em um único instrumento e envia ordens a mercado com níveis predefinidos de take profit e stop loss.

Lógica da estratégia

  1. Entrada comprada (direta) – última vela de alta, as duas anteriores de baixa.
  2. Entrada comprada (continuação) – última vela de alta, a anterior de baixa, as duas anteriores de alta. Esta regra só é ativada quando Continuation é true.
  3. Entrada vendida (direta) – última vela de baixa, as duas anteriores de alta.
  4. Entrada vendida (continuação) – última vela de baixa, a anterior de alta, as duas anteriores de baixa. Ativada apenas quando Continuation é true.
  5. Se Reverse Close estiver habilitado e aparecer um novo sinal oposto à posição atual, a estratégia fecha a posição existente antes de abrir uma nova.
  6. Todas as ordens são protegidas por valores fixos de take profit e stop loss medidos em passos de preço.

Parâmetros

Nome Descrição
Volume Volume da ordem para cada negociação.
TakeProfitTicks Distância do take profit em passos de preço.
StopLossTicks Distância do stop loss em passos de preço.
Continuation Habilita os padrões de continuação para entradas adicionais.
ReverseClose Fecha uma posição aberta antes de entrar na direção oposta.
CandleType Período de velas usado para análise.

Notas

  • A estratégia avalia apenas velas finalizadas.
  • Usa ordens a mercado e cancela quaisquer ordens ativas antes de enviar novas.
  • Os níveis de stop loss e take profit são aplicados via StartProtection.
  • O tamanho da posição pode ser otimizado por meio do parâmetro Volume.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on candle direction patterns.
/// Opens long or short positions depending on the directions of the last four candles.
/// </summary>
public class CandleTraderStrategy : Strategy
{
	private readonly StrategyParam<decimal> _volume;
	private readonly StrategyParam<decimal> _takeProfitTicks;
	private readonly StrategyParam<decimal> _stopLossTicks;
	private readonly StrategyParam<bool> _continuation;
	private readonly StrategyParam<bool> _reverseClose;
	private readonly StrategyParam<DataType> _candleType;

	private int _bar1Dir;
	private int _bar2Dir;
	private int _bar3Dir;
	private int _bar4Dir;

	/// <summary>
	/// Order volume.
	/// </summary>
	public decimal TradeVolume
	{
		get => _volume.Value;
		set => _volume.Value = value;
	}

	/// <summary>
	/// Take profit in price steps.
	/// </summary>
	public decimal TakeProfitTicks
	{
		get => _takeProfitTicks.Value;
		set => _takeProfitTicks.Value = value;
	}

	/// <summary>
	/// Stop loss in price steps.
	/// </summary>
	public decimal StopLossTicks
	{
		get => _stopLossTicks.Value;
		set => _stopLossTicks.Value = value;
	}

	/// <summary>
	/// Enable continuation pattern.
	/// </summary>
	public bool Continuation
	{
		get => _continuation.Value;
		set => _continuation.Value = value;
	}

	/// <summary>
	/// Close opposite position before opening a new one.
	/// </summary>
	public bool ReverseClose
	{
		get => _reverseClose.Value;
		set => _reverseClose.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public CandleTraderStrategy()
	{
		_volume = Param(nameof(TradeVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("Volume", "Order volume", "General");

		_takeProfitTicks = Param(nameof(TakeProfitTicks), 500m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit Ticks", "Take profit in price steps", "Risk Management");

		_stopLossTicks = Param(nameof(StopLossTicks), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss Ticks", "Stop loss in price steps", "Risk Management");

		_continuation = Param(nameof(Continuation), true)
			.SetDisplay("Use Continuation", "Allow continuation pattern", "Trading Logic");

		_reverseClose = Param(nameof(ReverseClose), true)
			.SetDisplay("Reverse Close", "Close opposite position on signal", "Trading Logic");

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

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_bar1Dir = _bar2Dir = _bar3Dir = _bar4Dir = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle)
	{
		// Process only finished candles
		if (candle.State != CandleStates.Finished)
			return;

		// Shift stored directions
		_bar4Dir = _bar3Dir;
		_bar3Dir = _bar2Dir;
		_bar2Dir = _bar1Dir;

		// Determine direction of current candle
		_bar1Dir = candle.ClosePrice > candle.OpenPrice ? 1 : candle.ClosePrice < candle.OpenPrice ? -1 : 0;

		// Ensure sufficient history
		if (_bar4Dir == 0)
			return;

		var buyDirect = _bar1Dir == 1 && _bar2Dir == -1 && _bar3Dir == -1;
		var buyCont = _bar1Dir == 1 && _bar2Dir == -1 && _bar3Dir == 1 && _bar4Dir == 1 && Continuation;

		var sellDirect = _bar1Dir == -1 && _bar2Dir == 1 && _bar3Dir == 1;
		var sellCont = _bar1Dir == -1 && _bar2Dir == 1 && _bar3Dir == -1 && _bar4Dir == -1 && Continuation;

		if ((buyDirect || buyCont) && Position <= 0)
		{
			if (ReverseClose && Position < 0) BuyMarket();
			BuyMarket();
		}
		else if ((sellDirect || sellCont) && Position >= 0)
		{
			if (ReverseClose && Position > 0) SellMarket();
			SellMarket();
		}
	}
}