GitHub で見る

ComFracti フラクタル RSI 戦略

概要

ComFracti Fractal RSI 戦略は、MetaTrader の専門家 ComFracti の StockSharp 移植です。このアルゴリズムは、2 つのタイムフレームでビル Williams フラクタルを使用して方向性のバイアスを検索し、毎日のローソク足で計算された高速な RSI でシグナルをフィルター処理します。有効な設定が表示されると、この戦略は単一のポジションをオープンし、設定可能なストップロスおよびテイクプロフィットディスタンスでポジションを保護し、シグナルが反転したときまたは保持時間制限に達したときにオプションで決済することもできます。

デフォルトの設定では、元のエキスパートと同様に、ローソク足の始値を使用して、1 時間の確認時間枠と 3 期間の毎日の RSI の長さを持つ 15 分の取引時間枠を複製します。

取引ロジック

  1. フラクタル バイアス検出
    • 取引時間枠とより高い時間枠からの完成したローソク足は、5 バーのフラクタル ウィンドウを通じて処理されます。
    • Primary*Shift パラメータと Higher*Shift パラメータは、戦略が最新の確認済みフラクタルを検査するバー数を定義します。デフォルトは、3 の元の値と一致します。これは、コードが 3 キャンドル前に確認されたフラクタルを評価することを意味します。
    • 上昇フラクタルを伴わない下降フラクタル (スイングが低く) は強気 (+1) として扱われます。ダウン フラクタルのないアップ フラクタルは弱気 (-1) として扱われます。
  2. 毎日の RSI フィルター
    • 構成可能な RsiPeriod (デフォルトは 3) を持つ RelativeStrengthIndex は、毎日のタイムフレームで実行され、MetaTrader の実装と一致するローソク足の始値を使用します。
    • セットアップに時間がかかる場合は、RSI を 50 - RsiBuyOffset 未満にする必要があります。短いセットアップでは、RSI が 50 + RsiSellOffset より大きい必要があります。
  3. エントリー条件
    • 購入: どちらのフラクタル トラッカーも +1 をレポートし、RSI フィルターは強気です。この戦略は、フラットまたはショートの場合にロングポジションをオープンし、ロングサイドに反転するのに十分なボリュームを送信します。
    • 売り: どちらのフラクタル トラッカーも -1 をレポートし、RSI フィルターは弱気です。この戦略は、フラットまたはロングの場合にショート ポジションをオープンし、ショート サイドに反転するのに十分なボリュームを送信します。
  4. ポジション管理
    • 保護的なストップロスとテイクプロフィットのレベルは、ポジションが変更された直後に、StopLossPipsTakeProfitPips に商品のピップサイズを掛けたものに基づいて計算されます。
    • 価格がストップまたはターゲットに達したとき、ExpiryMinutes が経過したとき、または CloseOnOppositeSignal が有効になってシグナルが反転したとき、ポジションを閉じることができます。

パラメーター

名前 説明 デフォルト
Volume 各エントリーに使用される注文量。 0.1
TakeProfitPips 利益目標距離 (pips)。無効にするには、0 に設定します。 700
StopLossPips ピップス単位のストップロス距離。無効にするには、0 に設定します。 2500
ExpiryMinutes 強制終了するまでの最大保持時間 (分単位)。 0 はタイマーを無効にします。 5555
CloseOnOppositeSignal 信号が反対方向に反転すると、アクティブなポジションを閉じます。 false
PrimaryBuyShift バーバックして取引時間枠の強気のフラクタルを検査します。 3
HigherBuyShift バーバックして、より高い時間枠での強気のフラクタルを検査します。 3
PrimarySellShift バーバックして取引時間枠の弱気フラクタルを検査します。 3
HigherSellShift バーバックして、より高い時間枠で弱気のフラクタルを検査します。 3
RsiBuyOffset 長いセットアップには 50 未満のオフセットが必要です。 3
RsiSellOffset 短いセットアップには 50 を超えるオフセットが必要です。 3
RsiPeriod 毎日の時間枠での長さは RSI です。 3
CandleType 取引時間枠のローソク足タイプ。 15分キャンドル
HigherTimeFrame 確認タイムフレームキャンドルタイプ。 1時間キャンドル
DailyTimeFrame 毎日の RSI に使用されるキャンドルの種類。 1dayキャンドル

実装メモ

  • この戦略は、高レベルのローソク足サブスクリプション API (SubscribeCandles().Bind(...)) を使用し、ガイドラインで要求されているように、Strategy.Indicators を介してインジケーターを公開することなく、内部でインジケーターを管理します。
  • Fractals は、ローリング 5 キャンドル ウィンドウを保存し、フラクタルが確認された後にのみ信号を更新する内部ヘルパーを介して計算されます。
  • RSI の値は、MetaTrader PRICE_OPEN モードと一致するローソク足の始値を使用して RelativeStrengthIndex.Process(...) を介して取得されます。
  • 一度に維持されるポジションは 1 つだけです。成行注文は、既存のエクスポージャーをカバーするために必要な量を追加することで、必要に応じてポジションを反転します。
  • Pip サイズは、小数点以下 3 桁以上で引用されたアセットに 10 倍の乗数を使用して、Security.PriceStepSecurity.Decimals から推定され、MetaTrader Point から pip への変換が再現されます。

使用のヒント

  • フラクタル シフトは、要求されたローソク足インデックスが存在することを保証するのに十分な大きさでなければなりません。シフトが 3 の場合、トラッカーはシグナルを生成する前に、タイムフレームごとに少なくとも 5 つの完成したローソク足を必要とします。
  • 異なるティックサイズの商品(指数や株式など)を取引する場合は、商品のピップ定義と一致するように TakeProfitPipsStopLossPips を調整します。
  • CloseOnOppositeSignal を無効にすると、元のエキスパート アドバイザの動作 (デフォルトで無効になっています) が複製され、停止、ターゲット、または終了の有効期限タイマーのみに依存します。
  • この戦略では、マーチンゲール法やリスクベースのサイジングは実装されていません。 MetaTrader のロット計算は、StockSharp では利用できない口座証拠金関数に依存していました。動的なポジションサイジングが必要な場合は、Volume パラメータを使用するか、ポートフォリオ マネージャーで戦略をラップします。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// ComFracti Fractal RSI: Fractal breakout with RSI filter and ATR stops.
/// </summary>
public class ComFractiFractalRsiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _entryPrice;
	private decimal _prevHigh5;
	private decimal _prevLow5;
	private decimal _high1, _high2, _high3, _high4, _high5;
	private decimal _low1, _low2, _low3, _low4, _low5;
	private int _barCount;

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

		_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 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();

		_entryPrice = 0;
		_barCount = 0;
		_prevHigh5 = 0;
		_prevLow5 = 0;
		_high1 = _high2 = _high3 = _high4 = _high5 = 0;
		_low1 = _low2 = _low3 = _low4 = _low5 = 0;
	}

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

		_entryPrice = 0;
		_barCount = 0;
		_prevHigh5 = 0;
		_prevLow5 = 0;
		_high1 = _high2 = _high3 = _high4 = _high5 = 0;
		_low1 = _low2 = _low3 = _low4 = _low5 = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		// Shift fractal window
		_high5 = _high4; _high4 = _high3; _high3 = _high2; _high2 = _high1;
		_high1 = candle.HighPrice;
		_low5 = _low4; _low4 = _low3; _low3 = _low2; _low2 = _low1;
		_low1 = candle.LowPrice;
		_barCount++;

		if (_barCount < 5 || atrVal <= 0)
			return;

		var close = candle.ClosePrice;

		// Detect fractal high (center bar _high3 is highest)
		var fractalUp = _high3 > _high1 && _high3 > _high2 && _high3 > _high4 && _high3 > _high5;
		// Detect fractal low (center bar _low3 is lowest)
		var fractalDown = _low3 < _low1 && _low3 < _low2 && _low3 < _low4 && _low3 < _low5;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || fractalDown)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || fractalUp)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fractalDown && rsiVal < 45)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fractalUp && rsiVal > 55)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevHigh5 = _high5;
		_prevLow5 = _low5;
	}
}