GitHub で見る

2 番目に簡単な戦略

概要

2 番目に簡単な戦略は、MetaTrader エキスパート Second_Easiest.mq4 の StockSharp 移植です。オリジナルのロボットがスキャンします。 現在の取引セッションの毎日のローソク足を基準とし、価格が現在の取引セッションから遠ざかる傾向にあることが証明されたら、単一の日中ポジションをオープンします。 その日は開いています。市場が閉まると、専門家はエクスポージャーを清算し、次のセッションに備えます。 StockSharp このバージョンでは、フレームワークのハイレベルなローソク足の API を利用しながら、この日中ブレイクアウト動作を維持します。 サブスクリプション、注文管理、ポジション追跡。

複数の指標を必要とするモメンタム戦略とは異なり、Second Easyst では実行中のオープン、高値、安値のみが必要です。 現在の日。これにより、方向性の確信の初期の兆候に反応しながら、非常に軽量になります。コードは保持されます 一度に 1 つの位置を指定し、すぐに逆転することはありません。新しい取引は、前の取引が終了した後にのみ開始できます。

取引ロジック

  1. CandleType によって定義された日中キャンドル シリーズを購読します。デフォルトの時間枠は 1 分です。 元の EA の毎日のロジックとの互換性を維持しながら、毎日の極端なビューを表示します。
  2. 終了したローソクごとに、セッションの始値、高値、安値のメモリ内レコードを更新します。最初に加工されたキャンドル 新しい取引日では、3 つの値すべてが定義されます。後続のローソク足は、新たな極値に達するたびに高値または安値のみを拡大します。
  3. クロックが EntryCutoffHour に達したら、新しい設定を無視します。 MetaTrader コードはサーバー時間の 16:00 に取引開始を停止し、 ポートも同じルールに従います。
  4. ロングポジションは、現在の終値が日次始値および 始値と終値の間の距離を上回っている場合にのみ許可されます。 毎日の最低値は RangePointsThreshold を超えています。これにより、MQL の「入札 > オープン」および「オープン - 安値 > 15 ポイント」の条件が再現されます。
  5. ショートポジションは、現在の終値が日次始値および日次高値と日次高値との距離を下回っている場合にのみ許可されます。 オープンが同じしきい値を超えます。
  6. エントリーシグナルが表示され、オープンポジションがない場合は、TradeVolume ロットを使用して成行注文を送信します。以下のヘルパー メソッド 基本の Strategy クラスは、正しい側の選択を処理します。
  7. 市場が MarketCloseHour に達したら、ClosePosition() を呼び出して既存のエクスポージャーをフラット化します。新しい取引は行われません この終了後、次のセッションが始まるまで。

パラメーター

名前 種類 デフォルト 説明
CandleType DataType 1分の時間枠 エントリーロジックとエグジットロジックを駆動する主要な日中ローソク足。
TradeVolume decimal 1 すべての成行注文に使用されるロットサイズ。
EntryCutoffHour int 16 戦略が新しいポジションのオープンを拒否する時間 (0-23)。
MarketCloseHour int 20 開いているポジションが強制的に閉じられるときの時間 (0 ~ 23)。
RangePointsThreshold decimal 15 ブローカーポイントで表される、毎日の始値と最も近い極値の間の最小距離。

MetaTrader バージョンとの違い

  • StockSharp バージョンは、ネット形式で位置を追跡します。動作は元の単一次ロジックと同じです なぜなら、いつでもオープンできる取引は 1 つだけであり、ポジションは新しいエントリーが評価される前にフラット化されるからです。
  • MetaTrader は、毎日の時間枠で iOpen/iHigh/iLow 呼び出しを通じて実行中の始値、高値、安値を取得します。ポートが再構築されます 日中ローソク足からの同じ情報を利用して、禁止されたインジケーターの呼び出しを回避し、データが常に利用可能であることを保証します。 証券会社のフィードでは日足は提供されません。
  • 注文のクローズは、チケット ID をループするのではなく、ClosePosition() を通じて実行されます。最終結果は同じです: オープン露出は、設定された終了時間に達するとすぐに削除されます。
  • 証券の PriceStep が指定されていない場合、変換では RangePointsThreshold が絶対価格距離として扱われます。 この安全フォールバックにより、ステップメタデータなしで価格を報告する商品でシステムが動作し続けます。

使用上の注意

  • VolumeOnStartedTradeVolume に設定されているため、パラメータを変更すると、後続の注文に直ちに影響します。 残りのコードを変更します。
  • 別の CandleType を選択する場合は、日中の始値、高値、安値を追跡するのに十分な粒度が提供されていることを確認してください。 正確に。たとえば、5 分足のローソク足はうまく機能しますが、1 時間足では毎日の極値の検出が遅れる可能性があります。
  • RangePointsThreshold を増やして、ボラティリティの低いセッションを除外します。これを減らすと、次のような場合でも戦略をトリガーできるようになります。 初期の範囲は狭いです。
  • このアルゴリズムは一日の終わりにすべてのポジションをクローズするため、一晩の証拠金は必要ありません。強制執行するブローカー セッションが中断されると、取引が再開されるときに内部レンジカウンターも自動的にリセットされます。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// SecondEasiest: RSI reversal with EMA filter and ATR stops.
/// </summary>
public class SecondEasiestStrategy : 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 SecondEasiestStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_emaLength = Param(nameof(EmaLength), 25)
			.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;
	}
}