GitHub で見る

WPR カスタム クラウドのシンプルな戦略

概要

WPR Custom Cloud Simple Strategy は、MetaTrader エキスパート アドバイザー WPR Custom Cloud Simple.mq5 の StockSharp 移植です。 EA はラリー Williams の %R オシレーターを監視し、インジケーターが売られすぎまたは買われすぎの領域を抜けたときに取引を開始します。この C# バージョンは、新しいローソク足でのみ取引するという元の設計を維持し、反対のシグナルが現れたときにポジションを反転し、リファレンス実装とまったく同様にストップロスまたはテイクプロフィット注文を回避します。

取引ロジック

  1. 構成されたタイムフレーム (CandleType) をサブスクライブし、受信ローソクを WilliamsR インジケーターにフィードします。
  2. ろうそくの火が消えるまで待ちます。ストラテジーは不完全なバーには決して作用しません。
  3. 最後に完了した 2 つの %R 値を保存します。これらは、MetaTrader からの wpr[1]wpr[2] の読み取り値を反映しています。
  4. クロスオーバーで信号を生成します。
    • ロングセットアップ: 前のバーは OversoldLevel の上で閉じますが、その前のバーはレベルを下回っています。これにより、EA の「売られ過ぎからの出口」状態 (wpr[2] < level および wpr[1] > level) が再現されます。
    • 簡単なセットアップ: 前のバーは OverboughtLevel の下で閉じますが、前のバーはその上にあり、元の wpr[2] > level および wpr[1] < level チェックと一致します。
  5. 長いセットアップが表示された場合は、短いエクスポージャをフラットにして、正味ボリュームを 1 つ購入します。ショートセットアップが発動したら、ロングサイドを平らにして正味1ボリュームを売ります。 StockSharp はネット ポジションで動作するため、Volume + |Position|BuyMarket/SellMarket を送信すると、MetaTrader のヘッジ口座からの決済と反転のフローが完全に複製されます。
  6. 追加の出口は使用されません。元のアドバイザーと同様に、新しい反対のクロスオーバーが取引を終了する唯一の方法です。

パラメーター

名前 種類 デフォルト MetaTraderの相手方 説明
WprPeriod int 14 Inp_WPR_Period Williams %R 計算のルックバックの長さ。
OverboughtLevel decimal -20 Inp_WPR_Level1 買われすぎの領域を定義するしきい値。それより下を通過するとショートがトリガーされます。
OversoldLevel decimal -80 Inp_WPR_Level2 売られ過ぎの領域を定義するしきい値。それを超えるとロングがトリガーされます。
CandleType DataType 1時間枠 InpWorkingPeriod インジケーターの更新とシグナルの評価に使用されるローソク足シリーズ。
Volume decimal 戦略ベースボリューム InpLots 成行注文のロットサイズ。この戦略は、新しい取引を開始する前に、現在のネットポジションを自動的に相殺します。

オリジナルの EA との違い

  • StockSharp はネット ポジションで動作します。逆エクスポージャーの決済は成行注文量を増やすことで処理されるため、その動作は STRUCT_POSITION のような追加の簿記構造を必要とせずにヘッジ モデルと一致します。
  • すべての注文管理ヘルパー クラス (CTradeCPositionInfo、マージン チェックなど) は、StockSharp の組み込みリスク管理によって置き換えられます。この戦略は、手動によるフリーマージンの計算ではなく、Strategy.Volume と取引所のメタデータに依存しています。
  • ロギングが簡素化されます。 StockSharp バージョンでは、高レベルの API が注文ステータスの更新をすでに提供しているため、冗長な Print ステートメントが回避されます。
  • 保護命令は、情報源 EA の「反対側シグナルに接近」設計を反映するために意図的に省略されています。

使い方のヒント

  • クロスオーバー周波数を同等に保つために、CandleType を MetaTrader で使用したのと同じ時間枠に調整します。
  • Williams %R しきい値は負の値です。 OverboughtLevel をゼロに近づけるとショートエントリーが少なくなり、OversoldLevel-100 に近づけるとロングエントリーが少なくなります。
  • この戦略は、Volume がブローカーの最小ステップとネッティング ルールにすでに準拠していることを前提としています。ライブ取引を開始する前に、UI またはコードを通じて基本ボリュームを調整します。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Williams %R cloud breakout strategy converted from the MetaTrader expert advisor.
/// Looks for %R crossings above -80 to go long and below -20 to go short.
/// Positions are reversed when an opposite signal appears.
/// </summary>
public class WprCustomCloudSimpleStrategy : Strategy
{
	private readonly StrategyParam<int> _wprPeriod;
	private readonly StrategyParam<decimal> _overboughtLevel;
	private readonly StrategyParam<decimal> _oversoldLevel;
	private readonly StrategyParam<DataType> _candleType;

	private WilliamsR _williamsR;
	private decimal? _previousWpr;
	private decimal? _olderWpr;

	public int WprPeriod
	{
		get => _wprPeriod.Value;
		set => _wprPeriod.Value = value;
	}

	public decimal OverboughtLevel
	{
		get => _overboughtLevel.Value;
		set => _overboughtLevel.Value = value;
	}

	public decimal OversoldLevel
	{
		get => _oversoldLevel.Value;
		set => _oversoldLevel.Value = value;
	}

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

	public WprCustomCloudSimpleStrategy()
	{
		_wprPeriod = Param(nameof(WprPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("WPR Period", "Williams %R lookback length", "Williams %R");

		_overboughtLevel = Param(nameof(OverboughtLevel), -10m)
			.SetDisplay("Overbought Level", "%R level that marks overbought conditions", "Williams %R");

		_oversoldLevel = Param(nameof(OversoldLevel), -90m)
			.SetDisplay("Oversold Level", "%R level that marks oversold conditions", "Williams %R");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for Williams %R", "Data");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_williamsR = new WilliamsR { Length = WprPeriod };
		_previousWpr = null;
		_olderWpr = null;

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

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

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

		if (!_williamsR.IsFormed)
		{
			_olderWpr = _previousWpr;
			_previousWpr = wprValue;
			return;
		}

		if (_previousWpr is not null && _olderWpr is not null)
		{
			var prev = _previousWpr.Value;
			var prevPrev = _olderWpr.Value;

			var crossedAboveOversold = prevPrev < OversoldLevel && prev > OversoldLevel;
			var crossedBelowOverbought = prevPrev > OverboughtLevel && prev < OverboughtLevel;

			var volume = Volume;
			if (volume <= 0)
				volume = 1;

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

		_olderWpr = _previousWpr;
		_previousWpr = wprValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_williamsR = null;
		_previousWpr = null;
		_olderWpr = null;

		base.OnReseted();
	}
}