GitHub で見る

KA-Gold ボット戦略

KA-Gold ボット戦略 は、MetaTrader エキスパート アドバイザ「KA-Gold ボット」を直接移植したものです。カスタムのケルトナースタイルチャネルのブレイクアウトをトレードし、シグナルを中期トレンドフィルターに合わせます。このポートは、StockSharp の高レベルのローソク足サブスクリプション、インジケーター バインディング、および戦略パラメーターに依存しているため、動作は UI から設定可能であり、最適化の準備が整っています。

取引ロジック

  • 3 つの指数移動平均 (EMA) を計算します。
    • EMA(10) 勢いを素早く確認します。
    • EMA(200) より高い時間枠のトレンドを検出します。
    • EMA(ピリオド)をチャンネルの中心として;同じ長さがローソク足の範囲 (高値から安値) を平均するために使用されます。
  • 単純移動平均を使用して日次範囲を平均して、動的なエンベロープを形成します。
    • 上部バンド = EMA(周期) + SMA(高値-低値、周期)。
    • 低域 = EMA(周期) − SMA(高値-低値、周期)。
  • 長いセットアップでは、最後に閉じたローソク足で次のすべてが必要です。
    • 終値は上部バンドを上回ります。
    • 終値は EMA(200) を上回ります。
    • EMA(10) 前の上部バンドの下から最新の上部バンドの上までクロスしました。
  • 短いセットアップはルールを反映しています:
    • 終値は下限バンドを下回ります。
    • 終値は EMA(200) を下回ります。
    • EMA(10) 前の下限バンドの上から最新の下限バンドの下までクロスしました。
  • 一度にオープンできるポジションは 1 つだけです。戦略がフラットになるまで、反対のシグナルは無視されます。

ポジションサイズ

次の 2 つのボリューム モデルがサポートされています。

  1. 固定ロットモードBaseVolume パラメータを直接使用します。
  2. リスク割合モードUseRiskPercent = true の場合、フリーエクイティ プロキシ (Portfolio.CurrentValue または Portfolio.BeginValue) に RiskPercent が乗算されます。結果は 100,000 (MetaTrader ロットの規則) でスケールされ、Security.MinVolumeSecurity.MaxVolume、および Security.VolumeStep を考慮して BaseVolume の倍数に丸められます。

リスク管理

  • ストップロスとテイクプロフィットのオフセットはピップ単位で定義されます。ピップは、セキュリティ ステップを使用して絶対価格距離に変換されます。 10 進数 3 桁と 5 桁の外国為替記号は、MetaTrader ルール pip = step × 10 を再利用します。
  • 初期の保護注文は最初の約定の直後に登録され、現在のポジションサイズと同期されます。
  • 未実現利益が TrailingTriggerPips に達すると、トレーリング ストップが有効になります:
    • ストップ TrailingStopPips を終値から遠ざけることでロングポジションを追跡します。
    • ショートポジションは市場上の対称距離を使用します。
    • オーバートリガーを避けるため、距離が少なくとも TrailingStepPips 改善された場合にのみストップが移動します。
  • ポジションが決済されると、保留中の保護注文は自動的にキャンセルされます。

セッションフィルターとスプレッドフィルター

  • UseTimeFilterStartHourStartMinuteEndHour、および EndMinute によって制御されるオプションの取引ウィンドウ (包含-排他ウィンドウ)。夜間のウィンドウがサポートされています (開始が午前 0 時を過ぎて終了するよりも早く終了します)。
  • オプションのスプレッド フィルタは、現在のスプレッド (価格ステップにおけるベスト アスクと入札の差) が MaxSpreadPoints を超える場合、新しいエントリを拒否します。

実装メモ

  • キャンドルは SubscribeCandles().Bind(...) 経由で処理されます。 EMA(10) と EMA(200) の値はバインディングを通じて到着しますが、チャネル EMA と範囲平均は GetValue を使用せずにハンドラー内で更新されます。
  • インジケーターの状態は、MetaTrader iClose および CopyBuffer シフト ロジックをミラーリングするスカラー フィールドを通じてのみ保存され、最後の 2 つの閉じたバーを比較するという要件は維持されます。
  • 保護ロジックとトレーリング ロジックは、高レベルの順序ヘルパー (BuyStopSellStopBuyLimitSellLimit) を使用して、MetaTrader の PositionModify 呼び出しをミラーリングします。
  • ポートフォリオベースのサイジングは、StockSharp で入手可能な株式情報に依存します。それが欠けている場合、戦略は固定ボリュームに戻ります。

パラメーター

パラメータ 説明 デフォルト
KeltnerPeriod チャンネル EMA の期間と範囲の平滑化。 50
FastEmaPeriod 高速 EMA フィルターの長さ。 10
SlowEmaPeriod 低速 EMA トレンド フィルターの長さ。 200
BaseVolume 最小注文量(ロットサイズ)。 0.01
UseRiskPercent バランスベースのポジションサイジングを有効にします。 本当の
RiskPercent リスクサイジングがアクティブな場合に取引ごとに使用される資本の割合。 1
StopLossPips ピップス単位のストップロス距離。 500
TakeProfitPips 利益確定距離 (ピップ単位) (0 を無効にします)。 500
TrailingTriggerPips トレーリングストップを作動させるための利益しきい値。 300
TrailingStopPips 作動後、トレーリングストップによって維持される距離。 300
TrailingStepPips ストップを移動する前の最小限の改善。 100
UseTimeFilter 取引セッションフィルターを切り替えます。 本当の
StartHour, StartMinute セッションの開始時間。 02:30
EndHour, EndMinute セッション終了時刻 (排他的)。 21:00
MaxSpreadPoints 価格ステップで許可される最大スプレッド (0 = 無効)。 65
CandleType シグナルキャンドルに使用されるタイムフレーム。 5分キャンドル

MetaTrader バージョンとの違い

  • トレーリングストップ実装は、StockSharp ストップ注文を使用して PositionModify シーケンスを再作成します。機能は同等ですが、交換確認済みの注文に依存します。
  • MetaTrader は平均高低範囲からチャネル幅を計算しました。ポートは、ブレークアウトを同一に保つために、単純な移動平均を使用して同じ平均化を再現します。
  • リスクサイジングでは、自由証拠金ではなくポートフォリオの資本にアクセスします。この近似値は意図 (資本の割合) と一致しますが、レバレッジ固有の証拠金データが利用できない場合は異なる場合があります。
  • スプレッドチェックでは、Security.BestAskPriceSecurity.BestBidPrice を使用します。深度が利用できない場合、フィルターはスキップされ、元のエキスパートの「フローティング スプレッド」オプションが反映されます。

使用のヒント

  • リスクパラメーターを元の専門家と一致させるために、ピップ定義が外国為替の慣例 (小数点以下 3 桁または 5 桁) に従っている商品にストラテジーを添付します。
  • ソース戦略が XAUUSD 向けに調整されているため、非ゴールド商品の EMA 期間とチャネル長を最適化します。
  • ポートフォリオ ウィンドウを監視して、UseRiskPercent が有効になっているときに株式の値が入力されていることを確認します。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the MetaTrader expert "KA-Gold Bot".
/// Trades breakouts of a Keltner-style channel confirmed by trend filters from fast and slow EMA.
/// Buys when close breaks above upper Keltner band and fast EMA is above slow EMA.
/// Sells when close breaks below lower Keltner band and fast EMA is below slow EMA.
/// Exits when price crosses the opposite Keltner band or when EMA trend reverses.
/// </summary>
public class KAGoldBotStrategy : Strategy
{
	private readonly StrategyParam<int> _keltnerPeriod;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private readonly StrategyParam<decimal> _bandMultiplier;

	private ExponentialMovingAverage _fastEma;
	private ExponentialMovingAverage _slowEma;
	private ExponentialMovingAverage _keltnerEma;
	private SimpleMovingAverage _rangeAverage;

	private bool _prevAboveUpper;
	private bool _prevBelowLower;
	private decimal _entryPrice;

	/// <summary>
	/// Keltner channel length used for the midline EMA and range average.
	/// </summary>
	public int KeltnerPeriod
	{
		get => _keltnerPeriod.Value;
		set => _keltnerPeriod.Value = value;
	}

	/// <summary>
	/// Fast EMA period for crossover signal.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period for trend filter.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	/// <summary>
	/// Multiplier for Keltner channel band width.
	/// </summary>
	public decimal BandMultiplier
	{
		get => _bandMultiplier.Value;
		set => _bandMultiplier.Value = value;
	}

	/// <summary>
	/// Candle type used for signal generation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public KAGoldBotStrategy()
	{
		_keltnerPeriod = Param(nameof(KeltnerPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Keltner Period", "Length of the EMA and range average", "Indicators");

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Period of the fast EMA filter", "Indicators");

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Period of the slow EMA trend filter", "Indicators");

		_bandMultiplier = Param(nameof(BandMultiplier), 3m)
			.SetGreaterThanZero()
			.SetDisplay("Band Multiplier", "Multiplier for Keltner band width", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for calculations", "General");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
		_slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };
		_keltnerEma = new ExponentialMovingAverage { Length = KeltnerPeriod };
		_rangeAverage = new SimpleMovingAverage { Length = KeltnerPeriod };

		_prevAboveUpper = false;
		_prevBelowLower = false;
		_entryPrice = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_fastEma, _slowEma, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _fastEma);
			DrawIndicator(area, _slowEma);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;

		// Process Keltner EMA and range average manually (IsFinal=true for finished candles)
		var midResult = _keltnerEma.Process(new DecimalIndicatorValue(_keltnerEma, close, candle.OpenTime) { IsFinal = true });
		var rangeResult = _rangeAverage.Process(new DecimalIndicatorValue(_rangeAverage, candle.HighPrice - candle.LowPrice, candle.OpenTime) { IsFinal = true });

		if (!_keltnerEma.IsFormed || !_rangeAverage.IsFormed)
			return;

		var mid = midResult.GetValue<decimal>();
		var avgRange = rangeResult.GetValue<decimal>();
		var upper = mid + avgRange * BandMultiplier;
		var lower = mid - avgRange * BandMultiplier;

		var aboveUpper = close > upper;
		var belowLower = close < lower;

		// Exit logic: close crosses opposite band
		if (Position > 0 && close < lower)
		{
			SellMarket();
		}
		else if (Position < 0 && close > upper)
		{
			BuyMarket();
		}

		// Entry logic: Keltner breakout + EMA trend confirmation
		if (Position == 0)
		{
			// Buy: close breaks above upper band, fast EMA above slow EMA
			if (!_prevAboveUpper && aboveUpper && fastValue > slowValue)
			{
				BuyMarket();
				_entryPrice = close;
			}
			// Sell: close breaks below lower band, fast EMA below slow EMA
			else if (!_prevBelowLower && belowLower && fastValue < slowValue)
			{
				SellMarket();
				_entryPrice = close;
			}
		}

		_prevAboveUpper = aboveUpper;
		_prevBelowLower = belowLower;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_fastEma = null;
		_slowEma = null;
		_keltnerEma = null;
		_rangeAverage = null;
		_prevAboveUpper = false;
		_prevBelowLower = false;
		_entryPrice = 0;

		base.OnReseted();
	}
}