GitHub で見る

AIS4 トレードマシン戦略

概要

AIS4 Trade Machine Strategy は、オリジナルの MetaTrader の「AIS4 Trade Machine」エキスパート アドバイザーを StockSharp に移植した手動取引アシスタントです。スクリプトからの 1 ポジションのワークフローが維持されます。オペレーターは絶対的なストップロスとテイクプロフィットのレベルを指定し、コマンドを発行します。そして、ストラテジーは、当座預金の資本と商品の仕様に基づいて取引サイズを計算します。成行注文が約定された後、ストラテジーはすぐにペアの保護注文 (ストップ + リミット) を送信し、要求されたリスクと報酬レベルが取引所側で強制されます。

この戦略は自動シグナルを生成しません。これは、ユーザーがいつ、どこでポジションを入力または変更するかを決定する裁量執行用に設計されています。

手動ワークフロー

  1. 接続された機器が PriceStepStepPriceVolumeStepMinVolume、および MaxVolume を公開していることを確認してください。価格リスクを契約サイズに変換し、注文量を為替制限に合わせる必要があります。
  2. コマンドを送信する前に、StopPriceTakePrice を使用する絶対価格レベルに設定します。
  3. CommandBuy または Sell に変更します。戦略:
    • 他にオープンしているポジションがないことを確認します。
    • 要求されたストップロスとテイクプロフィットが最小ティック距離を遵守していることを検証します。
    • OrderReserve × 現在のポートフォリオの資本からリスク バジェットを計算し、資本準備金 (AccountReserve) が尊重されるようにします。
    • ストップ距離と商品ティック値から注文量を推定します。
    • 成行注文を送信し、ペアの保護注文(ロングの場合は SellStop+SellLimit、ショートの場合は BuyStop+BuyLimit)を送信します。
  4. アクションが処理された後、Command は自動的に Wait にリセットされるため、誤って重複して実行されることが回避されます。

既存のポジションの管理

  • 新しい価格レベルを設定し(現在の値を維持するには 0 を使用します)、CommandModify に切り替えます。この戦略は、以前の保護命令をキャンセルし、更新された価格に一致する新しい保護命令に置き換えます。
  • CommandClose に切り替えて、市場でのアクティブなポジションを清算し、保護注文をキャンセルします。

リスク管理ロジック

  • AccountReserve – ピーク時の資本の一部をそのまま維持します。利用可能な株式 (equity - peak_equity × (1 - AccountReserve)) が要求されたリスク バジェットよりも小さい間、取引はブロックされます。
  • OrderReserve – 次の取引に割り当てられる現在の株式の一部。バジェットは、ストップ距離と商品ティック値 (PriceStep × StepPrice) を使用して契約サイズに変換されます。
  • 計算されたボリュームが MinVolume を下回るか、VolumeStep に違反する場合、コマンドは拒否され、警告がログに書き込まれます。

パラメーター

パラメータ デフォルト 説明
Command Wait 実行する手動コマンド (BuySellModifyClose)。処理後は自動的に Wait に戻ります。
StopPrice 0 絶対的なストップロスレベル。ロングの場合はエントリー価格より低く、ショートの場合はそれ以上である必要があります。
TakePrice 0 絶対的なテイクプロフィットレベル。ロングの場合はエントリー価格よりも高く、ショートの場合はエントリー価格よりも低くなければなりません。
AccountReserve 0.20 資本の一部は準備金として保持されます。値が高いほど、新しい取引が受け入れられる前に、より大きなクッションが必要になります。
OrderReserve 0.04 取引ごとにリスクが生じる株式の割合。停止距離から契約サイズを計算するために使用されます。
CandleType 1 minute 時間枠 Candle シリーズは、検証と記録のために最新の価格を観察するために使用されます。

注意事項と制限事項

  • 元の Expert Advisor の設計と一致する、一度に 1 つのポジションのみがサポートされます。
  • 最小価格距離、資本準備金、または数量の制約に違反するコマンドは無視され、警告が戦略ログに記録されます。
  • 保護注文は、ボリュームを実際のポジションサイズと同期させるために、修正または新規約定のたびに置き換えられます。
  • この戦略は、PriceStep/StepPrice の正確な市場データに依存しています。これらのフィールドを提供しない商品は、このポートで安全に取引できません。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AIS Trade Machine: EMA crossover strategy with ATR-based risk management.
/// Entry on EMA cross confirmed by RSI, exit on reversal or ATR stop.
/// </summary>
public class AisTradeMachineStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _stopMultiplier;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private decimal _stopPrice;

	public AisTradeMachineStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 30)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

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

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

		_stopMultiplier = Param(nameof(StopMultiplier), 2.0m)
			.SetDisplay("Stop Multiplier", "ATR multiplier for stop.", "Risk");
	}

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

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

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

	public decimal StopMultiplier
	{
		get => _stopMultiplier.Value;
		set => _stopMultiplier.Value = value;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_stopPrice = 0;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_stopPrice = 0;

		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;
		var stopDist = atrVal * StopMultiplier;

		var bullishCross = _prevFast <= _prevSlow && fastVal > slowVal;
		var bearishCross = _prevFast >= _prevSlow && fastVal < slowVal;

		// Stop management
		if (Position > 0 && _stopPrice > 0 && close <= _stopPrice)
		{
			SellMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}
		else if (Position < 0 && _stopPrice > 0 && close >= _stopPrice)
		{
			BuyMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}

		// Exit on opposite cross
		if (Position > 0 && bearishCross)
		{
			SellMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}
		else if (Position < 0 && bullishCross)
		{
			BuyMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}

		// Trail stop
		if (Position > 0)
		{
			var trail = close - stopDist;
			if (trail > _stopPrice) _stopPrice = trail;
		}
		else if (Position < 0 && _stopPrice > 0)
		{
			var trail = close + stopDist;
			if (trail < _stopPrice) _stopPrice = trail;
		}

		// Entry on cross + RSI confirmation
		if (Position == 0)
		{
			if (bullishCross && rsiVal > 50)
			{
				_entryPrice = close;
				_stopPrice = close - stopDist;
				BuyMarket();
			}
			else if (bearishCross && rsiVal < 50)
			{
				_entryPrice = close;
				_stopPrice = close + stopDist;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}