GitHub で見る

Killer Sell 2.0 (C#) 戦略

概要

Killer Sell 2.0は、長期間の買われすぎ状態の後にエントリーをタイミングし、モメンタムが売られすぎ領域に振れたときに利益を確定する、ショートのみのMetaTrader 4エキスパートアドバイザーです。このポートはStockSharpの高水準ストラテジーAPI上で元のロジックを書き直します。すべてのインジケーター処理はSubscribeCandles().BindEx(...)を通じてイベント駆動で行われ、マネーマネジメントルールはストラテジークラス内にカプセル化されています。

取引ロジック

変換されたロジックはStockSharpのネットポジションモデルを使用しながら元のシグナルチェーンに従います。設定された時間軸の各完成したローソク足が以下のステップを実行します:

  1. データ準備。 ストラテジーはMACD(12/120/9)、Williams %R(両方のフィルターに期間350)、2つのStochasticオシレーター(エントリーに10/1/3、エグジットに90/7/1)を更新します。インジケーター値は新しいバーが完成し入力が完全に形成されたときのみ消費されます。
  2. エントリーフィルター。 以下のすべての条件が満たされるとショートセットアップが有効です:
    • Williams %Rが−10を上回り、買われすぎ市場を示します。
    • MACDメインラインが0.0014より大きい。
    • エントリーStochastic %Kが設定可能なエントリーレベル(デフォルト90)を下回ってクロスします。クロス検出は連続する%K読み取りで実行されます。
  3. 注文配置。 フィルターが揃うと、ストラテジーは現在のマーチンゲールロットサイズを使用してマーケット売りを送信します。注文はStartProtectionを通じてN pips先(デフォルト100 pips)に設定されたテイクプロフィットを継承します。
  4. エグジット管理。 ショートエクスポージャーが存在する間、ストラテジーはオープンチケットの利益をpipsで算術平均を計算します。モメンタムに応じて:
    • 平均利益が10 pips未満でWilliams %Rが−80を下回ると、すべてのショートが即時クローズされます。
    • 平均利益が15 pipsでエグジットStochastic %Kが12を下回ると、利益を確保するためにポジションがクローズされます。

マネーマネジメント

Killer Sell 2.0は元のEAと同様のマーチンゲールラダーを使用します。StockSharp実装は、MetaTraderのチケットごとの計算を模倣するためにオープンなショートロットの内部リストを保持します:

  • 最初のトレードはInitialVolume(デフォルト0.05ロット)を使用します。
  • 収益またはブレイクイーブンサイクルの後、ボリュームは初期ロットサイズにリセットされます。
  • 損失サイクルの後、次の注文はMartingaleMultiplier(デフォルト×1.2)で乗算されます。セーフティキャップMaxVolumeが制御不能な成長を防ぎます。

ヘルパーは前のサイクルが収益だったかどうかを判断するためにフィル時の実現PnLも追跡します。

パラメーター

パラメーター 説明
CandleType すべてのインジケーターに供給するプライマリ時間軸。
EntryWprPeriod / ExitWprPeriod エントリーとエグジット確認のWilliams %R長。
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod MACD設定。
MacdThreshold 売りに必要なMACDメインライン最小値。
StochasticEntryKPeriod, StochasticEntryDPeriod, StochasticEntrySlow エントリーStochasticパラメーター。
EntryStochasticLevel シグナルを検証するために%Kが上から下回る必要があるレベル。
StochasticExitKPeriod, StochasticExitDPeriod, StochasticExitSlow エグジットStochasticパラメーター。
ExitStochasticLevel 利益確定前にチェックされる売られすぎ境界。
EntryWprThreshold / ExitWprThreshold エントリー/エグジットのWilliams %Rしきい値。
LossExitPips / ProfitExitPips 防衛的とターゲットエグジットを制御する平均利益境界(pips)。
TakeProfitPips 各売り注文に割り当てられる保護的テイクプロフィット。
InitialVolume 最初のマーチンゲールステップボリューム。
MartingaleMultiplier 損失後に適用される係数。
MaxVolume 次のロットサイズに適用される絶対上限。

変換に関する注記

  • MetaTraderは個別のチケットを保持します;StockSharpはネットポジションで動作します。そのためストラテジーは平均利益計算を再現し、マーチンゲールリセットを評価するために各フィルドショート(ボリューム+価格)を保存します。
  • MT4の「マーチンゲール」ブロックは多くの追加モード(固定、リスクパーセント、1326、Fibonacci等)を公開していました。元の設定はシンプルなマーチンゲールブランチを使用していました;その動作のみがここで複製されます。
  • 緊急ストップロスはソースプロジェクトで無効になっていました。ポートはテイクプロフィットのみをアタッチし、他のエグジットを内部で処理することでその設定を反映します。

使用上のヒント

  1. ストラテジーをポートフォリオとセキュリティにアタッチし、MT4バックテストで使用した同じ時間軸を設定します(デフォルトはH1を想定)。
  2. 市場データが完成したローソク足を配信することを確認してください;インジケーターはCandleStates.Finishedイベントに依存します。
  3. アカウントのレバレッジと許可されるロットサイズを確認してください。デフォルトのマーチンゲールキャップ(5ロット)はブローカーの要件に合わせて調整する必要があります。
  4. 徹底的にバックテストを実施してください — マーチンゲール戦略は市場がショートバイアスに強く逆らうトレンドを示すときにリスクを増大させます。
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 KillerSell20Strategy : 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 KillerSell20Strategy()
	{
		_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;
	}
}