MACDトレンド
MACDインジケーターに基づく戦略。
テスト結果では、年間平均リターンが約64%であることが示されています。外国為替市場で最も効果を発揮します。
MACDトレンドはMACDラインとシグナルラインのクロスオーバーに反応します。強気のクロスはロングを開始し、弱気のクロスはショートを開始します。逆のクロスまたはストップが取引を終了させます。
移動平均収束拡散インジケーターはモメンタムを測定することで変化する市場にうまく適応します。このアプローチは、インジケーターが明確な強気または弱気のバイアスを維持している間、トレンドのスウィングに乗ることを目指しています。
詳細
- エントリー条件: MA、MACDに基づくシグナル。
- ロング/ショート: 両方の方向。
- エグジット条件: 逆シグナルまたはストップ。
- ストップ: はい。
- デフォルト値:
FastEmaPeriod= 12SlowEmaPeriod= 26SignalPeriod= 9StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: MA, MACD
- ストップ: はい
- 複雑さ: 基本
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on MACD indicator.
/// It enters long position when MACD crosses above signal line and short position when MACD crosses below signal line,
/// reversing on the opposite cross, and protects the position with a percent stop.
/// </summary>
public class MacdTrendStrategy : Strategy
{
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
// Side of the signal line on the previous candle; unknown until the indicator forms.
private bool? _prevIsMacdAboveSignal;
/// <summary>
/// Period for fast EMA in MACD.
/// </summary>
public int FastEmaPeriod
{
get => _fastEmaPeriod.Value;
set => _fastEmaPeriod.Value = value;
}
/// <summary>
/// Period for slow EMA in MACD.
/// </summary>
public int SlowEmaPeriod
{
get => _slowEmaPeriod.Value;
set => _slowEmaPeriod.Value = value;
}
/// <summary>
/// Period for signal line in MACD.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// Stop-loss as percentage of entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the MACD Trend strategy.
/// </summary>
public MacdTrendStrategy()
{
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Period", "Period for fast EMA in MACD", "Indicators")
.SetOptimize(8, 16, 2);
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Period", "Period for slow EMA in MACD", "Indicators")
.SetOptimize(20, 32, 2);
_signalPeriod = Param(nameof(SignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Signal Period", "Period for signal line in MACD", "Indicators")
.SetOptimize(5, 13, 2);
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
.SetOptimize(1, 3, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_prevIsMacdAboveSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create MACD indicator with signal line
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastEmaPeriod },
LongMa = { Length = SlowEmaPeriod },
},
SignalMa = { Length = SignalPeriod }
};
// Create subscription and bind indicator
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished || !macdValue.IsFormed)
return;
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
return;
var isMacdAboveSignal = macd > signal;
var wasMacdAboveSignal = _prevIsMacdAboveSignal;
_prevIsMacdAboveSignal = isMacdAboveSignal;
if (wasMacdAboveSignal is not bool wasAbove || wasAbove == isMacdAboveSignal)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (isMacdAboveSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
LogInfo($"Buy signal: MACD ({macd:F5}) crossed above Signal ({signal:F5})");
}
else if (!isMacdAboveSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
LogInfo($"Sell signal: MACD ({macd:F5}) crossed below Signal ({signal:F5})");
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class macd_trend_strategy(Strategy):
"""
MACD Trend: enters long on MACD cross above signal, short on cross below,
reversing on the opposite cross, and protects the position with a percent stop.
"""
def __init__(self):
super(macd_trend_strategy, self).__init__()
self._fast_ema = self.Param("FastEmaPeriod", 12).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_ema = self.Param("SlowEmaPeriod", 26).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._signal_period = self.Param("SignalPeriod", 9).SetGreaterThanZero().SetDisplay("Signal Period", "Signal line period", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
# Side of the signal line on the previous candle; unknown until the indicator forms.
self._prev_above = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_trend_strategy, self).OnReseted()
self._prev_above = None
def OnStarted2(self, time):
super(macd_trend_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._fast_ema.Value
macd.Macd.LongMa.Length = self._slow_ema.Value
macd.SignalMa.Length = self._signal_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, macd)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, macd_value):
if candle.State != CandleStates.Finished or not macd_value.IsFormed:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
is_above = macd_value.Macd > macd_value.Signal
was_above = self._prev_above
self._prev_above = is_above
if was_above is None or was_above == is_above:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if is_above and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif not is_above and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return macd_trend_strategy()