GitHub で見る

Exp i-KlPrice Vol 戦略

概要

この戦略は、MetaTraderエキスパートExp_i-KlPrice_Vol.mq5のC#変換です。価格とボラティリティバンドの間の距離を測定する KlPriceオシレーターを再構築し、オシレーターを足のボリュームで乗算し、適応閾値によって生成されるカラー遷移を追跡します。 元のエキスパートアドバイザーのデュアルマジック動作を反映して、各方向に2つの独立したポジションスロットがエミュレートされます。

インジケーターロジック

  • 価格は選択されたAppliedPriceモード(close、open、median、Demakなど)を使用して変換されます。
  • 変換された価格はPriceMaMethodPriceMaLengthで定義された移動平均法で平滑化されます。
  • 足の値幅(High - Low)はRangeMaMethod/RangeMaLengthで平滑化されます。値幅は動的なバンド幅として機能します。
  • ベースKlPriceオシレーターは100 * (Price - (MA - RangeMA)) / (2 * RangeMA) - 50として計算されます。
  • オシレーターは選択されたボリュームソース(AppliedVolume.TickまたはAppliedVolume.Real)で乗算されます。
  • 長さSmoothingLengthのJurikスムーザーがオシレーターと生ボリュームの両方に適用され、2つの適応シリーズを作成します。
  • 適応閾値は平滑化されたボリュームにHighLevel2HighLevel1LowLevel1LowLevel2を掛けることで得られます。
  • 現在のオシレーターカラーは、平滑化されたオシレーター値を適応閾値と比較して決定されます:
    • 4HighLevel2 * volumeより上(極端な強気圧力)。
    • 3HighLevel1 * volumeと極端なレベルの間。
    • 2 – 強気と弱気の閾値の間。
    • 1 – 下閾値と中立線の間。
    • 0LowLevel2 * volumeより下(極端な弱気圧力)。

取引ルール

  1. SignalBar(通常は前の完成した足)のカラーとその前のカラーを評価する。
  2. ロングエントリー:
    • AllowLongEntrytrueのとき、カラーが4から4未満の任意の値に変わるとスロット1が開く。
    • カラーが3から3未満に変わるとスロット2が開く。
  3. ショートエントリー:
    • AllowShortEntrytrueのとき、カラーが0から0超に上昇するとスロット1が開く。
    • カラーが1から1超に上昇するとスロット2が開く。
  4. 以前のカラーが0または1AllowLongExitが有効な場合、ロング決済が発生する。
  5. 以前のカラーが4または3AllowShortExitが有効な場合、ショート決済が発生する。
  6. 各スロットは同じ足での重複注文を避けるために最後のシグナル時間を追跡する。保護ストップはオプションで、 StopLossPointsまたはTakeProfitPointsがゼロより大きい場合にStartProtectionを通じて処理される。

パラメーター

名前 デフォルト 説明
PrimaryVolume decimal 0.1 最初のロング/ショートスロットで使用するボリューム。
SecondaryVolume decimal 0.2 2番目のスロットで使用するボリューム。
StopLossPoints int 1000 価格ステップ単位のオプション保護ストップ距離。
TakeProfitPoints int 2000 価格ステップ単位のオプションテイクプロフィット距離。
AllowLongEntry bool true ロングポジションの開設を有効化。
AllowShortEntry bool true ショートポジションの開設を有効化。
AllowLongExit bool true 弱気カラーが現れたときにロングポジションを閉じる。
AllowShortExit bool true 強気カラーが現れたときにショートポジションを閉じる。
CandleType DataType H8 計算用の足のタイムフレーム。
PriceMaMethod SmoothMethod Sma 適用価格に使用する移動平均のタイプ。
PriceMaLength int 100 価格スムーザーの長さ。
PriceMaPhase int 15 Jurikベースフィルターの位相パラメーター。
RangeMaMethod SmoothMethod Jjma 足値幅に使用する移動平均のタイプ。
RangeMaLength int 20 値幅スムーザーの長さ。
RangeMaPhase int 100 値幅スムーザーの位相パラメーター。
SmoothingLength int 20 オシレーターとボリュームに適用されるJurik平滑化長さ。
AppliedPrice AppliedPrice Close オシレーター計算に使用する価格ソース。
VolumeType AppliedVolume Tick オシレーターで乗算するボリュームソース。
HighLevel2 int 150 適応閾値の上部極端乗数。
HighLevel1 int 20 上部中程度乗数。
LowLevel1 int -20 下部中程度乗数。
LowLevel2 int -150 下部極端乗数。
SignalBar int 1 カラー遷移を読むために使用される履歴オフセット。

使用上の注意

  • 価格とボリュームの両方の情報を提供する銘柄に戦略を接続してください;ティックボリュームのみが利用可能な場合、ティック カウンターがプロキシとして使用されます。
  • 2つのスロットボリュームは、元のEAのデュアルマネー管理設定をエミュレートするために独立して調整できます。
  • 部分的に形成された足を扱う場合や、履歴データを再同期する場合はSignalBarを調整してください。
  • 平滑化メソッドはMQLSmoothAlgorithmsライブラリの動作を複製するためにリフレクションを通じてJurikフィルターをサポートします。
  • StartProtectionはストップまたはターゲット距離が正の場合にのみ呼び出されるため、保護注文を無効にするにはゼロのままにしてください。
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>
/// KlPrice-Vol strategy using EMA crossover with volume confirmation.
/// Buys on fast EMA crossing above slow EMA with rising volume.
/// Sells on reverse crossover.
/// </summary>
public class ExpIKlPriceVolStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

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

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public ExpIKlPriceVolStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

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

		if (!_fast.IsFormed || !_slow.IsFormed)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// EMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 60;
		}
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 60;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}