四柱动量反转策略
四柱动量反转策略在选定时间窗口内,当收盘价连续 BuyThreshold 根K线低于 Lookback 根之前的收盘价时做多。一旦价格突破前一根K线的最高价,仓位被平掉。
细节
- 入场: 在时间窗口内,连续
BuyThreshold根收盘价低于Lookback根之前的收盘价。 - 出场: 收盘价高于前一根K线最高价。
- 止损: 无。
- 默认值:
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()