GitHub で見る

ドリームボット戦略

概要

DreamBot は、MetaTrader 4 エキスパート アドバイザ「DreamBot」の StockSharp ポートです。この戦略は、時間足ローソク足のフォース インデックス オシレーターを監視し、勢いが強気または弱気のしきい値を超えるのを待ちます。フォースインデックスが前のバーで強気レベルを下回った後、強気レベルを上回った場合、戦略はロングポジションをオープンします。フォースインデックスが弱気レベルを上回った後、それを下回ると、戦略はショートポジションをオープンします。取引は既存のポジションがない場合にのみ行われ、元のロボットの単一ポジションのロジックを反映しています。

取引ロジック

  • H1 キャンドルをサブスクライブし、平滑化されたフォース インデックス (デフォルトでは長さ 13) を計算します。
  • 最後に完了した 2 つの Force Index 値を追跡します。シグナルは、MT4 実装 (シフト 1 と 2 の iForce) とまったく同じように、前の バー値を使用して生成されます。
  • ポジションがオープンしていない場合、前のローソク足のフォース インデックスが BullsThreshold を上回り、2 つ前のローソク足の値がしきい値を下回った場合はロングと入力します。
  • オープンポジションがない限り、前のローソク足のフォースインデックスが BearsThreshold を下回り、2 つ前のローソク足の値がしきい値を上回っていた場合は、ショートを入力します。
  • オプションのトレーリング ストップは元の EA を複製します。利益が TrailingStepPoints を超えると、ストップ レベルは価格から離れた TrailingStartPoints に引き下げられ、さらに前進します。

リスク管理

  • StartProtection は、商品価格ステップを通じて変換された MetaTrader の「ポイント」距離を使用して、古典的なストップロス注文と利食い注文を添付します。
  • トレーリング保護は市場ベースです。計算されたトレーリングレベルを突破すると、ストラテジーはポジションを即座に閉じる成行注文を送信します。
  • ポジション追跡は出来高加重エントリー価格を取得するため、トレーリングロジックは部分的な約定と反転に一致します。

パラメーター

パラメータ 説明
ForcePeriod Force Index の平滑化期間 (デフォルトは 13)。
TakeProfitPoints MetaTrader ポイント単位のテイクプロフィット距離。
StopLossPoints MetaTrader ポイント単位のストップロス距離。
BullsThreshold ロングエントリーを可能にする強気フォースインデックスのしきい値。
BearsThreshold ショートエントリーを可能にする弱気フォースインデックスのしきい値。
EnableTrailing トレーリングストップロジックを有効にします。
TrailingStartPoints 有効化された後、価格とトレーリングストップの間で維持される距離(ポイント単位)。
TrailingStepPoints トレーリングストップが有効になるまでに必要な利益(ポイント単位)。
CandleType フォース インデックスの計算に使用される時間枠 (デフォルトは H1 ローソク足)。

注意事項

  • パラメータ検証により、後続トリガー (TrailingStepPoints) が後続距離 (TrailingStartPoints) を超えないようにして、MetaTrader の安全性チェックと一致します。
  • 元の EA (ブローカー MODE_STOPLEVEL) からのストップレベルの適用は、StockSharp の価格ステップ変換を通じて近似されます。ブローカーの制約によっては、追加の検証が必要になる場合があります。
  • すべてのコードのコメントとログは、変換ガイドラインの要求に従って英語で提供されます。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// DreamBot strategy: Force Index momentum with EMA trend filter.
/// Buys when Force Index positive and close above EMA, sells when negative and below EMA.
/// </summary>
public class DreamBotStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevClose;
	private bool _hasPrevClose;
	private bool _wasBullish;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public DreamBotStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0m;
		_hasPrevClose = false;
		_wasBullish = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrevClose = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		var close = candle.ClosePrice;
		var volume = candle.TotalVolume;

		if (_hasPrevClose && volume > 0)
		{
			// Simple force index: (close - prevClose) * volume
			var forceIndex = (close - _prevClose) * volume;
			var isBullish = forceIndex > 0 && close > emaValue;

			if (isBullish && !_wasBullish && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (!isBullish && forceIndex < 0 && close < emaValue && _wasBullish && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}

			_wasBullish = isBullish;
		}

		_prevClose = close;
		_hasPrevClose = true;
	}
}