MACD配合1日随机指标确认的反转策略
当MACD线向上穿越信号线并且日线随机指标K线高于D线且低于80时买入。价格跌破ATR计算的止损或跌破追踪EMA止盈线时平仓。
细节
- 入场条件:
- 多头:
MACD上穿Signal 且 DailyK > DailyD 且 DailyK < 80
- 多头:
- 多空方向:仅做多
- 止损:ATR止损和EMA追踪止盈
- 默认参数:
MacdFastLength= 12MacdSlowLength= 26MacdSignalLength= 9TrailingEmaLength= 20StopLossAtrMultiplier= 3.25mTrailingActivationAtrMultiplier= 4.25mCandleType= TimeSpan.FromHours(1).TimeFrame()
- 过滤器:
- 类别:反转
- 方向:多头
- 指标:MACD, Stochastic, ATR, EMA
- 止损:是
- 复杂度:中等
- 时间框架:中期
- 季节性:否
- 神经网络:否
- 背离:否
- 风险等级:中等
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that goes long on MACD crossover confirmed by daily Stochastic.
/// Uses ATR based stop loss and trailing EMA take profit.
/// </summary>
public class MacdStochasticConfirmationReversalStrategy : Strategy
{
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<int> _trailingEmaLength;
private readonly StrategyParam<decimal> _stopLossAtr;
private readonly StrategyParam<decimal> _trailingActivationAtr;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergenceSignal _macd;
private AverageTrueRange _atr;
private ExponentialMovingAverage _ema;
private StochasticOscillator _dailyStoch;
private decimal? _dailyK;
private decimal? _dailyD;
private decimal _prevMacd;
private decimal _prevSignal;
private bool _hasPrev;
private decimal _stopLossLevel;
private decimal _activationLevel;
private bool _trailingActive;
private decimal? _takeProfitLevel;
private decimal _entryPrice;
/// <summary>
/// MACD fast EMA length.
/// </summary>
public int MacdFastLength { get => _macdFast.Value; set => _macdFast.Value = value; }
/// <summary>
/// MACD slow EMA length.
/// </summary>
public int MacdSlowLength { get => _macdSlow.Value; set => _macdSlow.Value = value; }
/// <summary>
/// MACD signal EMA length.
/// </summary>
public int MacdSignalLength { get => _macdSignal.Value; set => _macdSignal.Value = value; }
/// <summary>
/// Trailing EMA length.
/// </summary>
public int TrailingEmaLength { get => _trailingEmaLength.Value; set => _trailingEmaLength.Value = value; }
/// <summary>
/// ATR multiplier for stop loss.
/// </summary>
public decimal StopLossAtrMultiplier { get => _stopLossAtr.Value; set => _stopLossAtr.Value = value; }
/// <summary>
/// ATR multiplier to activate trailing.
/// </summary>
public decimal TrailingActivationAtrMultiplier { get => _trailingActivationAtr.Value; set => _trailingActivationAtr.Value = value; }
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public MacdStochasticConfirmationReversalStrategy()
{
_macdFast = Param(nameof(MacdFastLength), 12)
.SetDisplay("MACD Fast", "Fast EMA length", "MACD");
_macdSlow = Param(nameof(MacdSlowLength), 26)
.SetDisplay("MACD Slow", "Slow EMA length", "MACD");
_macdSignal = Param(nameof(MacdSignalLength), 9)
.SetDisplay("MACD Signal", "Signal EMA length", "MACD");
_trailingEmaLength = Param(nameof(TrailingEmaLength), 20)
.SetDisplay("Trailing EMA", "EMA length for trailing take profit", "Strategy");
_stopLossAtr = Param(nameof(StopLossAtrMultiplier), 3.25m)
.SetDisplay("ATR Stop", "ATR multiplier for stop loss", "Strategy");
_trailingActivationAtr = Param(nameof(TrailingActivationAtrMultiplier), 4.25m)
.SetDisplay("ATR Activate", "ATR multiplier to activate trailing", "Strategy");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Base candle type", "Common");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macd = null;
_atr = null;
_ema = null;
_dailyStoch = null;
_dailyK = null;
_dailyD = null;
_prevMacd = 0m;
_prevSignal = 0m;
_hasPrev = false;
_stopLossLevel = 0m;
_activationLevel = 0m;
_trailingActive = false;
_takeProfitLevel = null;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastLength },
LongMa = { Length = MacdSlowLength },
},
SignalMa = { Length = MacdSignalLength }
};
_atr = new AverageTrueRange { Length = 14 };
_ema = new EMA { Length = TrailingEmaLength };
_dailyStoch = new StochasticOscillator();
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_macd, _atr, _ema, ProcessCandle)
.Start();
var dailySubscription = SubscribeCandles(TimeSpan.FromDays(1).TimeFrame());
dailySubscription
.BindEx(_dailyStoch, ProcessDaily)
.Start();
}
private void ProcessDaily(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is decimal k && stoch.D is decimal d)
{
_dailyK = k;
_dailyD = d;
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue atrValue, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
var macd = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macd.Macd is not decimal macdLine || macd.Signal is not decimal signalLine)
return;
var atr = atrValue.ToDecimal();
var ema = emaValue.ToDecimal();
if (!_macd.IsFormed || _dailyK is null || _dailyD is null)
{
_prevMacd = macdLine;
_prevSignal = signalLine;
_hasPrev = true;
return;
}
var crossUp = _hasPrev && _prevMacd <= _prevSignal && macdLine > signalLine;
if (crossUp && _dailyK > _dailyD && _dailyK < 80m && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopLossLevel = _entryPrice - StopLossAtrMultiplier * atr;
_activationLevel = _entryPrice + TrailingActivationAtrMultiplier * atr;
_trailingActive = false;
_takeProfitLevel = null;
}
if (Position > 0)
{
if (!_trailingActive && candle.HighPrice > _activationLevel)
_trailingActive = true;
if (_trailingActive)
_takeProfitLevel = ema;
if ((_takeProfitLevel is decimal tp && candle.ClosePrice < tp) ||
candle.LowPrice <= _stopLossLevel)
{
SellMarket(Position);
_trailingActive = false;
_takeProfitLevel = null;
}
}
_prevMacd = macdLine;
_prevSignal = signalLine;
_hasPrev = true;
}
}
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 (
MovingAverageConvergenceDivergenceSignal,
AverageTrueRange,
ExponentialMovingAverage as EMA,
StochasticOscillator,
MovingAverageConvergenceDivergenceSignalValue,
StochasticOscillatorValue,
)
from StockSharp.Algo.Strategies import Strategy
class macd_stochastic_confirmation_reversal_strategy(Strategy):
def __init__(self):
super(macd_stochastic_confirmation_reversal_strategy, self).__init__()
self._macd_fast = self.Param("MacdFastLength", 12) \
.SetDisplay("MACD Fast", "Fast EMA length", "MACD")
self._macd_slow = self.Param("MacdSlowLength", 26) \
.SetDisplay("MACD Slow", "Slow EMA length", "MACD")
self._macd_signal = self.Param("MacdSignalLength", 9) \
.SetDisplay("MACD Signal", "Signal EMA length", "MACD")
self._trailing_ema_length = self.Param("TrailingEmaLength", 20) \
.SetDisplay("Trailing EMA", "EMA length for trailing take profit", "Strategy")
self._stop_loss_atr = self.Param("StopLossAtrMultiplier", 3.25) \
.SetDisplay("ATR Stop", "ATR multiplier for stop loss", "Strategy")
self._trailing_activation_atr = self.Param("TrailingActivationAtrMultiplier", 4.25) \
.SetDisplay("ATR Activate", "ATR multiplier to activate trailing", "Strategy")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Base candle type", "Common")
self._daily_k = None
self._daily_d = None
self._prev_macd = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._stop_loss_level = 0.0
self._activation_level = 0.0
self._trailing_active = False
self._take_profit_level = None
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_stochastic_confirmation_reversal_strategy, self).OnReseted()
self._daily_k = None
self._daily_d = None
self._prev_macd = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._stop_loss_level = 0.0
self._activation_level = 0.0
self._trailing_active = False
self._take_profit_level = None
self._entry_price = 0.0
def OnStarted2(self, time):
super(macd_stochastic_confirmation_reversal_strategy, self).OnStarted2(time)
self._macd_ind = MovingAverageConvergenceDivergenceSignal()
self._macd_ind.Macd.ShortMa.Length = self._macd_fast.Value
self._macd_ind.Macd.LongMa.Length = self._macd_slow.Value
self._macd_ind.SignalMa.Length = self._macd_signal.Value
self._atr = AverageTrueRange()
self._atr.Length = 14
self._ema = EMA()
self._ema.Length = self._trailing_ema_length.Value
self._daily_stoch = StochasticOscillator()
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._macd_ind, self._atr, self._ema, self._process_candle).Start()
daily_sub = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromDays(1)))
daily_sub.BindEx(self._daily_stoch, self._process_daily).Start()
def _process_daily(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
stoch = stoch_value
k = stoch.K
d = stoch.D
if k is not None and d is not None:
self._daily_k = float(k)
self._daily_d = float(d)
def _process_candle(self, candle, macd_value, atr_value, ema_value):
if candle.State != CandleStates.Finished:
return
macd = macd_value
macd_line_val = macd.Macd
signal_line_val = macd.Signal
if macd_line_val is None or signal_line_val is None:
return
macd_line = float(macd_line_val)
signal_line = float(signal_line_val)
atr = float(atr_value)
ema = float(ema_value)
if not self._macd_ind.IsFormed or self._daily_k is None or self._daily_d is None:
self._prev_macd = macd_line
self._prev_signal = signal_line
self._has_prev = True
return
cross_up = self._has_prev and self._prev_macd <= self._prev_signal and macd_line > signal_line
if cross_up and self._daily_k > self._daily_d and self._daily_k < 80.0 and self.Position <= 0:
self.BuyMarket()
self._entry_price = float(candle.ClosePrice)
self._stop_loss_level = self._entry_price - float(self._stop_loss_atr.Value) * atr
self._activation_level = self._entry_price + float(self._trailing_activation_atr.Value) * atr
self._trailing_active = False
self._take_profit_level = None
if self.Position > 0:
if not self._trailing_active and float(candle.HighPrice) > self._activation_level:
self._trailing_active = True
if self._trailing_active:
self._take_profit_level = ema
if (self._take_profit_level is not None and float(candle.ClosePrice) < self._take_profit_level) or \
float(candle.LowPrice) <= self._stop_loss_level:
self.SellMarket(self.Position)
self._trailing_active = False
self._take_profit_level = None
self._prev_macd = macd_line
self._prev_signal = signal_line
self._has_prev = True
def CreateClone(self):
return macd_stochastic_confirmation_reversal_strategy()