MACD Volume BBO Reversal 策略
该策略结合成交量振荡器和 MACD 穿越零轴以及信号线关系。 当 MACD 上穿零轴且成交量振荡器为正并且 MACD 高于信号线时做多, 做空条件相反。止损设置在最近的低点或高点, 止盈按照风险收益比计算。
参数
VolumeShortLength– 成交量短期 EMA 周期 (默认 6)VolumeLongLength– 成交量长期 EMA 周期 (默认 12)MacdFastLength– MACD 快速周期 (默认 11)MacdSlowLength– MACD 慢速周期 (默认 21)MacdSignalLength– MACD 信号线周期 (默认 10)LookbackPeriod– 计算最近高低点的K线数 (默认 10)RiskReward– 止盈/止损比率 (默认 1.5)CandleType– K线时间框架 (默认 5 分钟)
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD zero-line crossover reversal.
/// </summary>
public class MacdVolumeBboReversalStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _emaFast;
private ExponentialMovingAverage _emaSlow;
private bool _prevFastAbove;
private bool _initialized;
private int _barsFromSignal;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MacdVolumeBboReversalStrategy()
{
_fastLength = Param(nameof(FastLength), 11).SetDisplay("Fast", "Fast EMA", "MACD");
_slowLength = Param(nameof(SlowLength), 21).SetDisplay("Slow", "Slow EMA", "MACD");
_cooldownBars = Param(nameof(CooldownBars), 10).SetDisplay("Cooldown", "Bars between signals", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_emaFast = null;
_emaSlow = null;
_prevFastAbove = false;
_initialized = false;
_barsFromSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFastAbove = false;
_initialized = false;
_barsFromSignal = 0;
_emaFast = new ExponentialMovingAverage { Length = FastLength };
_emaSlow = new ExponentialMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_emaFast, _emaSlow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _emaFast);
DrawIndicator(area, _emaSlow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (fast == 0 || slow == 0)
return;
var isFastAbove = fast > slow;
if (!_initialized) { _prevFastAbove = isFastAbove; _initialized = true; return; }
if (_barsFromSignal < 10000) _barsFromSignal++;
var crossUp = isFastAbove && !_prevFastAbove;
var crossDown = !isFastAbove && _prevFastAbove;
var canSignal = _barsFromSignal >= CooldownBars;
if (canSignal && crossUp && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (canSignal && crossDown && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
_prevFastAbove = isFastAbove;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class macd_volume_bbo_reversal_strategy(Strategy):
"""
MACD Volume BBO Reversal: EMA crossover with cooldown.
"""
def __init__(self):
super(macd_volume_bbo_reversal_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 11).SetDisplay("Fast", "Fast EMA", "MACD")
self._slow_length = self.Param("SlowLength", 21).SetDisplay("Slow", "Slow EMA", "MACD")
self._cooldown_bars = self.Param("CooldownBars", 10).SetDisplay("Cooldown", "Bars between signals", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_fast_above = False
self._initialized = False
self._bars_from_signal = 9999
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_volume_bbo_reversal_strategy, self).OnReseted()
self._prev_fast_above = False
self._initialized = False
self._bars_from_signal = 9999
def OnStarted2(self, time):
super(macd_volume_bbo_reversal_strategy, self).OnStarted2(time)
self._prev_fast_above = False
self._initialized = False
self._bars_from_signal = 0
fast = ExponentialMovingAverage()
fast.Length = self._fast_length.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
if fast == 0 or slow == 0:
return
is_fast_above = fast > slow
if not self._initialized:
self._prev_fast_above = is_fast_above
self._initialized = True
return
self._bars_from_signal += 1
cross_up = is_fast_above and not self._prev_fast_above
cross_down = not is_fast_above and self._prev_fast_above
can_signal = self._bars_from_signal >= self._cooldown_bars.Value
if can_signal and cross_up and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif can_signal and cross_down and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
self._prev_fast_above = is_fast_above
def CreateClone(self):
return macd_volume_bbo_reversal_strategy()