GitHub で見る

CashMachine 5分足戦略

概要

この戦略は MQL の CashMachine 5min Expert Advisor を StockSharp 高レベル API に直接変換したものです。5 分足ローソク足用に設計されており、DeMarker インジケーターとストキャスティクスのクロスオーバーフィルターを組み合わせています。トレード管理は隠れたストップロス / テイクプロフィットレベルと段階的なトレーリングルールを使用し、価格モメンタムが現れたら利益を確定しようとします。

トレードロジック

エントリー条件

  • ロング: 前の DeMarker 値が 0.30 未満で現在値が 0.30 以上かつ同じローソク足でストキャスティクス %K が 20 を上抜け。ポジションがオープンでないこと。
  • ショート: 前の DeMarker 値が 0.70 超で現在値が 0.70 以下かつストキャスティクス %K が 80 を下抜け。ポジションがオープンでないこと。

ポジション管理

  • 一度に 1 つのポジションのみ維持されます。現在のトレードが決済されるまで、反対のシグナルは無視されます。
  • 隠れた決済は価格が Entry ± HiddenStopLoss または Entry ± HiddenTakeProfit(pips で解釈された値)に触れたときにポジションを決済します。
  • 3 つの中間利益ターゲット(TargetTp1/2/3)は、ロングの場合 現在価格 - (ターゲット - 13) pips、ショートの場合 現在価格 + (ターゲット + 13) pips に隠れたトレーリングストップを移動します。追加の 13 pips は元の EA 動作を模倣し、即座に決済せずに各マイルストーン後に利益を確定します。
  • トレーリングストップが有効化後に触れられた場合、ポジションは成行で決済されます。

インジケーター

  • DeMarker – モメンタムのリバーサルを検出します。長さパラメーターは元の平均化期間と一致します。
  • ストキャスティクスオシレーター – 元の %K 期間(StochasticLength)、%K 平滑化(StochasticK)、%D 平滑化(StochasticD)を使用します。

パラメーター

名前 説明 デフォルト
HiddenTakeProfit 隠れたテイクプロフィット距離(pips)。 60
HiddenStopLoss 隠れたストップロス距離(pips)。 30
TargetTp1 最初のトレーリング有効化レベル(pips)。 20
TargetTp2 2 番目のトレーリング有効化レベル(pips)。 35
TargetTp3 3 番目のトレーリング有効化レベル(pips)。 50
DeMarkerLength DeMarker 平均化期間。 14
StochasticLength ストキャスティクス %K ルックバック期間。 5
StochasticK %K 平滑化の長さ。 3
StochasticD %D 平滑化の長さ。 3
CandleType 計算に使用するローソク足シリーズ(デフォルト 5 分)。 5 分足

注意事項

  • pip サイズは Security.PriceStep から算出されます。ステップが不明な場合は 0.0001 のフォールバック値が使用され、3 桁・5 桁の相場に対応する EA ロジックを再現します。
  • すべてのトレード決定は完了したローソク足に基づきます。MQL バージョンはすべてのティックで実行されていたため、元の EA のバー内動作は若干異なる場合があります。
  • 戦略は StockSharp の標準的な注文ボリューム処理に依存しています。トレードサイズを制御するには Strategy.Volume を設定してください。
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>
/// Cash Machine strategy: combines DeMarker and Stochastic oscillator crossovers
/// with staged profit protection using ATR-based distances.
/// </summary>
public class CashMachine5minStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tpAtrMult;
	private readonly StrategyParam<decimal> _slAtrMult;
	private readonly StrategyParam<decimal> _trailAtrMult;
	private readonly StrategyParam<int> _deMarkerLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevDeMarker;
	private decimal? _prevRsi;
	private decimal _entryPrice;
	private decimal? _stopPrice;

	public decimal TpAtrMult { get => _tpAtrMult.Value; set => _tpAtrMult.Value = value; }
	public decimal SlAtrMult { get => _slAtrMult.Value; set => _slAtrMult.Value = value; }
	public decimal TrailAtrMult { get => _trailAtrMult.Value; set => _trailAtrMult.Value = value; }
	public int DeMarkerLength { get => _deMarkerLength.Value; set => _deMarkerLength.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public CashMachine5minStrategy()
	{
		_tpAtrMult = Param(nameof(TpAtrMult), 2.5m)
			.SetGreaterThanZero()
			.SetDisplay("TP ATR Mult", "Take profit ATR multiplier", "Risk");

		_slAtrMult = Param(nameof(SlAtrMult), 1.5m)
			.SetGreaterThanZero()
			.SetDisplay("SL ATR Mult", "Stop loss ATR multiplier", "Risk");

		_trailAtrMult = Param(nameof(TrailAtrMult), 1.0m)
			.SetGreaterThanZero()
			.SetDisplay("Trail ATR Mult", "Trailing stop ATR multiplier", "Risk");

		_deMarkerLength = Param(nameof(DeMarkerLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("DeMarker Length", "DeMarker period", "Indicators");

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

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

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

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevDeMarker = null;
		_prevRsi = null;
		_entryPrice = 0;
		_stopPrice = null;
	}

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

		var deMarker = new DeMarker { Length = DeMarkerLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var close = candle.ClosePrice;

		// Manage position
		if (Position > 0)
		{
			// Trailing stop
			var trail = close - TrailAtrMult * atr;
			if (_stopPrice == null || trail > _stopPrice)
				_stopPrice = trail;

			// Take profit
			var tp = _entryPrice + TpAtrMult * atr;

			if (close <= _stopPrice || close >= tp)
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = null;
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			var trail = close + TrailAtrMult * atr;
			if (_stopPrice == null || trail < _stopPrice)
				_stopPrice = trail;

			var tp = _entryPrice - TpAtrMult * atr;

			if (close >= _stopPrice || close <= tp)
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = null;
				_entryPrice = 0;
			}
		}

		// Entry signals
		if (Position == 0 && _prevDeMarker is decimal prevDe && _prevRsi is decimal prevRsi)
		{
			var longSignal = (prevDe < 0.25m && deMarker >= 0.25m) || (prevRsi < 25m && rsi >= 25m);
			var shortSignal = (prevDe > 0.75m && deMarker <= 0.75m) || (prevRsi > 75m && rsi <= 75m);

			if (longSignal)
			{
				BuyMarket();
				_entryPrice = close;
				_stopPrice = close - SlAtrMult * atr;
			}
			else if (shortSignal)
			{
				SellMarket();
				_entryPrice = close;
				_stopPrice = close + SlAtrMult * atr;
			}
		}

		_prevDeMarker = deMarker;
		_prevRsi = rsi;
	}
}