Ver no GitHub

Estratégia CashMachine de 5 Minutos

Visão geral

Esta estratégia é uma conversão direta do Expert Advisor CashMachine 5min de MQL para a API de alto nível do StockSharp. É projetada para velas de cinco minutos e combina o indicador DeMarker com um filtro de cruzamento do oscilador Estocástico. O gerenciamento de operações usa níveis ocultos de stop-loss / take-profit junto com regras de trailing por estágios que tentam travar ganhos assim que o momentum do preço aparece.

Lógica de negociação

Condições de entrada

  • Comprado: Valor anterior do DeMarker abaixo de 0.30 e valor atual em ou acima de 0.30 e o Estocástico %K cruza acima de 20 na mesma vela. Nenhum posição deve estar aberta.
  • Vendido: Valor anterior do DeMarker acima de 0.70 e valor atual em ou abaixo de 0.70 e o Estocástico %K cruza abaixo de 80. Nenhuma posição deve estar aberta.

Gerenciamento de posição

  • Apenas uma posição é mantida por vez; sinais opostos são ignorados até que a operação atual seja fechada.
  • Saídas ocultas fecham a posição quando o preço toca Entry ± HiddenStopLoss ou Entry ± HiddenTakeProfit (valores interpretados em pips).
  • Três alvos de lucro intermediários (TargetTp1/2/3) movem um trailing stop oculto para preço atual - (alvo - 13) pips para comprados e preço atual + (alvo + 13) pips para vendidos. Os 13 pips extras imitam o comportamento do EA original, travando lucros após cada marco sem sair imediatamente.
  • Se o trailing stop for tocado após a ativação, a posição é fechada a mercado.

Indicadores

  • DeMarker – Detecta reversões de momentum; o parâmetro de comprimento corresponde ao período de média original.
  • Oscilador Estocástico – Usa o período original de %K (StochasticLength), suavização de %K (StochasticK) e suavização de %D (StochasticD).

Parâmetros

Nome Descrição Padrão
HiddenTakeProfit Distância oculta do take-profit em pips. 60
HiddenStopLoss Distância oculta do stop-loss em pips. 30
TargetTp1 Primeiro nível de ativação do trailing (pips). 20
TargetTp2 Segundo nível de ativação do trailing (pips). 35
TargetTp3 Terceiro nível de ativação do trailing (pips). 50
DeMarkerLength Período de média do DeMarker. 14
StochasticLength Período de lookback do %K do Estocástico. 5
StochasticK Comprimento de suavização do %K. 3
StochasticD Comprimento de suavização do %D. 3
CandleType Série de velas usada para cálculos (padrão 5 minutos). Período de 5 minutos

Notas

  • O tamanho do pip é derivado de Security.PriceStep. Quando o passo é desconhecido, é usado um valor de fallback de 0.0001, reproduzindo a lógica do EA que ajusta para cotações de 3 e 5 dígitos.
  • Todas as decisões de negociação são baseadas em velas terminadas; o comportamento intra-barra do EA original pode diferir ligeiramente porque a versão MQL rodava em cada tick.
  • A estratégia depende do tratamento padrão de volume de ordens do StockSharp — definir Strategy.Volume para controlar o tamanho da operação.
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;
	}
}