Внутренний бар образуется, когда диапазон свечи полностью помещается внутри максимума и минимума предыдущей свечи. Он сигнализирует кратковременную неопределённость, которая может привести к пробою, когда цена выйдет из паттерна. Стратегия ждёт этого пробоя и затем торгует в направлении расширения.
Тестирование показывает среднегодичную доходность около 118%. Стратегию лучше запускать на фондовом рынке.
Каждая новая свеча сравнивается с предыдущей. При появлении внутреннего бара система отмечает его максимум и минимум и наблюдает за закрытием выше или ниже этих уровней. Бычий пробой открывает длинную позицию со стопом под минимумом паттерна, медвежий – короткую со стопом над максимумом.
Если цена не пробивается сразу, стратегия управляет существующими позициями, закрывая их, если следующая свеча идёт против сделки за пределы экстремумов предыдущего бара.
Детали
Условия входа: Пробой максимума или минимума внутреннего бара.
Лонг/Шорт: Оба.
Условия выхода: Цена пересекает экстремум предыдущей свечи или стоп‑лосс.
Стопы: Да, размещены за пределами паттерна.
Значения по умолчанию:
CandleType = 5 минут
StopLossPercent = 1
Стоп ставится на столько процентов за противоположной границей внутреннего бара; он и выход за экстремум предыдущей свечи проверяются по закрытию свечей.
Фильтры:
Категория: Пробой
Направление: Оба
Индикаторы: Свечные модели
Стопы: Да
Сложность: Средняя
Таймфрейм: Внутридневной
Сезонность: Нет
Нейронные сети: Нет
Дивергенция: Нет
Уровень риска: Средний
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>
/// Inside Bar Breakout strategy.
/// An inside bar's range lies within the previous candle's high and low. While flat, a close above the latest inside bar's
/// high buys and a close below its low sells. The stop lies StopLossPercent percent beyond the opposite side of the pattern,
/// and a close beyond the previous candle's extreme against the position also exits.
/// </summary>
public class InsideBarBreakoutStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private (decimal High, decimal Low)? _prevCandle;
private (decimal High, decimal Low)? _insideBar;
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 and timeframe.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public InsideBarBreakoutStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Distance of the stop beyond the pattern, in percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_prevCandle = null;
_insideBar = null;
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
_insideBar = null;
_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;
var previous = _prevCandle;
var pattern = _insideBar;
_prevCandle = (candle.HighPrice, candle.LowPrice);
var isInside = previous is { } prev && candle.HighPrice <= prev.High && candle.LowPrice >= prev.Low;
var close = candle.ClosePrice;
// A close outside the pattern uses it up; a new inside bar replaces it.
if (pattern is { } bar && (close > bar.High || close < bar.Low))
_insideBar = null;
if (isInside)
_insideBar = (candle.HighPrice, candle.LowPrice);
if (previous is not { } prior || !IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if (close <= _stopPrice || close < prior.Low)
SellMarket(Position);
}
else if (Position < 0)
{
if (close >= _stopPrice || close > prior.High)
BuyMarket(-Position);
}
else if (pattern is { } inside)
{
if (close > inside.High)
{
BuyMarket(Volume);
_stopPrice = inside.Low * (1 - StopLossPercent / 100m);
}
else if (close < inside.Low)
{
SellMarket(Volume);
_stopPrice = inside.High * (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, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class inside_bar_breakout_strategy(Strategy):
"""
Inside Bar Breakout strategy.
An inside bar's range lies within the previous candle's high and low. While flat, a close above the latest inside bar's
high buys and a close below its low sells. The stop lies StopLossPercent percent beyond the opposite side of the pattern,
and a close beyond the previous candle's extreme against the position also exits.
"""
def __init__(self):
super(inside_bar_breakout_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 1.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(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_candle = None
self._inside_bar = None
self._stop_price = Decimal(0)
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(inside_bar_breakout_strategy, self).OnReseted()
self._prev_candle = None
self._inside_bar = None
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(inside_bar_breakout_strategy, self).OnStarted2(time)
self._prev_candle = None
self._inside_bar = None
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
previous = self._prev_candle
pattern = self._inside_bar
self._prev_candle = (candle.HighPrice, candle.LowPrice)
is_inside = previous is not None and candle.HighPrice <= previous[0] and candle.LowPrice >= previous[1]
close = candle.ClosePrice
# A close outside the pattern uses it up; a new inside bar replaces it.
if pattern is not None and (close > pattern[0] or close < pattern[1]):
self._inside_bar = None
if is_inside:
self._inside_bar = (candle.HighPrice, candle.LowPrice)
if previous is None or not self.IsFormedAndOnlineAndAllowTrading():
return
percent = Decimal(self._stop_loss_percent.Value) / Decimal(100)
if self.Position > 0:
if close <= self._stop_price or close < previous[1]:
self.SellMarket(self.Position)
elif self.Position < 0:
if close >= self._stop_price or close > previous[0]:
self.BuyMarket(-self.Position)
elif pattern is not None:
if close > pattern[0]:
self.BuyMarket(self.Volume)
self._stop_price = pattern[1] * (Decimal(1) - percent)
elif close < pattern[1]:
self.SellMarket(self.Volume)
self._stop_price = pattern[0] * (Decimal(1) + percent)
def CreateClone(self):
return inside_bar_breakout_strategy()