GitHub で見る

Vlado Williams %R しきい値戦略

概要

Vlado Williams %R しきい値戦略 は、MetaTrader 4 エキスパート アドバイザー Vlado_www_forex-instruments_info.mq4 を直接変換したものです。オリジナルのロボットは単一の Williams %R オシレーターを取引し、インジケーターがユーザー定義のレベルを横切るたびに市場エクスポージャーを反転します。この StockSharp ポートは、同じレジーム切り替え動作を再現しながら、最適化と UI 制御のための戦略パラメーターとして各調整可能な値を公開します。

主要な概念

  • Williams %R オシレーターの方向をしきい値 (デフォルト -50) を基準にしてトレードします。
  • 一度に最大 1 つの市場ポジションを保持し、前の取引が終了した後にのみ反転します。
  • MetaTrader の資金管理式 AccountFreeMargin * MaximumRisk / price を模倣したオプションのリスクベースのポジションサイジング。
  • CandleType パラメータを介して任意のローソク足の時間枠で動作します(デフォルトの 15 分足)。

取引ロジック

  1. 構成されたローソク足ストリームをサブスクライブし、長さ WprLength (デフォルトは 100) の Williams %R を計算します。
  2. Williams %R が WprLevelに上昇すると、戦略は強気バイアスを示します。
    • オープンなポジションがなく、前の取引が長くなかった場合は、成行買い注文を送信します。
    • ショートポジションが存在する場合は、直ちにクローズされます。ポジションがフラットになった後、新しいロングは次のローソク足で検討されます。
  3. Williams %R が WprLevel下回る と、バイアスは弱気に反転します。
    • オープンなポジションがなく、前回の取引がショートではなかった場合は、成行売り注文を送信します。
    • ロングポジションが存在する場合、すぐにフラット化されます。
  4. ポジションのサイズは CalculateOrderVolume によって決まります。
    • UseRiskMoneyManagementtrue の場合、ストラテジーは現在のポートフォリオ値から取引可能量を推定します: Portfolio.CurrentValue × MaximumRiskPercent ÷ 100 ÷ ClosePrice
    • それ以外の場合は、ベースの Strategy.Volume が使用されます。
    • 結果として得られるロットは商品 VolumeStep に合わせて調整され、これらの境界が利用可能な場合は MinVolume / MaxVolume によってクランプされます。

この戦略は、元の EA フローに合わせて、出口をトリガーしたのと同じローソク足で反転ポジションを開くことを意図的に回避します (CheckForCloseCheckForOpen の前に実行されます)。

変換メモ

  • 資金管理のデフォルトは MT4 スクリプトに従います: MaximumRiskPercent10 で始まり、取引ごとにおよそ 1 つのミニロットを対象とした元の MaximumRisk = 10 定数と一致します。
  • MetaTrader の shift パラメータ (インジケータ シフト) は、ソース ファイルでは常に 0 です。したがって省略されました。
  • MT4 の色の引数 (例: RedBlue) には、同等の StockSharp がなく、無視されます。
  • StockSharp 成行注文ではすでに現在の最良価格が使用されているため、スリッページの入力は必要ありません。

パラメーター

パラメータ 種類 デフォルト 説明
CandleType DataType 15分の時間枠 シグナル計算と注文トリガーの両方の時間枠。
WprLength int 100 Williams %R オシレーターのルックバック期間。
WprLevel decimal -50 強気体制と弱気体制を分けるしきい値。
UseRiskMoneyManagement bool false リスクベースのポジションサイジングを切り替えます。
MaximumRiskPercent decimal 10 リスク管理が有効な場合に、取引ごとに展開されるポートフォリオの株式の割合。

ヒント: 自動ストップロス処理が必要な場合は、戦略を StartProtection() または外部リスク管理と組み合わせてください。オリジナルの EA も手動による監視に依存しており、ハード ストップを定義していませんでした。

使用ガイドライン

  1. 正確な PriceStepStepPriceVolumeStep、および出来高制限を公開する証券にストラテジーをアタッチすると、ポジションサイジング ヘルパーが注文を正しく正規化できるようになります。
  2. Volume を希望のフォールバック ロット サイズに設定します。これは、ポートフォリオの資産が利用できない場合、または UseRiskMoneyManagement が無効になっている場合に使用されます。
  3. WprLevelWprLength を最適化して、システムをさまざまな市場に適応させます。狭いレベル(例: -20 / -80)では戦略がより選択的になりますが、広いしきい値(-50)ではほぼ常に投資されることが保証されます。
  4. この戦略はトレンドに従うものであり、レンジ条件では頻繁に反転します。必要に応じて、より高い時間枠のトレンド チェックやボラティリティのしきい値などのフィルターと組み合わせることを検討してください。

MetaTrader バージョンとの違い

  • StockSharp の高レベル API からのローソク足サブスクリプションとインジケーター バインディングを使用します。手動による注文ループや履歴のスキャンはありません。
  • リスクのサイジングは Portfolio.CurrentValue に依存します。アカウント評価が欠落している場合、ロジックは静的な Volume にフォールバックし、mm=0 が固定ロットサイズを強制する MT4 の動作と一致します。
  • リポジトリのガイドラインとの整合性を図るため、すべてのコメントとパラメータの説明は英語で記載されています。

検証チェックリスト

  • ✅ StockSharp 戦略テンプレート規則 (タブ、ファイル スコープの名前空間、XML 継承ドキュメント) に従ってコンパイルされた戦略ファイル。
  • ✅ パラメータは Param() によって作成され、必要に応じて最適化のマークが付けられます。
  • ✅ Williams %R 値は、GetValue() に直接アクセスすることなく、Bind を通じて消費されます。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the "Vlado" MetaTrader expert advisor that trades Williams %R level breakouts.
/// </summary>
public class VladoWilliamsPercentRangeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _wprLength;
	private readonly StrategyParam<decimal> _wprLevel;
	private readonly StrategyParam<bool> _useRiskMoneyManagement;
	private readonly StrategyParam<decimal> _maximumRiskPercent;

	private bool _buySignal;
	private bool _sellSignal;
	private int _lastSignal;

	private WilliamsR _williamsR;

	/// <summary>
	/// Initializes a new instance of the <see cref="VladoWilliamsPercentRangeStrategy"/> class.
	/// </summary>
	public VladoWilliamsPercentRangeStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe for the strategy", "General");

		_wprLength = Param(nameof(WprLength), 100)
			.SetGreaterThanZero()
			.SetDisplay("Williams %R Period", "Lookback period for Williams %R", "Indicators")
			;

		_wprLevel = Param(nameof(WprLevel), -50m)
			.SetDisplay("Williams %R Level", "Threshold that flips the bias", "Signals")
			;

		_useRiskMoneyManagement = Param(nameof(UseRiskMoneyManagement), false)
			.SetDisplay("Risk Money Management", "Recalculate volume from equity before entries", "Risk")
			;

		_maximumRiskPercent = Param(nameof(MaximumRiskPercent), 10m)
			.SetDisplay("Maximum Risk Percent", "Equity percentage used when sizing orders", "Risk")
			;
	}

	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Williams %R lookback length.
	/// </summary>
	public int WprLength
	{
		get => _wprLength.Value;
		set => _wprLength.Value = value;
	}

	/// <summary>
	/// Threshold that toggles bullish or bearish bias.
	/// </summary>
	public decimal WprLevel
	{
		get => _wprLevel.Value;
		set => _wprLevel.Value = value;
	}

	/// <summary>
	/// Enables risk based volume sizing similar to the MetaTrader version.
	/// </summary>
	public bool UseRiskMoneyManagement
	{
		get => _useRiskMoneyManagement.Value;
		set => _useRiskMoneyManagement.Value = value;
	}

	/// <summary>
	/// Fraction of the current equity used to size entries when risk management is enabled.
	/// </summary>
	public decimal MaximumRiskPercent
	{
		get => _maximumRiskPercent.Value;
		set => _maximumRiskPercent.Value = value;
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_williamsR = null;
		_buySignal = false;
		_sellSignal = false;
		_lastSignal = 0;
	}

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

		_williamsR = new WilliamsR
		{
			Length = WprLength
		};

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

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

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

		UpdateSignals(wprValue);

		if (Position != 0)
		{
			if (Position > 0 && _sellSignal)
			{
				// Exit long positions when the bearish Williams %R regime appears.
				if (Position > 0) SellMarket(); else BuyMarket();
				return;
			}

			if (Position < 0 && _buySignal)
			{
				// Exit short positions when the bullish Williams %R regime appears.
				if (Position > 0) SellMarket(); else BuyMarket();
				return;
			}

			return;
		}

		// No open position - evaluate fresh entries.
		var volume = CalculateOrderVolume(candle.ClosePrice);
		if (volume <= 0m)
			return;

		if (_sellSignal && _lastSignal != -1)
		{
			// Enter short once Williams %R falls below the chosen level.
			SellMarket();
			_lastSignal = -1;
			return;
		}

		if (_buySignal && _lastSignal != 1)
		{
			// Enter long once Williams %R rises above the chosen level.
			BuyMarket();
			_lastSignal = 1;
		}
	}

	private void UpdateSignals(decimal wprValue)
	{
		// Williams %R values are negative: less negative indicates bullish momentum.
		if (wprValue > WprLevel)
		{
			_buySignal = true;
			_sellSignal = false;
		}
		else if (wprValue < WprLevel)
		{
			_sellSignal = true;
			_buySignal = false;
		}
	}

	private decimal CalculateOrderVolume(decimal referencePrice)
	{
		var volume = Volume;

		if (UseRiskMoneyManagement && MaximumRiskPercent > 0m && referencePrice > 0m)
		{
			var equity = Portfolio?.CurrentValue ?? 0m;
			if (equity > 0m)
			{
				// Convert risk capital to volume using the latest close price as approximation.
				volume = equity * (MaximumRiskPercent / 100m) / referencePrice;
			}
		}

		return NormalizeVolume(volume);
	}

	private decimal NormalizeVolume(decimal volume)
	{
		var normalized = volume;

		if (Security?.VolumeStep is decimal step && step > 0m)
		{
			var steps = decimal.Floor(normalized / step);
			normalized = steps * step;

			if (normalized <= 0m)
				normalized = step;
		}

		if (Security?.MinVolume is decimal minVolume && minVolume > 0m && normalized < minVolume)
			normalized = minVolume;

		if (Security?.MaxVolume is decimal maxVolume && maxVolume > 0m && normalized > maxVolume)
			normalized = maxVolume;

		if (normalized <= 0m && volume > 0m)
			normalized = volume;

		return normalized;
	}
}