GitHub で見る

ピンボール マシンのランダム抽選戦略

概要

この戦略は、MetaTrader 4 エキスパート アドバイザー Pinball_machine.mq4 の直接の StockSharp 変換です。オリジナルロボットが描きました 受信ティックごとにランダムな整数を生成し、それらのドローのうち 2 つが一致するたびに成行注文を開きます。 StockSharp バージョン 同じ宝くじスタイルの動作を保持します。選択した時間枠の終了したローソク足ごとに、アルゴリズムは 2 つのペアを実行します。 対応するペアに等しい値が含まれている場合、ランダムにドローし、ロングまたはショートの市場ポジションを入力します。ストップロスとテイクプロフィット 距離も評価ごとにランダム化され、トレードが跳ね返って元の「ピンボール」ルーチンの感覚を再現します。 予想外にアウト。

取引ロジック

  • CandleType パラメータで定義されたローソク足をサブスクライブし、完全に形成されたバーを待ちます。
  • 完成したキャンドルごとに、[0, RandomMaxValue] に均等に分散された 4 つの整数が生成されます。最初のペアは、 潜在的なロングエントリーの場合、2 番目のペアは潜在的なショートエントリーに属します。
  • MinStopLossPoints/MaxStopLossPointsMinTakeProfitPoints/MaxTakeProfitPoints の間の 2 つの追加の整数を描画して、 評価の両側に共有される保護距離 (価格ステップで表される) を決定します。
  • 1 番目と 2 番目のランダムな整数が一致する場合は、数量 TradeVolume の成行買い注文を送信します。 3番目と4番目の場合 値が一致する場合は、同じ数量で成行売り注文を送信します。どちらの条件も同じキャンドル内で点火できます。 MQL バージョンでは、買い注文と売り注文が独立したイベントでした。
  • すぐにストップロス注文と利食い注文を追加します (描画距離がゼロより大きい場合)。距離が解釈される 金融商品の PriceStep の倍数として、MetaTrader で使用される Point 乗数をミラーリングします。

注文管理とリスク管理

  • StartProtection() は戦略の開始時に呼び出され、StockSharp が戦略に代わって保護命令を管理します。
  • 各エントリは結果の位置 (Position ± TradeVolume) を測定し、それを SetStopLossSetTakeProfit に渡します。 これにより、複数の取引が同時に実行されている場合でも、プラットフォームで保護注文を統合できます。
  • 最小距離パラメータまたは最大距離パラメータのいずれかがゼロまたは負の数値に設定されている場合、対応する保護は次のようになります。 そのサイクルではスキップされました。

パラメーター

パラメータ 説明
TradeVolume ランダムなエントリごとに送信されたロット/契約の注文サイズ。
CandleType ランダムな抽選をトリガーするローソク足の時間枠。期間を短くすると、元のティックベースの EA がより厳密にエミュレートされます。
RandomMaxValue 整数描画の包括的な上限。値が大きいほど、数字が一致する確率が低下するため、取引頻度が減少します。
MinStopLossPoints ランダムに生成されたストップロス距離の下限 (価格ステップ単位)。
MaxStopLossPoints ストップロス距離の上限(価格ステップ単位)。
MinTakeProfitPoints ランダムに生成されたテイクプロフィットディスタンスの下限(価格ステップ単位)。
MaxTakeProfitPoints 利益確定距離の上限 (価格ステップ単位)。
RandomSeed 擬似乱数生成器のシード。ゼロの場合は動作を時間ベースに保ち、その他の値を指定するとシーケンスが再現可能になります。

実装メモ

  • MetaTrader スクリプトはティック駆動でした。高レベルの API は時系列イベントで動作するため、StockSharp ポートはローソク足の完了を使用します。非常に短い CandleType (たとえば、1 秒またはティック ローソク足) を設定すると、元のペースの速い性質が復元されます。
  • ストップロスとテイクプロフィットの値は、ソース EA とまったく同様に、評価ごとに 1 回生成され、ロングブランチとショートブランチの両方で再利用されます。
  • 取引される商品が有効な PriceStep を露出していることを確認してください。そうでない場合は、ポイントで表される保護距離を手動で調整する必要がある場合があります。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Pinball Machine: Pseudo-random entry with ATR-based risk management.
/// Uses candle hash to generate deterministic random signals.
/// </summary>
public class PinballMachineRandomDrawStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;

	private decimal _entryPrice;
	private int _candleCount;

	public PinballMachineRandomDrawStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

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

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_entryPrice = 0;
		_candleCount = 0;
	}

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

		_entryPrice = 0;
		_candleCount = 0;

		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (atrVal <= 0)
			return;

		_candleCount++;
		var close = candle.ClosePrice;

		// Exit management
		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 1.5m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 1.5m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Pseudo-random entry based on candle price hash
		if (Position == 0)
		{
			var hash = (int)(close * 100m) ^ _candleCount;
			var mod = Math.Abs(hash) % 10;

			if (mod < 3)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (mod > 6)
			{
				_entryPrice = close;
				SellMarket();
			}
		}
	}
}