GitHub で見る

MACD Power戦略

概要

MACD Power戦略は、元のMetaTraderエキスパートアドバイザーから変換されたマルチタイムフレームのモメンタムシステムです。ロジックは、プライマリタイムフレームで計算された線形加重移動平均(LWMA)のペア、2つのMACD変種、上位タイムフレームのモメンタムフィルター、月次MACDバイアスを組み合わせています。モメンタムと上位タイムフレームのトレンド条件が一致すると、強力な動きへの参加を試みます。

コアロジック

  • プライマリ移動平均 – ローソク足の典型価格(((High + Low + Close) / 3))の高速・低速LWMAのペア。戦略は、月次バイアスの方向にエントリーする前に、支配的な下降傾斜内での押し目を待つ元のコードを反映し、信号を考慮する前に高速平均が低速平均を下回ることを必要とします。
  • デュアルMACD確認 – パラメーター(12, 26, 1)(6, 13, 1)の2つのMACDインジケーターが、ロング取引ではともにゼロより上、ショート取引ではともにゼロより下でなければなりません。これらの値は、短期加速度を測定するMQLエキスパートのMacdMAIN1MacdMAIN2条件を再現します。
  • モメンタムフィルター – モメンタム(長さ14)は、プライマリローソク足サイズから導出された上位タイムフレームで計算されます(例:15分ベース → 1時間モメンタム)。100からの絶対距離が最新3回のモメンタム読み取りで監視され、少なくとも1つが設定されたしきい値を超えて価格が決定的に動いていることを確認する必要があります。
  • 月次MACDバイアス – 月次(12, 26, 9) MACD(EAのMacdMAIN0/MacdSIGNAL0と同一)は、ロング取引ではメインラインがシグナルラインより上、ショートでは下でなければなりません。これはドミナントなマクロトレンドに逆らって取引することを防ぎます。

取引管理

  • エントリーサイジングOrderVolumeパラメーターが基本注文サイズを定義します。ポジションの反転が必要な場合、エンジンは自動的に反対ポジションの大きさを加算し、ネットボリュームを単一の市場注文で反転させます。
  • テイクプロフィット / ストップロス – 絶対距離は計器ポイントで表され、Security.PriceStepを使用して価格に変換されます(1への安全なフォールバック付き)。
  • Trailingストップ – 価格がTrailingActivationPoints分有利に動いたら、ストップはTrailingOffsetPointsで定義されたオフセットで最高値(ロング)または最安値(ショート)を追跡します。
  • ブレークイーブン – 価格がBreakEvenTriggerPointsに達すると、エントリー ± BreakEvenOffsetPointsに合成ブレークイーブンストップが設定されます。価格がそのレベルに戻ると、ポジションは決済されます。
  • 取引制限MaxTradesは実行ごとのポジション開始数を制限します。しきい値に達すると新規エントリーは発行されません。

パラメーター

名前 説明 デフォルト
CandleType シグナル生成のプライマリ時間軸。 15分ローソク足
FastMaLength 高速LWMAの長さ(典型価格)。 6
SlowMaLength 低速LWMAの長さ(典型価格)。 85
MomentumLength 上位タイムフレームでのモメンタムルックバック。 14
MomentumBuyThreshold 強気モメンタムに必要な100からの最小絶対距離。 0.3
MomentumSellThreshold 弱気モメンタムに必要な100からの最小絶対距離。 0.3
TakeProfitPoints 計器ポイントでのテイクプロフィット距離。 50
StopLossPoints 計器ポイントでのストップロス距離。 20
TrailingActivationPoints Trailingが有効になる前に必要な利益(ポイント)。 40
TrailingOffsetPoints Trailingストップと極端な価格との差(ポイント)。 40
BreakEvenTriggerPoints ブレークイーブン保護を有効にする利益(ポイント)。 30
BreakEvenOffsetPoints ストップをブレークイーブンに移動する際に適用するオフセット(ポイント)。 30
MaxTrades セッションあたりの最大取引数。 10
OrderVolume 基本注文ボリューム。 1

MQLエキスパートとの違い

  • 戦略は直接ティックポーリングの代わりにStockSharp高レベルAPI(SubscribeCandles + Bind/BindEx)を使用します。インジケーター値はローソク足完了後にのみ処理されます。
  • 元のコードのマネーベースのTrailingと資産ストップブロックはポートされていません。アカウントレベルのマネー管理は通常StockSharpリスクフレームワークによって処理されるためです。代わりに、ポイントベースのTrailingとブレークイーブンが残り、EAの動作をエミュレートするように設定できます。
  • MQLのアラート、通知、手動注文変更ヘルパーは省略されています。StockSharpエンジンは市場コールを介して直接注文を処理します。

使用上の注意

  1. CandleTypeを設定してプライマリ時間軸を選択します。上位タイムフレームのモメンタムと月次MACDはGetMomentumCandleType()に実装されたマッピングに従って自動的に導出されます。
  2. TakeProfitPointsStopLossPoints、およびTrailing/ブレークイーブンパラメーターを計器のティックサイズに合わせます。デフォルト値はEAの5桁Forex設定を反映していますが、他の市場に適応できます。
  3. 自動化されたバックテスト実行時はMaxTradesカウンターを監視してください。元のEAのマーチンゲール風のスタッキング動作が必要な場合は大きな数に設定してください。
  4. ビジュアル分析のためには、GUIでチャートを有効にします – 実装はデフォルトでローソク足と2つのLWMAカーブを描画します。
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;

public class MacdPowerStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public MacdPowerStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}