Merovinh - 均值回归最低低点
该策略在指定周期内的最低价连续创出新低达到设定次数时买入,当最高价突破前高时平仓。
参数
- Bars — 最高/最低的计算周期。
- Number Of Lows — 触发买入所需的新低次数。
- Start Date / End Date — 交易时间范围。
- Candle Type — 使用的K线类型。
using System;
using System.Linq;
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;
public class MerovinhMeanReversionLowestLowStrategy : Strategy
{
private readonly StrategyParam<int> _bars;
private readonly StrategyParam<decimal> _breakoutPercent;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private Highest _highest;
private Lowest _lowest;
private decimal _prevLow;
private decimal _prevHigh;
private int _barsFromSignal;
public int Bars { get => _bars.Value; set => _bars.Value = value; }
public decimal BreakoutPercent { get => _breakoutPercent.Value; set => _breakoutPercent.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MerovinhMeanReversionLowestLowStrategy()
{
_bars = Param(nameof(Bars), 20)
.SetGreaterThanZero()
.SetDisplay("Bars", "Lookback for highest/lowest", "General");
_breakoutPercent = Param(nameof(BreakoutPercent), 0.4m)
.SetGreaterThanZero()
.SetDisplay("Breakout Percent", "Minimum percentage change for new high/low", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 12)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candles timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highest = null;
_lowest = null;
_prevLow = 0m;
_prevHigh = 0m;
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_highest = new Highest { Length = Bars };
_lowest = new Lowest { Length = Bars };
_prevLow = 0;
_prevHigh = 0;
_barsFromSignal = SignalCooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_highest, _lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highestHigh, decimal lowestLow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_highest.IsFormed || !_lowest.IsFormed)
{
_prevLow = lowestLow;
_prevHigh = highestHigh;
return;
}
_barsFromSignal++;
var lowBreak = _prevLow > 0m && lowestLow < _prevLow * (1m - BreakoutPercent / 100m);
var highBreak = _prevHigh > 0m && highestHigh > _prevHigh * (1m + BreakoutPercent / 100m);
if (_barsFromSignal >= SignalCooldownBars && lowBreak && Position == 0)
{
BuyMarket();
_barsFromSignal = 0;
}
if (highBreak && Position > 0)
SellMarket();
_prevLow = lowestLow;
_prevHigh = highestHigh;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class merovinh_mean_reversion_lowest_low_strategy(Strategy):
def __init__(self):
super(merovinh_mean_reversion_lowest_low_strategy, self).__init__()
self._bars = self.Param("Bars", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bars", "Lookback for highest/lowest", "General")
self._breakout_percent = self.Param("BreakoutPercent", 0.4) \
.SetGreaterThanZero() \
.SetDisplay("Breakout Percent", "Minimum percentage change for new high/low", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 12) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candles timeframe", "General")
self._prev_low = 0.0
self._prev_high = 0.0
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(merovinh_mean_reversion_lowest_low_strategy, self).OnReseted()
self._prev_low = 0.0
self._prev_high = 0.0
self._bars_from_signal = 0
def OnStarted2(self, time):
super(merovinh_mean_reversion_lowest_low_strategy, self).OnStarted2(time)
self._prev_low = 0.0
self._prev_high = 0.0
self._bars_from_signal = self._signal_cooldown_bars.Value
self._highest = Highest()
self._highest.Length = self._bars.Value
self._lowest = Lowest()
self._lowest.Length = self._bars.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._highest, self._lowest, self.OnProcess).Start()
def OnProcess(self, candle, highest_high, lowest_low):
if candle.State != CandleStates.Finished:
return
hh = float(highest_high)
ll = float(lowest_low)
if not self._highest.IsFormed or not self._lowest.IsFormed:
self._prev_low = ll
self._prev_high = hh
return
self._bars_from_signal += 1
bp = float(self._breakout_percent.Value) / 100.0
low_break = self._prev_low > 0.0 and ll < self._prev_low * (1.0 - bp)
high_break = self._prev_high > 0.0 and hh > self._prev_high * (1.0 + bp)
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal >= cd and low_break and self.Position == 0:
self.BuyMarket()
self._bars_from_signal = 0
if high_break and self.Position > 0:
self.SellMarket()
self._prev_low = ll
self._prev_high = hh
def CreateClone(self):
return merovinh_mean_reversion_lowest_low_strategy()