Паттерн «Три белых солдата» — классический бычий разворот, состоящий из трёх последовательных сильных свеч роста. После нисходящего тренда такая последовательность часто знаменует начало устойчивого движения вверх, поскольку покупатели подавляют продавцов.
Тестирование показывает среднегодичную доходность около 175%. Стратегию лучше запускать на фондовом рынке.
Стратегия открывает длинную позицию после формирования третьего солдата, ожидая продолжения импульса. Короткие сделки не открываются, потому что сетап чисто бычий, однако система позволяет закрывать короткие позиции, открытые другими методами.
Стопы размещаются немного ниже паттерна, чтобы защититься от ложных сигналов, а позиции закрываются, если цена закрывается ниже этого уровня.
Детали
Условия входа: совпадение паттерна
Лонг/шорт: оба направления
Условия выхода: стоп-лосс или противоположный сигнал
Стопы: да, на процентной основе
Значения по умолчанию:
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 White Soldiers strategy.
/// While flat it buys after three bullish candles in a row, each closing above the previous close.
/// The stop lies StopLossPercent below the pattern's lowest low, and a close beyond it closes the position.
/// </summary>
public class ThreeWhiteSoldiersStrategy : 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 ThreeWhiteSoldiersStrategy()
{
_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)
SellMarket(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;
BuyMarket(Volume);
_stopPrice = Math.Min(Math.Min(c0.LowPrice, c1.LowPrice), c2.LowPrice) * (1 - StopLossPercent / 100m);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class three_white_soldiers_strategy(Strategy):
"""
Three White Soldiers strategy.
While flat it buys after three bullish candles in a row, each closing above the previous close.
The stop lies StopLossPercent below the pattern's lowest low, and a close beyond it closes the position.
"""
def __init__(self):
super(three_white_soldiers_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Distance of the stop beyond the pattern, in percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._candles = []
self._stop_price = Decimal(0)
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(three_white_soldiers_strategy, self).OnReseted()
self._candles = []
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(three_white_soldiers_strategy, self).OnStarted2(time)
self._candles = []
self._stop_price = Decimal(0)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._candles.append(candle)
if len(self._candles) > 3:
self._candles.pop(0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if candle.ClosePrice <= self._stop_price:
self.SellMarket(self.Position)
return
if self.Position != 0 or len(self._candles) < 3:
return
c0, c1, c2 = self._candles
if not (c0.ClosePrice > c0.OpenPrice and c1.ClosePrice > c1.OpenPrice and c2.ClosePrice > c2.OpenPrice and c1.ClosePrice > c0.ClosePrice and c2.ClosePrice > c1.ClosePrice):
return
self.BuyMarket(self.Volume)
self._stop_price = min(c0.LowPrice, c1.LowPrice, c2.LowPrice) * (Decimal(1) - Decimal(self._stop_loss_percent.Value) / Decimal(100))
def CreateClone(self):
return three_white_soldiers_strategy()