Ver no GitHub

Estratégia de linha de tendência por ângulo

Visão geral

A estratégia é uma versão para StockSharp do expert advisor do MetaTrader Trend Line By Angle. O robô original misturava entradas manuais por botões com ferramentas extensas de gestão de dinheiro. Esta versão converte o fluxo discricionário em um sistema automatizado de seguimento de tendência por MACD, preservando a lógica de proteção:

  • MACD mensal (12/26/9), calculado no tipo de candle de sinal configurado, define a direção. Cruzamentos altistas abrem exposição comprada; cruzamentos baixistas abrem exposição vendida.
  • As entradas escalam até o número configurado de blocos, espelhando os cliques manuais repetidos no EA de origem.
  • Bollinger Bands (20, 2) observam o período de execução. Tocar a banda superior liquida a exposição comprada; tocar a banda inferior liquida posições vendidas, replicando os botões visuais de stop do MetaTrader.
  • Controles clássicos de risco - stop-loss, take-profit, trailing stop e movimento para break-even - operam sobre distâncias em pips convertidas pelo PriceStep do instrumento.
  • A proteção em nível de conta fecha todas as ordens quando um alvo de lucro monetário ou percentual é atingido. Um bloqueio trailing adicional baseado em dinheiro acompanha o lucro flutuante e sai no drawdown configurado.

Fluxo de execução

  1. Preparação dos indicadores - MovingAverageConvergenceDivergenceSignal roda em SignalCandleType, enquanto BollingerBands rodam no CandleType de negociação.
  2. Sinais de entrada - Em cada candle de execução concluído, o cruzamento MACD mais recente é avaliado. Um cruzamento para cima aciona BuyMarket; um cruzamento para baixo aciona SellMarket. A exposição oposta existente é fechada antes da reversão.
  3. Lógica de escalonamento - A estratégia continua comprando/vendendo até que a posição agregada atinja TradeVolume * MaxEntries.
  4. Gestão de risco - Níveis de break-even, trailing stop, stop-loss e take-profit são recalculados em cada candle. Um toque em Bollinger força uma saída mesmo que outros níveis não sejam atingidos.
  5. Proteção da conta - Verificações de take-profit monetário e percentual rodam antes de gerar novos sinais. O módulo de trailing monetário acompanha o maior PnL total e fecha tudo quando a queda excede MoneyTrailStop.

Detalhes de gestão de dinheiro

  • PnL total é a soma do lucro realizado (PnL) e do PnL flutuante calculado a partir do preço de fechamento do candle, do passo de preço e do valor do passo.
  • Break-even move o stop de proteção para Entry + BreakEvenOffsetPips (comprado) ou Entry - BreakEvenOffsetPips (vendido) quando o movimento excede BreakEvenTriggerPips.
  • Trailing stop se desloca para mais perto do preço sempre que o ganho excede TrailingStopPips. Níveis trailing comprados só aumentam; níveis trailing vendidos só diminuem.
  • Trailing monetário é ativado depois que o lucro de MoneyTrailTrigger é visto. A partir daí, o maior lucro é memorizado; perder mais de MoneyTrailStop a partir desse pico fecha todas as posições.

Parâmetros

Parâmetro Descrição
TradeVolume Volume de cada bloco de entrada.
MaxEntries Número máximo de blocos de volume a acumular.
StopLossPips Distância do stop-loss em pips.
TakeProfitPips Distância do take-profit em pips.
TrailingStopPips Distância de trailing em pips.
UseBreakEven Habilita o movimento do stop para break-even.
BreakEvenTriggerPips Lucro necessário antes da ativação do break-even.
BreakEvenOffsetPips Pips extras adicionados ao mover para break-even.
UseBollingerExit Habilita saídas por toques na Bollinger band.
BollingerPeriod / BollingerDeviation Configurações das Bollinger Bands.
UseProfitMoneyTarget / ProfitMoneyTarget Chave e valor do alvo absoluto de lucro.
UseProfitPercentTarget / ProfitPercentTarget Chave e valor do alvo percentual de lucro.
EnableMoneyTrail Habilita o trailing stop monetário.
MoneyTrailTrigger Lucro necessário antes de o trailing monetário se tornar ativo.
MoneyTrailStop Drawdown permitido a partir do pico antes da saída.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod Configuração MACD.
CandleType Período de execução.
SignalCandleType Período usado para o sinal MACD.

Notas de uso

  • A estratégia depende de valores corretos de PriceStep e StepPrice do instrumento. Configure o ativo antes de iniciar.
  • Se a conta não informar valor de portfólio (Portfolio.CurrentValue ou Portfolio.BeginValue), o take-profit percentual será ignorado automaticamente.
  • Todos os comentários dentro do arquivo C# documentam a lógica de negociação em inglês para simplificar a manutenção futura.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class TrendLineByAngleStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public TrendLineByAngleStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}