GitHub で見る

Parabolic SAR バグ 2 戦略

概要

Parabolic SAR バグ 2 戦略 は、フォルダ MQL/9503 からの MetaTrader エキスパート アドバイザー pSAR_bug2 の StockSharp の高レベルの変換です。元の EA は、価格の反対側に表示される最初の Parabolic SAR ドットに反応します。ドットが終値を下回ると、システムはショート取引をクローズし、すぐにロングポジションをオープンします。ドットが終値を超えてジャンプすると、ロジックはショートサイドの動作を反映します。保護的なストップロスとテイクプロフィットのレベルは、値が商品 Point のサイズで乗算される MetaTrader とまったく同様に、生の価格ポイントで計算されます。

StockSharp ポートは、フレームワークの高レベルの API を利用しながら、同じインテントを維持します。完成したローソク足をサブスクライブし、Parabolic SAR インジケーターを設定可能な加速パラメータにバインドし、ドット反転を監視し、以前のエクスポージャーを平坦化し、新しい取引を確立するサイズの成行注文を送信します。

取引ロジック

  1. インジケーターの準備。この戦略は、ユーザー定義のローソク足タイプ (デフォルトでは 15 分の時間枠) をサブスクライブし、Parabolic SAR を加速ステップ SarStep と最大加速 SarMaximum でバインドします。
  2. 状態追跡。最初に完了したローソク足で、アルゴリズムは SAR の値が終値より上か下かを記録します。新しいローソク足はすべて、新しい SAR の位置を以前に保存された状態と比較します。
  3. エントリールール
    • ロングエントリー: SAR が終値の上から終値の下に移動したときにトリガーされます。注文量は TradeVolume + |Position| として計算されるため、既存のショート ポジションは 1 回の成行注文でクローズされ、取り消されます。エントリー後、ストップロスとテイクプロフィットのレベルはローソク足の終値を基準にして保存されます。
    • ショートエントリー: SAR が終値の下から終値の上に移動したときにトリガーされます。既存のロングポジションはフラット化され、同じ合計サイズの計算式で市場で新しいショート取引が入力されます。
  4. 保護出口。完了したすべてのローソク足で、保存されたストップロスとテイクプロフィットのレベルが高値/安値と比較されます。価格が保護レベルを突破した場合、ストラテジーはオープンポジションを閉じる成行注文を送信し、キャッシュされたストップとテイクの値をリセットします。

リスク管理

  • ストップロスとテイクプロフィットの距離は、設定された StopLossPoints または TakeProfitPoints に証券価格ステップを乗算することにより、生の価格ポイントで計算されます。商品が価格ステップを公開しない場合は、0.0001 の保守的なフォールバックが使用されます。
  • この戦略は注文を送信する前に IsFormedAndOnlineAndAllowTrading() をチェックし、市場データがオンラインであり、取引が許可されていることを確認します。
  • 反転エントリーには常に絶対的な現在のポジションサイズが含まれており、反対取引を確立する前に新しい注文が以前のエクスポージャーを平坦化することが保証されます。

パラメーター

名前 デフォルト 説明
TradeVolume 0.1 ロット単位の基本注文量。同じ値が内部の Strategy.Volume プロパティに割り当てられます。
StopLossPoints 90 価格ポイントでのストップロス距離。この距離に商品価格ステップを乗算して、実際の価格オフセットを取得します。
TakeProfitPoints 20 商品価格ステップを通じて変換された価格ポイントでのテイクプロフィットディスタンス。
SarStep 0.001 Parabolic SAR インジケーターの初期加速係数。
SarMaximum 0.2 Parabolic SAR インジケーターの最大加速係数。
CandleType 15m time-frame 計算と信号評価に使用されるキャンドル タイプ。

変換時の注意点

  • MetaTrader のブローカー側のストップロス注文とテイクプロフィット注文は、ローソク足の極値を監視し、しきい値を超えたときに市場からの撤退を送信することによってエミュレートされます。
  • MetaTrader EA では、OrdersTotal() と明示的な OrderClose() 呼び出しを手動で管理する必要がありました。 StockSharp バージョンは、TradeVolume + |Position| のサイズの単一の成行注文を送信することで同じ動作を実現します。これにより、反対のポジションがあればクローズされ、新しいポジションがオープンされます。
  • タスクリクエストに一致する Python 実装は提供されていません。現在、フォルダーには C# バージョンのストラテジーのみが含まれています。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Parabolic SAR Bug2: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class ParabolicSarBug2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public ParabolicSarBug2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 30)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
	public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

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

		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
		}

		_prevFast = fastVal; _prevSlow = slowVal;
	}
}