GitHub で見る

2 つの未決注文 2

概要

この戦略は、MetaTrader エキスパート アドバイザー 「2 つの保留中の注文 2」 の StockSharp 移植です。市場価格を中心に 2 つの対称的な未決注文を保持し、最初にトリガーされた側が設定可能なストップロス、テイクプロフィット、およびトレーリングルールを使用して取引を管理できるようにします。変換では高レベルの StockSharp API が使用され、元のアルゴリズムの核となるアイデアを維持しながら、戦略パラメータを通じてすべての調整ノブが公開されます。

取引ロジック

  1. この戦略は、選択したローソク足シリーズ (デフォルトでは毎日のローソク足) をサブスクライブします。ローソク足が終了すると、それが次の取引サイクルの決定点になります。
  2. 有効な保留中の注文は、期限切れになるか、新しい注文が発注される前にキャンセルされます。これにより、市場には最も新鮮なレベルのみが存在することが保証されます。
  3. 現在のスプレッドが許容しきい値内にあり、アクティブなポジション/注文数が設定された制限を下回っている場合、ストラテジーは 2 つの対称未決注文を配置します。
    • ストップ モード (デフォルト) は、市場の上に買いストップを配置し、市場の下に売りストップを配置します。
    • 指値モードは、市場よりも低い買い指値とそれより高い売り指値を設定します。
    • Reverse Levels フラグは注文タイプを交換して、元の EA のリバース スイッチを複製します。
  4. エントリー価格は、Pending Indent パラメーターによって現在のビッド/アスクからオフセットされます。注文が既存のポジションから Min Step 距離よりも近い場合、注文はスキップされます。
  5. 保留中の注文は、指定された分数後に期限切れになる場合があります。有効期限が切れると、残りの注文はすべてキャンセルされます。

ポジション管理

  • 注文が約定されると、戦略は対応するサイドの平均エントリー価格と出来高を追跡します。反対の約定は、新しいポジションをオープンする前に、既存のポジションを減らすかクローズします。
  • この戦略は、価格が次のいずれかの条件に達したときにロング ポジションを終了します。
    • 価格は平均エントリー価格を下回るストップロス距離に達します。
    • 価格は平均エントリー価格を上回る利益確定距離に達します。
    • トレーリングストップは、利益が有効化しきい値を超えた後に有効化され、その後価格がトレーリングレベルに戻ります(段階的に移動します)。
  • ショートトレードでは、価格を反転したミラールールを使用します。
  • Only One Position が有効になっている場合、エンジンは現在のエクスポージャーがクローズされるまで待機してから、新しい未決注文が発注されます。

パラメーター

名前 説明
StopLossPoints 保護ストップロスまでの距離 (ポイント単位) (0 は無効にします)。
TakeProfitPoints テイクプロフィットターゲットまでの距離 (ポイント単位) (0 は無効にします)。
MaxPositions 同時にアクティブなポジションと未決注文の最大数。
MinStepPoints 既存の取引のエントリー価格と新しい未決注文の間の最小距離。
TrailingActivatePoints トレーリングストップを有効にする利益のしきい値 (0 はトレーリングを無効にします)。
TrailingStopPoints 有効化された後の市場価格とトレーリングストップの間の距離。
TrailingStepPoints トレーリングストップを再度移動させるために必要な最低価格改善。
TradeMode 新しい未決注文に許可される方向: BuySell、または BuySell
PendingType 発注する未決注文のタイプ: Stop または Limit
PendingExpirationMinutes 保留中の注文の存続期間 (分単位) (0 は手動で約定またはキャンセルされるまで保持されます)。
PendingIndentPoints 未決注文価格の計算に使用される現在のビッド/アスクからのオフセット。
PendingMaxSpreadPoints 未決注文を出すための買値と売値の間の最大許容スプレッド (0 はフィルタを無効にします)。
OnlyOnePosition true の場合、現在のポジションがクローズされるまで新しい取引を開始できなくなります。
ReverseLevels 買い注文と売り注文の配置を交換して、元の EA のリバース モードを反映します。
CandleType 信号評価をトリガーするために使用される時間枠 (デフォルトでは毎日)。

注意事項

  • 価格距離はポイントで表され、自動的に商品ティックサイズに変換されます。
  • この戦略は、注文の登録に StockSharp ヘルパー メソッド (BuyStopSellStopBuyLimitSellLimit) を使用し、新しい決定が行われるたびに書籍をリセットするために CancelActiveOrders を使用します。
  • トレーリングストップロジックは、終了したローソク足で評価されます。バー内のトレーリング動作には、より短い CandleType を使用します。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "Two pending orders 2" MetaTrader expert.
/// Places symmetric breakout levels around recent range and enters on breakout.
/// Uses high/low of N bars as breakout boundaries.
/// </summary>
public class TwoPendingOrders2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _lookback;

	private readonly Queue<decimal> _highs = new();
	private readonly Queue<decimal> _lows = new();

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

	public int Lookback
	{
		get => _lookback.Value;
		set => _lookback.Value = value;
	}

	public TwoPendingOrders2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for breakout detection", "General");

		_lookback = Param(nameof(Lookback), 10)
			.SetGreaterThanZero()
			.SetDisplay("Lookback", "Number of bars for high/low range", "Indicators");
	}

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

		_highs.Clear();
		_lows.Clear();

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

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

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

		if (_highs.Count < Lookback)
		{
			EnqueueCandle(candle);
			return;
		}

		decimal highest = decimal.MinValue;
		decimal lowest = decimal.MaxValue;
		var highs = _highs.ToArray();
		var lows = _lows.ToArray();
		foreach (var h in highs)
			if (h > highest) highest = h;
		foreach (var l in lows)
			if (l < lowest) lowest = l;

		var close = candle.ClosePrice;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;
		var range = highest - lowest;
		var breakoutPadding = range * 0.05m;

		// Breakout above range
		if (close > highest + breakoutPadding)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		// Breakout below range
		else if (close < lowest - breakoutPadding)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		EnqueueCandle(candle);
	}

	private void EnqueueCandle(ICandleMessage candle)
	{
		_highs.Enqueue(candle.HighPrice);
		_lows.Enqueue(candle.LowPrice);

		if (_highs.Count > Lookback)
		{
			_highs.Dequeue();
			_lows.Dequeue();
		}
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_highs.Clear();
		_lows.Clear();

		base.OnReseted();
	}
}