GitHub で見る

Expotest戦略

概要

Expotest戦略はオリジナルのExpotest.mq5エキスパートアドバイザーのStockSharpへの直接変換です。パラボリックSARインジケーターとシンプルなマーチンゲール風のマネー管理ルールを使用して単一のインストゥルメントを取引します。戦略は一度に1つのポジションのみを開き、エグジットのために事前に定義されたストップロスとテイクプロフィットレベルに依存します。

トレーディングロジック

  • インジケーター: 選択されたローソク足シリーズで計算されるパラボリックSAR。加速係数(SarStep)と最大加速(SarMaximum)の両方が設定可能です。
  • エントリー条件: ポジションが開いていないとき、戦略は最後に閉じたローソク足を確認します。
    • パラボリックSARの値が終値以下の場合、ロングポジションが開始されます。
    • パラボリックSARの値が終値以上の場合、ショートポジションが開始されます。
  • エグジット条件: ストップロスとテイクプロフィットレベルは、価格ステップで測定されたエントリー価格からの固定距離に配置されます。新しいローソク足ごとに、戦略はローソク足の範囲がどちらかのレベルに触れるかどうかを監視し、それに応じてポジションを閉じます。エグジットタイプ(利益または損失)は将来のポジションサイジング決定のために記憶されます。

ポジションサイジング

  • 基本ボリューム: ゼロより大きい場合はFixedVolumeパラメーターで定義されます。そうでない場合、戦略は現在のポートフォリオエクイティを使用してRiskPercentStopLossPointsの値からサイズを推定します。どちらの方法も有効なサイズを返さない場合、デフォルトのStrategy.Volumeが使用されます。
  • マーチンゲールステップ: 負けトレードの後、次のポジションサイズは負けポジションのボリュームと比較して2倍になります。利益のあるエグジットは乗数をリセットし、次の注文は基本ボリュームを再び使用します。

設定可能なパラメーター

  • CandleType – ローソク足集計のデータタイプ(時間軸または他のローソク足フォーマット)。
  • SarStep – パラボリックSARの初期加速係数。
  • SarMaximum – パラボリックSARの最大加速係数。
  • StopLossPoints – 価格ステップで表されるエントリーからのストップロス距離。
  • TakeProfitPoints – 価格ステップで表されるエントリーからのテイクプロフィット距離。
  • RiskPercent – 動的サイジングが有効な場合、取引ごとにリスクをとるポートフォリオエクイティのパーセンテージ。
  • FixedVolume – 明示的な注文ボリューム。リスクベースのサイジングを有効にするには0に設定。

追加ノート

  • 戦略はStockSharpサブスクリプションとの互換性を保ちながら、オリジナルのティックベースのMQL実装に近い状態を保つために、完了したローソク足のみを処理します。
  • 保護レベルは別のストップ/リミット注文の代わりに内部的に追跡され、ロジックを透明でバックテストしやすい状態に保ちます。
  • Pythonの実装は要求に従って意図的に省略されています。
namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// Parabolic SAR based strategy converted from the Expotest MQL expert advisor.
/// Enters long when SAR is below price, short when SAR is above price.
/// </summary>
public class ExpotestStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _sarStep;
	private readonly StrategyParam<decimal> _sarMaximum;

	private bool _prevSarBelow;
	private bool _initialized;

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

	public decimal SarStep
	{
		get => _sarStep.Value;
		set => _sarStep.Value = value;
	}

	public decimal SarMaximum
	{
		get => _sarMaximum.Value;
		set => _sarMaximum.Value = value;
	}

	public ExpotestStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for signal generation", "General");

		_sarStep = Param(nameof(SarStep), 0.02m)
			.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Indicators");

		_sarMaximum = Param(nameof(SarMaximum), 0.2m)
			.SetDisplay("SAR Maximum", "Maximum acceleration factor for Parabolic SAR", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevSarBelow = false;
		_initialized = false;
	}

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

		_prevSarBelow = false;
		_initialized = false;

		var sar = new ParabolicSar
		{
			Acceleration = SarStep,
			AccelerationMax = SarMaximum
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sar, OnProcess)
			.Start();

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var sarBelow = sarValue < candle.ClosePrice;

		if (!_initialized)
		{
			_prevSarBelow = sarBelow;
			_initialized = true;
			return;
		}

		// SAR flipped from above to below price => Buy signal
		if (sarBelow && !_prevSarBelow && Position <= 0)
		{
			BuyMarket();
		}
		// SAR flipped from below to above price => Sell signal
		else if (!sarBelow && _prevSarBelow && Position >= 0)
		{
			SellMarket();
		}

		_prevSarBelow = sarBelow;
	}
}