GitHub で見る

リリスのハリウッド戦略

概要

この戦略は、StockSharp のハイレベル API 内での MetaTrader エキスパート「リリスがハリウッドへ行く」の動作を再現します。これは、2 つのまったく異なるモードで動作できるヘッジ グリッドを実装します。

  • 自動モード – Parabolic SAR は、価格がストップアンドリバース値を超えるたびに即時市場エントリーをトリガーします。
  • 手動モード – 未決のストップ/指値注文はユーザー定義の参照価格付近で保留され、約定するまで放置されます。

どちらの場合も、戦略は長期エクスポージャーと短期エクスポージャーを個別に追跡し、オープングリッドの変動損益を計算し、その情報を使用して追加の回収注文をいつ展開するかを決定します。

動作モード

  • 自動 – オープンなポジションがない場合、戦略は Parabolic SAR インジケーター (0.02/0.2 ファクター) をサブスクライブします。ローソク足の終値が市場での指標を上回っている場合は買い、下回っている場合は売ります。約定価格が新しいフォーカスとなり、その周囲の設定可能なアンカー距離でリカバリストップが準備されます。
  • マニュアル – オープンなポジションがない場合、ストラテジーはサイドごとに 1 つの指値注文を送信します。市場が買いレベルを下回って取引される場合は買いストップが作成され、それ以外の場合は買い指値が発行されます。売り側は、PriceDown レベルに関して同じロジックを反映します。一方の注文が約定すると、もう一方の注文は手動または戦略によってキャンセルされるまでアクティブのままになります。

注文管理ロジック

  • グリッドは、約定したロング/ショートのボリュームと保留中の買い/売り注文の合計を実行し続けます。これにより、戦略は本の両側の不均衡を測定できるようになります。
  • 変動利益が動的ターゲット (account value / 1000) に達すると、戦略はすべてのポジションを閉じ、すべての未決注文をキャンセルします。
  • 変動損益が -AccountValue * RiskPercent / 100 を下回った場合、ネットショートまたはロング超過をカバーする成行注文をオープンすることで緊急ヘッジが展開されます。
  • リカバリ注文は、フォーカス価格 (自動モード) の周囲、または設定された手動価格の周囲で発注されるストップ注文として表されます。それらのサイズは (opposite exposure * XFactor) - current exposure として計算され、グリッドのバランスを再調整するために次の注文をオーバーサイズする MT4 ロジックを模倣します。

パラメーター

名前 説明
Automated Parabolic SAR 主導の市場参入を可能にします。手動保留注文モードでの作業を無効にします。
PriceUp 手動モードで買いストップ/指値注文を作成するために使用される参照価格。
PriceDown 手動モードで売りストップ/指値注文を作成するために使用される参照価格。
AnchorSteps 価格ステップで表される距離。フォーカス価格からリカバリ注文を相殺するために使用されます。
ManualVolume 手動で操作する場合、または動的ポジションサイジングでゼロが生成される場合の基本ロットサイズ。
XFactor リカバリ注文のサイジング時に反対のエクスポージャーに適用される乗数。
RiskPercent 戦略が緊急ヘッジを展開する前に許容される最大変動損失 (口座価値の割合)。
CandleType Parabolic SAR および一般的な管理ロジックを駆動するために使用される時間枠。

リスク管理

  • 利益確定は動的で口座価値に応じて拡大するため、口座の成長に応じて目標を自動的に引き上げることができます。
  • 緊急ヘッジは、浮動損失が RiskPercent のしきい値を超えた場合に、グリッドの最も露出されている側を平らにすることで、極度のドローダウンを無効化できます。
  • すべての未決注文は商品ティック サイズに四捨五入され、元の MetaTrader エキスパートの一般的な保護と一致するように、取引限度を尊重するように数量が調整されます。

変換メモ

  • MetaTrader ティックは完成したキャンドルに置き換えられます。デフォルトの 1 分の時間枠では戦略が反応的に保たれますが、CandleType パラメーターを介して調整できます。
  • MQL ソースの Anchor 設定は、距離をポイント単位で表しました。ここでは、金融商品のティックサイズに自動的に適応するように、いくつかの価格ステップとして設定されています。
  • 元の「コメント」出力は戦略ログ メッセージ (LogInfo) に変換されているため、チャートの注釈に依存せずにプラットフォーム ジャーナルに同じフィードバックが含まれます。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Lilith Goes To Hollywood: SMA crossover with RSI filter and ATR stops.
/// </summary>
public class LilithGoesToHollywoodStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastSmaLength;
	private readonly StrategyParam<int> _slowSmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

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

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

		_fastSmaLength = Param(nameof(FastSmaLength), 10)
			.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators");

		_slowSmaLength = Param(nameof(SlowSmaLength), 25)
			.SetDisplay("Slow SMA", "Slow SMA period.", "Indicators");

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

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

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

	public int FastSmaLength
	{
		get => _fastSmaLength.Value;
		set => _fastSmaLength.Value = value;
	}

	public int SlowSmaLength
	{
		get => _slowSmaLength.Value;
		set => _slowSmaLength.Value = value;
	}

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

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

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

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

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

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

		var fastEma = new ExponentialMovingAverage { Length = FastSmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowSmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastEma);
			DrawIndicator(area, slowEma);
			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;

		if (Position > 0)
		{
			if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (close >= _entryPrice + atrVal * 2m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 45)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 55)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}