GitHub で見る

AIS2 トレーディング ロボット 20005 (StockSharp ポート)

概要

AIS2 Trading Robot 20005 は、もともと MetaTrader 4 用に書かれた日中ブレイクアウト エキスパート アドバイザーです。ポートは、StockSharp の高レベル戦略 API に基づいてマルチタイムフレーム ロジックを再作成します。この戦略は、以前のより高いタイムフレームのローソク足の中間点を上回る/下で勢いがブレイクアウトするのを待ち、そのローソク足の範囲から導出される動的なテイクプロフィットとストップロスの距離を適用し、トレーリングストップを駆動する2番目のより速いタイムフレームでポジションを管理します。

この変換は透明性と手動制御に重点を置いています。ポジションは成行注文でオープンされ、戦略自体の内部で保護レベルが強制され、設定可能な取引一時停止により急速な再エントリーが防止されます。株式ベースのポジションサイジングは元の「リザーブ」ロジックを反映しており、ユーザーは資本バッファーに手を加えずにポートフォリオ価値の一部を各取引に割り当てることができます。

中核ロジック

  1. プライマリ タイムフレーム分析 – メイン タイムフレーム (デフォルトは 15 分) の終了したローソク足ごとに、戦略は以下を計算します。
    • ローソクの中点 (High + Low) / 2
    • レンジベースのテイクプロフィットとストップロスの距離 (range * TakeFactorrange * StopFactor)。
    • 現在の広がり近似、バッファーの停止/フリーズ、および最小のトレーリング ステップ。
  2. ブレイクアウト条件 – ロングエントリーには、中間点を上回る終値と、現在のアスク価格が前の高値プラススプレッドを突破する両方が必要です。ショートパンツは低音の状態を反映しています。計算されたストップ/ターゲット距離がブローカーレベルの制約を満たさない場合、注文はブロックされます。
  3. リスク管理 – ポジション サイズはポートフォリオの資本から導出されます。OrderReserve は取引可能な部分を定義し、AccountReserve は一部をそのまま維持します。利用可能な資本またはブローカーの制限により取引が許可されない場合、セットアップはスキップされます。
  4. 取引管理 – より速い時間枠 (デフォルトは 1 分) により、後続距離が継続的に更新されます。価格が上昇するにつれて、セカンダリレンジが正当化されると、ストップは取引に有利に移動します。目標またはストップのいずれかに到達すると、即時に市場から退場します。
  5. 運用上のガードレール – クールダウン タイマー (TradingPauseSeconds) は、元の MQL 取引一時停止を再現します。この戦略は、ライブの買値/売値を取得するためにオーダーブックにもサブスクライブします。利用できない場合は、ローソク足の終値に戻ります。

パラメーター

パラメータ 説明 デフォルト
PrimaryCandleType エントリーシグナルの生成に使用されるより高いタイムフレーム。 15分キャンドル
SecondaryCandleType トレーリングストップ計算のより短い時間枠。 1分キャンドル
TakeFactor テイクプロフィットディスタンスを構築するためにプライマリローソク足レンジに適用される乗数。 1.7
StopFactor ストップロス距離を構築するためにプライマリローソク足の範囲に適用される乗数。 1.7
TrailFactor トレーリング更新のセカンダリローソク足範囲に適用される乗数。 0.5
AccountReserve 準備金として保有される株式の一部(取引には使用されません)。 0.20
OrderReserve バッファ前の取引ごとに割り当てられた総資本の割合。 0.04
BaseVolume リスクサイジングを計算できない場合のフォールバック取引量。 1ロット
StopBufferTicks ブローカーのストップレベルのコンプライアンスチェックに追加のティックが追加されました。 0
FreezeBufferTicks 追加のティックにより、フリーズ レベル付近での頻繁な停止更新が防止されます。 0
TrailStepMultiplier 後続のステップを検証するときにスプレッドに適用される乗数。 1
TradingPauseSeconds 連続した取引間のクールダウン。 5秒

すべての数値パラメーターは SetCanOptimize() (意味のある場合) を公開するため、StockSharp の最適化シナリオに参加できます。

使用上の注意

  • ストラテジーを証券に添付し、レベル 1/オーダーブック データが正確なスプレッド検出に利用できるようにします。ライブクオートがない場合でも、ロジックはローソク足の終値を使用して実行されますが、ストップ検証は保守的になります。
  • PrimaryCandleType/SecondaryCandleType をデータ フィードに存在するタイムフレームに設定します。ポートは SubscribeCandles を使用し、StockSharp の高レベル API を通じてハンドラーをバインドします。
  • トレーリングストップは仮想です (内部で管理されます)。ストップ注文はブローカーに送信されません。サーバー側での停止が必要な場合は、エントリ後に保護命令を登録するようにコードを拡張します。
  • StartProtection() は開始時に呼び出され、必要に応じてエンジンが予期しないポジションを清算します。

オリジナルの EA との違い

  • MetaTrader バージョンは端末全体のグローバル変数を操作しました。このポートはパラメータをストラテジー内に保持し、StrategyParam ラッパー経由で公開します。
  • StockSharp はアルゴリズム自体内でストップ/ターゲット ロジックを処理するため、注文の変更は直接市場からの決済に置き換えられました。
  • リスク計算は、MT4 からの口座残高クエリではなく、StockSharp によって提供されるポートフォリオの資本に基づいて行われます。

ファイル

  • CS/Ais2TradingRobot20005Strategy.cs – StockSharp の高レベル API を使用した戦略の実装。
  • README.md – 英語の説明 (このファイル)。
  • README_zh.md – 中国語翻訳。
  • README_ru.md – ロシア語の翻訳。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AIS2 Trading Robot: range breakout strategy.
/// Enters on close above/below previous candle range midpoint,
/// uses ATR for trailing stop management.
/// </summary>
public class Ais2TradingRobot20005Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _takeFactor;
	private readonly StrategyParam<decimal> _stopFactor;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _prevMid;
	private decimal _entryPrice;
	private decimal _stopPrice;

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

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");

		_takeFactor = Param(nameof(TakeFactor), 1.7m)
			.SetDisplay("Take Factor", "ATR multiplier for take profit.", "Risk");

		_stopFactor = Param(nameof(StopFactor), 1.0m)
			.SetDisplay("Stop Factor", "ATR multiplier for stop loss.", "Risk");
	}

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

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal TakeFactor
	{
		get => _takeFactor.Value;
		set => _takeFactor.Value = value;
	}

	public decimal StopFactor
	{
		get => _stopFactor.Value;
		set => _stopFactor.Value = value;
	}

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

		_prevHigh = 0;
		_prevLow = 0;
		_prevMid = 0;
		_entryPrice = 0;
		_stopPrice = 0;
	}

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

		_prevHigh = 0;
		_prevLow = 0;
		_prevMid = 0;
		_entryPrice = 0;
		_stopPrice = 0;

		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (_prevHigh == 0 || atrVal <= 0)
		{
			_prevHigh = candle.HighPrice;
			_prevLow = candle.LowPrice;
			_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
			return;
		}

		var close = candle.ClosePrice;
		var takeDistance = atrVal * TakeFactor;
		var stopDistance = atrVal * StopFactor;

		// Manage open position
		if (Position > 0)
		{
			// Take profit
			if (close - _entryPrice >= takeDistance)
			{
				SellMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			// Stop loss
			else if (_stopPrice > 0 && close <= _stopPrice)
			{
				SellMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			// Trail stop
			else
			{
				var newStop = close - stopDistance;
				if (newStop > _stopPrice)
					_stopPrice = newStop;
			}
		}
		else if (Position < 0)
		{
			if (_entryPrice - close >= takeDistance)
			{
				BuyMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			else if (_stopPrice > 0 && close >= _stopPrice)
			{
				BuyMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			else
			{
				var newStop = close + stopDistance;
				if (newStop < _stopPrice || _stopPrice == 0)
					_stopPrice = newStop;
			}
		}

		// New entry: breakout above previous high with close above midpoint
		if (Position == 0)
		{
			if (close > _prevHigh && close > _prevMid)
			{
				_entryPrice = close;
				_stopPrice = close - stopDistance;
				BuyMarket();
			}
			else if (close < _prevLow && close < _prevMid)
			{
				_entryPrice = close;
				_stopPrice = close + stopDistance;
				SellMarket();
			}
		}

		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
	}
}