GitHub で見る

PricerEA 戦略

概要

PricerEA 戦略 は、StockSharp の高レベル API を使用して、MetaTrader 4 エキスパート「PricerEA v1.0」の動作を再作成します。 手動で定義した価格レベルで最大 4 つの未決注文 (買いストップ、売りストップ、買い指値、売り指値) を発注します。いつか 未決注文が約定されると、戦略は保護的なストップロス注文と利食い注文を付加し、オプションでトレーリングストップと利食い注文を有効にします。 元の Expert Advisor に従う損益分岐点調整。

仕組み

  1. 未決注文 – 戦略は入力から絶対価格レベルを読み取り、対応する未決注文のみを送信します。 起動時に一度。オプションの有効期限は分単位で設定できます。
  2. ボリューム選択 – ユーザーは固定の手動ロットサイズを維持することも、ボリュームが導出される自動モードに切り替えることもできます。 ポートフォリオのバランスと MT4 のリスクファクターの類似点。
  3. 保護 – エントリー注文が約定された後、戦略は設定された距離でストップロス注文とテイクプロフィット注文を作成します (価格ポイントで表されます)。トレーリングと損益分岐点の両方が有効な場合、ストップは元の MQL 条件に従います。 価格が損益分岐点と最初のストップをカバーした後にのみ移動します。
  4. 注文のメンテナンス – 保留中の注文は、有効期間が終了するか戦略が停止すると、自動的にキャンセルされます。

パラメーター

パラメータ 説明
BuyStopPrice, SellStopPrice, BuyLimitPrice, SellLimitPrice 対応する未決注文の絶対価格。 0 の値は注文を無効にします。
TakeProfitPoints エントリー価格から利益確定注文までの距離。価格ポイント (Security.PriceStep) で測定されます。
StopLossPoints エントリー価格からストップロス注文までの距離。これも価格ポイントで測定されます。
EnableTrailingStop トレーリングストップロジックを有効にします。
TrailingStepPoints トレーリングストップが移動する前に必要な最小限の移動 (ポイント単位)。
EnableBreakEven 十分な利益が得られた後にストップをエントリーの上または下に引き上げる損益分岐点ルールを有効にします。
BreakEvenTriggerPoints 損益分岐点のためにストップが移動される前に必要な追加利益 (ポイント)。
PendingExpiryMinutes 保留中の注文の存続期間 (分単位)。 0 は、入力されるか手動でキャンセルされるまで存続します。
VolumeMode 手動ボリュームと自動サイジングのどちらかを選択します。
RiskFactor 自動サイジングで使用されるリスク乗数 (MQL 入力を反映します)。
ManualVolume VolumeModeManual に設定されている場合に使用されるロットサイズを修正しました。

MT4バージョンとの違い

  • 自動ボリューム計算では、StockSharp ポートフォリオ残高と証券契約乗数が使用されます。さまざまなブローカー は異なる数式を使用する場合があるため、結果の値は MetaTrader とは若干異なる場合があります。
  • 保護命令は、StockSharp ヘルパーを通じて発注され、会場の音量ステップ、最小および最大音量を尊重します。
  • 有効期限は戦略内で実装されます (MetaTrader はサーバー側の注文の有効期限に依存します)。

使用上の注意

  • 戦略を開始する前に価格レベルを設定します。値がゼロに等しい場合、対応する順序は無効のままになります。
  • MT4 の「Digits」ロジックを模倣するために、ポイントベースのパラメーターは Security.PriceStep 単位で動作します。
  • この戦略と StockSharp のポートフォリオおよびロギング ツールを組み合わせて、未決注文と保護ストップを監視します。
namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// PricerEA strategy: Bollinger Bands mean reversion with RSI filter.
/// Buys at lower band when RSI is oversold, sells at upper band when overbought.
/// </summary>
public class PricerEaStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _bbPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public PricerEaStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_bbPeriod = Param(nameof(BbPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period for adaptive band distance", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 8)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candlesSinceTrade = SignalCooldownCandles;
		var sma = new SimpleMovingAverage { Length = BbPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var atr = new AverageTrueRange { Length = AtrPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(sma, rsi, atr, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;
		var bandDistance = atr * 2.5m;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (Position > 0 && (close >= sma || rsi >= 50))
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}
		else if (Position < 0 && (close <= sma || rsi <= 50))
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (Position == 0 && _candlesSinceTrade >= SignalCooldownCandles && close <= sma - bandDistance && rsi < 30)
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (Position == 0 && _candlesSinceTrade >= SignalCooldownCandles && close >= sma + bandDistance && rsi > 70)
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}
	}
}