GitHub で見る

最も単純なデマーカー戦略

概要

Simplest DeMarker Strategy は、元の MetaTrader エキスパート アドバイザーのロジックを再現します。 DeMarker オシレーターを追跡して、価格の勢いが買われすぎゾーンまたは売られすぎゾーンを離れるときを検出します。オシレーターがニュートラルレンジ内でクロスバックすると、この戦略は設定可能なストップロスとテイクプロフィットの距離を通じてリスクを管理しながら、予想される反転の方向にポジションをオープンします。

中核ロジック

  1. 選択した時間枠のローソク足をサブスクライブし、構成された期間で DeMarker インジケーターを計算します。
  2. 以前の DeMarker 値が買われ過ぎのしきい値を上回る場合は、市場状態を 買われ過ぎ としてマークし、売られ過ぎのしきい値を下回る場合には 売られ過ぎ としてマークします。
  3. 現在の DeMarker 値がニュートラル エリア内に戻ったときにシグナルを生成します。
    • オシレーターが買われ過ぎのレベルを上回った後、それを下回ったときに売ります。
    • オシレーターが売られ過ぎの水準を下回っていた後、売られ過ぎの水準を上回ったときに買います。
  4. 一度に 1 つの位置のみを配置します。 Trade On Bar Open が有効な場合、注文は次のバーが開くまで延期されます。それ以外の場合、ポジションは現在の足の終値ですぐに入力されます。
  5. 組み込みの保護サービスを使用してストップロス注文と利食い注文を適用し、MQL バージョンからの固定距離を模倣します。

パラメーター

  • ボリューム – ロット/契約の注文サイズ。
  • DeMarker Period – DeMarker オシレーターの周期。
  • 買われすぎレベル – 買われすぎの状態を定義する DeMarker の上限しきい値。
  • 売られ過ぎレベル – 売られ過ぎの状態を定義する下限デマーカーしきい値。
  • バーオープン時のトレード – 有効にすると、エントリーはすぐにではなく、次のバーが開いたときに実行されます。
  • ストップロスポイント – 価格ポイントで表される保護ストップロス距離。
  • テイクプロフィットポイント – 価格ポイントで表される利益目標距離。
  • ローソク足タイプ – インジケーターの計算に使用されるローソク足タイプ (時間枠)。

資金管理

  • ストップロス注文とテイクプロフィット注文は、価格ポイントに変換された距離とともに StartProtection を通じて自動的に登録されます。
  • 一度にアクティブにできるポジションは 1 つだけです。ポジションが存在する間、新しいシグナルは無視されます。

グラフの要素

  • 選択したサブスクリプションの価格キャンドル。
  • DeMarker インジケーターの曲線。
  • エントリーとエグジットを視覚的に検証するための独自のトレードマーカー。

注意事項

  • ストップロスとテイクプロフィットの約定品質を確保するために、十分に高い流動性商品を使用してください。
  • Trade On Bar Open フラグは、注文を送信する前に新しいバーを待機する元のエキスパートアドバイザーの動作に近似しています。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Simplest DeMarker strategy: DeMarker oscillator crossover.
/// Buys when DeMarker crosses above oversold, sells when crosses below overbought.
/// </summary>
public class SimplestDeMarkerStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _demarkerPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevValue;
	private int _candlesSinceTrade;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int DemarkerPeriod { get => _demarkerPeriod.Value; set => _demarkerPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public SimplestDeMarkerStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_demarkerPeriod = Param(nameof(DemarkerPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("DeMarker Period", "DeMarker period", "Indicators");
		_oversold = Param(nameof(Oversold), 0.2m)
			.SetDisplay("Oversold", "DeMarker oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 0.8m)
			.SetDisplay("Overbought", "DeMarker overbought level", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevValue = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevValue = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
		var demarker = new DeMarker { Length = DemarkerPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(demarker, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev)
		{
			if (_prevValue < Oversold && demarkerValue >= Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (_prevValue > Overbought && demarkerValue <= Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevValue = demarkerValue;
		_hasPrev = true;
	}
}