GitHub で見る

Exp XWPR ヒストグラム Vol ダイレクト戦略

概要

この戦略は MetaTrader エキスパートアドバイザー Exp_XWPR_Histogram_Vol_Direct の StockSharp ポートです。Williams %R 値をボリュームで重み付けし、結果を平滑化し、ヒストグラムの傾きが色を変えたときに取引を開く元のアプローチを再現します。注文は完全に形成されたローソク足でトリガーされ、価格ステップで測定されるオプションの保護ストップロスとテイクプロフィットを使用します。

コアロジック

  1. 選択された時間軸で Williams %R を計算します。
  2. オシレーターを +50 シフトし、選択されたボリュームソース (ティックまたはリアル) で掛け合わせ、設定可能な移動平均でストリームを平滑化します。
  3. 同じ移動平均で生のボリュームを平滑化して、インジケーターバンド (HighLevel2、HighLevel1、LowLevel1、LowLevel2) を再構築します。
  4. ヒストグラムの傾きの色を追跡します: 平滑化された値が上昇すると青 (0)、下降するとマゼンタ (1)。戦略は SignalShift パラメーターを尊重して最後の2つの完了した色を比較するための短い履歴バッファを維持します。
  5. 前の色が変わったときにアクションを実行します:
    • 色の遷移 0 → 1: ショートをクローズ (有効な場合) してオプションで新しいロングポジションを開きます。
    • 色の遷移 1 → 0: ロングをクローズ (有効な場合) してオプションで新しいショートポジションを開きます。

ゾーン分類 (ニュートラル/強気/弱気/エクストリーム) はコンテキストのためにログに記録されますが、取引をブロックしません。これはカラーバッファのみを読む元のアドバイザーの動作と一致します。

パラメーター

パラメーター 説明
WilliamsPeriod Williams %R のルックバック長。
HighLevel2HighLevel1LowLevel1LowLevel2 インジケーターバンドを再構築するために平滑化されたボリュームに適用される乗数。
SmoothingType 加重値とボリュームストリームの両方に使用される移動平均ファミリー (SMA、EMA、SMMA、WMA、Hull、VWMA、DEMA、TEMA)。
SmoothingLength 平滑化移動平均の長さ。
SignalShift カラーバッファを読むバー数 (1 は MetaTrader のデフォルトを再現)。
EnableLongEntries / EnableShortEntries ロング/ショートポジションの開設を許可またはブロック。
EnableLongExits / EnableShortExits ロング/ショートポジションのクローズを許可またはブロック。
VolumeSource 重み付けにティック数またはリアルボリュームのどちらかを選択。
StopLossPoints / TakeProfitPoints 価格ステップで表されるオプションの保護ターゲット。
CandleType 分析と取引に使用するローソク足のタイプと時間軸。

エントリーサイズを定義するには戦略の基本 Volume プロパティを使用します。ポジションの反転は、MQL エキスパートアドバイザーと同様に、絶対ポジション量に設定されたロットサイズを加えた量を送信することで処理されます。

使用上の注意

  • 平滑化フェーズ (MetaTrader の MA_Phase) は、StockSharp の移動平均がそのパラメーターを公開しないためサポートされていません。
  • 選択した時間軸に対して十分な履歴がロードされ、移動平均が取引開始前に完全に形成されていることを確認してください。
  • 戦略は StockSharp がサポートする任意のインストゥルメントで動作します; CandleType を希望の解像度に設定してください (例えば、元のデフォルトに合わせるには4時間の時間軸)。
  • ティックボリュームの重み付けには、ローソク足メッセージ内にティック数を提供するデータソースが必要です。そうでなければ、リアルボリュームに切り替えてください。

ログと視覚化

戦略はデフォルトのチャートエリアにローソク足と Williams %R インジケーターを描画します。取引アクションは検出されたゾーンと平滑化されたヒストグラム値をログに記録し、MetaTrader リファレンス実装とのデバッグと比較を支援します。

namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

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

/// <summary>
/// Direct Williams %R histogram strategy with volume-weighted smoothing.
/// Trades only on strong bullish and bearish zone flips.
/// </summary>
public class ExpXwprHistogramVolDirectStrategy : Strategy
{
	private readonly StrategyParam<int> _williamsPeriod;
	private readonly StrategyParam<int> _highLevel1;
	private readonly StrategyParam<int> _lowLevel1;
	private readonly StrategyParam<MovingAverageKinds> _smoothingType;
	private readonly StrategyParam<int> _smoothingLength;
	private readonly StrategyParam<bool> _enableLongEntries;
	private readonly StrategyParam<bool> _enableShortEntries;
	private readonly StrategyParam<bool> _enableLongExits;
	private readonly StrategyParam<bool> _enableShortExits;
	private readonly StrategyParam<VolumeSources> _volumeSource;
	private readonly StrategyParam<int> _signalCooldownBars;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private WilliamsR _williams;
	private DecimalLengthIndicator _valueSmoother;
	private DecimalLengthIndicator _volumeSmoother;
	private int? _previousZone;
	private int _cooldownRemaining;

	public int WilliamsPeriod { get => _williamsPeriod.Value; set => _williamsPeriod.Value = value; }
	public int HighLevel1 { get => _highLevel1.Value; set => _highLevel1.Value = value; }
	public int LowLevel1 { get => _lowLevel1.Value; set => _lowLevel1.Value = value; }
	public MovingAverageKinds SmoothingType { get => _smoothingType.Value; set => _smoothingType.Value = value; }
	public int SmoothingLength { get => _smoothingLength.Value; set => _smoothingLength.Value = value; }
	public bool EnableLongEntries { get => _enableLongEntries.Value; set => _enableLongEntries.Value = value; }
	public bool EnableShortEntries { get => _enableShortEntries.Value; set => _enableShortEntries.Value = value; }
	public bool EnableLongExits { get => _enableLongExits.Value; set => _enableLongExits.Value = value; }
	public bool EnableShortExits { get => _enableShortExits.Value; set => _enableShortExits.Value = value; }
	public VolumeSources VolumeSource { get => _volumeSource.Value; set => _volumeSource.Value = value; }
	public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ExpXwprHistogramVolDirectStrategy()
	{
		_williamsPeriod = Param(nameof(WilliamsPeriod), 14)
			.SetRange(5, 200)
			.SetDisplay("Williams %R Period", "Lookback for the Williams %R oscillator", "Indicator");

		_highLevel1 = Param(nameof(HighLevel1), 1)
			.SetRange(-200, 200)
			.SetDisplay("High Level 1", "Bullish threshold", "Indicator");

		_lowLevel1 = Param(nameof(LowLevel1), -1)
			.SetRange(-200, 200)
			.SetDisplay("Low Level 1", "Bearish threshold", "Indicator");

		_smoothingType = Param(nameof(SmoothingType), MovingAverageKinds.Simple)
			.SetDisplay("Smoothing Type", "Moving average type used for smoothing", "Indicator");

		_smoothingLength = Param(nameof(SmoothingLength), 12)
			.SetRange(2, 200)
			.SetDisplay("Smoothing Length", "Moving average length", "Indicator");

		_enableLongEntries = Param(nameof(EnableLongEntries), true)
			.SetDisplay("Enable Long Entries", "Allow the strategy to open long positions", "Trading Rules");

		_enableShortEntries = Param(nameof(EnableShortEntries), true)
			.SetDisplay("Enable Short Entries", "Allow the strategy to open short positions", "Trading Rules");

		_enableLongExits = Param(nameof(EnableLongExits), true)
			.SetDisplay("Enable Long Exits", "Allow the strategy to close long positions", "Trading Rules");

		_enableShortExits = Param(nameof(EnableShortExits), true)
			.SetDisplay("Enable Short Exits", "Allow the strategy to close short positions", "Trading Rules");

		_volumeSource = Param(nameof(VolumeSource), VolumeSources.Tick)
			.SetDisplay("Volume Source", "Type of volume used for weighting", "Indicator");

		_signalCooldownBars = Param(nameof(SignalCooldownBars), 5)
			.SetRange(1, 200)
			.SetDisplay("Signal Cooldown", "Bars to wait between new entries", "Trading Rules");

		_stopLossPoints = Param(nameof(StopLossPoints), 1000)
			.SetRange(0, 10000)
			.SetDisplay("Stop Loss (ticks)", "Protective stop distance in price steps", "Risk Management");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
			.SetRange(0, 10000)
			.SetDisplay("Take Profit (ticks)", "Profit target distance in price steps", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type used for analysis", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_williams = null;
		_valueSmoother = null;
		_volumeSmoother = null;
		_previousZone = null;
		_cooldownRemaining = 0;
	}

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

		_williams = new WilliamsR { Length = WilliamsPeriod };
		_valueSmoother = CreateMovingAverage(SmoothingType, SmoothingLength);
		_volumeSmoother = CreateMovingAverage(SmoothingType, SmoothingLength);
		_previousZone = null;
		_cooldownRemaining = 0;

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

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);
	}

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

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		var williamsValue = _williams.Process(candle);
		if (!_williams.IsFormed)
			return;

		var wprValue = williamsValue.ToDecimal();

		// Williams %R ranges from -100 to 0; shift to 0..100
		var normalized = wprValue + 100m;
		var bullishLevel = 80m;
		var bearishLevel = 20m;
		var zone = normalized >= bullishLevel ? 1 : normalized <= bearishLevel ? -1 : 0;

		if (_previousZone == null)
		{
			_previousZone = zone;
			return;
		}

		if (_previousZone.Value != zone && _cooldownRemaining == 0 && Position == 0)
		{
			if (zone > 0 && EnableLongEntries)
			{
				BuyMarket();
				_cooldownRemaining = SignalCooldownBars;
			}
			else if (zone < 0 && EnableShortEntries)
			{
				SellMarket();
				_cooldownRemaining = SignalCooldownBars;
			}
		}

		_previousZone = zone;
	}

	private decimal GetWeightedVolume(ICandleMessage candle)
	{
		if (VolumeSource == VolumeSources.Tick && candle.TotalTicks is int ticks && ticks > 0)
			return ticks;

		return candle.TotalVolume > 0m ? candle.TotalVolume : 1m;
	}

	private static DecimalLengthIndicator CreateMovingAverage(MovingAverageKinds type, int length)
	{
		return type switch
		{
			MovingAverageKinds.Simple => new SMA { Length = length },
			MovingAverageKinds.Exponential => new EMA { Length = length },
			MovingAverageKinds.Smoothed => new SmoothedMovingAverage { Length = length },
			MovingAverageKinds.Weighted => new WeightedMovingAverage { Length = length },
			MovingAverageKinds.Hull => new HullMovingAverage { Length = length },
			MovingAverageKinds.VolumeWeighted => new VolumeWeightedMovingAverage { Length = length },
			MovingAverageKinds.DoubleExponential => new DoubleExponentialMovingAverage { Length = length },
			MovingAverageKinds.TripleExponential => new TripleExponentialMovingAverage { Length = length },
			_ => new SMA { Length = length },
		};
	}

	public enum MovingAverageKinds
	{
		Simple,
		Exponential,
		Smoothed,
		Weighted,
		Hull,
		VolumeWeighted,
		DoubleExponential,
		TripleExponential,
	}

	public enum VolumeSources
	{
		Tick,
		Real,
	}
}