GitHub で見る

MasterMind 逆転戦略 (StockSharp ポート)

概要

  • MetaTrader 4 エキスパート アドバイザ「TheMasterMind」のポート。Stochastic オシレーターと Williams %R を組み合わせて、極端な反転を捉えます。
  • ローソク足サブスクリプションとインジケーター バインディングを使用して、StockSharp の高レベルの API で実装されます。
  • 単一の証券を取引し、終了したローソク足のみに反応し、元の「クローズでの取引」実行スタイルを反映します。

取引ロジック

  1. インジケーターの準備
    • StochasticOscillator は、構成可能な %K/%D スムージングと合計ルックバック長を備えた %D 信号ラインを提供します。
    • WilliamsR は、最近の高値/安値範囲内の終値の相対位置を測定します。
  2. エントリールール
    • %D <= 3 and Williams %R <= -99.5 のときに 買い、これは下限を下回る WPR の深い浸透とともに、確率論的な売られ過ぎの極値を示しています。
    • %D >= 97 and Williams %R >= -0.5 のときに 売り、Williams %R が 0 付近に留まることで確認された買われすぎの極度のシグナルです。
    • 反対のポジションが存在する場合は、最初にフラット化され、その後、設定された基本ボリュームで新しい成行注文が送信されます。
  3. 退出ルール
    • リバースシグナルは、現在のポジションをクローズし、方向を反転します(一度に 1 つのポジション、MQL スクリプトで使用されるヘッジ無効モードに一致します)。
    • オプションの StartProtection ストップロス、テイクプロフィット、およびトレーリングストップサービスは、戦略開始ごとに 1 回だけ保護エグジットを処理します。

リスク管理

  • パラメータ StopLossTakeProfitUseTrailingStopTrailingStop、および TrailingStep は、元の EA の資金管理制御にマッピングされます。
  • ブローカーに依存しないように、すべての距離は絶対価格単位で表されます。それぞれの保護機能を無効にするには、これらを 0 のままにしておきます。
  • StartProtection は、少なくとも 1 つの保護距離がゼロ以外の場合に自動的にアクティブになります。

Strategy Parameters

パラメータ 説明 デフォルト
TradeVolume 新規エントリーごとの基本ロットサイズ。 1
StochasticPeriod 確率的オシレーターの合計ルックバック。 100
KPeriod %K スムージング長。 3
DPeriod %D 信号の長さ。 3
WilliamsPeriod Williams %R のルックバックの長さ。 100
StochasticBuyThreshold Long を許可するには、%D がこの上限を下回っていなければなりません。 3
StochasticSellThreshold ショートを許可するには、%D がこの値を超えていなければならない下限。 97
WilliamsBuyLevel Williams %R の売られ過ぎレベル。 -99.5
WilliamsSellLevel Williams %R の買われすぎレベル。 -0.5
StopLoss 絶対的なストップロス距離。 0
TakeProfit 絶対的なテイクプロフィットディスタンス。 0
UseTrailingStop true の場合、トレーリング保護を有効にします。 false
TrailingStop 絶対的なトレーリングストップ距離。 0
TrailingStep トレーリング中にステップが適用されました。 0
CandleType プライマリキャンドルサブスクリプションの時間枠 (デフォルトは 15 分)。 15m time frame

実装メモ

  • この戦略は、SubscribeCandles(CandleType) を介して単一のローソク足シリーズをサブスクライブし、BindEx を使用して確率指標と Williams %R インジケーターをバインドします。
  • 取引の決定は、candle.State == CandleStates.FinishedIsFormedAndOnlineAndAllowTrading() が満たされた場合にのみ行われます。
  • チャート ヘルパー (DrawCandlesDrawIndicatorDrawOwnTrades) は、インジケーターと取引を視覚化するためにチャート領域が使用できるときに呼び出されます。
  • ログ ステートメント (LogInfo) は元のアラート文字列を反映しており、ライブ取引やバックテスト中の意思決定プロセスを追跡するのに役立ちます。
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;

using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Stochastic + Williams %R reversal system ported from the MetaTrader expert "TheMasterMind".
/// </summary>
public class TheMasterMindReversalStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<int> _stochasticPeriod;
	private readonly StrategyParam<int> _kPeriod;
	private readonly StrategyParam<int> _dPeriod;
	private readonly StrategyParam<int> _williamsPeriod;
	private readonly StrategyParam<decimal> _stochasticBuyThreshold;
	private readonly StrategyParam<decimal> _stochasticSellThreshold;
	private readonly StrategyParam<decimal> _williamsBuyLevel;
	private readonly StrategyParam<decimal> _williamsSellLevel;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<bool> _useTrailingStop;
	private readonly StrategyParam<decimal> _trailingStop;
	private readonly StrategyParam<decimal> _trailingStep;
	private readonly StrategyParam<DataType> _candleType;

	private StochasticOscillator _stochastic = null!;
	private WilliamsR _williams = null!;

	/// <summary>
	/// Trade volume in lots.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Total period for the stochastic oscillator.
	/// </summary>
	public int StochasticPeriod
	{
		get => _stochasticPeriod.Value;
		set => _stochasticPeriod.Value = value;
	}

	/// <summary>
	/// %K smoothing period.
	/// </summary>
	public int KPeriod
	{
		get => _kPeriod.Value;
		set => _kPeriod.Value = value;
	}

	/// <summary>
	/// %D signal period.
	/// </summary>
	public int DPeriod
	{
		get => _dPeriod.Value;
		set => _dPeriod.Value = value;
	}

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

	/// <summary>
	/// Stochastic signal threshold for longs.
	/// </summary>
	public decimal StochasticBuyThreshold
	{
		get => _stochasticBuyThreshold.Value;
		set => _stochasticBuyThreshold.Value = value;
	}

	/// <summary>
	/// Stochastic signal threshold for shorts.
	/// </summary>
	public decimal StochasticSellThreshold
	{
		get => _stochasticSellThreshold.Value;
		set => _stochasticSellThreshold.Value = value;
	}

	/// <summary>
	/// Williams %R oversold level.
	/// </summary>
	public decimal WilliamsBuyLevel
	{
		get => _williamsBuyLevel.Value;
		set => _williamsBuyLevel.Value = value;
	}

	/// <summary>
	/// Williams %R overbought level.
	/// </summary>
	public decimal WilliamsSellLevel
	{
		get => _williamsSellLevel.Value;
		set => _williamsSellLevel.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in absolute price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take-profit distance in absolute price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Enables trailing stop management.
	/// </summary>
	public bool UseTrailingStop
	{
		get => _useTrailingStop.Value;
		set => _useTrailingStop.Value = value;
	}

	/// <summary>
	/// Trailing stop distance in absolute price units.
	/// </summary>
	public decimal TrailingStop
	{
		get => _trailingStop.Value;
		set => _trailingStop.Value = value;
	}

	/// <summary>
	/// Trailing step distance in absolute price units.
	/// </summary>
	public decimal TrailingStep
	{
		get => _trailingStep.Value;
		set => _trailingStep.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
/// Initializes a new instance of the <see cref="TheMasterMindReversalStrategy"/> class.
/// </summary>
public TheMasterMindReversalStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Volume", "Base order size", "Trading")
			
			.SetOptimize(0.5m, 5m, 0.5m);

		_stochasticPeriod = Param(nameof(StochasticPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic Length", "Total lookback for stochastic", "Indicators")
			
			.SetOptimize(50, 150, 10);

		_kPeriod = Param(nameof(KPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("%K Smoothing", "Stochastic %K smoothing length", "Indicators")
			
			.SetOptimize(1, 5, 1);

		_dPeriod = Param(nameof(DPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("%D Signal", "Stochastic %D signal length", "Indicators")
			
			.SetOptimize(1, 5, 1);

		_williamsPeriod = Param(nameof(WilliamsPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("Williams %R Length", "Lookback period for Williams %R", "Indicators")
			
			.SetOptimize(50, 150, 10);

		_stochasticBuyThreshold = Param(nameof(StochasticBuyThreshold), 3m)
			.SetDisplay("Stoch Buy Threshold", "%D level required to buy", "Signals");

		_stochasticSellThreshold = Param(nameof(StochasticSellThreshold), 97m)
			.SetDisplay("Stoch Sell Threshold", "%D level required to sell", "Signals");

		_williamsBuyLevel = Param(nameof(WilliamsBuyLevel), -99.5m)
			.SetDisplay("Williams Buy Level", "Williams %R oversold level", "Signals");

		_williamsSellLevel = Param(nameof(WilliamsSellLevel), -0.5m)
			.SetDisplay("Williams Sell Level", "Williams %R overbought level", "Signals");

		_stopLoss = Param(nameof(StopLoss), 0m)
			.SetDisplay("Stop Loss", "Protective stop distance", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 0m)
			.SetDisplay("Take Profit", "Target distance", "Risk");

		_useTrailingStop = Param(nameof(UseTrailingStop), false)
			.SetDisplay("Use Trailing", "Enable trailing stop management", "Risk");

		_trailingStop = Param(nameof(TrailingStop), 0m)
			.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk");

		_trailingStep = Param(nameof(TrailingStep), 0m)
			.SetDisplay("Trailing Step", "Trailing step distance", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle series", "Trading");
	}

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

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

		Volume = TradeVolume;

		_stochastic = new StochasticOscillator();
		_stochastic.K.Length = StochasticPeriod;
		_stochastic.D.Length = DPeriod;

		_williams = new WilliamsR
		{
			Length = WilliamsPeriod
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_stochastic, _williams, ProcessSignals)
			.Start();

		// Note: BindEx passes all indicator values as IIndicatorValue

		var takeProfit = TakeProfit > 0m ? new Unit(TakeProfit, UnitTypes.Absolute) : null;
		var stopLoss = StopLoss > 0m ? new Unit(StopLoss, UnitTypes.Absolute) : null;

		if (takeProfit != null || stopLoss != null)
		{
			StartProtection(
				takeProfit: takeProfit,
				stopLoss: stopLoss,
				isStopTrailing: UseTrailingStop);
		}

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

	private void ProcessSignals(ICandleMessage candle, IIndicatorValue stochasticValue, IIndicatorValue williamsRawValue)
	{
		if (candle.State != CandleStates.Finished)
		return;

		var stochasticTyped = (StochasticOscillatorValue)stochasticValue;

		if (stochasticTyped.D is not decimal signalValue)
		return;

		var williamsValue = williamsRawValue.IsEmpty ? (decimal?)null : williamsRawValue.ToDecimal();
		if (williamsValue is null)
		return;

		if (!IsFormedAndOnlineAndAllowTrading())
		return;

		var buySignal = signalValue <= StochasticBuyThreshold && williamsValue.Value <= WilliamsBuyLevel;
		var sellSignal = signalValue >= StochasticSellThreshold && williamsValue.Value >= WilliamsSellLevel;

		if (buySignal)
		{
			LogInfo($"Buy setup detected. %D={signalValue:F2}, WilliamsR={williamsValue.Value:F2}");

			if (Position < 0)
			{
				BuyMarket(Math.Abs(Position));
			}

			if (Position <= 0)
			{
				BuyMarket(Volume);
			}

			return;
		}

		if (sellSignal)
		{
			LogInfo($"Sell setup detected. %D={signalValue:F2}, WilliamsR={williamsValue.Value:F2}");

			if (Position > 0)
			{
				SellMarket(Math.Abs(Position));
			}

			if (Position >= 0)
			{
				SellMarket(Volume);
			}
		}
	}
}