GitHub で見る

DeMarker 保留中の戦略

概要

この StockSharp 戦略は、MetaTrader エキスパート アドバイザー「DeMarker Pending 2.5」の動作を再現します。ボットは、設定可能な時間枠で DeMarker オシレーターを評価し、極端なレベルを超えると、ブレイクアウト方向に指値注文を出します。注文は、固定ポイント数でオフセットされたストップ注文または指値注文のいずれかになります。オプションの取引ウィンドウのフィルタリングと自動有効期限により、未決注文を元のエキスパートの行動に合わせた状態に保つことができます。

取引ロジック

  • 選択したローソク足シリーズをサブスクライブし、期間 DemarkerPeriod で DeMarker インジケーターを計算します。
  • 現在および以前の終了ローソク足の値を使用して、下限 (DemarkerLowerLevel) と上限 (DemarkerUpperLevel) のしきい値のクロスオーバーを検出します。
  • 下のレベルが上にクロスすると、長いセットアップをキューに入れます。上のレベルが下にクロスしたら、短いセットアップをキューに入れます。
  • Modeに応じて、ブレークアウトモードの逆指値注文またはプルバックエントリーの指値注文を使用して、セットアップを価格Close ± PendingIndentPoints * PriceStepの未決注文に変換します。
  • エントリー価格を StopLossPoints ポイントと TakeProfitPoints ポイントオフセットすることで、ストップロスとテイクプロフィットのレベルを未決注文に付加します。
  • 新しい保留中の注文を登録する前に、ReplacePreviousPendingSinglePendingOnly に従って古い保留中の注文をキャンセルまたは再利用してください。
  • PendingExpirationMinutes の有効期間が経過すると、保留中の注文が自動的に削除されます。
  • UseTimeWindow が有効な場合、日中ウィンドウ外のシグナルを無視します。すべてのバーは 1 回だけ処理されるため、バーおよび方向ごとに最大 1 つの新しい未決注文が作成されます。

注文管理

  • すべてのエントリは未決注文として作成されます (BuyStopSellStopBuyLimitSellLimit)。
  • 各未決注文には独自のストップロス価格とテイクプロフィット価格が設定されているため、アクティブ化直後にポジションが保護されます。
  • 保留中の注文は、有効期限が切れたとき、新しい設定に置き換えられたとき、または注文の状態が非アクティブなステータス (約定、キャンセル、拒否) に変化したときにキャンセルされます。

パラメーター

パラメータ 説明
Volume ロット単位での注文量。
StopLossPoints エントリー価格とストップロスの間の距離(ポイント単位)。
TakeProfitPoints エントリー価格とテイクプロフィットの間の距離(ポイント単位)。
PendingIndentPoints 市場価格と未決注文の間のオフセット。
PendingExpirationMinutes 各未決注文の存続期間 (分単位) (0 は有効期限を無効にします)。
Mode 未決注文のタイプ (ブレイクアウトの場合はストップ、プルバックの場合はリミット)。
SinglePendingOnly 有効にすると、複数のアクティブな未決注文を発注できなくなります。
ReplacePreviousPending 新しい注文を発行する前に、アクティブな未決注文をキャンセルします。
DemarkerPeriod DeMarker オシレーターのルックバック期間。
DemarkerUpperLevel 売り設定をトリガーする DeMarker しきい値。
DemarkerLowerLevel 購入セットアップをトリガーする DeMarker しきい値。
CandleType ローソク足のサブスクリプションとインジケーターの評価に使用される時間枠。
UseTimeWindow 日中の時間フィルタリングを有効にします。
StartTime 日中取引ウィンドウの開始。
EndTime 日中取引ウィンドウの終了。

注意事項

  • オリジナルのエキスパートには、洗練された資金管理とトレーリングストップルーチンが含まれています。このポートはシグナル生成と未決注文の処理を維持しますが、ポジションのサイジングを単一の固定 Volume パラメーターに簡素化します。
  • StockSharp は注文登録時にストップロス価格とテイクプロフィット価格を付加します。ブローカーによっては、ストップ注文と指値注文がこれらの保護レベルをサポートしているかどうかを確認する必要がある場合があります。
  • ポイントベースの距離が取引シンボルの PriceStep と互換性があることを常に確認してください。 PendingIndentPointsStopLossPoints、および TakeProfitPoints をブローカーの最小距離要件を満たす値に設定します。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from the "DeMarker Pending 2.5" MetaTrader expert.
/// Uses DeMarker indicator level crossovers to generate buy/sell market signals.
/// Original used pending orders; this version uses market orders.
/// </summary>
public class DeMarkerPendingStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _demarkerPeriod;
	private readonly StrategyParam<decimal> _demarkerUpper;
	private readonly StrategyParam<decimal> _demarkerLower;

	private RelativeStrengthIndex _rsi;
	private decimal? _prevOscillator;

	/// <summary>
	/// Candle type used for signal evaluation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// DeMarker indicator period.
	/// </summary>
	public int DemarkerPeriod
	{
		get => _demarkerPeriod.Value;
		set => _demarkerPeriod.Value = value;
	}

	/// <summary>
	/// Upper DeMarker threshold that triggers sell signals.
	/// </summary>
	public decimal DemarkerUpperLevel
	{
		get => _demarkerUpper.Value;
		set => _demarkerUpper.Value = value;
	}

	/// <summary>
	/// Lower DeMarker threshold that triggers buy signals.
	/// </summary>
	public decimal DemarkerLowerLevel
	{
		get => _demarkerLower.Value;
		set => _demarkerLower.Value = value;
	}

	public DeMarkerPendingStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for signal evaluation", "General");

		_demarkerPeriod = Param(nameof(DemarkerPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("DeMarker Period", "Averaging period for DeMarker indicator", "Indicator");

		_demarkerUpper = Param(nameof(DemarkerUpperLevel), 0.7m)
			.SetDisplay("Upper Level", "DeMarker value that triggers sell setup", "Indicator");

		_demarkerLower = Param(nameof(DemarkerLowerLevel), 0.3m)
			.SetDisplay("Lower Level", "DeMarker value that triggers buy setup", "Indicator");
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevOscillator = null;
		_rsi = new RelativeStrengthIndex { Length = DemarkerPeriod };

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

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

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

		if (!_rsi.IsFormed)
		{
			_prevOscillator = rsiValue / 100m;
			return;
		}

		if (_prevOscillator is null)
		{
			_prevOscillator = rsiValue / 100m;
			return;
		}

		var oscillatorValue = rsiValue / 100m;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// DeMarker crosses below lower level -> buy signal
		var crossDown = _prevOscillator.Value > DemarkerLowerLevel && oscillatorValue <= DemarkerLowerLevel;
		// DeMarker crosses above upper level -> sell signal
		var crossUp = _prevOscillator.Value < DemarkerUpperLevel && oscillatorValue >= DemarkerUpperLevel;

		if (crossDown)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (crossUp)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevOscillator = oscillatorValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_rsi = null;
		_prevOscillator = null;

		base.OnReseted();
	}
}