GitHub で見る

KA-Gold ボット戦略

KA-Gold ボット戦略 は、元の MetaTrader 4「KA-Gold ボット」エキスパート アドバイザーの高レベルの StockSharp 変換です。ケルトナー スタイルのチャネルとトレンド フィルター、および固定ストップロス、テイクプロフィット、多段階のトレーリング保護を含む積極的なリスク管理を組み合わせています。取引は設定可能な日中ウィンドウ中にのみ許可され、ライブスプレッドがしきい値を超えると新しいポジションはブロックされます。

取引ロジック

  1. インジケーターの準備

    • 長さ KeltnerPeriod の指数移動平均 (EMA) がチャネルの正中線を構築します。
    • 同じ期間のローソク足の範囲(高値から安値を引いたもの)の単純移動平均により、チャネルの半値幅が推定されます。
    • 短期および長期の指数移動平均 (EmaShortPeriod および EmaLongPeriod) は、それぞれ速い勢いとより高い時間枠のトレンドを追跡します。
    • すべてのインジケーター値は、MT4 シフトベースの計算を反映するために、最後に完了した 2 つのローソク足に対して記録されます。
  2. エントリー条件

    • 計算は、現在のローソク足が終了し、取引許可が付与されて戦略が市場に接続された場合にのみ実行されます。
    • チャネルの上部バンドと下部バンドは、前の (shift = 1) と前の (shift = 2) の両方のローソク足の EMA 正中線から平均範囲を加算または減算することによって導出されます。
    • 長いセットアップ:
      • 前回の終値は直近のアッパーバンドを上抜けます。
      • 同じ終値は長期の EMA を上回っており、上昇トレンドが確認されています。
      • 短い EMA は、古い上部バンドの下から最新のバンド(EMA_short[2] < Upper[2]EMA_short[1] > Upper[1])の上まで交差します。
    • 簡単なセットアップ:
      • 前回終値は最近の下値バンドを下回っています。
      • 同じ終値はロングの EMA を下回っており、下降トレンドが確認されています。
      • 短い EMA は、古い下のバンドの上から最新のバンドの下まで交差します (EMA_short[2] > Lower[2]EMA_short[1] < Lower[1])。
    • 一度に許可されるポジションは 1 つだけです。取引がすでに開始されている場合、シグナルは無視されます。
  3. タイミングフィルターとスプレッドフィルター

    • UseTimeFilter が有効な場合、新しいエントリは取引所現地時間を使用する [StartHour:StartMinute, EndHour:EndMinute) ウィンドウに制限されます。終了時刻が開始時刻より早い場合、夜間セッションがサポートされます。
    • レベル 1 の見積もりサブスクリプションは、最良の買値/売値を追跡します。注文を行う前に、このストラテジーは現在のスプレッドを商品ポイントに変換し、それを MaxSpreadPoints と比較します。しきい値に違反すると、注文はログに記録されてスキップされます。
  4. リスク管理

    • ポジションのサイジングのデフォルトは FixedVolume です。 UseRiskPercenttrue の場合、取引サイズはポートフォリオの資本から RiskPercent% / (riskPips * PipValue) として再計算されます。ここで、riskPipsStopLossPips と等しくなります (固定ストップが定義されていない場合は、TrailingStopPips にフォールバックします)。最終結果は機器の容量ステップに正規化され、最小交換限界と最大交換限界の間にクランプされます。
    • ロングポジションがオープンされると、ストラテジーは以下を保存します。
      • 最初のストップロスは entry - StopLossPips * pipSize (定義されている場合)。
      • 最初のテイクプロフィットは entry + TakeProfitPips * pipSize (定義されている場合)。
      • トレーリング状態フラグ。ショートサイド トラッカーをリセットします。
    • ショートトレードは、価格の方向が反転した同じロジックを反映しています。
  5. トレーリングプロテクション

    • ライブ入札/売値更新は 2 つの後続エンジンにフィードされます。
      • 変動利益が TrailingTriggerPips を超えると、トレーリングがアクティブになります。
      • トレーリングストップは現在の有利な価格から TrailingStopPips 離れた位置にあり、動きが前のストップレベルを超えて TrailingStopPips + TrailingStepPips を超えた場合にのみ前進します。
      • ロングポジションの場合、トレーリングストップは元の保護ストップを下回ることはなく、ショートポジションの場合、それを超えることはありません。
    • 終了監視は、入ってくる相場と終了したローソク足の両方で実行されます。
      • 価格がアクティブストップ(オリジナルまたはトレーリング)に達すると、ポジションはすぐにクローズされます。
      • ローソク足の高値/安値が保存されたテイクプロフィットレベルに達すると、利益も確定します。
    • ポジションを決済した後、データが古くならないように保護状態が完全にリセットされます。

パラメーター

パラメータ 説明 デフォルト
CandleType 実行時間枠を表すデータ型。 1分の時間枠
KeltnerPeriod EMA 正中線の期間とチャネルの範囲平均。 50
EmaShortPeriod 高速の EMA の長さはクロスオーバーの確認に使用されます。 10
EmaLongPeriod トレンド フィルターとして機能する低速の EMA 長さ。 200
FixedVolume パーセンテージサイジングが無効になっている場合のフォールバック注文量。 1
UseRiskPercent パーセンテージベースのポジションサイジングを有効にします。 true
RiskPercent 取引ごとにリスクが生じる株式の割合。 1
StopLossPips 固定ストップロスの距離 (ピップ単位) (0 は無効)。 500
TakeProfitPips ピップ単位での固定テイクプロフィットの距離 (0 を無効にします)。 500
TrailingTriggerPips トレーリングストップを有効にするために必要な利益 (pips)。 300
TrailingStopPips アクティブになった後の価格とトレーリングストップの間の距離。 300
TrailingStepPips トレーリングストップが前進する前の最小追加利益(ピップ単位)。 100
UseTimeFilter 取引セッションフィルターを切り替えます。 true
StartHour / StartMinute セッションは交換現地時間で開始されます。 02:30
EndHour / EndMinute セッションは交換現地時間で終了します。 21:00
MaxSpreadPoints 機器ポイントで許容される最大スプレッド (0 はチェックを無効にします)。 65
PipValue 1 ピップの金銭的価値。リスクベースのポジションサイジングに使用されます。 1

追加メモ

  • ピップ変換は、為替商品の小数点以下に従います。5 桁の相場 (小数点以下の奇数) は、価格ステップを 10 倍して、MT4 ピップ サイズ ロジックをエミュレートします。
  • この戦略はローソク足とレベル 1 データの両方をサブスクライブしますが、チャート上に追加のインジケーターを登録することはなく、高レベルの API ガイドラインに準拠しています。
  • 保護的出口は、戦略によって発行された成行注文に依存します。取引所では個別のストップ注文や指値注文は行われません。
  • Python サポートは、元のリクエストと一致するため、この配信には含まれていません。
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;

/// <summary>
/// Simplified from "KA Gold Bot" MetaTrader expert.
/// Uses Keltner channel (EMA + ATR-based bands) with EMA crossover for entries.
/// Buys when short EMA crosses above long EMA and close is above Keltner center.
/// Sells on reverse conditions.
/// </summary>
public class KaGoldBotStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _keltnerPeriod;
	private readonly StrategyParam<int> _emaShortPeriod;
	private readonly StrategyParam<int> _emaLongPeriod;

	private ExponentialMovingAverage _emaShort;
	private ExponentialMovingAverage _emaLong;

	// Manual ATR-like range average for Keltner
	private readonly Queue<decimal> _rangeQueue = new();
	private decimal _rangeSum;
	private ExponentialMovingAverage _emaKeltner;

	private decimal? _prevEmaShort;
	private decimal? _prevEmaLong;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int KeltnerPeriod
	{
		get => _keltnerPeriod.Value;
		set => _keltnerPeriod.Value = value;
	}

	public int EmaShortPeriod
	{
		get => _emaShortPeriod.Value;
		set => _emaShortPeriod.Value = value;
	}

	public int EmaLongPeriod
	{
		get => _emaLongPeriod.Value;
		set => _emaLongPeriod.Value = value;
	}

	public KaGoldBotStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_keltnerPeriod = Param(nameof(KeltnerPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Keltner Period", "EMA period for Keltner channel center", "Indicators");

		_emaShortPeriod = Param(nameof(EmaShortPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("EMA Short Period", "Short EMA for crossover signal", "Indicators");

		_emaLongPeriod = Param(nameof(EmaLongPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Long Period", "Long EMA for crossover signal", "Indicators");
	}

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

		_emaShort = new ExponentialMovingAverage { Length = EmaShortPeriod };
		_emaLong = new ExponentialMovingAverage { Length = EmaLongPeriod };
		_emaKeltner = new ExponentialMovingAverage { Length = KeltnerPeriod };

		_rangeQueue.Clear();
		_rangeSum = 0;
		_prevEmaShort = null;
		_prevEmaLong = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_emaShort, _emaLong, ProcessCandle)
			.Start();

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

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

		var close = candle.ClosePrice;

		// Process Keltner EMA manually
		var keltnerInput = new DecimalIndicatorValue(_emaKeltner, close, candle.OpenTime);
		var keltnerResult = _emaKeltner.Process(keltnerInput);
		var emaKeltnerValue = keltnerResult.IsEmpty ? close : keltnerResult.GetValue<decimal>();

		// Calculate range average (manual SMA of high-low) for Keltner bands
		var range = candle.HighPrice - candle.LowPrice;
		_rangeQueue.Enqueue(range);
		_rangeSum += range;
		while (_rangeQueue.Count > KeltnerPeriod)
			_rangeSum -= _rangeQueue.Dequeue();

		if (_prevEmaShort == null || _prevEmaLong == null)
		{
			_prevEmaShort = emaShortValue;
			_prevEmaLong = emaLongValue;
			return;
		}

		// Keltner bands
		var rangeAvg = _rangeQueue.Count > 0 ? _rangeSum / _rangeQueue.Count : 0;
		var upper = emaKeltnerValue + rangeAvg;
		var lower = emaKeltnerValue - rangeAvg;

		// Buy: short EMA crosses above long EMA and close above Keltner center
		var buySignal = _prevEmaShort.Value <= _prevEmaLong.Value && emaShortValue > emaLongValue
			&& close > emaKeltnerValue;

		// Sell: short EMA crosses below long EMA and close below Keltner center
		var sellSignal = _prevEmaShort.Value >= _prevEmaLong.Value && emaShortValue < emaLongValue
			&& close < emaKeltnerValue;

		if (buySignal)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (sellSignal)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevEmaShort = emaShortValue;
		_prevEmaLong = emaLongValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_emaShort = null;
		_emaLong = null;
		_emaKeltner = null;
		_rangeQueue.Clear();
		_rangeSum = 0;
		_prevEmaShort = null;
		_prevEmaLong = null;

		base.OnReseted();
	}
}