GitHub で見る

シグナル戦略による平均化

概要

信号による平均化戦略 は、MetaTrader エキスパート AveragingBySignal.mq4 を StockSharp のハイレベル API に移植します。元のアドバイザーは、移動平均クロスオーバー エントリー フィルターと、Martingale スタイルの平均化、共有バスケットのテイクプロフィット、および最初の注文に対してのみ有効になるオプションのトレーリング ストップを組み合わせていました。この C# バージョンは、同じ構成要素を再作成しながら、StockSharp のネッティング実行モデルとインジケーター フレームワークに適応させます。

取引ロジック

  1. 構成された時間枠 (CandleType) をサブスクライブし、要求された期間とメソッド (FastPeriod/FastMethodSlowPeriod/SlowMethod) で構築された 2 つの移動平均をフィードします。
  2. キャンドルが完全に閉じられるまで待ちます。バーが完了したら、両方の平均の前の値と現在の値を比較して、速い/遅いクロスオーバーを検出します。
  3. シグナルを生成します。
    • 強気のクロスオーバー(低速を上回る高速上昇)は長いシグナルを生成します。
    • 弱気のクロスオーバー(低速を下回る高速低下)は短いシグナルを生成します。
    • それ以外の場合、戦略はアイドル状態のままになります。
  4. 新しいロングシグナルで、ロングバスケットがアクティブでない間に、ポジションサイジングブロックによって返された基本ボリュームを使用して成行買い注文を送信します。
  5. 新しいショートシグナルで、アクティブなショートバスケットがない間に成行売り注文を送信します。
  6. 平均化ルール:
    • 次のレイヤーまでの距離は、MetaTrader スタイルのピップに変換された LayerDistancePips によって制御されます。
    • 追加のロングレイヤーには、強気シグナル (AveragingBySignal が true の場合) または価格条件のみ (false の場合) が必要です。
    • 追加の短い層は対称ロジックに従います。
    • 新しい各レイヤーのロット サイズは LotSizing モードで計算され、方向ごとに MaxLayers エントリに制限されます。
  7. バスケット管理:
    • 満たされたすべての取引は、ロングバスケットとショートバスケットの平均エントリー価格を再構築するためにFIFOで追跡されます。
    • 加重平均価格プラス/マイナス TakeProfitPips が共有テイクプロフィットを形成します。終値がそのレベルに達すると、バスケット全体が閉じられます。
    • EnableTrailing が有効で、バスケットに注文が 1 つだけ存在する場合、浮動利益の TrailingStartPips の後にトレーリング ストップが設定されます。価格が少なくとも TrailingStepPips 改善するたびにストップが繰り上げられます。
  8. この戦略はネッティング環境で機能します。次のバスケットを開く前に、反対のシグナルが既存のエクスポージャを自動的に相殺します。

ポジションサイジングとピップ計算

  • InitialVolume は基本ロットを定義します。 LotSizingMultiplier に設定されている場合、追加の各レイヤーは基本ロットに Multiplier^layerIndex を乗算し、MQL LotType ロジックを再現します。
  • ヘルパーは、リクエストされたボリュームを楽器の VolumeStepMinVolume、および MaxVolume に合わせて調整し、すべての注文が取引所に準拠するようにします。
  • Pip 値は Security.PriceStep から導出され、元の「2 桁」調整を模倣します。5 桁の FX シンボルは 0.0001 を使用しますが、4 桁のシンボルは 0.0001 をそのまま使用します。

パラメーター

名前 種類 デフォルト 説明
CandleType DataType 1時間枠 インジケーター計算の主要な時間枠。
InitialVolume decimal 0.1 バスケット内の最初の注文の基本ロット サイズ。
LotSizing LotSizingMode Multiplier 固定ロットまたは幾何学的スケーリングのいずれかを選択します。
Multiplier decimal 2 LotSizing = Multiplier の場合、ロット乗数がすべての追加レイヤーに適用されます。
FastPeriod int 28 高速移動平均の振り返り。
FastMethod MovingAverageMethod LinearWeighted 高速ラインの移動平均法。
SlowPeriod int 50 遅い移動平均の振り返り。
SlowMethod MovingAverageMethod Smoothed 遅いラインの移動平均法。
TakeProfitPips int 15 バスケット全体の共有テイプロフィットディスタンス (0 無効)。
AveragingBySignal bool true 新しいレイヤーを追加する前に、新しい信号が必要です。
LayerDistancePips decimal 10 平均化前の最小の逆方向の動き (ピップ単位)。
MaxLayers int 10 最初のものも含め、方向ごとの最大同時注文数。
EnableTrailing bool false 単一注文バスケットのトレーリングストップを有効にします。
TrailingStartPips decimal 10 トレーリングを開始する前に変動利益が必要です。
TrailingStepPips decimal 1 トレーリングストップを移動するには追加の進捗が必要です。

元のエキスパートアドバイザーとの違い

  • StockSharp はネッティング モードで動作しますが、MetaTrader 4 では独立したヘッジ ポジションが可能でした。シグナルの方向が反転すると、新しい成行注文が既存のエクスポージャーを相殺してから、新しいバスケットが作成されます。
  • 共有テイクプロフィットは、各チケットを OrderModify で変更するのではなく、明示的な終了コマンドとして実装されます。
  • トレーリングストップは、ローソク足の終値によって引き起こされる市場の出口でモデル化されています。元の専門家はティックレベルのストップ更新に依存していました。したがって、C# バージョンはわずかに遅れる可能性がありますが、同じしきい値に従います。
  • StockSharp ブローカーは証拠金/価格ルールを直接適用するため、AccountFreeMarginCheck やスリッページ処理などのリスク チェックは省略されます。

使い方のヒント

  • 正しいピップとボリュームの変換のために、正確な楽器メタデータ (PriceStepVolumeStep、最小および最大ボリューム) を提供します。
  • FastPeriodSlowPeriod よりも厳密に小さくしてください。構成によって有効なクロスオーバーが妨げられる場合、戦略は自動的に停止します。
  • 最新のクロスオーバーに関係なく、価格レベルのみに反応する純粋なグリッドが必要な場合は、AveragingBySignal を無効にします。
  • 終了ロジックは閉じたローソク足で動作するため、タイムフレームが低いほど反応が速くなりますが、ノイズと平均化レイヤーの数も増加する可能性があります。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Averaging By Signal strategy: WMA crossover with averaging logic.
/// Buys when fast WMA crosses above slow WMA, sells on cross below.
/// </summary>
public class AveragingBySignalStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	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 AveragingBySignalStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
				SellMarket();
		}
		else
		{
			if (fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (fastValue < slowValue && Position >= 0)
				SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
		_hasPrev = true;
	}
}