GitHub で見る

戦略 Universal Signal Demo

この戦略は、StockSharp の高レベル API を使用して、MetaTrader 5「ユニバーサル シグナル」エキスパートを複製します。 8 つの重み付けされた市場パターンを評価し、それらを 1 つの複合スコアに集約します。スコアが設定可能なしきい値を超えると、ストラテジーはロングポジションとショートポジションをオープンまたはクローズし、オプションで設定されたバー数の後に期限切れになる保留中の指値注文を使用します。

Strategy Parameters

  • CandleType – 分析に使用されるローソク足データ。
  • SignalThresholdOpen – ポジションをオープンするために必要な最小複合スコア。
  • SignalThresholdClose – 既存のポジションから抜け出すには相手のスコアが必要です。
  • PriceLevel – 保留中の指値エントリーを配置するための価格オフセット (0 は市場執行を意味します)。
  • StopLevel / TakeLevel – 内蔵保護モジュールによって使用される絶対的なストップロスとテイクプロフィットの距離。
  • SignalExpiration – まだアクティブな保留中のエントリがキャンセルされるまでのバーの数。
  • Pattern0WeightPattern7Weight – 集計前に各パターンに適用される重み。
  • UniversalWeight – すべてのパターン寄与の合計に適用される最終乗数。
  • ShortMaPeriodLongMaPeriodRsiPeriodBollingerPeriodBollingerWidthTrendSmaPeriodVolumeSmaPeriod – パターン チェック内で使用されるインジケーター設定。

取引ロジック

  1. 設定されたキャンドル ストリームをサブスクライブし、EMA、RSI、MACD シグナル、Bollinger バンド、およびサポートされる SMA をバインドします。
  2. ローソク足が完成するたびに、8 つのブール パターン (トレンドの調整、RSI の勢い、MACD のヒストグラム、Bollinger の位置、ローソク足の方向、ボリュームの拡大) を計算します。
  3. 各パターンにその重みを乗算し、寄与を合計し、全体的な重みを適用して最終スコアを取得します。
  4. スコアが反対方向に決済しきい値を超えた場合、オープンポジションを決済します。
  5. スコアが開始しきい値を超えたときに、新しいロング ポジションまたはショート ポジションをオープンします。 PriceLevel がプラスの場合、構成された距離だけオフセットされた指値注文を送信し、SignalExpiration バーの後に自動的にキャンセルします。
  6. StartProtection は、StockSharp のリスク管理ヘルパーを使用して、すべてのポジションに対して固定のストップロスとテイクプロフィットのレベルを設定します。

この変換では、StockSharp のコーディング規約とインジケーターベースの処理に従いながら、元の MQL5 エキスパートの柔軟な重み付けワークフローが維持されます。

namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Universal Signal Demo strategy: Multi-indicator scoring.
/// Combines RSI and EMA signals to generate aggregate score for entries.
/// </summary>
public class UniversalSignalDemoStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;
	private int _prevScore;
	private int _candlesSinceTrade;
	private bool _hasPrevScore;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public UniversalSignalDemoStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_rsiPeriod = Param(nameof(RsiPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevScore = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrevScore = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevScore = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrevScore = false;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		var close = candle.ClosePrice;
		var score = 0;

		// RSI signal
		if (rsiValue < 30) score += 2;
		else if (rsiValue < 45) score += 1;
		else if (rsiValue > 70) score -= 2;
		else if (rsiValue > 55) score -= 1;

		// EMA signal
		if (close > emaValue) score += 1;
		else if (close < emaValue) score -= 1;

		// Candle direction
		if (candle.ClosePrice > candle.OpenPrice) score += 1;
		else if (candle.ClosePrice < candle.OpenPrice) score -= 1;

		var crossedUp = (!_hasPrevScore || _prevScore < 2) && score >= 2;
		var crossedDown = (!_hasPrevScore || _prevScore > -2) && score <= -2;

		if (crossedUp && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (crossedDown && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}

		_prevScore = score;
		_hasPrevScore = true;
	}
}