GitHub で見る

AbsolutelyNoLagLWMA レンジチャンネル TM Plus 戦略

概要

この戦略はMetaTraderエキスパート「Exp_AbsolutelyNoLagLwma_Range_Channel_Tm_Plus」の直接ポートです。ローソク足の高値と安値を二重平滑化した線形加重移動平均(LWMA)から導出された価格チャンネルを取引します。StockSharpバージョンはオリジナルの動作を維持しています:シグナルは選択可能な時間軸の完成したローソク足で評価され、チャンネル状態はMQLインジケーターと同じ方法でエンコードされ、ポジション管理は同じ優先順序に従います(時間による決済が最初、インジケーターによる決済が2番目、新規エントリーが最後)。

インジケーターの構築

  1. 完成した各ローソク足について、高値と安値のシリーズが最初のLWMAに入力されます。長さパラメーターは高値と安値のストリーム間で共有されます。
  2. 最初のLWMAの出力が同じ長さの別のLWMAで再度平滑化されます。これによりオリジナルインジケーターが使用する「AbsolutelyNoLagLWMA」平滑化が再現されます。
  3. 最終的な上部と下部チャンネル値がローソク足の終値と比較されます:
    • 上線より上のクローズ → 強気ブレイクアウト状態。
    • 下線より下のクローズ → 弱気ブレイクアウト状態。
    • チャンネル内のクローズ → 中立状態。
  4. 戦略は最新のチャンネル状態を保存します。SignalBar パラメーターはシグナル生成のためにどのバーインデックスを確認するかを制御します(0 = 最後にクローズしたローソク足、1 = 1バー前など)、MQLプログラムの SignalBar 入力と一致しています。

シグナルの解釈

  • ロングエントリーEnableBuyEntries で有効化。戦略は SignalBar + 1 でインデックスされたバーで強気ブレイクアウトを探し、SignalBar のバーがすでにチャンネル内に戻っている間に行います。動作はオリジナルの「前のバーブレイクアウト」テストを複製します。
  • ショートエントリーEnableSellEntries で有効化。弱気ブレイクアウトのためのロングロジックを反映します。
  • ロング決済EnableBuyExits で有効化。参照バーでの弱気ブレイクアウトは既存のロングポジションをクローズします(現在のローソク足で時間ベースの決済によりすでにクローズされていない限り)。
  • ショート決済EnableSellExits で有効化。参照バーでの強気ブレイクアウトはオープンショートをクローズします(時間ベースの決済がすでにクローズを要求していない限り)。

トレード管理

  • 注文ボリュームOrderVolume パラメーターから取得。反転注文は残余エクスポージャーを避けるために現在のポジションの絶対値を自動的に加算します。
  • ストップロス / テイクプロフィット – 楽器のポイントで定義されるオプションの絶対オフセット(StopLossPointsTakeProfitPoints)。正の場合、楽器の PriceStep を使用して価格オフセットに変換され、StartProtection に渡されます。
  • 時間ベースの決済 – オリジナルEAは保持時間を超えたポジションをクローズします(TimeTradenTime)。StockSharpでは UseTimeExitHoldingLimit で処理されます。決済は完成した各ローソク足でインジケーターシグナルの前に評価されます。
  • ポジションタイミング – 戦略はロングまたはショートポジションをもたらした最後のトレードのタイムスタンプを記録します。これらのタイムスタンプは時間ベースの決済に使用されます。

パラメーター

パラメーター 説明
Length チャンネルを形成する両方のLWMAパスの長さ。
SignalBar シグナルのために調べられるバーのシフト(0 = 最後にクローズしたローソク足)。
CandleType インジケーターとトレード評価に使用される時間軸。
OrderVolume 新規エントリー注文の送信時に使用されるボリューム。
StopLossPoints 楽器のポイントでのストップロス距離(0はストップを無効化)。
TakeProfitPoints 楽器のポイントでのテイクプロフィット距離(0は目標を無効化)。
EnableBuyEntries 新規ロングポジションを許可または禁止。
EnableSellEntries 新規ショートポジションを許可または禁止。
EnableBuyExits インジケーターがロングポジションをクローズすることを許可。
EnableSellExits インジケーターがショートポジションをクローズすることを許可。
UseTimeExit HoldingLimit 経過後のポジションクローズを有効化。
HoldingLimit 時間決済がトリガーされる前の最大保持時間。

注意事項

  • チャンネルは付属のMQLインジケーター AbsolutelyNoLagLwma_Range_Channel と全く同じようにローソク足の高値と安値から計算されます。
  • 戦略は未完成のローソク足を無視し、早期シグナルを避けるために完成したデータのみで作業します。
  • SignalBar0 に設定すると、最後のクローズしたローソク足が分析される典型的なMT5設定に対応します。より高い値はデフォルトEAが使用する遅延確認を再現します(SignalBar = 1)。
  • 選択した楽器に PriceStep が利用できない場合、ストップロスとテイクプロフィットのオフセットは無視され、オリジナルスクリプトのゼロ値入力の動作を維持します。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// LWMA range channel breakout strategy.
/// Uses fast/slow WMA crossover with mid-channel exit logic.
/// </summary>
public class AbsolutelyNoLagLwmaRangeChannelTmPlusStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

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

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast WMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow WMA period", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

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

		_prevFast = null;
		_prevSlow = null;

		var fastWma = new WeightedMovingAverage { Length = FastPeriod };
		var slowWma = new WeightedMovingAverage { Length = SlowPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fast > slow;

		_prevFast = fast;
		_prevSlow = slow;

		// Crossover: fast crosses above slow → buy
		if (!prevAbove && currAbove)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Crossover: fast crosses below slow → sell
		else if (prevAbove && !currAbove)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}
	}
}