GitHub で見る

Ang Zad C Time MM Recovery戦略

概要

Ang Zad C Time MM Recovery戦略は、MetaTrader 5エキスパートアドバイザーExp_Ang_Zad_C_Tm_MMRecのC#ポートです。この戦略は、カスタムAng_Zad_Cチャンネルインジケーターと設定可能なトレードセッションフィルター、そして設定可能な数の負けトレード後にリスクを削減する適応型ポジションサイズモデルを組み合わせています。

インジケーターロジック

Ang_Zad_Cインジケーターは価格の周りに2つの適応型エンベロープを構築します。各エンベロープは、現在と前のローソク足の選択された適用価格を比較し、平滑化係数Kiで新しい価格に向かって移動することで更新されます。上線と下線は、未完成のローソク足での行動を避けるため、Signal Barで定義された過去のバーで評価されます。

トレードルール

  • ロングエントリー – 前の参照バーで上線が下線の上にあり、最新の参照バーで下線を下に交差するか触れた場合。これが起こると、新しいロングポジションを開く前に、開いているショートポジションが閉じられます(有効な場合)。
  • ショートエントリー – 前の参照バーで上線が下線の下にあり、最新の参照バーで下線を上に交差するか触れた場合。開いているロングポジションが新しいショートポジションを開く前に閉じられます(有効な場合)。
  • ロングエグジット – 前の参照バーで上線が下線の下にある場合。エグジットはEnable Long Exitで無効にできます。
  • ショートエグジット – 前の参照バーで上線が下線の上にある場合。エグジットはEnable Short Exitで無効にできます。

マネーマネジメントと保護

  • Use Time Filterが有効な場合、設定された時間ウィンドウ内でのみトレードが許可されます。以前に開かれたポジションはセッション終了時に閉じられます。
  • トレードボリュームは、各サイドで発生した負けトレードの数に応じてNormal VolumeSmall Volumeの間で選択されます。Buy Loss Trigger回のロング負けトレード(またはSell Loss Trigger回のショート負けトレード)の後、利益が出るトレードがカウンターをリセットするまで、減少したボリュームが使用されます。
  • オプションのストップロスとテイクプロフィットレベルは、Stop Loss StepsTake Profit Stepsで定義された価格ステップ距離を使用して登録されます。

パラメーター

名前 説明
Candle Type インジケーターとシグナルが使用するローソク足のタイムフレーム。
Ki Ang_Zad_Cエンベロープの平滑化係数。
Applied Price インジケーターに入力されるローソク足価格。
Signal Bar シグナル評価に使用するバーの遡り数(1 = 前の終値バー)。
Use Time Filter / Trade Start / Trade End セッションベースのトレードを有効にし、セッションの開始時刻と終了時刻を設定する。
Enable Long/Short Entry 新しいロングまたはショートトレードの開始を許可する。
Enable Long/Short Exit インジケーターの逆転時にポジションを閉じることを戦略に許可する。
Buy/Sell Loss Trigger 減少したボリュームが適用される前の負けトレードの数。
Small Volume / Normal Volume 減少したリスクと通常リスクに使用される注文サイズ。
Stop Loss Steps / Take Profit Steps 価格ステップで表現された保護注文の距離。

変換ノート

  • ロジックは、方向性クロスチェックと時間ウィンドウ動作を含む、オリジナルのMQL5コードに従っています。
  • 適応型マネーマネジメントは、方向ごとの実現損益を追跡し、設定された損失数の後に減少したボリュームに切り替えることで実装されています。
  • インジケーターの計算はバッファへの直接アクセスを避け、StockSharpの高レベルAPIを使用して完成したローソク足で処理されます。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Ang_Zad_C adaptive channel strategy.
/// Trades when the upper/lower lines cross, indicating trend reversal.
/// </summary>
public class AngZadCTimeMMRecoveryStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _ki;

	private bool _hasState;
	private decimal _upperLine;
	private decimal _lowerLine;
	private decimal _previousPrice;
	private decimal? _prevUp;
	private decimal? _prevDn;

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

	public decimal Ki
	{
		get => _ki.Value;
		set => _ki.Value = value;
	}

	public AngZadCTimeMMRecoveryStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_ki = Param(nameof(Ki), 4.000001m)
			.SetDisplay("Ki", "Smoothing coefficient", "Indicator")
			.SetGreaterThanZero();
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

		_hasState = false;
		_upperLine = 0m;
		_lowerLine = 0m;
		_previousPrice = 0m;
		_prevUp = null;
		_prevDn = null;
	}

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

		_hasState = false;
		_prevUp = null;
		_prevDn = null;

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var price = candle.ClosePrice;
		var (upper, lower) = UpdateIndicator(price);

		if (_prevUp == null || _prevDn == null)
		{
			_prevUp = upper;
			_prevDn = lower;
			return;
		}

		var prevUp = _prevUp.Value;
		var prevDn = _prevDn.Value;

		// Buy signal: previous upper was below lower, now crossing above
		if (prevUp <= prevDn && upper > lower)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Sell signal: previous upper was above lower, now crossing below
		else if (prevUp >= prevDn && upper < lower)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevUp = upper;
		_prevDn = lower;
	}

	private (decimal Up, decimal Down) UpdateIndicator(decimal price)
	{
		if (!_hasState)
		{
			_upperLine = price;
			_lowerLine = price;
			_previousPrice = price;
			_hasState = true;
			return (_upperLine, _lowerLine);
		}

		var ki = Ki;

		if (price > _upperLine && price > _previousPrice)
			_upperLine += (price - _upperLine) / ki;

		if (price < _upperLine && price < _previousPrice)
			_upperLine += (price - _upperLine) / ki;

		if (price > _lowerLine && price < _previousPrice)
			_lowerLine += (price - _lowerLine) / ki;

		if (price < _lowerLine && price > _previousPrice)
			_lowerLine += (price - _lowerLine) / ki;

		_previousPrice = price;

		return (_upperLine, _lowerLine);
	}
}