GitHub で見る

Risk Reward Ratio戦略

概要

Risk Reward Ratio戦略は、MetaTraderエキスパート「Risk Reward Ratio」のStockSharp高レベルポートです。この戦略は、複数のモメンタムおよびトレンド確認フィルターを規律あるリスク管理モジュールと組み合わせています。エントリーは、ストキャスティックオシレーターの合流、線形加重移動平均(LWMA)クロスオーバー、14期間RSIフィルター、およびMACDトレンド確認の組み合わせから生成されます。リスク制御は、pip基準のストップロス、自動リワードレシオのテイクプロフィット、オプションのトレーリングストップとブレイクイーブンロジック、および緊急フラットスイッチによって達成されます。

変換はMetaTraderエキスパートの本来の精神を保ちながら、StockSharpのロウソク足サブスクリプションとインジケーターバインディングAPIを使用します。すべてのインジケーター処理は確定したロウソク足で行われ、インジケーターバッファへの直接アクセスを避け、エンジンのストリーミングパラダイムを維持します。

トレードロジック

  1. ストキャスティック合流
    • 速いストキャスティック (5, 2, 2) は %K ラインを使用して主要なモメンタムシグナルを提供します。
    • 遅いストキャスティック (21, 10, 4) は平滑化された %D ラインを通じて方向バイアスを提供します。
    • ロングセットアップでは速い %K が遅い %D の上に、ショートセットアップでは逆が必要です。
  2. RSI確認
    • 14期間RSIはロング取引で50以上、ショート取引で50以下でなければならず、市場が提案された方向に整合していることを確認します。
  3. LWMAによるトレンドフィルター
    • 2つの線形加重移動平均(長さ6と85)が方向を確認する必要があります:ロングでは速いLWMAが遅いLWMAの上、ショートでは下。
  4. MACDトレンド修飾子
    • MACDヒストグラム (12, 26, 9) はシグナル方向と一致している必要があります。メイン線はシグナル線をリードしながらゼロの適切な側に留まらなければなりません。
  5. モメンタム偏差フィルター
    • 14期間のモメンタムインジケーターが100からの距離を測定します。直近3つのモメンタム読み取りのうち少なくとも1つが設定可能な閾値を超えて、価格が取引を正当化するほど加速していることを証明する必要があります。
  6. ポジション制限
    • ネットエクスポージャーは MaxPositions * TradeVolume によって上限が設定され、戦略が元のEAの制約を超えてピラミッドできないようにします。

注文は BuyMarketSellMarket を使用した成行執行として送信されます。戦略は未確定のロウソク足を無視し、StockSharpのイベント駆動アーキテクチャに従うためにすべての状態をクラスフィールド内に保持します。

リスク管理

  • pipでのストップロス – すべてのエントリーは、約定価格から StopLossPips * PriceStep の保護ストップを設置します。
  • リワードレシオのテイクプロフィット – テイクプロフィット距離はストップ距離に RewardRatio を掛けたもので、固定のリワードリスク比を維持します。
  • トレーリングストップ – 有効化されると、市場がエントリーから少なくともその距離だけ前進した後、TrailingStopPips だけ価格の後ろにストップが移動します。
  • ブレイクイーブン移動 – 有利な BreakEvenTriggerPips の移動後、ストップはエントリーに加えて追加の BreakEvenOffsetPips クッション(ショートでは引く)まで押し上げられ、利益を確保します。
  • 終了スイッチExitSwitchtrue に設定すると、次の確定バーで現在のポジションをフラットにし、フラグがオフになるまで以降の処理を無効にします。

パラメーター

名前 デフォルト値 説明
TradeVolume 0.1 各成行注文のボリューム。
CandleType 15m 時間軸 主要ロウソク足シリーズ。
FastMaPeriod 6 速いLWMAの期間。
SlowMaPeriod 85 遅いLWMAの期間。
MomentumThreshold 0.3 エントリーを許可するために必要なモメンタムインジケーターの100からの最小絶対距離。
RewardRatio 2 ストップロスに対するテイクプロフィットの倍率。
StopLossPips 20 pip単位のストップロス距離(PriceStepの倍数)。
MaxPositions 10 同時に許可されるボリュームユニット(TradeVolume)の最大数。
EnableTrailing true pip基準のトレーリングストップ更新を有効にします。
TrailingStopPips 40 pip単位のトレーリングストップ距離。
EnableBreakEven true ブレイクイーブンストップ管理を有効にします。
BreakEvenTriggerPips 30 ストップをブレイクイーブンに移動する前に必要な利益(pip単位)。
BreakEvenOffsetPips 30 ストップをブレイクイーブンに移動するときの追加pipオフセット。
ExitSwitch false 次の確定ロウソク足ですべてのエクスポージャーをフラットにするよう戦略に強制します。

ワークフロー

  1. 希望の銘柄とロウソク足シリーズを設定し、リスクパラメーターを設定します。
  2. 戦略を開始します。ロウソク足を購読し、インジケーターをバインドし、確定したバーの処理を開始します。
  3. エントリー条件が揃うと、エンジンは成行注文を送信し、ストップ/ターゲットレベルを保存します。
  4. 確定したロウソク足ごとに、リスクブロックがトレーリング、ブレイクイーブン、および緊急出口ルールを評価します。
  5. ストップ/テイクプロフィットレベルへの到達、トレーリング更新、ブレイクイーブン調整、または緊急スイッチによって出口がトリガーされます。

注意事項

  • 変換はマニュアルバッファアクセスの代わりにStockSharpのインジケーターバインディングを活用し、各インジケーターが同期されたデータを受け取ることを確実にします。
  • すべての計算は楽器の PriceStep に依存します。ステップがゼロまたは欠落している場合、無効な価格レベルを避けるためにリスク距離は無効のままです。
  • 戦略は保留中の注文を変更しません。単に成行注文を送信してポジションを開閉し、閾値に達したときに元の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;

public class RiskRewardRatioStrategy : 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;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public RiskRewardRatioStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).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");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	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;

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

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

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}