Стратегия Average High-Low Range IBS Reversal
Эта стратегия ищет возврат к среднему после того, как цена удерживается ниже динамического порога, основанного на среднем диапазоне между максимумом и минимумом. Рассчитывается скользящая средняя диапазона бара, а также наивысший максимум и наименьший минимум за период. Порог покупки определяется как наивысший максимум минус 2.5 среднего диапазона. Когда цена остаётся ниже этого уровня заданное количество баров и показатель IBS падает ниже заданного значения в пределах торгового окна, открывается длинная позиция. Позиция закрывается, если цена закрытия превышает максимум предыдущего бара.
Подробности
- Условия входа:
- Цена находится ниже порога покупки в течение
BarsBelowThresholdбаров. - IBS <
IbsBuyThreshold. - Время между
StartTimeиEndTime.
- Цена находится ниже порога покупки в течение
- Направление: только лонг.
- Условия выхода:
- Цена закрытия выше максимума предыдущего бара.
- Стопы: нет.
- Параметры по умолчанию:
Length= 20BarsBelowThreshold= 2IbsBuyThreshold= 0.2
- Фильтры:
- Категория: Возврат к среднему
- Направление: Лонг
- Индикаторы: SMA, Highest, Lowest
- Стопы: Нет
- Сложность: Низкая
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Низкий
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>
/// Average high-low range IBS reversal strategy.
/// The buy threshold is the highest high of the last Length candles minus 2.5 times the SMA of the candle range over Length
/// candles. When the close has stayed below that threshold for BarsBelowThreshold consecutive candles and the internal bar
/// strength (close - low) / (high - low) is below IbsBuyThreshold, a long opens, provided the candle lies between StartTime and
/// EndTime. The long closes when a close exceeds the previous candle's high. Long only.
/// </summary>
public class AverageHighLowRangeIbsReversalStrategy : Strategy
{
private const decimal _rangeMultiplier = 2.5m;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<int> _barsBelowThreshold;
private readonly StrategyParam<decimal> _ibsBuyThreshold;
private readonly StrategyParam<DateTime> _startTime;
private readonly StrategyParam<DateTime> _endTime;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _rangeAverage;
private int _barsBelow;
private decimal? _prevHigh;
/// <summary>
/// Lookback of the range average and the highest high.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Consecutive closes below the threshold required for an entry.
/// </summary>
public int BarsBelowThreshold
{
get => _barsBelowThreshold.Value;
set => _barsBelowThreshold.Value = value;
}
/// <summary>
/// Maximum internal bar strength for an entry.
/// </summary>
public decimal IbsBuyThreshold
{
get => _ibsBuyThreshold.Value;
set => _ibsBuyThreshold.Value = value;
}
/// <summary>
/// Start of the trading window.
/// </summary>
public DateTime StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// End of the trading window.
/// </summary>
public DateTime EndTime
{
get => _endTime.Value;
set => _endTime.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AverageHighLowRangeIbsReversalStrategy()
{
_length = Param(nameof(Length), 20)
.SetGreaterThanZero()
.SetDisplay("Length", "Lookback of the range average and the highest high", "Indicators");
_barsBelowThreshold = Param(nameof(BarsBelowThreshold), 2)
.SetGreaterThanZero()
.SetDisplay("Bars Below Threshold", "Consecutive closes below the threshold required for an entry", "Signals");
_ibsBuyThreshold = Param(nameof(IbsBuyThreshold), 0.2m)
.SetDisplay("IBS Buy Threshold", "Maximum internal bar strength for an entry", "Signals");
_startTime = Param(nameof(StartTime), new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc))
.SetDisplay("Start Time", "Start of the trading window", "Time");
_endTime = Param(nameof(EndTime), new DateTime(2100, 1, 1, 0, 0, 0, DateTimeKind.Utc))
.SetDisplay("End Time", "End of the trading window", "Time");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
_rangeAverage = null;
_barsBelow = 0;
_prevHigh = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_barsBelow = 0;
_prevHigh = null;
var highest = new Highest { Length = Length };
_rangeAverage = new SimpleMovingAverage { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highest)
{
if (candle.State != CandleStates.Finished)
return;
var range = candle.HighPrice - candle.LowPrice;
var averageRange = _rangeAverage.Process(range, candle.ServerTime, true).ToDecimal();
var prevHigh = _prevHigh;
_prevHigh = candle.HighPrice;
if (!_rangeAverage.IsFormed)
return;
var close = candle.ClosePrice;
var threshold = highest - _rangeMultiplier * averageRange;
_barsBelow = close < threshold ? _barsBelow + 1 : 0;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if (prevHigh is decimal ph && close > ph)
SellMarket(Position);
return;
}
var ibs = range > 0m ? (close - candle.LowPrice) / range : 0.5m;
var inWindow = candle.OpenTime >= StartTime && candle.OpenTime <= EndTime;
if (Position == 0 && inWindow && _barsBelow >= BarsBelowThreshold && ibs < IbsBuyThreshold)
BuyMarket(Volume);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, DateTime, DateTimeKind
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
RANGE_MULTIPLIER = 2.5
class average_high_low_range_ibs_reversal_strategy(Strategy):
"""
Average high-low range IBS reversal strategy.
The buy threshold is the highest high of the last Length candles minus 2.5 times the SMA of the candle range over Length
candles. When the close has stayed below that threshold for BarsBelowThreshold consecutive candles and the internal bar
strength (close - low) / (high - low) is below IbsBuyThreshold, a long opens, provided the candle lies between StartTime and
EndTime. The long closes when a close exceeds the previous candle's high. Long only.
"""
def __init__(self):
super(average_high_low_range_ibs_reversal_strategy, self).__init__()
self._length = self.Param("Length", 20) \
.SetGreaterThanZero() \
.SetDisplay("Length", "Lookback of the range average and the highest high", "Indicators")
self._bars_below_threshold = self.Param("BarsBelowThreshold", 2) \
.SetGreaterThanZero() \
.SetDisplay("Bars Below Threshold", "Consecutive closes below the threshold required for an entry", "Signals")
self._ibs_buy_threshold = self.Param("IbsBuyThreshold", 0.2) \
.SetDisplay("IBS Buy Threshold", "Maximum internal bar strength for an entry", "Signals")
self._start_time = self.Param("StartTime", DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc)) \
.SetDisplay("Start Time", "Start of the trading window", "Time")
self._end_time = self.Param("EndTime", DateTime(2100, 1, 1, 0, 0, 0, DateTimeKind.Utc)) \
.SetDisplay("End Time", "End of the trading window", "Time")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._range_average = None
self._bars_below = 0
self._prev_high = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(average_high_low_range_ibs_reversal_strategy, self).OnReseted()
self._range_average = None
self._bars_below = 0
self._prev_high = None
def OnStarted2(self, time):
super(average_high_low_range_ibs_reversal_strategy, self).OnStarted2(time)
self._bars_below = 0
self._prev_high = None
highest = Highest()
highest.Length = self._length.Value
self._range_average = SimpleMovingAverage()
self._range_average.Length = self._length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, highest_value):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
candle_range = high - low
average_range = float(to_decimal(process_float(self._range_average, candle_range, candle.ServerTime, True)))
prev_high = self._prev_high
self._prev_high = high
if not self._range_average.IsFormed:
return
threshold = float(highest_value) - RANGE_MULTIPLIER * average_range
self._bars_below = self._bars_below + 1 if close < threshold else 0
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if prev_high is not None and close > prev_high:
self.SellMarket(self.Position)
return
ibs = (close - low) / candle_range if candle_range > 0 else 0.5
open_time = candle.OpenTime
in_window = open_time >= self._start_time.Value and open_time <= self._end_time.Value
if self.Position == 0 and in_window and self._bars_below >= self._bars_below_threshold.Value \
and ibs < float(self._ibs_buy_threshold.Value):
self.BuyMarket(self.Volume)
def CreateClone(self):
return average_high_low_range_ibs_reversal_strategy()