GitHub で見る

ニューストレーダー戦略

この戦略は、予定されているマクロ経済リリースの直前に市場の両側を武装させることにより、元の NewsTrader.mq4 スクリプトの動作を再現します。設定されたニュース タイムスタンプの 10 分前に、ボットは 1 対のブレイクアウト ストップ オーダーを送信し、一方がトリガーされるとすぐに保護出口を取り付けます。

中核ロジック

  • 1 分間のキャンドル サブスクリプション (構成可能) を純粋にタイミング ソースとして使用します。
  • アクティブ化の瞬間を news time - LeadMinutes として計算し、開始時間がその時点以上である最初の終了したローソク足まで待機します。
  • 現在の価格より下に売りストップを配置し、その上に買いストップを配置します。これは、Security.PriceStep によって変換された BiasPips によってオフセットされます (MQL4 の bias * Point ロジックを反映しています)。
  • 未決注文が約定されると、反対側の未決注文はキャンセルされます。専用のストップロス注文とテイクプロフィット注文は、設定されたピップ距離を使用して発注されます。
  • ストップロスまたはテイクプロフィットフィルは残りの保護注文をキャンセルし、戦略をフラット化します。
  • 開始時に StartProtection() を呼び出し、戦略がより高いレベルの StockSharp セーフガードと連携するようにします。

パラメーター

名前 説明 デフォルト
TradeVolume 各保留中の注文とともに提出された契約。 1
StopLossPips ピップ単位のストップロス距離 (0 はストップ注文を無効にします)。 10
TakeProfitPips 利益確定距離 (ピップ単位) (0 はターゲット注文を無効にします)。 10
BiasPips 基準価格からブレイクアウトストップオーダーまでの距離。 20
LeadMinutes ブレイクアウト命令が準備されるニュースのタイムスタンプの数分前。 10
NewsYear, NewsMonth, NewsDay, NewsHour, NewsMinute 予定されているニュース時間のコンポーネント (プラットフォーム クロック)。 2010, 3, 8, 1, 30
CandleType 時間の経過を追跡するために使用されるローソク足のデータ型。 1 Minute

実装メモ

  • この戦略では、OnStarted 中に VolumeTradeVolume に設定し、BuyStopSellStop などのヘルパー メソッドが予想されるサイズを使用するようにします。
  • Security.PriceStep を定義する必要があります。それ以外の場合、pip ベースの距離を価格に変換できないため、ロジックは例外をスローします。
  • ローソク足の終値は、ストップレベルを計算する際に最新の買値/売値の代用として使用され、トリガー時の最新の相場に依存した元の MQL4 ロジックと一致します。
  • 未決注文は 1 回だけ発注されます。設定されたニュース イベントが終了した後、アルゴリズムは自動的に再準備を行いません。
  • 保護命令は、それぞれのピップ距離がゼロの場合にスキップされ、手動介入用に動作を構成できるようになります。

ファイル

  • CS/NewsTraderStrategy.cs — 戦略の C# 実装。

Python のバージョンはリクエストに応じて意図的に省略されています。

using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// News Trader: RSI breakout with EMA filter and ATR stops.
/// </summary>
public class NewsTraderStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public NewsTraderStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_emaLength = Param(nameof(EmaLength), 30)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

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

		_prevRsi = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = 0; _entryPrice = 0;
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
			else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
		}
		_prevRsi = rsiVal;
	}
}