GitHub で見る

ウディ・イワン・マドゥメアの戦略

概要

Udy Ivan Madumere エキスパート アドバイザーは、1 日に 1 回、特定の時間足のローソク足が表示されたときに単一の市場ポジションをオープンします。 StockSharp ポートは、設定されたローソク足シリーズを監視し、過去の始値を比較し、ターゲットバーが閉じた直後に反応することで、この動作をそのまま維持します。すべての実行決定、ポジション管理、ボリューム処理が再現されるため、ストラテジーは StockSharp 環境内でオリジナルの MetaTrader 4 のように動作します。

主な特徴:

  • 1 日あたり TradeHour で完成したローソク足を 1 つ評価し、同時に複数のポジションを送信することはありません。
  • 始値 Open[FirstLookback]Open[SecondLookback] の差を測定して、ショートするかロングするかを決定します。
  • UseAutoVolume が有効な場合、MetaTrader バランス ラダーをミラーリングして基本ロット サイズを自動的に調整します。
  • 非対称のストップロスとテイクプロフィットの距離 (ロングとショートで別々) と、ショートポジションにのみ影響するトレーリングストップを適用します。
  • 保護レベルに達していない場合でも、設定可能な時間数が経過するとすべての取引を強制的に終了します。

取引ワークフロー

  1. 選択したローソク タイプ (CandleType) をサブスクライブし、時期尚早のシグナルを防ぐためにバーが完全に終了するまで待ちます。
  2. 始値履歴を追跡することで、Open[FirstLookback] - Open[SecondLookback] (短い設定) と Open[SecondLookback] - Open[FirstLookback] (長い設定) の違いを MetaTrader と同様に正確に評価できます。
  3. 最新のローソク足が TradeHour で開くとき:
    • 弱気の差が ShortDeltaPoints * PriceStep より大きい場合は、成行売り注文を送信します。
    • それ以外の場合、強気の差が LongDeltaPoints * PriceStep を超える場合は、成行買い注文を送信します。
  4. ご注文は1日1回のみとさせていただきます。 canTrade フラグは、設定された時間が経過するとリセットされ、次のセッションでの再試行が可能になります。
  5. 注文入力時に、ストラテジーは基本ロットを再計算します。
    • UseAutoVolume = true は、口座残高が事前定義されたしきい値を超えたときにロットサイズを増やす従来のラダーをアクティブにします。
    • 現在の残高が前の取引のスナップショットを下回っている場合、結果は BigLotMultiplier 倍され、EA の「大ロット」回復動作と一致します。
  6. ポジションがオープンしている間、完了したローソクごとに次の終了ロジックが実行されます。
    • ハードテイクプロフィットとストップロスは、記録されたエントリー価格に対して評価されます。
    • 最良価格が少なくとも TrailingStopPoints 上昇した場合は、ショートトレードもストップを下回ります。
    • ポジションは MaxHoldingHours 存続すると強制的にクローズされます。

パラメーター

名前 種類 デフォルト 説明
CandleType DataType H1 戦略的に加工されたキャンドルシリーズ。
TradeHour int 18 毎日のシグナルが評価される時刻 (0 ~ 23)。
FirstLookback int 6 Open[FirstLookback] として参照される完了したキャンドルの数。
SecondLookback int 2 Open[SecondLookback] として参照される完了したキャンドルの数。
LongDeltaPoints decimal 6 ロングにエントリーするために必要な最小強気始値差 (MetaTrader ポイント単位)。
ShortDeltaPoints decimal 21 ショートをエントリーするために必要な最低弱気始値差 (MetaTrader ポイント単位)。
TakeProfitLongPoints decimal 39 ロングポジションのテイクプロフィットディスタンス(ポイント単位で表示)。
StopLossLongPoints decimal 147 ロングポジションのストップロス距離(ポイント単位)。
TakeProfitShortPoints decimal 200 ショートポジションのテイクプロフィット距離(ポイント単位)。
StopLossShortPoints decimal 267 ショートポジションのストップロス距離(ポイント単位)。
TrailingStopPoints decimal 30 トレーリングストップの距離(ポイント)はショートポジションにのみ適用されます。
BaseVolume decimal 0.01 資金管理調整前の初期ロットサイズ。
UseAutoVolume bool true BaseVolume をオーバーライドする MetaTrader バランス ラダーを有効にします。
BigLotMultiplier decimal 1 前回の取引以降に残高が減少した場合に適用される追加の乗数。
MaxHoldingHours int 504 最大保持時間 (時間単位)。ゼロを指定するとタイマーが無効になります。

実装メモ

  • 価格しきい値は、商品の PriceStep を使用して、MetaTrader の「ポイント」から実際の価格距離に変換されます。
  • オープン価格バッファは max(FirstLookback, SecondLookback) + 1 エントリにトリミングされ、必要な履歴を保持しながら不必要な割り当てを回避します。
  • ショートトレードのトレーリングストップは、最高の達成安値を保存し、新しい候補が現在の価格に近づいた場合にのみ保護レベルを更新します。
  • アカウント残高スナップショットは Portfolio.CurrentValue (BeginValue にフォールバック) に依存しているため、デモ環境、ライブ環境、バックテスト環境が一貫して動作します。
  • コード内のすべてのコメントは要求に応じて英語で記述されるため、ロジックの監査や拡張が容易になります。

使い方のヒント

  • CandleType を過去の EA で使用されている時間枠と一致させます(元のテンプレートでは 1 時間のローソク足が想定されています)。
  • マイクロ ロットを使用するシンボルで実行する場合は、BaseVolume と自動ロット ラダー値を会場の契約仕様に合わせて調整します。
  • 組み込みヘルパー (DrawCandlesDrawOwnTrades) を介して戦略を StockSharp チャートと組み合わせて、注文が構成された時間に 1 日 1 回だけ表示されることを確認します。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Udy Ivan Madumere: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class UdyIvanMadumereStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

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

	public UdyIvanMadumereStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 20)
			.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 50)
			.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
	public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, rsi, atr, 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 fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastVal; _prevSlow = slowVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; _cooldown = 10; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; _cooldown = 10; }
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 55) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 45) { _entryPrice = close; SellMarket(); }
		}
		_prevFast = fastVal; _prevSlow = slowVal;
	}
}