GitHub で見る

MA価格クロス戦略

MA 価格クロス戦略は、MetaTrader 4 エキスパート アドバイザー「MA 価格クロス」を StockSharp の高レベル API に直接変換したものです。設定可能な時間枠内で取引が許可されている間、選択した移動平均が現在の価格を超えるまで待機します。下からクロスが発生すると、アルゴリズムはロングポジションをオープンします。クロスが上から発生すると、ショートポジションがオープンします。保護的なストップロスとテイクプロフィットの距離は MetaTrader ポイントで定義され、商品の PriceStep を使用して絶対価格オフセットに自動的に変換されます。

すべてのティックに反応する元の MQL 実装とは異なり、StockSharp バージョンは完成したキャンドルで動作し、SubscribeCandles の高レベルのサブスクリプションを使用します。これにより、取引の決定がバーごとに 1 回実行され、インジケーター バインディング パイプラインとの互換性が維持されることが保証されます。移動平均は 4 つの MetaTrader モードすべてに一致するように構成でき、さまざまな価格ソース (終値、始値、高値、安値、中央値、標準、加重値) を受け入れます。

取引ロジック

  1. 現在時刻が [StartTime, StopTime) 取引ウィンドウ内に収まるまで待ちます。夜間ウィンドウは、午前 0 時をラップすることでサポートされます。
  2. 完成したキャンドルのみを処理します。設定された移動平均に、選択した適用価格を入力します。
  3. 以前の移動平均値を保存して、MetaTrader で使用される iMA シフト ロジックをエミュレートします。
  4. 以前の平均が最新の価格を下回っており、新しい平均が価格を上回っている場合、ロングポジションをオープン(またはリバース)します。
  5. 以前の平均が最新の価格を上回っており、新しい平均が価格を下回っている場合、ショートポジションをオープン(またはリバース)します。
  6. 反対側で新しいポジションを開く前に、元のコードの OrdersTotal() == 0 制約を反映するために既存のエクスポージャをフラット化します。
  7. MetaTrader ポイントと現在の商品 PriceStep を乗算して表される距離で仮想ストップロスとテイクプロフィットを開始します。

デフォルトパラメータ

パラメータ デフォルト 説明
CandleType TimeFrame(1m) あらゆる計算を駆動するキャンドルシリーズ。
MaPeriod 160 移動平均で使用されるバーの数。
MaMethod Simple 移動平均タイプ: 単純、指数、平滑化、または線形加重。
PriceType Close 価格ソースは移動平均 (終値/始値/高値/安値/中央値/典型/加重) に転送されます。
StartTime 01:00 取引が活発になる時間帯。
StopTime 22:00 新しいエントリが停止する時刻。
StopLossPoints 200 MetaTrader ポイントが絶対保護停止距離に変換されます。
TakeProfitPoints 600 MetaTrader ポイントが絶対利益目標距離に変換されます。
OrderVolume 0.1 デフォルトのボリュームは成行注文で送信されます。

注意事項

  • StartTimeStopTime に等しい場合、時間フィルターは無効になり、取引は終日許可されます。
  • StopLossPoints または TakeProfitPoints が 0 の場合、対応する保護レベルは登録されません。
  • 時間フィルターはローソク足の終了時間 (candle.CloseTime.TimeOfDay) を使用するため、MarketDataConnector によって提供される為替タイムゾーンに適応します。
  • セキュリティが PriceStep を公開しない場合、ポイントベースの距離がスケーリングなしで直接使用されます。

オリジナル攻略参考資料

  • 出典: MQL/44133/MA Price Cross.mq4
  • 著者: JBlanked (2023)
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "MA Price Cross" MetaTrader expert.
/// Enters when SMA crosses above/below the current close price.
/// </summary>
public class MaPriceCrossStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maPeriod;

	private ExponentialMovingAverage _sma;
	private decimal? _prevAverage;
	private decimal? _prevClose;

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

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	public MaPriceCrossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for MA cross detection", "General");

		_maPeriod = Param(nameof(MaPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "SMA period", "Indicators");
	}

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

		_sma = new ExponentialMovingAverage { Length = MaPeriod };
		_prevAverage = null;
		_prevClose = null;

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

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

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

		if (!_sma.IsFormed)
		{
			_prevAverage = smaValue;
			_prevClose = candle.ClosePrice;
			return;
		}

		if (_prevAverage is null || _prevClose is null)
		{
			_prevAverage = smaValue;
			_prevClose = candle.ClosePrice;
			return;
		}

		var close = candle.ClosePrice;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// MA was below price, now crosses above -> sell signal (price goes under MA)
		var sellSignal = _prevClose.Value >= _prevAverage.Value && close < smaValue;
		// MA was above price, now crosses below -> buy signal (price goes above MA)
		var buySignal = _prevClose.Value <= _prevAverage.Value && close > smaValue;

		if (buySignal)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (sellSignal)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevAverage = smaValue;
		_prevClose = close;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_sma = null;
		_prevAverage = null;
		_prevClose = null;

		base.OnReseted();
	}
}