GitHub で見る

デイトレードインパルス戦略

概要

DayTrading Strategy は、2005 年に NazFunds によってリリースされた古典的な MetaTrader 4 エキスパート アドバイザー「DayTrading」を忠実に C# 変換したものです。元のロボットは 5 分外国為替チャート用に設計されており、複数の勢いとトレンド追跡指標を組み合わせて、控えめな固定ターゲットとオプションのトレーリング ストップで短期的な方向性の動きを捕捉します。この StockSharp 実装は、コアの意思決定ロジックを再現すると同時に、すべての重要なしきい値を戦略パラメータとして公開し、さまざまな手段に最適化または適応できるようにします。

インジケータースタック

この戦略は、選択したローソク足シリーズの 4 つの指標を評価します。

  • Parabolic SAR (ParabolicSar)、加速度、増分、上限を設定可能。これはベースラインのトレンドの方向を定義し、新しいエントリーを可能にするために価格を上下に反転する必要があります。
  • MACD (12、26、9) (MovingAverageConvergenceDivergenceSignal)。 MACD ラインは、ロングの場合はシグナル ラインより下に、ショートの場合はシグナル ラインより上にある必要があり、MQL の元のヒストグラムとシグナルの比較を反映しています。
  • Stochastic オシレーター (5、3、3) (StochasticOscillator)。市場が売られ過ぎ/買われ過ぎゾーンから確実に抜け出すために、%K ラインはロングの場合は 35 未満、ショートの場合は 60 を超える必要があります。
  • 勢い (14) (Momentum)。 MT4 スクリプトとまったく同様に、100 未満の値はロング取引を許可し、100 を超える値はショートを許可します。

すべてのインジケーターは高レベルの BindEx パイプラインを通じて処理されるため、手動のバッファ管理や履歴インデックス作成は必要ありません。

取引ルール

エントリー条件

ロング ポジションは、最後に完成したローソク足で次のすべてが当てはまる場合にオープンされます。

  1. Parabolic SAR のドットは現在の売値以下で印刷されます。かつ、前のドットは現在のドットよりも上にありました (新鮮な SAR が強気に反転)。
  2. 運動量は100を下回っています。
  3. MACD ラインは信号ラインの下にあります。
  4. Stochastic %K は 35 未満です。

ショート ポジションは、対称条件が満たされるとオープンされます。

  1. Parabolic SAR ドットは現在の入札価格以上で印刷されます ** かつ**、前のドットは現在のドットよりも下でした (弱気反転)。
  2. 勢いは100を超えています。
  3. MACD ラインは信号ラインの上にあります。
  4. Stochastic %K は 60 を超えています。

一度にオープンできるポジションは 1 つだけです。反対のシグナルが現れるたびに、既存のポジションはクローズされ、同じローソク足での再エントリーは行われません。これは、OrdersTotal スキャンによって即時のリロードが妨げられる MetaTrader の実装と同様です。

出口管理

  • ストップロス/テイクプロフィット: オプションの固定距離(ポイント単位)は、商品のティックサイズを使用して絶対価格に変換されます。これらはローソク足ごとに再評価され、イントラバーを突破した場合はポジションを閉じます。
  • トレーリングストップ: 価格が設定されたポイント数だけ上昇すると、トレーリングストップが有効になります。ロングトレードの場合、ストップは終値を下回ります。短期トレードの場合、終値を上回ります。ストップが後退することはないため、利益は徐々に固定されます。
  • オポジットシグナル: 有効なオポジット設定は、新しいエントリーが検討される前に、現在のポジションを即座に清算します。

追加のグリッド、スケーリング、またはヘッジ ロジックは追加されません。この戦略は、元の EA と同じくらい軽量かつ決定的です。

パラメーター

パラメータ デフォルト 説明
LotSize 1 各成行注文のボリューム。 Strategy.Volume プロパティは起動時にこの値に同期されます。
TrailingStopPoints 15 後続距離 (ポイント単位)。末尾を無効にするには、ゼロに設定します。
TakeProfitPoints 20 テイクプロフィット距離をポイント単位で固定しました。ターゲットを削除するには、ゼロに設定します。
StopLossPoints 0 保護停止距離 (ポイント単位)。 Zero は、元の「停止しない」動作を再現します。
SlippagePoints 3 最大実行スリッページ プレースホルダー (MT4 入力との互換性のため)。自動的には適用されませんが、完全を期すために保持されます。
CandleType 5分の時間枠 すべてのインジケーターで使用されるローソク足シリーズ。 EA の元の推奨事項と一致するように、M5 のままにしてください。
MacdFastPeriod 12 MACD 計算での高速な EMA の長さ。
MacdSlowPeriod 26 MACD の計算における EMA の長さが遅いです。
MacdSignalPeriod 9 MACD 計算における信号 EMA の長さ。
StochasticLength 5 Stochastic オシレーターの %K ルックバック長。
StochasticSignal 3 %D スムージングの長さ。
StochasticSlow 3 %K ラインに追加の減速が適用されました。
MomentumPeriod 14 勢いのある振り返り丈。
SarAcceleration 0.02 Parabolic SAR の初期加速係数。
SarStep 0.02 新しい極値が発生するたびに加速係数に適用される増分。
SarMaximum 0.2 Parabolic SAR の最大加速係数。

すべての数値パラメータは、SetCanOptimize(true) ヒントのおかげで、StockSharp の最適化ワークフローを通じて最適化できます。

実装メモ

  • 買値/売値は、利用可能な場合はライブのレベル 1 データから導出されます。それ以外の場合は、ローソク足の終値がフォールバックとして機能するため、履歴テストにおいてロジックが堅牢なままになります。
  • ポイント変換は商品の Step/PriceStep に依存します。何も指定されない場合は、標準の Forex pip と一致する保守的な 0.0001 フォールバックが使用されます。
  • ポジション管理は MT4 EA を反映しています。戦略は決してピラミッド化せず、両方向を同時に保持することもありません。
  • コード内のコメントはプロジェクト ガイドラインごとに英語で記載されていますが、この README にはオンボーディングを容易にするための拡張ドキュメントが含まれています。

使用のヒント

  1. 目的の外国為替ペアを戦略に割り当て、ローソク足のタイプを 5 分のままにして、戦略を開始します。インジケーターは自動的にウォームアップします。
  2. ライブデータで実行する場合は、ゼロ以外のストップロスを有効にすることを検討してください。元のスクリプトではストップロスなしで取引することを推奨していましたが、トレーリングストップだけではリスク制御に十分ではない可能性があります。
  3. アルゴリズム ポートフォリオの場合、この戦略を BasketStrategy に追加して資本配分を外部で管理しながら、最適化のために公開されたパラメータの恩恵を受けることができます。

このドキュメントは、同じフォルダー内のロシア語と中国語の翻訳とともに、変換されたロジックの完全な透明性を提供します。

namespace StockSharp.Samples.Strategies;

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

/// <summary>
/// Intraday trend strategy converted from the MetaTrader "DayTrading" expert advisor.
/// Combines Parabolic SAR, MACD, Stochastic and Momentum filters with trailing exits.
/// </summary>
public class DayTradingImpulseStrategy : Strategy
{
	private readonly StrategyParam<decimal> _lotSize;
	private readonly StrategyParam<decimal> _trailingStopPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _slippagePoints;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _macdFastPeriod;
	private readonly StrategyParam<int> _macdSlowPeriod;
	private readonly StrategyParam<int> _macdSignalPeriod;
	private readonly StrategyParam<int> _stochasticLength;
	private readonly StrategyParam<int> _stochasticSignal;
	private readonly StrategyParam<int> _stochasticSlow;
	private readonly StrategyParam<decimal> _stochasticBuyThreshold;
	private readonly StrategyParam<decimal> _stochasticSellThreshold;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<decimal> _momentumNeutralLevel;
	private readonly StrategyParam<decimal> _sarAcceleration;
	private readonly StrategyParam<decimal> _sarStep;
	private readonly StrategyParam<decimal> _sarMaximum;

	private ParabolicSar _parabolicSar = null!;
	private MovingAverageConvergenceDivergenceSignal _macd = null!;
	private StochasticOscillator _stochastic = null!;
	private Momentum _momentum = null!;

	private decimal? _previousSar;
	private decimal? _longStopPrice;
	private decimal? _shortStopPrice;
	private decimal? _longTakeProfit;
	private decimal? _shortTakeProfit;
	private decimal? _longEntryPrice;
	private decimal? _shortEntryPrice;
	private decimal _pointSize;

	/// <summary>
	/// Initializes a new instance of <see cref="DayTradingImpulseStrategy"/>.
	/// </summary>
	public DayTradingImpulseStrategy()
	{
		_lotSize = Param(nameof(LotSize), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Order Volume", "Trade volume used for each market entry", "Trading")
			;

		_trailingStopPoints = Param(nameof(TrailingStopPoints), 15m)
			.SetNotNegative()
			.SetDisplay("Trailing Stop (points)", "Distance used to trail profitable positions", "Risk")
			;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 20m)
			.SetNotNegative()
			.SetDisplay("Take Profit (points)", "Fixed profit target measured in points", "Risk")
			;

		_stopLossPoints = Param(nameof(StopLossPoints), 0m)
			.SetNotNegative()
			.SetDisplay("Stop Loss (points)", "Protective stop distance measured in points", "Risk")
			;

		_slippagePoints = Param(nameof(SlippagePoints), 3m)
			.SetNotNegative()
			.SetDisplay("Slippage (points)", "Maximum acceptable execution slippage", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Time frame used for indicator calculations", "Data");

		_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("MACD Fast", "Length of the fast EMA in MACD", "Indicators")
			;

		_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("MACD Slow", "Length of the slow EMA in MACD", "Indicators")
			;

		_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("MACD Signal", "Length of the MACD signal EMA", "Indicators")
			;

		_stochasticLength = Param(nameof(StochasticLength), 5)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
			;

		_stochasticSignal = Param(nameof(StochasticSignal), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %D", "Period of the %D smoothing", "Indicators")
			;

		_stochasticSlow = Param(nameof(StochasticSlow), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic Slowing", "Final smoothing applied to %K", "Indicators")
			;
		_stochasticBuyThreshold = Param(nameof(StochasticBuyThreshold), 35m)
			.SetDisplay("Stochastic Buy", "Oversold %K threshold for long entries", "Indicators")
			;

		_stochasticSellThreshold = Param(nameof(StochasticSellThreshold), 60m)
			.SetDisplay("Stochastic Sell", "Overbought %K threshold for short entries", "Indicators")
			;


		_momentumPeriod = Param(nameof(MomentumPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Period", "Number of candles used for Momentum", "Indicators")
			;

		_momentumNeutralLevel = Param(nameof(MomentumNeutralLevel), 100m)
			.SetDisplay("Momentum Neutral", "Neutral momentum value used for signal confirmation", "Indicators")
			;

		_sarAcceleration = Param(nameof(SarAcceleration), 0.02m)
			.SetGreaterThanZero()
			.SetDisplay("SAR Acceleration", "Initial acceleration factor of Parabolic SAR", "Indicators")
			;

		_sarStep = Param(nameof(SarStep), 0.02m)
			.SetGreaterThanZero()
			.SetDisplay("SAR Step", "Increment applied to the acceleration factor", "Indicators")
			;

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

	/// <summary>
	/// Trade volume used for each market entry.
	/// </summary>
	public decimal LotSize
	{
		get => _lotSize.Value;
		set => _lotSize.Value = value;
	}

	/// <summary>
	/// Distance used to trail profitable positions.
	/// </summary>
	public decimal TrailingStopPoints
	{
		get => _trailingStopPoints.Value;
		set => _trailingStopPoints.Value = value;
	}

	/// <summary>
	/// Fixed profit target measured in points.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Protective stop distance measured in points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Maximum acceptable execution slippage.
	/// </summary>
	public decimal SlippagePoints
	{
		get => _slippagePoints.Value;
		set => _slippagePoints.Value = value;
	}

	/// <summary>
	/// Time frame used for indicator calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Length of the fast EMA in MACD.
	/// </summary>
	public int MacdFastPeriod
	{
		get => _macdFastPeriod.Value;
		set => _macdFastPeriod.Value = value;
	}

	/// <summary>
	/// Length of the slow EMA in MACD.
	/// </summary>
	public int MacdSlowPeriod
	{
		get => _macdSlowPeriod.Value;
		set => _macdSlowPeriod.Value = value;
	}

	/// <summary>
	/// Length of the MACD signal EMA.
	/// </summary>
	public int MacdSignalPeriod
	{
		get => _macdSignalPeriod.Value;
		set => _macdSignalPeriod.Value = value;
	}

	/// <summary>
	/// Period of the %K line.
	/// </summary>
	public int StochasticLength
	{
		get => _stochasticLength.Value;
		set => _stochasticLength.Value = value;
	}

	/// <summary>
	/// Period of the %D smoothing.
	/// </summary>
	public int StochasticSignal
	{
		get => _stochasticSignal.Value;
		set => _stochasticSignal.Value = value;
	}

	/// <summary>
	/// Final smoothing applied to %K.
	/// </summary>
	public int StochasticSlow
	{
		get => _stochasticSlow.Value;
		set => _stochasticSlow.Value = value;
	}

	/// <summary>
	/// Stochastic %K level that qualifies oversold conditions.
	/// </summary>
	public decimal StochasticBuyThreshold
	{
		get => _stochasticBuyThreshold.Value;
		set => _stochasticBuyThreshold.Value = value;
	}

	/// <summary>
	/// Stochastic %K level that qualifies overbought conditions.
	/// </summary>
	public decimal StochasticSellThreshold
	{
		get => _stochasticSellThreshold.Value;
		set => _stochasticSellThreshold.Value = value;
	}

	/// <summary>
	/// Number of candles used for Momentum.
	/// </summary>
	public int MomentumPeriod
	{
		get => _momentumPeriod.Value;
		set => _momentumPeriod.Value = value;
	}

	/// <summary>
	/// Momentum value considered neutral for trend confirmation.
	/// </summary>
	public decimal MomentumNeutralLevel
	{
		get => _momentumNeutralLevel.Value;
		set => _momentumNeutralLevel.Value = value;
	}

	/// <summary>
	/// Initial acceleration factor of Parabolic SAR.
	/// </summary>
	public decimal SarAcceleration
	{
		get => _sarAcceleration.Value;
		set => _sarAcceleration.Value = value;
	}

	/// <summary>
	/// Increment applied to the acceleration factor.
	/// </summary>
	public decimal SarStep
	{
		get => _sarStep.Value;
		set => _sarStep.Value = value;
	}

	/// <summary>
	/// Maximum acceleration factor of Parabolic SAR.
	/// </summary>
	public decimal SarMaximum
	{
		get => _sarMaximum.Value;
		set => _sarMaximum.Value = value;
	}

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

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

		_previousSar = null;
		_longStopPrice = null;
		_shortStopPrice = null;
		_longTakeProfit = null;
		_shortTakeProfit = null;
		_longEntryPrice = null;
		_shortEntryPrice = null;
		_pointSize = 0m;
	}

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

		Volume = LotSize;
		_pointSize = CalculatePointSize();

		_parabolicSar = new ParabolicSar
		{
			Acceleration = SarAcceleration,
			AccelerationStep = SarStep,
			AccelerationMax = SarMaximum,
		};

		_macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = MacdFastPeriod },
				LongMa = { Length = MacdSlowPeriod },
			},
			SignalMa = { Length = MacdSignalPeriod },
		};

		_stochastic = new StochasticOscillator();
		_stochastic.K.Length = StochasticLength;
		_stochastic.D.Length = StochasticSignal;

		_momentum = new Momentum
		{
			Length = MomentumPeriod,
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_parabolicSar, _macd, _stochastic, _momentum, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _parabolicSar);
			DrawIndicator(area, _macd);
			DrawIndicator(area, _stochastic);
			DrawIndicator(area, _momentum);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(
		ICandleMessage candle,
		IIndicatorValue sarValue,
		IIndicatorValue macdValue,
		IIndicatorValue stochasticValue,
		IIndicatorValue momentumValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!sarValue.IsFinal || !macdValue.IsFinal || !stochasticValue.IsFinal || !momentumValue.IsFinal)
			return;

		if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macd)
			return;

		if (stochasticValue is not StochasticOscillatorValue stochastic)
			return;

		var sar = sarValue.ToDecimal();
		var previousSar = _previousSar;
		_previousSar = sar;

		if (previousSar is null)
			return;

		var momentum = momentumValue.ToDecimal();
		var ask = GetAskPrice(candle);
		var bid = GetBidPrice(candle);

		var buySignal = sar <= ask && previousSar.Value > sar && momentum < MomentumNeutralLevel &&
			macd.Macd < macd.Signal && stochastic.K < StochasticBuyThreshold;
		var sellSignal = sar >= bid && previousSar.Value < sar && momentum > MomentumNeutralLevel &&
			macd.Macd > macd.Signal && stochastic.K > StochasticSellThreshold;

		var closedPosition = false;

		if (Position > 0)
		{
			if (sellSignal)
			{
				SellMarket(Math.Abs(Position));
				ResetLongState();
				closedPosition = true;
			}
			else if (HandleLongRisk(candle))
			{
				closedPosition = true;
			}
		}
		else if (Position < 0)
		{
			if (buySignal)
			{
				BuyMarket(Math.Abs(Position));
				ResetShortState();
				closedPosition = true;
			}
			else if (HandleShortRisk(candle))
			{
				closedPosition = true;
			}
		}

		if (closedPosition)
			return;

		if (Position == 0)
		{
			if (buySignal)
			{
				var entryPrice = ask;
				BuyMarket(Volume);
				_longEntryPrice = entryPrice;
				_longStopPrice = StopLossPoints > 0m ? entryPrice - ConvertPoints(StopLossPoints) : null;
				_longTakeProfit = TakeProfitPoints > 0m ? entryPrice + ConvertPoints(TakeProfitPoints) : null;
			}
			else if (sellSignal)
			{
				var entryPrice = bid;
				SellMarket(Volume);
				_shortEntryPrice = entryPrice;
				_shortStopPrice = StopLossPoints > 0m ? entryPrice + ConvertPoints(StopLossPoints) : null;
				_shortTakeProfit = TakeProfitPoints > 0m ? entryPrice - ConvertPoints(TakeProfitPoints) : null;
			}
		}
	}

	private bool HandleLongRisk(ICandleMessage candle)
	{
		if (Math.Abs(Position) <= 0m)
			return false;

		if (_longTakeProfit is decimal takeProfit && candle.HighPrice >= takeProfit)
		{
			SellMarket(Math.Abs(Position));
			ResetLongState();
			return true;
		}

		if (_longStopPrice is decimal stop && candle.LowPrice <= stop)
		{
			SellMarket(Math.Abs(Position));
			ResetLongState();
			return true;
		}

		var trailingDistance = ConvertPoints(TrailingStopPoints);
		if (trailingDistance > 0m && _longEntryPrice is decimal entry)
		{
			var progressed = candle.HighPrice - entry;
			if (progressed >= trailingDistance)
			{
				var candidate = candle.ClosePrice - trailingDistance;
				if (!_longStopPrice.HasValue || candidate > _longStopPrice.Value)
					_longStopPrice = candidate;
			}
		}

		return false;
	}

	private bool HandleShortRisk(ICandleMessage candle)
	{
		if (Math.Abs(Position) <= 0m)
			return false;

		if (_shortTakeProfit is decimal takeProfit && candle.LowPrice <= takeProfit)
		{
			BuyMarket(Math.Abs(Position));
			ResetShortState();
			return true;
		}

		if (_shortStopPrice is decimal stop && candle.HighPrice >= stop)
		{
			BuyMarket(Math.Abs(Position));
			ResetShortState();
			return true;
		}

		var trailingDistance = ConvertPoints(TrailingStopPoints);
		if (trailingDistance > 0m && _shortEntryPrice is decimal entry)
		{
			var progressed = entry - candle.LowPrice;
			if (progressed >= trailingDistance)
			{
				var candidate = candle.ClosePrice + trailingDistance;
				if (!_shortStopPrice.HasValue || candidate < _shortStopPrice.Value)
					_shortStopPrice = candidate;
			}
		}

		return false;
	}

	private void ResetLongState()
	{
		_longEntryPrice = null;
		_longStopPrice = null;
		_longTakeProfit = null;
	}

	private void ResetShortState()
	{
		_shortEntryPrice = null;
		_shortStopPrice = null;
		_shortTakeProfit = null;
	}

	private decimal GetBidPrice(ICandleMessage candle)
	{
		return candle.ClosePrice;
	}

	private decimal GetAskPrice(ICandleMessage candle)
	{
		return candle.ClosePrice;
	}

	private decimal ConvertPoints(decimal points)
	{
		if (points <= 0m)
			return 0m;

		if (_pointSize > 0m)
			return points * _pointSize;

		var step = Security?.PriceStep ?? 0m;
		return step > 0m ? points * step : points;
	}

	private decimal CalculatePointSize()
	{
		var step = Security?.PriceStep ?? 0m;
		return step > 0m ? step : 0.0001m;
	}
}