GitHub で見る

XDeMarker ヒストグラムボリューム戦略

この戦略は、MetaTrader のオリジナルエキスパートアドバイザー Exp_XDeMarker_Histogram_Vol を StockSharp の高水準 API の上で再現したものです。DeMarker オシレーターを出来高加重ヒストグラムに変換し、オシレーターと出来高の両方を設定可能な移動平均で平滑化し、ヒストグラムが事前定義されたバンドを超えたときのレジーム変化に反応します。

ロジックは意図的に対称的です。ヒストグラムが強気ゾーンの一つに入るとロングポジションが開かれ、弱気ゾーンに移動するとショートが開かれます。反対のシグナルはアクティブなポジションを閉じ、有効になっている場合は即座に方向を反転します。

コンセプト

  1. 出来高加重 DeMarker
    • DeMarker は選択した期間で計算されます。
    • オシレーターは [-50; +50] の範囲にスケーリングされ、選択したローソク足の出来高を掛けます。
    • 移動平均が加重オシレーターを平滑化します。同じ移動平均が出来高自体にも適用されます。StockSharp にネイティブに利用可能なため、移動平均タイプは 4 種類のみ(単純、指数、平滑化、加重)提供されます。
  2. 動的レベル
    • 4 つのユーザー定義乗数(HighLevel1HighLevel2LowLevel1LowLevel2)が強気と弱気の閾値を定義します。
    • 閾値は平滑化された出来高でスケーリングされ、参加が多いほど許容範囲が広がります。
  3. 状態機械
    • 完成した各ローソク足は 5 つの状態のいずれかに分類されます:0(極度の強気)、1(強気)、2(中立)、3(弱気)、4(極度の弱気)。
    • 最後に閉じたローソク足(SignalBar でオフセット)の状態が、強気または弱気領域への移行を示す方法で前の状態と異なる場合にシグナルが生成されます。

パラメーター

名前 説明
CandleType 主要な時間軸。オリジナルのエキスパートアドバイザーを反映するためにデフォルトは 2 時間ローソク足。
DeMarkerPeriod DeMarker オシレーターの期間。
HighLevel1 / HighLevel2 第 1 および第 2 の強気閾値を定義する正の乗数。
LowLevel1 / LowLevel2 第 1 および第 2 の弱気閾値を定義する負の乗数。
Smoothing ヒストグラムと出来高の両方に対する移動平均タイプ。選択肢:Simple、Exponential、Smoothed、Weighted。
SmoothingLength 平滑化移動平均の長さ。
SignalBar シグナル比較に使用する閉じたバーの数。1 は「最近閉じたローソク足を使用する」を意味します。
VolumeType 出来高ソース。StockSharp がすべてのフィードでティック数を公開していないため、両オプションともローソク足の出来高にフォールバックします。
EnableLongEntries / EnableShortEntries それぞれの方向に新しいポジションを開くことを許可します。
EnableLongExits / EnableShortExits 反対のセットアップが現れたときに既存のポジションを閉じることを許可します。

シグナルとポジション管理

  • ロングエントリー: 最後のシグナルバーが状態 1 または 0 に移行し、前のバーが番号が大きい状態(>1)にあった場合。ショートポジションはエントリー前にオプションで閉じられます。
  • ショートエントリー: 最後のシグナルバーが状態 3 または 4 に移行し、前のバーが番号が小さい状態(<3 または <4 それぞれ)にあった場合。ロングポジションはエントリー前にオプションで閉じられます。
  • エグジット: 反対のシグナルが引き起こされ、現在の方向のエグジットが有効になっているとき。反転前にフラットにするために ClosePosition() が使用されます。
  • ポジションサイジング: ストラテジーは標準の Strategy.Volume プロパティに依存します。MetaTrader バージョンのマネーマネジメントブロック(2 つの別々の「マジック」ID)は意図的に簡略化されています。

実装メモ

  • 完成したローソク足のみが処理されます。ストラテジーは SubscribeCandles().WhenNew(ProcessCandle) を介して設定された時間軸をサブスクライブします。
  • DeMarker の実装は MetaTrader の計算に合わせるために DeMax/DeMin 値の移動合計を保持し、シグナルを出力する前に十分なバーが蓄積されるまで待機します。
  • 出来高データが欠落している場合、加重オシレーターと閾値の両方がゼロになるため、ヒストグラムはゼロに安全にデグレードします。
  • オリジナルインジケーターの非対応平滑化モード(JJMA、JurX、ParMA、T3、VIDYA、AMA)は再現されません。Smoothing パラメーターで最も近い代替手段を選択してください。
  • SignalBar バッファは、オリジナルの CopyBuffer の動作を模倣して古いシグナルを避けるために必要な最小履歴(現在、前、および 1 つの余分なスロット)のみを保持します。

使用上のヒント

  • 希望の時間軸と出来高を設定した後、Designer または Runner でストラテジーを開始します。
  • DeMarkerPeriodSmoothingLength、閾値乗数を一緒に最適化します。閾値のわずかな変更がエントリーの頻度を大きく変えます。
  • ヒストグラムは出来高加重であるため、フィードの品質が重要です。意図した効果を捉えるために信頼性の高いローソク足の出来高を報告するデータプロバイダーを使用してください。
  • ストップロスやテイクプロフィットルールが必要な場合は、外部のマネーマネジメントまたはリスクモジュールの追加を検討してください。高水準の変換にはそれらが存在しませんでした。
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>
/// XDeMarker Histogram Vol strategy. Uses DeMarker 0.5-line crossover.
/// </summary>
public class XDeMarkerHistogramVolStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _demarkerPeriod;
	private decimal? _prevDm;

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

	public XDeMarkerHistogramVolStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_demarkerPeriod = Param(nameof(DemarkerPeriod), 14).SetGreaterThanZero().SetDisplay("DeMarker Period", "DeMarker lookback", "Indicators");
	}

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

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevDm = null;
		var dm = new DeMarker { Length = DemarkerPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(dm, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal dmVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevDm = dmVal; return; }
		if (_prevDm == null) { _prevDm = dmVal; return; }
		if (_prevDm.Value < 0.45m && dmVal >= 0.55m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (_prevDm.Value > 0.55m && dmVal <= 0.45m && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevDm = dmVal;
	}
}