GitHub で見る

セパレートトレード戦略

概要

セパレートトレード戦略は、MetaTrader 5エキスパートアドバイザー「Separate trade」の変換です。強力な注文管理とインジケーター処理のためにStockSharpの高レベルAPIを採用しながら、オリジナルのマルチフィルターロジックを保持します。戦略はボラティリティと分散が抑制されているときに静かな市場の転換点を捉えることを試みます。同時ポジション数を制限したオリジナルコードの意図を反映して、一度に1つのネットポジションのみが維持されます。

インジケーターとデータ

  • 設定可能な方法(SMA、EMA、SMMA、またはLWMA)と共有の価格ソースを持つ2つの移動平均。
  • ロングとショートのフィルターに別々の期間と閾値を持つAverage True Range(ATR)。
  • 移動平均と同じ適用価格を使用した標準偏差、再び方向固有の期間と上限を使用。
  • 戦略を任意のタイムフレームまたはカスタムローソク足ビルダーに添付できるように、設定可能なDataTypeパラメーターでローソク足が供給されます。

パラメーター

パラメーター 説明 デフォルト
TradeVolume ロットで表現された注文サイズ。 1
SlowMaPeriod 遅い移動平均の期間。 65
FastMaPeriod 速い移動平均の期間。 14
MaMethod 両方の移動平均に適用される平滑化方法(SimpleExponentialSmoothedLinearWeighted)。 Exponential
PriceType 移動平均と標準偏差の価格入力(CloseOpenHighLowMedianTypicalWeighted)。 Close
StopLossBuyPips / StopLossSellPips ロングとショートのトレードのストップロス距離(pips)(0はストップを無効にします)。 50
TakeProfitBuyPips / TakeProfitSellPips ロングとショートのトレードのテイクプロフィット距離(pips)(0はテイクプロフィットを無効にします)。 50
TrailingStopPips pipsでのトレーリングストップ距離。 5
TrailingStepPips トレーリングストップが移動する前の最小利益前進(pips)。トレーリングが有効な場合は正でなければなりません。 5
MaxPositions 許可される最大同時ネットポジション数。StockSharpバージョンは値が1より大きい場合でも単一の集計ポジションで動作します。 1
DeltaBuyPips / DeltaSellPips 高速と低速の移動平均間の最大許容距離(方向ごと)。ゼロの値は距離フィルターを無効にします。 2
AtrPeriodBuy / AtrPeriodSell ロングとショートのフィルターのATRルックバック期間。 26
AtrLevelBuy / AtrLevelSell トレードにエントリーする前に超えてはならない上部ATR閾値。 0.0016
StdDevPeriodBuy / StdDevPeriodSell ロングとショートのフィルターの標準偏差ルックバック期間。 54
StdDevLevelBuy / StdDevLevelSell トレードにエントリーする前に超えてはならない標準偏差の上限。 0.0051
CandleType サブスクリプションで使用されるローソク足データタイプ。 TimeSpan.FromMinutes(15)

トレードロジック

  1. バー同期 – 戦略は設定されたサブスクリプションから受信した完成したローソク足でのみ動作します。これはMetaTraderスクリプトのOnTick新しいバーガードを複製します。
  2. インジケーターフィルター – ロングエントリーの場合、低速MAは高速MAより下でなければならず、ATRはAtrLevelBuy未満、標準偏差はStdDevLevelBuy未満、MA距離はDeltaBuyPips未満(デルタが正の場合)でなければなりません。ショートエントリーは条件を逆転し、独自のATRと偏差パラメーターを使用します。
  3. ポジションゲーティング – オープンポジションがなく、それぞれの側の最後のエントリー時刻が現在のローソク足より古い場合にのみトレードが行われます。これにより同じバー内での再エントリーが防止され、ソースEAのm_last_deal_IN_*チェックに一致します。
  4. 注文実行 – 成行注文は設定されたボリュームで配置されます。逆転トレードは、MQLの反対エクスポージャーをクローズする動作に一致するVolume + Math.Abs(Position)量のおかげで、新しいものを開く前に現在のポジションを自動的にフラット化します。

リスク管理

  • pip変換 – pip距離はインストゥルメントのPriceStepを使用して変換されます。3桁または5桁の小数で引用されるインストゥルメントの場合、pipサイズはPriceStep * 10に等しく、オリジナルのdigits_adjustロジックを反映します。
  • ストップロス / テイクプロフィット – 戦略は価格レベルを内部で追跡し、ローソク足の範囲が指定されたストップまたはターゲットに触れると退出します。pip距離をゼロに設定することで両方を無効にできます。
  • トレーリングストップ – 価格がTrailingStopPips + TrailingStepPipsを超えて前進すると、ストップはトレーリング距離を維持するために移動します。トレーリングステップの要件はMetaTrader実装と一致し、ストップを無視できる量だけ移動することを避けます。

実装ノート

  • StockSharpがデフォルトでネットポジションで動作するため、戦略は単一の集計ポジションを使用します。MaxPositionsパラメーターは互換性のために保持されていますが、1を超えると現在のポジションがクローズされるまで新しいエントリーが単純に防止されます。
  • インジケーター値はStockSharpインジケータークラスとBindインフラストラクチャを使用して計算され、プロジェクトガイドラインに従って手動のバッファーアクセスを避けます。
  • 変換はすべてのコメントを英語で維持し、各オリジナル入力を専用のStrategyParamにマッピングして、最適化とDesigner統合が利用可能なままにします。
  • TrailingStopPipsが正の場合、TrailingStepPipsも正でなければなりません。この要件が違反された場合、コードは戦略を早期に停止してエラーメッセージを書き込み、MQLエキスパートのセキュリティチェックを再現します。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Separate trade strategy using dual EMA crossover with ATR volatility filter.
/// Trades when fast EMA crosses slow EMA and ATR confirms adequate volatility.
/// </summary>
public class SeparateTradeStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _atrPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

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

		_fastPeriod = Param(nameof(FastPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 65)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "Indicators");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period for volatility filter", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

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

		_prevFast = null;
		_prevSlow = null;

		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
		var atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, 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 fast, decimal slow, decimal atr)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fast > slow;

		_prevFast = fast;
		_prevSlow = slow;

		// Require minimum ATR relative to price
		if (atr < candle.ClosePrice * 0.0005m)
			return;

		// Golden cross
		if (!prevAbove && currAbove)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Death cross
		else if (prevAbove && !currAbove)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}
	}
}