Macd パターン トレーダー ダブルトップ戦略
概要
MetaTrader 4 エキスパート アドバイザー MacdPatternTraderv04cb のポート。この戦略は、構成可能な MACD 主回線をスキャンします。 弱気のダブルトップパターンと強気のダブルボトムパターン。 MACD の間に 2 回目のスイングが最初のスイングを超えられなかった場合 プラスまたはマイナスのトリガーレベルを超えたままの場合、戦略は市場ポジションを次の方向に開きます。 予想される逆転。プロテクト注文は、元の固定の 100 ピップのストップロスと 300 ピップのテイクプロフィット距離を再現します。
取引ルール
- 選択したローソク足シリーズ (デフォルト: 30 分の時間枠) をサブスクライブし、次の値を使用して MACD メインラインを計算します。 構成された高速、低速、および信号期間 (デフォルト: 5、13、および 1)。
- 完了した最後の 3 つの MACD 値を追跡します。 MACD が
TriggerLevelを上回れば、弱気のセットアップは準備完了です。 局所的な高値を形成してから下落します。セットアップは、次の MACD の高値が以前に保存された高値より低いかどうかを検証します。 MACD がまだトリガーを上回っている間は High になります。その瞬間に市場の売りが送信されます。 - 同じロジックをゼロ以下でミラーリングします。 MACD が
-TriggerLevelを下回ったままになると、谷と次の谷が形成されます 前回よりも高い場合、この戦略は市場での買いを開始します。 - MACD の線が
[-TriggerLevel, TriggerLevel]内で交差するたびに、保存されている山と谷をリセットします。 範囲。これは、勢いが弱まったときにパターン検索をキャンセルする元の EA の動作と一致します。 - ポジションサイズは設定された
TradeVolumeから始まります。方向を切り替えるとき、この戦略は十分なボリュームを追加します。 新しい取引を確立する前に、反対のエクスポージャをフラット化します。 - 開始時に
StartProtectionを 1 回呼び出して、100 ピップのストップロスと 300 ピップのテイクプロフィットの両方が 再起動後もプラットフォーム。
パラメーター
| 名前 | 説明 |
|---|---|
FastPeriod |
MACD によって使用される高速な EMA の長さ。 |
SlowPeriod |
MACD が使用する低速の EMA の長さ。 |
SignalPeriod |
MACD の信号線の平滑化長。 |
TriggerLevel |
ダブルトップ/ダブルボトム検出を有効にするには絶対 MACD レベルが必要です。 |
StopLossPips |
ピップ単位での保護停止の距離 (デフォルトは 100)。 |
TakeProfitPips |
利益確定の距離 (pips 単位) (デフォルトは 300)。 |
TradeVolume |
新しいポジションの基本注文量。 |
CandleType |
インジケーターの計算に使用されるローソク足シリーズ。 |
注意事項
- ストップロスとテイクプロフィットは、ピップスから商品ステップに変換されてから、
StartProtection、動作は元の MQL4 エキスパートと同一に保たれます。 - リポジトリの要求に応じて、C# ソース コード内のすべてのインジケーターと取引コメントは英語で書かれています。 ガイドライン。
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>
/// Port of the MetaTrader 4 expert advisor MacdPatternTraderv04cb.
/// Detects bearish and bullish double-top/double-bottom patterns on the MACD main line.
/// The second swing needs to be weaker than the first one before a market order is sent.
/// Fixed protective orders replicate the original 100/300 pip stop-loss and take-profit distances.
/// </summary>
public class MacdPatternTraderDoubleTopStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<decimal> _triggerLevel;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergenceSignal _macd = null!;
private decimal? _previousMacd;
private decimal? _previousMacd2;
private decimal? _firstPeak;
private decimal? _firstTrough;
private bool _sellPatternArmed;
private bool _buyPatternArmed;
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public MacdPatternTraderDoubleTopStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast moving average length used by MACD", "MACD")
.SetOptimize(3, 15, 1);
_slowPeriod = Param(nameof(SlowPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow moving average length used by MACD", "MACD")
.SetOptimize(10, 30, 1);
_signalPeriod = Param(nameof(SignalPeriod), 1)
.SetGreaterThanZero()
.SetDisplay("Signal EMA", "Signal smoothing period for MACD", "MACD")
.SetOptimize(1, 9, 1);
_triggerLevel = Param(nameof(TriggerLevel), 50m)
.SetNotNegative()
.SetDisplay("Trigger Level", "Absolute MACD level that arms the pattern logic", "MACD");
_stopLossPips = Param(nameof(StopLossPips), 100m)
.SetNotNegative()
.SetDisplay("Stop-Loss (pips)", "Stop-loss distance expressed in pips", "Risk Management")
.SetOptimize(50m, 200m, 10m);
_takeProfitPips = Param(nameof(TakeProfitPips), 300m)
.SetNotNegative()
.SetDisplay("Take-Profit (pips)", "Take-profit distance expressed in pips", "Risk Management")
.SetOptimize(150m, 500m, 10m);
_tradeVolume = Param(nameof(TradeVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Order volume used for new entries", "Trading")
.SetOptimize(0.05m, 1m, 0.05m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for MACD calculations", "General");
}
/// <summary>
/// Fast EMA length used by MACD.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA length used by MACD.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Signal EMA length used by MACD.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// Absolute MACD level that enables pattern tracking.
/// </summary>
public decimal TriggerLevel
{
get => _triggerLevel.Value;
set => _triggerLevel.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in pips.
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance expressed in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trading volume used for new market orders.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousMacd = null;
_previousMacd2 = null;
_firstPeak = null;
_firstTrough = null;
_sellPatternArmed = false;
_buyPatternArmed = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = TradeVolume;
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastPeriod },
LongMa = { Length = SlowPeriod },
},
SignalMa = { Length = SignalPeriod },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_macd, ProcessCandle)
.Start();
var pipSize = Security?.PriceStep ?? 0.01m;
if (pipSize <= 0m) pipSize = 0.01m;
StartProtection(
takeProfit: TakeProfitPips > 0m ? new Unit(TakeProfitPips * pipSize, UnitTypes.Absolute) : null,
stopLoss: StopLossPips > 0m ? new Unit(StopLossPips * pipSize, UnitTypes.Absolute) : null,
useMarketOrders: true);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFinal || macdValue is not MovingAverageConvergenceDivergenceSignalValue typed)
return;
if (typed.Macd is not decimal macdLine)
return;
var previous = _previousMacd;
var previous2 = _previousMacd2;
if (previous.HasValue && previous2.HasValue)
{
ProcessSellPattern(macdLine, previous.Value, previous2.Value);
ProcessBuyPattern(macdLine, previous.Value, previous2.Value);
}
_previousMacd2 = previous;
_previousMacd = macdLine;
}
private void ProcessSellPattern(decimal current, decimal previous, decimal previous2)
{
if (current > TriggerLevel && current < previous && previous > previous2)
{
if (!_sellPatternArmed)
{
_firstPeak = previous;
_sellPatternArmed = true;
}
else if (_firstPeak is decimal referencePeak && previous < referencePeak)
{
EnterShort();
ResetSellPattern();
}
}
else if (current < TriggerLevel)
{
ResetSellPattern();
}
}
private void ProcessBuyPattern(decimal current, decimal previous, decimal previous2)
{
var negativeTrigger = -TriggerLevel;
if (current < negativeTrigger && current > previous && previous < previous2)
{
if (!_buyPatternArmed)
{
_firstTrough = previous;
_buyPatternArmed = true;
}
else if (_firstTrough is decimal referenceTrough && previous > referenceTrough)
{
EnterLong();
ResetBuyPattern();
}
}
else if (current > negativeTrigger)
{
ResetBuyPattern();
}
}
private void EnterShort()
{
if (!IsFormedAndOnlineAndAllowTrading())
return;
var volume = Volume + Math.Max(0m, Position);
if (volume <= 0m)
return;
SellMarket(volume);
}
private void EnterLong()
{
if (!IsFormedAndOnlineAndAllowTrading())
return;
var volume = Volume + Math.Max(0m, -Position);
if (volume <= 0m)
return;
BuyMarket(volume);
}
private void ResetSellPattern()
{
_sellPatternArmed = false;
_firstPeak = null;
}
private void ResetBuyPattern()
{
_buyPatternArmed = false;
_firstTrough = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, UnitTypes, Unit
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal
class macd_pattern_trader_double_top_strategy(Strategy):
def __init__(self):
super(macd_pattern_trader_double_top_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast EMA", "Fast moving average length used by MACD", "MACD")
self._slow_period = self.Param("SlowPeriod", 13) \
.SetDisplay("Slow EMA", "Slow moving average length used by MACD", "MACD")
self._signal_period = self.Param("SignalPeriod", 1) \
.SetDisplay("Signal EMA", "Signal smoothing period for MACD", "MACD")
self._trigger_level = self.Param("TriggerLevel", 50.0) \
.SetDisplay("Trigger Level", "Absolute MACD level that arms the pattern logic", "MACD")
self._stop_loss_pips = self.Param("StopLossPips", 100.0) \
.SetDisplay("Stop-Loss (pips)", "Stop-loss distance expressed in pips", "Risk Management")
self._take_profit_pips = self.Param("TakeProfitPips", 300.0) \
.SetDisplay("Take-Profit (pips)", "Take-profit distance expressed in pips", "Risk Management")
self._trade_volume = self.Param("TradeVolume", 0.1) \
.SetDisplay("Trade Volume", "Order volume used for new entries", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Timeframe used for MACD calculations", "General")
self._previous_macd = None
self._previous_macd2 = None
self._first_peak = None
self._first_trough = None
self._sell_pattern_armed = False
self._buy_pattern_armed = False
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def SignalPeriod(self):
return self._signal_period.Value
@property
def TriggerLevel(self):
return self._trigger_level.Value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@property
def TradeVolume(self):
return self._trade_volume.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(macd_pattern_trader_double_top_strategy, self).OnStarted2(time)
self.Volume = float(self.TradeVolume)
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = self.FastPeriod
self._macd.Macd.LongMa.Length = self.SlowPeriod
self._macd.SignalMa.Length = self.SignalPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._macd, self.ProcessCandle).Start()
ps = self.Security.PriceStep if self.Security is not None else None
pip_size = float(ps) if ps is not None else 0.01
if pip_size <= 0:
pip_size = 0.01
tp = float(self.TakeProfitPips)
sl = float(self.StopLossPips)
take_profit = Unit(tp * pip_size, UnitTypes.Absolute) if tp > 0 else None
stop_loss = Unit(sl * pip_size, UnitTypes.Absolute) if sl > 0 else None
self.StartProtection(take_profit, stop_loss, useMarketOrders=True)
def ProcessCandle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFinal:
return
macd_line = macd_value.Macd
if macd_line is None:
return
macd_line = float(macd_line)
previous = self._previous_macd
previous2 = self._previous_macd2
if previous is not None and previous2 is not None:
self._process_sell_pattern(macd_line, previous, previous2)
self._process_buy_pattern(macd_line, previous, previous2)
self._previous_macd2 = previous
self._previous_macd = macd_line
def _process_sell_pattern(self, current, previous, previous2):
trigger = float(self.TriggerLevel)
if current > trigger and current < previous and previous > previous2:
if not self._sell_pattern_armed:
self._first_peak = previous
self._sell_pattern_armed = True
elif self._first_peak is not None and previous < self._first_peak:
self._enter_short()
self._reset_sell_pattern()
elif current < trigger:
self._reset_sell_pattern()
def _process_buy_pattern(self, current, previous, previous2):
negative_trigger = -float(self.TriggerLevel)
if current < negative_trigger and current > previous and previous < previous2:
if not self._buy_pattern_armed:
self._first_trough = previous
self._buy_pattern_armed = True
elif self._first_trough is not None and previous > self._first_trough:
self._enter_long()
self._reset_buy_pattern()
elif current > negative_trigger:
self._reset_buy_pattern()
def _enter_short(self):
volume = self.Volume + max(0.0, float(self.Position))
if volume <= 0:
return
self.SellMarket(volume)
def _enter_long(self):
volume = self.Volume + max(0.0, -float(self.Position))
if volume <= 0:
return
self.BuyMarket(volume)
def _reset_sell_pattern(self):
self._sell_pattern_armed = False
self._first_peak = None
def _reset_buy_pattern(self):
self._buy_pattern_armed = False
self._first_trough = None
def OnReseted(self):
super(macd_pattern_trader_double_top_strategy, self).OnReseted()
self._previous_macd = None
self._previous_macd2 = None
self._first_peak = None
self._first_trough = None
self._sell_pattern_armed = False
self._buy_pattern_armed = False
def CreateClone(self):
return macd_pattern_trader_double_top_strategy()