GitHub で見る

Blau TVI タイムド・リバーサル戦略

概要

  • MetaTrader 5エキスパートアドバイザー Exp_BlauTVI_Tm.mq5MQL/21014に配置)から変換。
  • 3つの設定可能なスムージング段階でBlau Tick Volume Index (TVI)ロジックを再実装。
  • スムージングされたTVIが傾きを変えると反転トレードを生成し、オプションで注文をユーザー定義のトレーディングセッションに制限。
  • 価格ポイントで定義されたオプションのストップロスとテイクプロフィット保護をサポート。

Blau Tick Volume Indexロジック

元のエキスパートはBlauTVIカスタムインジケーターを使用し、ティックボリュームからアップティックとダウンティックをカウントして結果を複数回スムージングします。C#ポートは同じアイデアを維持します:

  1. 生のアップ/ダウンティック数
    • UpTicks = (Volume + (Close - Open) / PriceStep) / 2
    • DownTicks = Volume - UpTicks
    • StockSharpフィードは集約されたキャンドルのティック数を公開しないため、キャンドルボリュームをティックボリュームの代替として使用。
  2. ステージ1スムージングUpTicksDownTicksを選択した移動平均タイプ(EMASMASMMAWMAJMA)と長さLength1でスムージング。
  3. ステージ2スムージング – ステージ1の出力を長さLength2で再度スムージング。
  4. TVI計算TVI = 100 * (Up2 - Down2) / (Up2 + Down2)
  5. ステージ3スムージング – ノイズを軽減するためTVIをさらに長さLength3でスムージング。

戦略は元のEA(SignalBarオフセットのCopyBuffer)で使用されるSignalBarオフセットを複製するために最終TVI値の短いローリング履歴を保存します。

トレーディングルール

  • シグナル傾き検出
    • 前のTVI値(SignalBar + 1)が古い値(SignalBar + 2)より小さい場合、TVIは上向きに転換していると見なされます。最新の利用可能な値も前の値より大きい場合、強気の反転シグナルが生成されます。
    • 前のTVI値が古い値より大きい場合、TVIは下向きです。最新の値が前の値を下回ると、弱気の反転シグナルが生成されます。
  • ポジション管理
    • ロングエントリーにはEnableBuyOpen = true、上記の強気シグナル、非正の現在ポジションが必要。絶対ショートサイズを設定Volumeに追加することで既存のショートポジションを決済してから買い。
    • ショートエントリーにはEnableSellOpen = true、弱気シグナル、非負のポジションが必要。
    • TVI傾きが弱気になりEnableBuyClose = trueの場合ロングエグジットが発動。
    • TVI傾きが強気になりEnableSellClose = trueの場合ショートエグジットが発動。
  • 時間フィルター
    • EnableTimeFilter = trueの場合、戦略は[StartHour:StartMinute, EndHour:EndMinute]ウィンドウ内でのみ新規ポジションを開く。夜間セッションをサポート(開始 > 終了)。時間がセッションを離れるとすぐにポジションを強制決済。
  • 保護
    • StopLossPointsTakeProfitPointsはインストゥルメントのPriceStepで乗算して絶対価格距離に変換し、StartProtectionに渡される。無効化するには0に設定。

パラメーター

パラメーター 説明
Volume 各エントリーに使用される注文サイズ(逆のエクスポージャーをカバーするために追加コントラクトが追加される)。
CandleType すべての計算に使用されるキャンドルデータタイプ/時間軸(デフォルト:4時間時間軸)。
MaType すべてのスムージング段階の移動平均タイプ(EMA、SMA、SMMA、WMA、JMA)。
Length1, Length2, Length3 Blau TVI計算の各段階のスムージング長。
SignalBar シグナル生成に使用されるTVI値のオフセット(1はMQLバージョンと同様に前の閉じたキャンドルに対応)。
EnableBuyOpen, EnableSellOpen シグナル時にロング/ショートポジションを開くことを許可。
EnableBuyClose, EnableSellClose 傾きが反転したときに既存のロング/ショートポジションを閉じることを許可。
EnableTimeFilter トレーディングセッションウィンドウの切り替え。
StartHour, StartMinute, EndHour, EndMinute 取引所時間のセッション境界。イントラデイと夜間範囲をサポート。
StopLossPoints, TakeProfitPoints 価格ポイントで表された固定保護距離(0で各保護を無効化)。

実装上の注記

  • StockSharp環境は集約されたキャンドルのティック数を公開しないため、ティックボリュームの代わりにキャンドルボリュームが使用されます。これにより利用可能なデータと互換性を保ちながら元のインジケーターに近い動作を維持します。
  • 戦略はコンパクトなTVI履歴のみを追跡(最新の数値のみ)して大規模カスタムコレクションを避けるリポジトリガイドラインに違反せずにSignalBarシフトを複製します。
  • StartProtectionは有効なPriceStepが利用可能な場合にのみ初期化されます。それ以外の場合は固定ターゲットなしの保護にフォールバックします。
  • すべてのコメントはリポジトリルールに準拠するため英語で書き直され、AGENTS.mdの要件に従いインデントにはタブが使用されています。

使用上のヒント

  1. 元のエキスパートアドバイザー設定に一致するデフォルトのH4時間軸とEMAスムージングから始める。
  2. 一バー待つのではなく最後の閉じたキャンドルに反応したい場合はSignalBarを0に調整するが、これはMQLロジックから逸脱することに注意。
  3. 不規則な取引時間のインストゥルメントで実行する場合、流動性の低い期間のシグナルを避けるために時間フィルターを設定する。
  4. 動的なサイジングが必要な場合はポートフォリオレベルの資金管理と組み合わせる。Volumeは設計上静的であり、ソースEAの固定ロットアプローチを反映している。
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>
/// Blau TVI timed reversal strategy (simplified). Uses Momentum indicator
/// to detect slope changes and generate reversal entries.
/// </summary>
public class BlauTviTimedReversalStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momentumLength;

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

	public int MomentumLength
	{
		get => _momentumLength.Value;
		set => _momentumLength.Value = value;
	}

	public BlauTviTimedReversalStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_momentumLength = Param(nameof(MomentumLength), 12)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Length", "Momentum period", "Indicators");
	}

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

		var momentum = new Momentum { Length = MomentumLength };

		decimal prevMom = 0;
		decimal prevPrevMom = 0;
		var count = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(momentum, (ICandleMessage candle, decimal momValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				count++;
				if (count < 3)
				{
					prevPrevMom = prevMom;
					prevMom = momValue;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevPrevMom = prevMom;
					prevMom = momValue;
					return;
				}

				// Detect slope reversal: was falling, now rising => buy
				if (prevMom < prevPrevMom && momValue > prevMom && Position <= 0)
					BuyMarket();
				// Was rising, now falling => sell
				else if (prevMom > prevPrevMom && momValue < prevMom && Position >= 0)
					SellMarket();

				prevPrevMom = prevMom;
				prevMom = momValue;
			})
			.Start();

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