GitHub で見る

Stoch Sell戦略

概要

この戦略は、元のMetaTraderエキスパートstochSellの動作を再現します。単一のローソク足ストリームを監視し、初期の市場売り注文を送信する前に、ボラティリティフィルターと組み合わせたトリプルストキャスティクス確認を待ちます。ショートエントリーの直後、価格がさらに下落した場合に動きにスケールインするための保留売りストップのラダーを展開します。

トレーディングロジック

  • ボラティリティフィルター – 設定可能な長さの平均真のレンジ(ATR)が指定されたしきい値を下回っていなければなりません。
  • 低速ストキャスティクス確認 – 最長のストキャスティクスオシレーターが長期の売られすぎレベルを下回っていなければ取引は許可されません。
  • クロス確認 – 中速と高速ストキャスティクスオシレーターの両方が、同じ完了ローソク足の間に売られすぎトリガーを下方クロスしなければなりません。
  • ポジション確認 – 新規エントリーは戦略にアクティブ注文がなくポジションがフラットの場合にのみ配置されます。

すべての条件が満たされると、戦略は設定されたボリュームを使用して市場売り注文を送り、グリッド設定に従ってすぐに一連の売りストップ注文をスケジュールします。保留注文はオプションで、グリッド注文数をゼロに設定することで無効にできます。

出口ルール

  • 利益目標 – ショートバスケットがpipsで望ましい利益を蓄積したとき(ボリューム加重エントリー価格から計算)、戦略はポジション全体を買い戻し、残っている保留注文をすべて削除します。
  • 手動ストップ – グリッド注文は設定可能なライフタイムを尊重します。ストップ注文が約定せずに期限切れになると、自動的にキャンセルされます。
  • 完全決済 – ポジションをゼロに戻す買い取引はすべて、内部エントリー統計をクリアし、保留グリッドをキャンセルします。

グリッド管理

  • 保留注文は、pipsで表されたスタートオフセットとステップを使用して参照価格より下に配置されます。
  • 各保留注文はグリッドボリューム乗数を使用し、バスケットサイズを初期市場エントリーと異なるようにできます。
  • 有効期限(分)は各保留注文に適用されます。ゼロはタイムアウトを無効にします。

パラメーター

名前 説明
CandleType すべてのインジケーターと取引決定のプライマリ時間軸。
AtrPeriod / AtrThreshold 戦略が取引できるタイミングを制御するボラティリティフィルター。
FastKPeriod, FastDPeriod, FastSlowing 高速ストキャスティクスオシレーターの設定。
MediumKPeriod, MediumDPeriod, MediumSlowing 中速ストキャスティクスオシレーターの設定。
SlowKPeriod, SlowDPeriod, SlowSlowing 低速ストキャスティクスオシレーターの設定。
OversoldLevel 高速・中速ストキャスティクス値が下方クロスしなければならないレベル。
LongTermOversoldLevel エントリー時の低速ストキャスティクスの上限。
ProfitTargetPips ショートバスケットを決済するために必要なpipsでの純利益。
GridOrdersCount エントリー後に作成される保留売りストップの数。
GridStartOffsetPips エントリー価格と最初の保留注文間のpipsでのオフセット。
GridStepPips 連続する保留注文間のpipsでの距離。
GridVolume 各保留注文に適用されるボリューム。
GridExpirationMinutes 保留注文のライフタイム(分)。
MarketVolume 初期市場売りに使用されるボリューム。

注意事項

  • インジケーター値は高レベルのBindEx APIを通じて処理され、完了したローソク足のみが取引決定をトリガーします。
  • ポジション追跡ロジックは、生の利益目標をpipsに変換するためにボリューム加重エントリー価格を保持します。
  • スケーリングを無効にするには、グリッド注文数をゼロに設定するだけです。戦略は引き続き単発取引のストキャスティクス確認とATRフィルターに依存します。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class StochSellStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public StochSellStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}