Ver no GitHub

Estratégia Flip SimpleTrade

Visão geral

  • StockSharp porta do consultor especialista MetaTrader 4 SimpleTrade.mq4 (também conhecido como "neroTrade").
  • Projetado para negociação de símbolo único no período configurado por meio do parâmetro CandleType.
  • Sempre mantém no máximo uma posição aberta e muda de direção na abertura de cada nova barra.

Lógica de negociação

  1. Cada vez que uma nova vela se torna ativa, a estratégia compara o preço de abertura da vela com o preço de abertura da vela LookbackBars períodos mais antiga.
  2. Se a nova abertura for estritamente superior à referência histórica, todas as posições existentes serão fechadas e uma nova ordem longa de mercado com TradeVolume lotes será enviada.
  3. Caso contrário (abertura é igual ou inferior), a estratégia fecha quaisquer posições existentes e abre uma posição curta de mercado do mesmo tamanho.
  4. O parâmetro StopLossPoints reflete a configuração stop original do EA. Quando PriceStep e StopLossPoints do título estão disponíveis, a estratégia converte o valor em uma distância absoluta e o encaminha para StartProtection, permitindo que StockSharp mantenha as ordens de stop-loss de proteção automaticamente.
  5. As aberturas de velas são rastreadas usando a assinatura de velas de alto nível API. As velas finalizadas preenchem a lista do histórico, enquanto a vela ativa aciona a decisão uma vez por barra.

Parâmetros

Parâmetro Descrição Padrão
TradeVolume Tamanho base do pedido expresso em lotes. Deve ser positivo. 1
StopLossPoints Distância de parada protetora nos pontos do instrumento. Defina como 0 para desativar o stop loss automático. 120
LookbackBars Número de barras utilizadas para comparação de preços abertos. Um valor de 3 reproduz Open[0] vs Open[3] do código original. 3
CandleType Período de tempo (como DataType) a partir do qual as velas são solicitadas. Controla quando novos sinais aparecem. 1 hour timeframe

Notas de implementação

  • Usa o fluxo de trabalho SubscribeCandles(...).Bind(...) de alto nível, para que a estratégia permaneça leve e reaja a velas históricas e ativas.
  • StartProtection é invocado uma vez durante OnStarted. Garanta que a segurança conectada forneça PriceStep; caso contrário, a distância stop-loss não pode ser traduzida em preços absolutos.
  • Como todas as negociações são inseridas com ordens de mercado no início de cada barra, o tratamento da derrapagem é delegado à plataforma de negociação e não há parâmetro slippage adicional.
  • O buffer aberto histórico mantém apenas uma pequena janela contínua (LookbackBars + 5 valores) para evitar uso desnecessário de memória.
  • Nenhuma porta Python é fornecida; o diretório CS/ contém a única implementação.

Estrutura de arquivo

4002_SimpleTrade/
├──CS/
│ └── SimpleTradeFlipStrategy.cs
├── README.md
├── README_zh.md
└── README_ru.md
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>
/// StockSharp port of the MetaTrader "SimpleTrade" expert advisor.
/// Compares the opening price of the current bar with the bar from several periods ago and flips the position accordingly.
/// </summary>
public class SimpleTradeFlipStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<int> _lookbackBars;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _openHistory = new();
	private int _cooldown;

	/// <summary>
	/// Initializes a new instance of the <see cref="SimpleTradeFlipStrategy"/> class.
	/// </summary>
	public SimpleTradeFlipStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order size in lots", "Trading");

		_stopLossPoints = Param(nameof(StopLossPoints), 120m)
			.SetNotNegative()
			.SetDisplay("Stop-Loss Points", "Protective stop distance expressed in instrument points", "Risk");

		_lookbackBars = Param(nameof(LookbackBars), 10)
			.SetGreaterThanZero()
			.SetDisplay("Lookback Bars", "Number of bars used for the open price comparison", "Signals");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe used for signal calculations", "General");
	}

	/// <summary>
	/// Order size submitted with each entry.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in instrument points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = Math.Max(0m, value);
	}

	/// <summary>
	/// Number of historical bars used for the open price comparison.
	/// </summary>
	public int LookbackBars
	{
		get => _lookbackBars.Value;
		set => _lookbackBars.Value = Math.Max(1, value);
	}

	/// <summary>
	/// Candle type that defines the working timeframe.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

		_openHistory.Clear();
		_cooldown = 0;
	}

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

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var step = Security?.PriceStep ?? 0m;
		Unit stopLossUnit = null;

		if (StopLossPoints > 0m && step > 0m)
			stopLossUnit = new Unit(StopLossPoints * step, UnitTypes.Absolute);

		StartProtection(null, stopLossUnit);
	}

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

		// Store the open price for future comparisons.
		_openHistory.Add(candle.OpenPrice);

		var maxHistory = Math.Max(LookbackBars + 5, 5);
		if (_openHistory.Count > maxHistory)
			_openHistory.RemoveRange(0, _openHistory.Count - maxHistory);

		var lookback = LookbackBars;
		if (_openHistory.Count <= lookback)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		var volume = TradeVolume;
		if (volume <= 0m)
			return;

		var currentOpen = candle.OpenPrice;
		var referenceOpen = _openHistory[^(lookback + 1)];

		// Only trade on clear directional difference
		var diff = currentOpen - referenceOpen;
		if (Math.Abs(diff) < currentOpen * 0.001m)
			return;

		if (diff > 0 && Position <= 0)
		{
			if (Position < 0m)
				BuyMarket(Math.Abs(Position));
			BuyMarket(volume);
			_cooldown = 5;
		}
		else if (diff < 0 && Position >= 0)
		{
			if (Position > 0m)
				SellMarket(Position);
			SellMarket(volume);
			_cooldown = 5;
		}
	}
}