GitHub で見る

クローズエージェント戦略

概要

Close Agent Strategy は、MQL CloseAgent エキスパート アドバイザーを反映したリスク管理アシスタントです。この戦略は新しい取引を開始するものではありません。代わりに、既存のポジションを監視し、相対強度指数 (RSI) が極端なゾーンに達している間に価格が Bollinger バンドを超えたときにポジションを閉じます。このツールは、手動または他の自動戦略によって作成されたポジションを監視し、グローバルな利益目標が達成されたら、オプションですべてを清算することができます。

指標とデータ

  • ローソク足: インジケーターの計算に使用される構成可能な時間枠 (デフォルト: 5 分)。
  • Bollinger バンド (長さ 21、幅 2): 上部バンドを上回る、または下部バンドを下回る価格変動を検出します。
  • 相対力指数 (長さ 13): 市場が買われすぎ (>70) か売られすぎ (<30) かを確認します。
  • レベル 1 データ: は、可能な限り正確にエグジット条件を評価するために、最新の買値と売値を取得します。

パラメーター

  • 決済モード (CloseMode): 決済の対象となるポジションを選択します。
    • Manual – この戦略識別子のないポジションのみ (手動取引または他のボット)。
    • Auto – この戦略インスタンスによってオープンされたポジションのみ。
    • Both – 戦略シンボルのすべてのポジションを監視します。
  • ローソク足タイプ (CandleType): Bollinger バンドと RSI の計算に使用されるタイムフレーム。
  • 動作モード (OperationMode):
    • LiveBar – 最新の成形キャンドルを使用します。反応は速くなりますが、未完成のデータが使用される可能性があります。
    • NewBar – 信号を生成する前にローソク足が閉じるのを待ちます (安全ですが時間がかかります)。
  • すべてのターゲットをクローズ (CloseAllTarget): 変動利益 (PnL) がこの絶対値に達すると、監視されているすべてのポジションが直ちにクローズされます。
  • アラートを有効にする (EnableAlerts): true の場合、Exit がトリガーされるたびに、実現利益の見積もりを含むメッセージをログに記録します。

取引ロジック

  1. レベル 1 の相場と設定されたローソク足シリーズを購読します。 Bollinger バンドと RSI は、入ってくるローソクごとに更新されます。
  2. OperationModeNewBar に設定されている場合に、ストラテジーが最新のクローズされたローソク足を参照できるように、コンパクトな履歴バッファーを維持します。
  3. グローバルな利益目標が達成されているかどうかを確認します。 CloseAllTarget > 0 かつ PnL がしきい値を超えると、適格なポジションはすべて市場価格でフラット化されます。
  4. 戦略シンボル上の監視されている各ポジションについて:
    • ロングポジション: 最良入札が Bollinger バンドの上部を上回り、RSI が 70 を上回り、価格がエントリー平均価格を上回っている場合、クローズされます。
    • ショートポジション: ベストアスクが下側の Bollinger バンドを下回り、RSI が 30 を下回り、価格がエントリー平均価格を下回ったままの場合、クローズされます。
  5. 利用可能な場合は買値/売値を使用します。それ以外の場合は、終了の見逃しを避けるために、最後に処理されたローソク足に近い値に戻ります。

使用上の注意

  • この戦略は保護層として設計されており、ポジションが外部にオープンされる可能性があることを想定しています。
  • このロジックは市場の退場に対してのみ機能するため、戦略はリスクエクスポージャを管理するために他の取引システムと並行して実行する必要があります。
  • EnableAlerts がアクティブな場合、元の MQL アラートと一致するアラートが Designer ログに表示されます。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Closes open positions when price stretches beyond Bollinger Bands and RSI reaches extreme values.
/// Adds SMA crossover entries for backtesting.
/// </summary>
public class CloseAgentStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _bollingerLength;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

	/// <summary>
	/// Candle type used to calculate indicators.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Length of the RSI indicator.
	/// </summary>
	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	/// <summary>
	/// Length of the Bollinger Bands indicator.
	/// </summary>
	public int BollingerLength
	{
		get => _bollingerLength.Value;
		set => _bollingerLength.Value = value;
	}

	/// <summary>
	/// RSI threshold for overbought.
	/// </summary>
	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	/// <summary>
	/// RSI threshold for oversold.
	/// </summary>
	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public CloseAgentStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for indicators", "General");

		_rsiLength = Param(nameof(RsiLength), 13)
			.SetGreaterThanZero()
			.SetDisplay("RSI Length", "RSI period", "Indicators");

		_bollingerLength = Param(nameof(BollingerLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Length", "Bollinger Bands period", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetRange(0m, 100m)
			.SetDisplay("RSI Overbought", "Overbought threshold", "Signals");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetRange(0m, 100m)
			.SetDisplay("RSI Oversold", "Oversold threshold", "Signals");
	}

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

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var smaFast = new SimpleMovingAverage { Length = 10 };
		var smaSlow = new SimpleMovingAverage { Length = 30 };

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

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

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

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		var crossUp = _prevFast <= _prevSlow && fast > slow;
		var crossDown = _prevFast >= _prevSlow && fast < slow;

		if (crossUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}