GitHub で見る

リターン戦略

この戦略は、クラシックな「Return Strategy」エキスパートアドバイザーを再現します。設定されたトレードウィンドウの開始時に、ペアの買い指値注文と売り指値注文のグリッドを準備します。グリッドは市場価格を中心に対称で、固定のpipsスペースを使用し、固定ボリュームまたはパーセントリスクモデルのいずれかでサイズを調整できます。注文が約定されると、戦略は静的およびトレーリングストップロスロジックでポジションを監視し、累積オープン利益を監視し、日次カットオフ時刻または毎週金曜日に完全なフラット化を強制します。

元のシステムはネッティング口座向けに設計され、スケジュールされた時刻後の平均回帰ムーブを捉えることに焦点を当てていました。変換はその構造を維持しながら、注文管理、トレーリング、および資本コントロールをStockSharpの高レベルAPIに適応させます。

トレードルール

  • 日次準備StartHourに戦略はグリッド注文がアクティブでないことを確認し、現在の価格の下にPendingOrderCountの買い指値を、上に売り指値を配置します。最初のレベルはDistancePipsでオフセットされ、後続の各レベルはStepPipsのスペースを追加します。
  • リスク管理 – 各保留注文は固定のOrderVolumeまたはRiskPercentから派生したリスクベースのサイズを使用できます。リスクサイジングを使用する場合、利用可能な資本とストップロス距離が1注文あたりのボリュームを決定し、グリッドの総リスクが設定されたパーセンテージに等しくなるようにします。
  • ストップ管理 – 各約定ポジションはStopLossPipsに基づく初期ストップロスを受け取ります。TrailingStopPipsがゼロより大きい場合、価格がトレーリング閾値を超えて進んだ後、ストップはTrailingStepPipsのステップで調整されます。
  • 利益目標とセッション退出 – ネットオープン利益はpipsで追跡されます。TotalProfitPipsに達すると、戦略はすべてのポジションと注文をクローズのためにマークします。設定されたEndHourにも同じフラッシュを実行し、利益に関わらず毎週金曜日にも実行します。
  • 注文の有効期限 – 保留注文はExpirationHours後に自動的に期限切れになることができます。期限切れまたは手動でキャンセルされた注文は、翌日に新しいグリッドを配置できるように追跡リストから削除されます。

パラメーター

パラメーター 説明
StopLossPips 約定した任意のポジションの初期ストップ距離(調整済みpips)。
StartHour 保留注文グリッドが作成される時間(0–23)。
EndHour ポジションと注文の完全な退出をトリガーする時間(0–23)。
TotalProfitPips すべてのトレードを強制的にクローズするネットオープン利益目標(pips)。
TrailingStopPips アクティブ化後の価格からのトレーリングストップ距離。トレーリングを無効にするにはゼロに設定します。
TrailingStepPips トレーリングストップを移動する前に必要な追加の前進。トレーリングが有効な場合は正でなければなりません。
DistancePips 市場の各側の最初の保留注文の初期オフセット。
StepPips 連続する保留注文間の増分スペース。
PendingOrderCount StartHourに登録する買い指値と売り指値の数。
ExpirationHours 保留注文の有効期間(時間)。ゼロは有効期限を無効にします。
OrderVolume 保留注文ごとの固定ボリューム。リスクベースのサイジングを有効にするにはゼロのままにします。
RiskPercent グリッド全体に割り当てられたポートフォリオのパーセンテージ。OrderVolumeがゼロの場合、この값から1注文あたりのサイズが導出されます。
CandleType タイミングとストップ管理ロジックを駆動するために使用されるローソク足シリーズ。

追加メモ

  • pipの変換は、3桁および5桁の小数点を持つインストゥルメントのステップサイズを調整することで、元のMetaTraderロジックを反映します。
  • RiskPercentを使用する場合、パーセンテージは結合されたグリッドに適用され、すべての保留注文に均等に分割されます。
  • 戦略はソースEAと同一の検証ルールを適用します:時間は日次範囲内でなければならず、トレーリングには非ゼロのステップが必要で、OrderVolume/RiskPercentのいずれか一方のみが一度にアクティブになれます。
  • コード内のすべてのパブリックコメントは、リポジトリガイドラインとの一貫性のために英語で提供されています。
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>
/// Mean reversion strategy using Bollinger Bands.
/// Buys when price drops below lower band and sells when price rises above upper band.
/// </summary>
public class ReturnStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<decimal> _width;

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

	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

	public decimal Width
	{
		get => _width.Value;
		set => _width.Value = value;
	}

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

		_period = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Bollinger Bands period", "Indicators");

		_width = Param(nameof(Width), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Width", "Bollinger Bands width", "Indicators");
	}

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

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

		var ma = new SimpleMovingAverage { Length = Period };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var bandWidth = Width / 100m;
		var upper = middle * (1m + bandWidth);
		var lower = middle * (1m - bandWidth);
		var close = candle.ClosePrice;

		// Buy when price drops below lower band (mean reversion)
		if (close < lower && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Sell when price rises above upper band
		else if (close > upper && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
		// Exit long at middle band
		else if (Position > 0 && close >= middle)
		{
			SellMarket();
		}
		// Exit short at middle band
		else if (Position < 0 && close <= middle)
		{
			BuyMarket();
		}
	}
}