Открыть на GitHub

Стратегия Candle Trader

Обзор

Candle Trader анализирует направление (бычье или медвежье) последних четырех завершенных свечей, ища краткосрочные сигналы на разворот. Стратегия работает с одним инструментом, использует рыночные заявки и настраиваемые уровни тейк-профита и стоп-лосса.

Логика торговли

  1. Покупка (прямой сигнал) – последняя свеча бычья, две предыдущие – медвежьие.
  2. Покупка (подтверждение) – последняя свеча бычья, предыдущая – медвежья, ещё две ранее – бычьие. Срабатывает только при Continuation = true.
  3. Продажа (прямой сигнал) – последняя свеча медвежья, две предыдущие бычьие.
  4. Продажа (подтверждение) – последняя свеча медвежья, предыдущая – бычья, ещё две ранее – медвежьие. Срабатывает только при Continuation = true.
  5. Если включен Reverse Close, новый сигнал противоположен текущей позиции, стратегия сначала закрывает старую позицию, затем открывает новую.
  6. Каждая сделка защищается фиксированными уровнями тейк-профита и стоп-лосса ( в шагах цены ).

Параметры

Название Описание
Volume объём заявки на каждую сделку.
TakeProfitTicks дистанция до тейк-профита (в шагах цены).
StopLossTicks дистанция до стоп-лосса (в шагах цены).
Continuation включение схем продолжения.
ReverseClose закрыть ли противоположную позицию перед открытием новой.
CandleType тип свечей для анализа.

Замечания

  • учитываются только закрытые свечи;
  • перед отправкой новой заявки стратегия удаляет все активные заявки;
  • ограничения на убыток и прибыль задаются через StartProtection;
  • объём позиций допускает оптимизацию.
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();
		}
	}
}