GitHub で見る

シンプルな戦略

概要

シンプルな戦略は、MQL/9019 にある MetaTrader 4 エキスパート アドバイザー S!mple.mq4 の StockSharp の高レベルの変換です。オリジナルのシステムは、FX シンボルの固定バスケットを監視し、50 期間の線形加重移動平均が 200 期間の単純移動平均を横切るたびに取引しました。各エントリーは設定可能な回数繰り返すことができ、オプションの金額ベースのストップロスとテイクプロフィットレベルがすべての取引に付加されました。変換では同じロジックが維持され、すべてのユーザー入力が戦略パラメータとして公開され、EA が MetaTrader 端末コメントに出力したのと同じ診断情報がログに記録されます。

取引ロジック

  1. データの準備。 この戦略は、構成可能なローソク足タイプ (デフォルトでは 5 分足ローソク足) をサブスクライブし、高レベルの SubscribeCandles().Bind(...) API を通じて両方の移動平均をバインドします。
  2. 移動平均クロスオーバー。 すべての移動平均の 2 つの履歴値がバッファされます。買いシグナルは、速い LWMA が 2 足前に遅い SMA を下回り、前の終了バーで遅い SMA を上回って終了したときに発生します。逆条件が発生すると売りシグナルが検出されます。
  3. トレンド マージンの追跡。 TrendMargin バー前に発生した遅い SMA 値は、EA のテキスト トレンド レポートを再現するためにキャッシュされます。ライブスロー SMA がその参照と比較され、価格ステップで表される距離とともにバックグラウンド トレンドが UPDOWN、または WAIT として分類されます。
  4. 実行モデル
    • 買いシグナルがトリガーされると、NumOrders * TradeVolume まで買う前に短期エクスポージャーはクローズされます。リクエストされたボリュームは、最大数に達するまで複数の同一の注文が積み重ねられる EA の動作を反映しています。
    • 売りシグナルは、まずロングエクスポージャーを終了し、次に同じ合計目標ボリュームまで売ります。
  5. プロテクトレベル。 オプションの金額ベースのストップとターゲット (StopLossMoneyTakeProfitMoney) は、商品 PriceStep/StepPrice および注文ごとの TradeVolume を使用して価格距離に変換されます。レベルが保存されると、完成したすべてのキャンドルが高値/低値の範囲をチェックします。レベルを突破すると、ポジションは市場でフラット化されます。
  6. 運用ガード。 実際の注文発注は、EnableTradingtrue に設定されている場合にのみ実行され、EA を「分析のみ」モードで実行させる元の makeTrades フラグを複製します。

リスク管理とマネーストップ

  • ストップロスとテイクプロフィットの金額は、エントリーブロックごと(MetaTrader注文ごと)のキャッシュリスク/ターゲットとして解釈されます。変換では、セキュリティ メタデータ (PriceStepStepPrice) を使用して、その金額を四捨五入された価格ステップ数に変換します。いずれかのフィールドが欠落している場合、警告がログに記録され、マネタリーストップは無効のままになります。
  • 保護レベルは、完成した各ローソク足の高値/安値で評価され、StockSharp の高レベルのフレームワーク内に留まりながら、EA によって行われるティックレベルのチェックと一致します。
  • StartProtection() は開始時に呼び出され、戦略の実行中、StockSharp で構成されたアカウント レベルの保護が有効なままになります。

パラメーター

名前 デフォルト 説明
TradeVolume 0.1 単一の MetaTrader のような注文の量。ベースの Strategy.Volume はこの値と同期されます。
NumOrders 1 同じ方向に蓄積できるボリューム ブロックの最大数。最終的なターゲット ボリュームは TradeVolume * NumOrders となります。
StopLossMoney 0 ボリュームブロックごとのアカウント通貨でのオプションのストップロス額。停止を無効にするには、ゼロに設定します。
TakeProfitMoney 0 ボリュームブロックごとのアカウント通貨でのオプションのテイクプロフィット金額。ターゲットを無効にするには、0 に設定します。
TrendMargin 10 背景のトレンド テキストを生成するために使用された完成したローソク足の数 (TrendMargin バー前の値と比較して SMA が遅い)。
FastLength 50 高速線形加重移動平均の長さ。
SlowLength 200 遅い単純移動平均の長さ。
EnableTrading false false の場合、戦略はシグナルのみをログに記録します。これは、makeTrades=false の場合の EA とまったく同様です。
CandleType 5m time-frame インジケーターの計算に使用されるローソク足のタイプ。

変換時の注意点

  • MetaTrader EA は、6 つのハードコードされた外国為替シンボルを反復処理しました。 StockSharp 戦略は、ユーザーが指定した Strategy.Security で動作します。バスケット取引の動作を再現するには、ストラテジーのインスタンスを複数 (商品ごとに 1 つ) 起動するか、同じシグナルを複数の証券に送信する親ストラテジー内にラップします。
  • 金銭ベースの保護レベルは、金融商品のメタデータに依存します。外国為替ペアの場合、PriceStepStepPrice の両方が満たされていることを確認してください (たとえば、0.0001 と 1 ロットあたりのピップ値)。それ以外の場合、警告を記録した後、停止/ターゲット距離は何も言わずにゼロとして扱われます。
  • 終了したローソク足ごとに出力されるログ メッセージは、EA コメントを反映しています。シグナル (BUYSELL、または WAIT)、両方の移動平均、価格ステップでのそれらの間の距離、遅延スロー SMA から得られたトレンド評価がリストされています。
  • スタックされた注文の数は、総ターゲット量としてモデル化されます。これにより、複数の個別の OrderSend 呼び出しの代わりに、StockSharp の高レベルの成行注文ヘルパーを使用しながら、合計エクスポージャが元の実装と同一に保たれます。
  • タスク要件に一致する Python ポートはまだ作成されていません。

使用のヒント

  • 正しく構成された PriceStepStepPrice、および VolumeStep の値を使用して外国為替証券を割り当てます。 TradeVolume を希望のロットサイズに設定し、ログに記録された診断に満足したら取引を有効にします。
  • EA のデフォルト動作 (分析のみ) を模倣するには、EnableTradingfalse のままにしておきます。取引の準備ができたら、true に切り替えると、次のクロスオーバー シグナルが成行注文を発行します。
  • 保護レベルはローソク足の終値で監視されるため、MetaTrader のティックごとの動作と比較して、より厳密な足内反応が必要な場合は、より短いローソク足の使用を検討してください。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simple: Weighted MA crosses Simple MA crossover strategy.
/// Trades direction changes when fast WMA crosses slow SMA.
/// </summary>
public class SimpleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

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

		_fastLength = Param(nameof(FastLength), 50)
			.SetDisplay("Fast WMA", "Fast weighted moving average period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 200)
			.SetDisplay("Slow SMA", "Slow simple moving average period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

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

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fast = new WeightedMovingAverage { Length = FastLength };
		var slow = new SimpleMovingAverage { Length = SlowLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, atr, ProcessCandle)
			.Start();

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

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

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		// Exit management
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (fastVal < slowVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (fastVal > slowVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry: WMA/SMA crossover
		if (Position == 0)
		{
			if (_prevFast <= _prevSlow && fastVal > slowVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevFast >= _prevSlow && fastVal < slowVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}