双MACD
该策略结合两组MACD指标。慢速MACD的柱线突破零轴且快速MACD柱线同向时入场。若快速MACD反转或触发止损/止盈则平仓。
测试显示年化收益约为65%,在股票市场表现最佳。
细节
- 入场条件:慢速MACD柱线穿越零轴并得到快速MACD确认。
- 多空方向:双向。
- 退出条件:快速MACD反转或达到止损/止盈。
- 止损:有。
- 默认参数:
Macd1FastLength= 34Macd1SlowLength= 144Macd1SignalLength= 9Macd2FastLength= 100Macd2SlowLength= 200Macd2SignalLength= 50StopLossPercent= 1.0mTakeProfitPercent= 1.5mCandleType= TimeSpan.FromMinutes(15)
- 过滤器:
- 类别: 趋势
- 方向: 双向
- 指标: MACD
- 止损: 有
- 复杂度: 中等
- 时间框架: 日内 (15m)
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
using System;
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;
/// <summary>
/// Dual MACD strategy.
/// MACD 1 is the fast MACD and MACD 2 the slow one; a histogram is the MACD line minus its signal line. When the slow histogram
/// crosses above zero while the fast histogram is positive the strategy goes long, and when it crosses below zero while the fast
/// histogram is negative it goes short, reversing an opposite position. A long closes when the fast histogram turns negative and
/// a short when it turns positive. Percent stop loss and take profit protect the position.
/// </summary>
public class DualMacdStrategy : Strategy
{
private readonly StrategyParam<int> _macd1FastLength;
private readonly StrategyParam<int> _macd1SlowLength;
private readonly StrategyParam<int> _macd1SignalLength;
private readonly StrategyParam<int> _macd2FastLength;
private readonly StrategyParam<int> _macd2SlowLength;
private readonly StrategyParam<int> _macd2SignalLength;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevSlowHist;
/// <summary>
/// Fast period of the fast MACD.
/// </summary>
public int Macd1FastLength
{
get => _macd1FastLength.Value;
set => _macd1FastLength.Value = value;
}
/// <summary>
/// Slow period of the fast MACD.
/// </summary>
public int Macd1SlowLength
{
get => _macd1SlowLength.Value;
set => _macd1SlowLength.Value = value;
}
/// <summary>
/// Signal period of the fast MACD.
/// </summary>
public int Macd1SignalLength
{
get => _macd1SignalLength.Value;
set => _macd1SignalLength.Value = value;
}
/// <summary>
/// Fast period of the slow MACD.
/// </summary>
public int Macd2FastLength
{
get => _macd2FastLength.Value;
set => _macd2FastLength.Value = value;
}
/// <summary>
/// Slow period of the slow MACD.
/// </summary>
public int Macd2SlowLength
{
get => _macd2SlowLength.Value;
set => _macd2SlowLength.Value = value;
}
/// <summary>
/// Signal period of the slow MACD.
/// </summary>
public int Macd2SignalLength
{
get => _macd2SignalLength.Value;
set => _macd2SignalLength.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Take profit percentage from entry price.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public DualMacdStrategy()
{
_macd1FastLength = Param(nameof(Macd1FastLength), 34)
.SetGreaterThanZero()
.SetDisplay("MACD1 Fast", "Fast period of the fast MACD", "MACD 1");
_macd1SlowLength = Param(nameof(Macd1SlowLength), 144)
.SetGreaterThanZero()
.SetDisplay("MACD1 Slow", "Slow period of the fast MACD", "MACD 1");
_macd1SignalLength = Param(nameof(Macd1SignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD1 Signal", "Signal period of the fast MACD", "MACD 1");
_macd2FastLength = Param(nameof(Macd2FastLength), 100)
.SetGreaterThanZero()
.SetDisplay("MACD2 Fast", "Fast period of the slow MACD", "MACD 2");
_macd2SlowLength = Param(nameof(Macd2SlowLength), 200)
.SetGreaterThanZero()
.SetDisplay("MACD2 Slow", "Slow period of the slow MACD", "MACD 2");
_macd2SignalLength = Param(nameof(Macd2SignalLength), 50)
.SetGreaterThanZero()
.SetDisplay("MACD2 Signal", "Signal period of the slow MACD", "MACD 2");
_stopLossPercent = Param(nameof(StopLossPercent), 1.0m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 1.5m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_prevSlowHist = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSlowHist = null;
var fastMacd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = Macd1FastLength },
LongMa = { Length = Macd1SlowLength },
},
SignalMa = { Length = Macd1SignalLength }
};
var slowMacd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = Macd2FastLength },
LongMa = { Length = Macd2SlowLength },
},
SignalMa = { Length = Macd2SignalLength }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(fastMacd, slowMacd, ProcessCandle)
.Start();
var take = TakeProfitPercent > 0 ? new Unit(TakeProfitPercent, UnitTypes.Percent) : new Unit();
var stop = StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : new Unit();
StartProtection(take, stop, useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, fastMacd);
DrawIndicator(oscillators, slowMacd);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastValue, IIndicatorValue slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!fastValue.IsFormed || fastValue is not MovingAverageConvergenceDivergenceSignalValue { Macd: decimal fastMacd, Signal: decimal fastSignal })
return;
if (!slowValue.IsFormed || slowValue is not MovingAverageConvergenceDivergenceSignalValue { Macd: decimal slowMacd, Signal: decimal slowSignal })
return;
var fastHist = fastMacd - fastSignal;
var slowHist = slowMacd - slowSignal;
var prevSlowHist = _prevSlowHist;
_prevSlowHist = slowHist;
if (prevSlowHist is not decimal prev)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var crossUp = prev <= 0 && slowHist > 0;
var crossDown = prev >= 0 && slowHist < 0;
if (crossUp && fastHist > 0 && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (crossDown && fastHist < 0 && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && fastHist < 0)
SellMarket(Position);
else if (Position < 0 && fastHist > 0)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class dual_macd_strategy(Strategy):
"""
Dual MACD strategy.
MACD 1 is the fast MACD and MACD 2 the slow one; a histogram is the MACD line minus its signal line. When the slow histogram
crosses above zero while the fast histogram is positive the strategy goes long, and when it crosses below zero while the fast
histogram is negative it goes short, reversing an opposite position. A long closes when the fast histogram turns negative and
a short when it turns positive. Percent stop loss and take profit protect the position.
"""
def __init__(self):
super(dual_macd_strategy, self).__init__()
self._macd1_fast_length = self.Param("Macd1FastLength", 34).SetGreaterThanZero().SetDisplay("MACD1 Fast", "Fast period of the fast MACD", "MACD 1")
self._macd1_slow_length = self.Param("Macd1SlowLength", 144).SetGreaterThanZero().SetDisplay("MACD1 Slow", "Slow period of the fast MACD", "MACD 1")
self._macd1_signal_length = self.Param("Macd1SignalLength", 9).SetGreaterThanZero().SetDisplay("MACD1 Signal", "Signal period of the fast MACD", "MACD 1")
self._macd2_fast_length = self.Param("Macd2FastLength", 100).SetGreaterThanZero().SetDisplay("MACD2 Fast", "Fast period of the slow MACD", "MACD 2")
self._macd2_slow_length = self.Param("Macd2SlowLength", 200).SetGreaterThanZero().SetDisplay("MACD2 Slow", "Slow period of the slow MACD", "MACD 2")
self._macd2_signal_length = self.Param("Macd2SignalLength", 50).SetGreaterThanZero().SetDisplay("MACD2 Signal", "Signal period of the slow MACD", "MACD 2")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 1.5).SetNotNegative().SetDisplay("Take Profit %", "Take profit percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_slow_hist = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(dual_macd_strategy, self).OnReseted()
self._prev_slow_hist = None
def OnStarted2(self, time):
super(dual_macd_strategy, self).OnStarted2(time)
self._prev_slow_hist = None
fast_macd = MovingAverageConvergenceDivergenceSignal()
fast_macd.Macd.ShortMa.Length = self._macd1_fast_length.Value
fast_macd.Macd.LongMa.Length = self._macd1_slow_length.Value
fast_macd.SignalMa.Length = self._macd1_signal_length.Value
slow_macd = MovingAverageConvergenceDivergenceSignal()
slow_macd.Macd.ShortMa.Length = self._macd2_fast_length.Value
slow_macd.Macd.LongMa.Length = self._macd2_slow_length.Value
slow_macd.SignalMa.Length = self._macd2_signal_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(fast_macd, slow_macd, self._process_candle).Start()
take_percent = Decimal(self._take_profit_percent.Value)
stop_percent = Decimal(self._stop_loss_percent.Value)
take = Unit(take_percent, UnitTypes.Percent) if take_percent > 0 else Unit()
stop = Unit(stop_percent, UnitTypes.Percent) if stop_percent > 0 else Unit()
self.StartProtection(take, stop, useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, fast_macd)
self.DrawIndicator(oscillators, slow_macd)
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
if not fast_value.IsFormed or fast_value.Macd is None or fast_value.Signal is None:
return
if not slow_value.IsFormed or slow_value.Macd is None or slow_value.Signal is None:
return
fast_hist = fast_value.Macd - fast_value.Signal
slow_hist = slow_value.Macd - slow_value.Signal
prev = self._prev_slow_hist
self._prev_slow_hist = slow_hist
if prev is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
cross_up = prev <= 0 and slow_hist > 0
cross_down = prev >= 0 and slow_hist < 0
if cross_up and fast_hist > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif cross_down and fast_hist < 0 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and fast_hist < 0:
self.SellMarket(self.Position)
elif self.Position < 0 and fast_hist > 0:
self.BuyMarket(-self.Position)
def CreateClone(self):
return dual_macd_strategy()