Стратегия Four Bar Momentum Reversal
Стратегия Four Bar Momentum Reversal открывает длинную позицию, когда закрытие было ниже значения Lookback баров назад не менее BuyThreshold подряд свечей в указанном временном окне. Позиция закрывается, когда цена закрытия превышает максимум предыдущей свечи.
Детали
- Вход:
BuyThresholdпоследовательных закрытий ниже закрытияLookbackбаров назад в пределах временного окна. - Выход: Закрытие выше максимума предыдущей свечи.
- Стопы: нет.
- Значения по умолчанию:
BuyThreshold= 4Lookback= 4StartTime= 2014-01-01EndTime= 2099-01-01
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Four Bar Momentum Reversal strategy.
/// Counts consecutive candles whose close is below the close from Lookback bars ago. Once the count reaches BuyThreshold inside the
/// StartTime..EndTime window the strategy buys, and it closes the long when the close breaks above the previous candle high.
/// </summary>
public class FourBarMomentumReversalStrategy : Strategy
{
private readonly StrategyParam<int> _buyThreshold;
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<DateTimeOffset> _startTime;
private readonly StrategyParam<DateTimeOffset> _endTime;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _closes = [];
private int _belowCount;
private decimal? _prevHigh;
/// <summary>
/// Consecutive closes below the reference close required to buy.
/// </summary>
public int BuyThreshold
{
get => _buyThreshold.Value;
set => _buyThreshold.Value = value;
}
/// <summary>
/// How many bars back the reference close is taken.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <summary>
/// Beginning of the trading window.
/// </summary>
public DateTimeOffset StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// End of the trading window.
/// </summary>
public DateTimeOffset 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 FourBarMomentumReversalStrategy()
{
_buyThreshold = Param(nameof(BuyThreshold), 4)
.SetGreaterThanZero()
.SetDisplay("Buy Threshold", "Consecutive closes below the reference close required to buy", "Strategy");
_lookback = Param(nameof(Lookback), 4)
.SetGreaterThanZero()
.SetDisplay("Lookback", "How many bars back the reference close is taken", "Strategy");
_startTime = Param(nameof(StartTime), new DateTimeOffset(2014, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("Start Time", "Beginning of the trading window", "General");
_endTime = Param(nameof(EndTime), new DateTimeOffset(2099, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("End Time", "End of the trading window", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).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();
_closes.Clear();
_belowCount = 0;
_prevHigh = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_closes.Clear();
_belowCount = 0;
_prevHigh = null;
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 close = candle.ClosePrice;
var prevHigh = _prevHigh;
_prevHigh = candle.HighPrice;
// _closes holds the previous Lookback closes, so its first element is the close from Lookback bars ago.
var ready = _closes.Count >= Lookback;
if (ready)
_belowCount = close < _closes[0] ? _belowCount + 1 : 0;
_closes.Add(close);
if (_closes.Count > Lookback)
_closes.RemoveAt(0);
if (!ready || !IsFormedAndOnlineAndAllowTrading())
return;
var inWindow = candle.OpenTime >= StartTime.UtcDateTime && candle.OpenTime <= EndTime.UtcDateTime;
if (Position == 0 && inWindow && _belowCount >= BuyThreshold)
BuyMarket(Volume);
else if (Position > 0 && prevHigh is decimal high && close > high)
SellMarket(Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, DateTimeOffset
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class four_bar_momentum_reversal_strategy(Strategy):
"""
Four Bar Momentum Reversal strategy.
Counts consecutive candles whose close is below the close from Lookback bars ago. Once the count reaches BuyThreshold inside the
StartTime..EndTime window the strategy buys, and it closes the long when the close breaks above the previous candle high.
"""
def __init__(self):
super(four_bar_momentum_reversal_strategy, self).__init__()
self._buy_threshold = self.Param("BuyThreshold", 4).SetGreaterThanZero().SetDisplay("Buy Threshold", "Consecutive closes below the reference close required to buy", "Strategy")
self._lookback = self.Param("Lookback", 4).SetGreaterThanZero().SetDisplay("Lookback", "How many bars back the reference close is taken", "Strategy")
self._start_time = self.Param("StartTime", DateTimeOffset(2014, 1, 1, 0, 0, 0, TimeSpan.Zero)).SetDisplay("Start Time", "Beginning of the trading window", "General")
self._end_time = self.Param("EndTime", DateTimeOffset(2099, 1, 1, 0, 0, 0, TimeSpan.Zero)).SetDisplay("End Time", "End of the trading window", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._closes = []
self._below_count = 0
self._prev_high = None
def OnReseted(self):
super(four_bar_momentum_reversal_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(four_bar_momentum_reversal_strategy, self).OnStarted2(time)
self._reset_state()
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
lookback = self._lookback.Value
close = candle.ClosePrice
prev_high = self._prev_high
self._prev_high = candle.HighPrice
# _closes holds the previous Lookback closes, so its first element is the close from Lookback bars ago.
ready = len(self._closes) >= lookback
if ready:
self._below_count = self._below_count + 1 if close < self._closes[0] else 0
self._closes.append(close)
while len(self._closes) > lookback:
self._closes.pop(0)
if not ready or not self.IsFormedAndOnlineAndAllowTrading():
return
in_window = self._start_time.Value.UtcDateTime <= candle.OpenTime <= self._end_time.Value.UtcDateTime
if self.Position == 0 and in_window and self._below_count >= self._buy_threshold.Value:
self.BuyMarket(self.Volume)
elif self.Position > 0 and prev_high is not None and close > prev_high:
self.SellMarket(self.Position)
def CreateClone(self):
return four_bar_momentum_reversal_strategy()