Ver no GitHub

Estratégia do Painel de Operações em Piloto Automático

Visão geral

Esta estratégia reproduz a lógica central do expert MQL4 original "trade panel with autopilot". Ela agrega a direção do preço em múltiplos períodos e abre ou fecha uma única posição de acordo com o sentimento dominante do mercado.

A estratégia monitora as duas últimas velas em oito períodos diferentes (M1, M5, M15, M30, H1, H4, D1, W1). Para cada período, compara vários componentes de preço entre as duas velas mais recentes:

  • Open
  • High
  • Low
  • (High + Low) / 2
  • Close
  • (High + Low + Close) / 3
  • (High + Low + Close + Close) / 4

Cada comparação contribui para uma pontuação de compra ou venda. As pontuações de todos os períodos são somadas e convertidas em percentuais. Quando o percentual de compra ou venda cruza um limiar configurado, a estratégia entra em uma posição. A posição existente é fechada se o percentual oposto cair abaixo do limiar de fechamento.

Parâmetros

  • Autopilot — ativa ou desativa a negociação automática.
  • OpenThreshold — nível percentual necessário para abrir uma nova posição. Padrão: 85.
  • CloseThreshold — nível percentual para fechar uma posição existente. Padrão: 55.
  • LotFixed — volume fixo da ordem quando UseFixedLot está habilitado.
  • LotPercent — volume como percentual do valor da carteira quando UseFixedLot está desabilitado.
  • UseFixedLot — alterna entre volume fixo e percentual.
  • UseStopLoss — inicia a proteção da posição quando habilitado.

Lógica de negociação

  1. Subscrever candles em todos os períodos configurados.
  2. Calcular as pontuações de compra/venda para cada novo candle concluído.
  3. Somar as pontuações por períodos e calcular os percentuais de compra/venda.
  4. Se Autopilot estiver desabilitado, a estratégia apenas rastreia as pontuações.
  5. Se não houver posição aberta e o percentual de compra exceder OpenThreshold, entrar em uma posição comprada. Se o percentual de venda exceder o limiar, entrar em uma posição vendida.
  6. Se existir uma posição comprada e o percentual de compra cair abaixo de CloseThreshold, sair da posição. A mesma lógica se aplica para posições vendidas usando o percentual de venda.

Notas

  • A estratégia mantém no máximo uma posição aberta por vez.
  • O gerenciamento opcional de stop-loss é ativado via StartProtection() quando UseStopLoss é verdadeiro.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Trade panel autopilot strategy v2.
/// Aggregates 7 price comparison metrics over rolling window.
/// Buys when buy percentage exceeds open threshold, sells on opposite.
/// </summary>
public class TradePanelAutopilotStrategy : Strategy
{
	private readonly StrategyParam<decimal> _openThreshold;
	private readonly StrategyParam<decimal> _closeThreshold;
	private readonly StrategyParam<int> _windowSize;
	private readonly StrategyParam<DataType> _candleType;

	private readonly Queue<(int buy, int sell)> _signalWindow = new();
	private ICandleMessage _prevCandle;

	public decimal OpenThreshold { get => _openThreshold.Value; set => _openThreshold.Value = value; }
	public decimal CloseThreshold { get => _closeThreshold.Value; set => _closeThreshold.Value = value; }
	public int WindowSize { get => _windowSize.Value; set => _windowSize.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TradePanelAutopilotStrategy()
	{
		_openThreshold = Param(nameof(OpenThreshold), 65m)
			.SetDisplay("Open %", "Threshold for new position", "General");

		_closeThreshold = Param(nameof(CloseThreshold), 40m)
			.SetDisplay("Close %", "Threshold for closing", "General");

		_windowSize = Param(nameof(WindowSize), 8)
			.SetGreaterThanZero()
			.SetDisplay("Window Size", "Number of candles for signal aggregation", "General");

		_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();
		_prevCandle = null;
		_signalWindow.Clear();
	}

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

		_prevCandle = null;
		_signalWindow.Clear();

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

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

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

		if (_prevCandle == null)
		{
			_prevCandle = candle;
			return;
		}

		int buy = 0, sell = 0;

		if (candle.OpenPrice > _prevCandle.OpenPrice) buy++; else sell++;
		if (candle.HighPrice > _prevCandle.HighPrice) buy++; else sell++;
		if (candle.LowPrice > _prevCandle.LowPrice) buy++; else sell++;
		if (candle.ClosePrice > _prevCandle.ClosePrice) buy++; else sell++;

		var hlCurr = (candle.HighPrice + candle.LowPrice) / 2m;
		var hlPrev = (_prevCandle.HighPrice + _prevCandle.LowPrice) / 2m;
		if (hlCurr > hlPrev) buy++; else sell++;

		var hlcCurr = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
		var hlcPrev = (_prevCandle.HighPrice + _prevCandle.LowPrice + _prevCandle.ClosePrice) / 3m;
		if (hlcCurr > hlcPrev) buy++; else sell++;

		var hlccCurr = (candle.HighPrice + candle.LowPrice + 2m * candle.ClosePrice) / 4m;
		var hlccPrev = (_prevCandle.HighPrice + _prevCandle.LowPrice + 2m * _prevCandle.ClosePrice) / 4m;
		if (hlccCurr > hlccPrev) buy++; else sell++;

		_signalWindow.Enqueue((buy, sell));

		while (_signalWindow.Count > WindowSize)
			_signalWindow.Dequeue();

		_prevCandle = candle;

		if (_signalWindow.Count < WindowSize)
			return;

		int totalBuy = 0, totalSell = 0;
		foreach (var (b, s) in _signalWindow)
		{
			totalBuy += b;
			totalSell += s;
		}

		var total = totalBuy + totalSell;
		if (total == 0) return;

		var buyPct = (decimal)totalBuy / total * 100m;
		var sellPct = (decimal)totalSell / total * 100m;

		if (Position > 0 && buyPct < CloseThreshold)
			SellMarket();
		else if (Position < 0 && sellPct < CloseThreshold)
			BuyMarket();

		if (Position == 0)
		{
			if (buyPct >= OpenThreshold)
				BuyMarket();
			else if (sellPct >= OpenThreshold)
				SellMarket();
		}
	}
}