Открыть на GitHub

Стратегия «Три чёрных ворона»

«Три чёрных ворона» — медвежий аналог паттерна «Три белых солдата», состоящий из трёх длинных красных свеч после роста. Паттерн указывает, что продавцы захватили контроль, поскольку каждая свеча закрывается возле минимума сессии.

Тестирование показывает среднегодичную доходность около 178%. Стратегию лучше запускать на фондовом рынке.

Стратегия открывает короткую позицию после появления третьего ворона, ожидая продолжения движения вниз. Её также можно использовать для выхода из лонгов, открытых другими системами, если паттерн образуется у сопротивления.

Риск ограничивается жёстким процентным стопом над максимумом паттерна, сделки закрываются, если цена закрывается выше этого уровня.

Детали

  • Условия входа: совпадение паттерна
  • Лонг/шорт: оба направления
  • Условия выхода: стоп-лосс или противоположный сигнал
  • Стопы: да, на процентной основе
  • Значения по умолчанию:
    • CandleType = 15 минут
    • StopLossPercent = 2 Стратегия только продаёт. Стоп ставится на StopLossPercent выше самого высокого максимума паттерна, и позиция закрывается, когда свеча закрывается выше него.
  • Фильтры:
    • Категория: Паттерн
    • Направление: Оба
    • Индикаторы: Свечные
    • Стопы: Да
    • Сложность: Средняя
    • Таймфрейм: Внутридневной
    • Сезонность: Нет
    • Нейронные сети: Нет
    • Дивергенция: Нет
    • Уровень риска: Средний
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;

/// <summary>
/// Three Black Crows strategy.
/// While flat it sells after three bearish candles in a row, each closing below the previous close.
/// The stop lies StopLossPercent above the pattern's highest high, and a close beyond it closes the position.
/// </summary>
public class ThreeBlackCrowsStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<ICandleMessage> _candles = [];
	private decimal _stopPrice;

	/// <summary>
	/// Distance of the stop beyond the pattern, in percent.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public ThreeBlackCrowsStrategy()
	{
		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetNotNegative()
			.SetDisplay("Stop Loss %", "Distance of the stop beyond the pattern, in percent", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_stopPrice = default;
	}

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

		_candles.Clear();
		_stopPrice = default;

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

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

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_candles.Add(candle);

		if (_candles.Count > 3)
			_candles.RemoveAt(0);

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (Position < 0)
		{
			if (candle.ClosePrice >= _stopPrice)
				BuyMarket(-Position);

			return;
		}

		if (Position != 0 || _candles.Count < 3)
			return;

		var c0 = _candles[0];
		var c1 = _candles[1];
		var c2 = _candles[2];

		if (!(c0.ClosePrice < c0.OpenPrice && c1.ClosePrice < c1.OpenPrice && c2.ClosePrice < c2.OpenPrice && c1.ClosePrice < c0.ClosePrice && c2.ClosePrice < c1.ClosePrice))
			return;

		SellMarket(Volume);
		_stopPrice = Math.Max(Math.Max(c0.HighPrice, c1.HighPrice), c2.HighPrice) * (1 + StopLossPercent / 100m);
	}
}