GitHub で見る

Psar バグ戦略

概要

Psar Bug Strategy は、MetaTrader 4 エキスパート アドバイザー pSAR_bug.mq4 の直接移植です。価格の反対側に表示される最初の Parabolic SAR ドットに反応し、すぐにポジションを逆転させます。 StockSharp 実装はローソク足をサブスクライブし、完了したバーのみを評価し、高レベルの API を使用して成行注文を出し、保護ストップを管理します。

取引ロジック

  • 加速ステップ 0.02 と最大加速度 0.2 を使用して Parabolic SAR を計算します (両方とも構成可能)。
  • Parabolic SAR の値が終値に対して反転する終了したローソク足を待ちます。
    • ロングエントリー: 現在の SAR の値は終値を下回っていますが、前の SAR の値は前の終値を上回っています。
    • 簡単なエントリ: 現在の SAR の値は終値を上回っていますが、前の SAR の値は前の終値を下回っています。
  • すべての信号で既存の露出を反転します。買いシグナルが表示されると、オープンしているショート ポジションはフラット化され、設定されたサイズのロング ポジションに置き換えられます。売りシグナルにはその逆が当てはまります。
  • 商品の価格ステップで表される固定のストップロスとテイクプロフィット距離を適用します。保護は StartProtection で実装されるため、リスク パラメータはすべての新しいポジションに自動的に付加されます。

パラメーター

名前 説明
TradeVolume エントリーに使用されるロットの注文量。デフォルト値は 0.1 ロットです。
StopLossPoints エントリー価格からストップロスまでの距離を価格ステップで表します。 MetaTrader StopLoss 入力をミラーリングします。
TakeProfitPoints エントリー価格からテイクプロフィットまでの距離を価格ステップで表します。 MetaTrader TakeProfit 入力をミラーリングします。
SarAccelerationStep Parabolic SAR インジケーターの初期加速係数。
SarAccelerationMax Parabolic SAR 計算の最大加速係数。
CandleType インジケーターの計算に使用されるローソク足のデータ タイプ (タイムフレーム)。デフォルトでは、この戦略は 15 分足のローソク足で機能します。

変換時の注意点

  • オリジナルの専門家は、現在のチャートのシンボルと時間枠を直接参照します。 StockSharp バージョンはローソク足のタイプをパラメータとして公開しているため、再コンパイルせずにタイムフレームを変更できます。
  • プロテクティブストップは絶対価格オフセットとして表されます。これらは起動時に一度初期化され、プラットフォームによって自動的に管理されます。
  • 注文管理はネッティング ロジックに依存しています。Volume + |Position| ロットを購入すると、前のショートがクローズされ、新しいロングがオープンされ、反対方向にオープンする前にクローズするという MetaTrader の動作が再現されます。

使用法

  1. StockSharp デザイナーまたはバックテスター内で、目的のセキュリティ、タイムフレーム (CandleType)、およびリスク パラメーターを構成します。
  2. 市場データが利用可能であることを確認して戦略を開始してください。シグナルは完成したキャンドルのみで評価されます。
  3. 標準の StockSharp ツールを通じて位置とパフォーマンスを監視します。チャートには、ローソク足、Parabolic SAR インジケーター、および反転シグナルを視覚的に検証するために実行された取引が表示されます。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// PsarBug: EMA trend with RSI confirmation and ATR stops.
/// </summary>
public class PsarBugStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevClose;
	private decimal _entryPrice;

	public PsarBugStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_emaLength = Param(nameof(EmaLength), 30)
			.SetDisplay("EMA Length", "Trend filter.", "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 EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.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();

		_prevClose = 0;
		_entryPrice = 0;
	}

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

		_prevClose = 0;
		_entryPrice = 0;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		var close = candle.ClosePrice;

		if (_prevClose == 0 || atrVal <= 0)
		{
			_prevClose = close;
			return;
		}

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || close < emaVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || close > emaVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (close > emaVal && _prevClose <= emaVal && rsiVal > 50)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < emaVal && _prevClose >= emaVal && rsiVal < 50)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevClose = close;
	}
}