GitHub で見る

モメンタム最小利益戦略に関する修士号

概要

この戦略は、モメンタム指標とモメンタム系列に基づいて計算される移動平均の間のクロスオーバーを取引することにより、MetaTrader 5 エキスパート アドバイザー Momentum Min Profit.mq5 の MA を複製します。強気のシグナルは、前のバーが勢いをニュートラル 100 レベル以下に維持している間に、勢いが平均を上回ったときに表示されます。弱気シグナルは、モメンタムが平均を下回り、前のバーが 100 を超えたときに生成されます。実装では、元の金額ベースのエクイティ ストップとポイント単位で測定される固定のテイクプロフィット ディスタンスが維持されます。

取引ロジック

  1. CandleType で定義されたローソク足をリクエストし、モメンタム インジケーターにフィードします。
  2. MomentumMovingAverageTypeMomentumMovingAveragePeriod で定義される移動平均を使用して、運動量ストリームを平滑化します。
  3. 前のバーの値を使用してクロスオーバーを検出し、二重信号を回避します。
  4. MQL バージョンのオプション機能:
    • 生成された信号の方向を反転します。
    • 新しい取引を開始する前に逆のエクスポージャーをクローズするか、エントリーを完全にスキップします。
    • いつでも単一のネット ポジションを強制します。
    • 完全に閉じたバーではなく、現在の (形成中の) ローソク足でトリガーできるようにします。
  5. リスク管理を適用します。
    • 株式のストップインマネー: PnL + Position * (close - PositionPrice)StopLossMoney を超えていなければなりません。
    • Security.PriceStep を通じて変換されたポイント単位のテイクプロフィット距離。

パラメーター

パラメータ 種類 デフォルト 説明
CandleType DataType TimeSpan.FromMinutes(5).TimeFrame() キャンドルは運動量の計算に使用されます。
MomentumPeriod int 14 モメンタムインジケーターのルックバック期間。
MomentumMovingAveragePeriod int 6 モメンタムに適用される移動平均の長さ。
MomentumMovingAverageType MomentumMovingAverageType Smoothed 移動平均アルゴリズム (単純、指数、平滑化、加重)。
ReverseSignals bool false MetaTrader の売買シグナルをミラーリングします。
CloseOpposite bool true 新しいポジションを開く前に、反対側のエクスポージャーを閉じます。
OnlyOnePosition bool true 単一のネットポジションを維持します。
UseCurrentCandle bool false 閉じたバーではなく、現在形成されているローソク足のシグナルを評価します。
StopLossMoney decimal 15 すべての取引を終了する前に、株式のドローダウンが許可されます。
TakeProfitPoints decimal 460 商品ポイントでの利益目標(PriceStepを乗算)。
MomentumReference decimal 100 MQL 戦略からコピーされたニュートラルな運動量レベル。

実装メモ

  • 移動平均は、StockSharp の組み込み SMA/EMA/SMMA/WMA クラスを再利用するために、LengthIndicator<decimal> インスタンスで実装されています。
  • 元の注文キューとマジックナンバー フィルターは StockSharp のネット ポジションにマッピングされているため、この戦略は、CloseOpposite が有効な場合に反対側をフラットにし、新しいエクスポージャーをオープンするサイズの単一の成行注文を送信します。
  • 株式保護は、浮動損失がしきい値に達すると、CloseAll() を介してすべてのポジションをクローズします。これは、手数料、スワップ、利益の合計を監視する MetaTrader の動作と正確に一致します。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Momentum crossing its own moving average strategy.
/// Converted from MetaTrader 5 (MA on Momentum Min Profit.mq5).
/// </summary>
public class MaOnMomentumMinProfitStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<int> _maPeriod;

	private Momentum _momentum;
	private readonly Queue<decimal> _momentumHistory = new();
	private decimal? _prevMomentum;
	private decimal? _prevSignal;

	/// <summary>
	/// Candle type used for signal calculation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Momentum period.
	/// </summary>
	public int MomentumPeriod
	{
		get => _momentumPeriod.Value;
		set => _momentumPeriod.Value = value;
	}

	/// <summary>
	/// Moving average period applied to momentum values.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Initialize <see cref="MaOnMomentumMinProfitStrategy"/>.
	/// </summary>
	public MaOnMomentumMinProfitStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles used for the momentum calculation", "General");

		_momentumPeriod = Param(nameof(MomentumPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Period", "Lookback for the momentum indicator", "Momentum");

		_maPeriod = Param(nameof(MaPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Period of the moving average applied to momentum", "Momentum");
	}

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

		_prevMomentum = null;
		_prevSignal = null;
		_momentumHistory.Clear();

		_momentum = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_momentum, ProcessCandle)
			.Start();

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

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

		_momentumHistory.Enqueue(momentumValue);
		while (_momentumHistory.Count > MaPeriod)
			_momentumHistory.Dequeue();

		if (!_momentum.IsFormed)
			return;

		if (_momentumHistory.Count < MaPeriod)
		{
			_prevMomentum = momentumValue;
			return;
		}

		// Calculate SMA of momentum
		var sum = 0m;
		var history = _momentumHistory.ToArray();
		foreach (var v in history)
			sum += v;
		var signalValue = sum / history.Length;

		if (_prevMomentum is null || _prevSignal is null)
		{
			_prevMomentum = momentumValue;
			_prevSignal = signalValue;
			return;
		}

		var crossUp = _prevMomentum < _prevSignal && momentumValue > signalValue;
		var crossDown = _prevMomentum > _prevSignal && momentumValue < signalValue;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		var minSpread = 0.5m;

		if (crossUp && Math.Abs(momentumValue - signalValue) >= minSpread)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (crossDown && Math.Abs(momentumValue - signalValue) >= minSpread)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevMomentum = momentumValue;
		_prevSignal = signalValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_prevMomentum = null;
		_prevSignal = null;
		_momentum = null;
		_momentumHistory.Clear();

		base.OnReseted();
	}
}