GitHub で見る

マーチンゲイルのエキスパート戦略

概要

マーチンゲイル エキスパートは、Stochastic オシレーターに依存して新しい取引シーケンスのタイミングを計る、トレンド追跡マーチンゲール戦略です。インジケーターが方向を生成すると、戦略は成行注文のはしごを開始し、動的な利益目標と幾何学的なポジションサイジングスキームを使用してエクスポージャーを管理します。

取引ロジック

  • 構成されたローソク足シリーズで Stochastic オシレーターを計算します。 %K と %D の最新の最終値は、意思決定のためにキャッシュされます。
  • %K (previous) > %D (previous)%D (previous)BuyLevel のしきい値を超えたときに、新しい長いシーケンスを開始します。
  • %K (previous) < %D (previous)%D (previous)SellLevel のしきい値を下回ったときに、新しい短いシーケンスを開始します。
  • シーケンスに入った後、ProfitFactor × openOrders ピップに等しい有利な価格変動が発生するたびに、基本ボリュームで新しいポジションが追加されます。
  • StepPoints ピップのすべての逆方向の動きは、最後に約定したボリュームに Multiplier を乗算し、同じ方向に平均化注文を送信します。

終了ルール

  • 最後の約定価格が有利な方向の ProfitFactor × openOrders ピップスの動的な利益目標に達したらすぐにポジション全体をクローズします。
  • 集約されたポジションサイズがゼロに戻るたびに、マーチンゲール状態をリセットします。

リスク管理

マーチンゲール数列では、価格がポジションに反して動くとエクスポージャが急速に増加します。アカウントのサイズと商品のボラティリティに合わせて、MultiplierStepPoints、および ProfitFactor を慎重に調整してください。

パラメーター

名前 説明
Volume 最初の取引とすべての有利なアドオンに使用される基本成行注文量。
Multiplier 逆方向の移動中に平均化するときに、最後に実行されたボリュームに適用される係数。
StepPoints マーチンゲール平均順序をトリガーするポイント単位の距離。
ProfitFactor オープン注文ごとの利益目標をポイントで表します。実際の距離は ProfitFactor × number_of_orders です。
KPeriod %K ラインのルックバック長。
DPeriod %D ラインのスムージング長。
Slowing %D と比較する前に、%K に追加の平滑化が適用されました。
BuyLevel 新しい長いシーケンスを許可するために必要な最小 %D 値。
SellLevel 新しい短いシーケンスを許可するために必要な最大 %D 値。
CandleType 計算に使用されるローソク足シリーズ (デフォルト: 5 分の時間枠)。

注意事項

  • ピップサイズとボリュームステップにより細かいスケーリングが可能なリキッドFXペアで最も効果的です。
  • 複数のマーチンゲール ステップに耐えられる十分なマージンが必要です。
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>
/// Martingale strategy driven by stochastic oscillator crossovers.
/// Buy when K crosses above D, sell when K crosses below D.
/// Doubles down on adverse moves with martingale averaging.
/// </summary>
public class MartingailExpertSequenceStrategy : Strategy
{
	private readonly StrategyParam<int> _kPeriod;
	private readonly StrategyParam<int> _dPeriod;
	private readonly StrategyParam<decimal> _stepPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevK;
	private decimal? _prevD;
	private decimal _entryPrice;

	public int KPeriod { get => _kPeriod.Value; set => _kPeriod.Value = value; }
	public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
	public decimal StepPoints { get => _stepPoints.Value; set => _stepPoints.Value = value; }
	public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MartingailExpertSequenceStrategy()
	{
		_kPeriod = Param(nameof(KPeriod), 14)
			.SetDisplay("K Period", "Stochastic K period", "Indicators");

		_dPeriod = Param(nameof(DPeriod), 3)
			.SetDisplay("D Period", "Stochastic D period", "Indicators");

		_stepPoints = Param(nameof(StepPoints), 500m)
			.SetDisplay("Step Points", "Distance for martingale averaging", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
			.SetDisplay("Take Profit", "Take profit in price steps", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

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

		_prevK = null;
		_prevD = null;
		_entryPrice = 0m;
	}

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

		_prevK = null;
		_prevD = null;

		var stoch = new StochasticOscillator
		{
			K = { Length = KPeriod },
			D = { Length = DPeriod }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(stoch, ProcessCandle)
			.Start();
	}

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

		if (!stochValue.IsFinal)
			return;

		if (stochValue is not StochasticOscillatorValue stoch)
			return;

		if (stoch.K is not decimal k || stoch.D is not decimal d)
			return;

		var step = Security?.PriceStep ?? 1m;
		if (step <= 0) step = 1m;

		// Check take profit
		if (Position > 0 && candle.ClosePrice >= _entryPrice + TakeProfitPoints * step)
		{
			SellMarket();
			_prevK = k;
			_prevD = d;
			return;
		}
		else if (Position < 0 && candle.ClosePrice <= _entryPrice - TakeProfitPoints * step)
		{
			BuyMarket();
			_prevK = k;
			_prevD = d;
			return;
		}

		if (_prevK is not decimal prevK || _prevD is not decimal prevD)
		{
			_prevK = k;
			_prevD = d;
			return;
		}

		// Stochastic crossover signals
		var bullCross = prevK <= prevD && k > d;
		var bearCross = prevK >= prevD && k < d;

		if (Position == 0)
		{
			if (bullCross)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
			}
			else if (bearCross)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
			}
		}
		else if (Position > 0 && bearCross)
		{
			SellMarket(); // Close long
			SellMarket(); // Open short
			_entryPrice = candle.ClosePrice;
		}
		else if (Position < 0 && bullCross)
		{
			BuyMarket(); // Close short
			BuyMarket(); // Open long
			_entryPrice = candle.ClosePrice;
		}

		_prevK = k;
		_prevD = d;
	}
}