GitHub で見る

Exp DEMA レンジチャンネル Tm Plus 戦略

Exp DEMA Range Channel Tm Plus戦略はオリジナルのMetaTraderエキスパートアドバイザーをStockSharpの高レベルAPIにポートします。価格の極値周辺に二重指数移動平均(DEMA)チャンネルを構築し、チャンネルによって生成されるローソク足の色を解釈していつトレードするかを決定します。実装はソースコードのブレイクアウトとタイムアウトルールを再現しながら、プラットフォームの Volume プロパティとオプションの保護注文に頼ることでマネー管理ロジックをシンプルに保ちます。

コアロジック

  • チャンネル構築
    • 同じ期間の2つのDEMAインジケーターが計算されます:1つはローソク足の高値に、もう1つは安値に。
    • それらの出力はオリジナルのカスタムインジケーターがチャンネルを描く方法に合わせて設定可能な数のバー(Shift)だけ前方にシフトされます。
    • チャンネルを広げたり狭めたりするために価格ポイントでの価格オフセット(PriceShiftPoints)を追加できます。
  • シグナルカラー
    • シフトされた上部バンドより上でクローズするローソク足は強気と見なされます。
    • シフトされた下部バンドより下でクローズするローソク足は弱気と見なされます。
    • MQLインジケーターの4つの可能な色(0–3)を模倣するためにローソク足本体の方向(終値≥始値または終値≤始値)が保持されます。
  • エントリー条件
    • 戦略は SignalBar バー前を振り返って最新のブレイクアウトカラーを評価し、前のバーがすでに同じシグナルを示していなかったことを確認します。これにより新しいブレイクアウトが現れた瞬間を捉えます。
    • ロングエントリーは EnableBuyEntry が真で検出された色が上方ブレイクアウトに対応する場合にのみ許可されます。
    • ショートエントリーは EnableSellEntry と下方ブレイクアウトが必要です。
  • エグジット条件
    • EnableBuyExit が有効であれば、ロングポジションは下方ブレイクアウトでクローズできます。
    • EnableSellExit が有効であれば、ショートポジションは上方ブレイクアウトでクローズできます。
    • UseHoldingLimit が真であれば、設定可能な保持時間(HoldingMinutes)後にポジションをクローズすることもでき、エキスパートアドバイザーの時間フィルターを反映します。
  • リスク管理
    • オプションのテイクプロフィットとストップロス距離(価格ポイント)は StartProtection を起動し、閾値に達した時に市場執行を使用して保護注文を発行します。

パラメーター

パラメーター 説明
MaPeriod 上部と下部のチャンネルラインの両方に使用されるDEMA期間。
Shift 比較前にDEMAラインが前方にシフトされるバー数。
PriceShiftPoints 上部ラインに追加され下部ラインから引かれる価格ポイント(PriceStep の倍数)での追加距離。
SignalBar ブレイクアウトカラーを評価するために使用される遡るバー数。0 は現在のバー、1 は最後にクローズしたバーなど。
EnableBuyEntry / EnableSellEntry ロングとショートのブレイクアウトエントリーのトグル。
EnableBuyExit / EnableSellExit 反対のシグナルでロングまたはショートポジションを終了するためのトグル。
UseHoldingLimit HoldingMinutes 分間市場にいた後のポジションクローズを有効にします。
HoldingMinutes 強制クローズ前の最大保持時間;フラグを真に保ちながら無効にするには 0 に設定。
StopLossPoints / TakeProfitPoints 価格ポイントでの保護距離。ゼロより大きい場合は絶対価格オフセットに変換されて StartProtection に渡されます。
CandleType すべての計算に使用されるローソク足タイプと時間軸(MQLスクリプトと同様にデフォルトは8時間ローソク足)。

トレードワークフロー

  1. CandleType で定義されたローソク足を購読してDEMAインジケーターを開始します。
  2. アルゴリズムが Shift バー前に存在した値を参照できるように最新のチャンネル値をキューに保存し、オリジナルのインジケーターシフトを再現します。
  3. ローソク足が完成したとき、そのブレイクアウトカラーを計算してローリングバッファに追加します。SignalBar に従って新しい上方または下方ブレイクアウトを識別するためにバッファを使用します。
  4. 反対のシグナルが現れるか時間フィルターが期限切れになった場合に既存のポジションをクローズします。
  5. 必要に応じて反対側からフリップするために Volume + |Position| のサイズの市場注文を送信して新しいトレードに入ります。
  6. 保持時間フィルターを正確に保つためにアクティブポジションの内部タイムスタンプを更新します。

注意事項

  • 戦略はチャートデータが時系列順に処理されることを前提としています。バックテストや本取引を実行する際には、正しいシフト動作を維持するためにローソク足ストリームが順序付けられていることを確認してください。
  • ポジションサイズを制御するために起動前に戦略に Volume を設定する必要があります(UIまたはコード経由)。MQLエキスパートのマネー管理モードは意図的に複製されていません。
  • 保護注文はオプションであるため、本番環境に展開する際にはストップロスとテイクプロフィットの両方の値を設定することを覚えておいてください。
  • チャートヘルパーはローソク足と実行されたトレードを自動的に描画し、チャンネルブレイクアウトが期待されるエントリーと決済をトリガーすることを視覚的に確認できます。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// DEMA channel breakout strategy.
/// Uses fast and slow DEMA crossover to detect trend changes.
/// </summary>
public class ExpDemaRangeChannelTmPlusStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public ExpDemaRangeChannelTmPlusStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast DEMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow DEMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = null;
		_prevSlow = null;

		var fastDema = new DoubleExponentialMovingAverage { Length = FastPeriod };
		var slowDema = new DoubleExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastDema, slowDema, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastDema);
			DrawIndicator(area, slowDema);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fast > slow;

		_prevFast = fast;
		_prevSlow = slow;

		if (!prevAbove && currAbove)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (prevAbove && !currAbove)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}
	}
}