Ver no GitHub

Estratégia de Máquina Comercial AIS4

Visão geral

A AIS4 Trade Machine Strategy é um assistente de negociação manual que transporta o consultor especialista original MetaTrader "AIS4 Trade Machine" para StockSharp. Ele mantém o fluxo de trabalho de uma posição do script: o operador fornece níveis absolutos de stop-loss e take-profit, emite um comando e a estratégia calcula o tamanho da negociação com base no patrimônio da conta corrente e nas especificações do instrumento. Após o preenchimento da ordem de mercado, a estratégia envia imediatamente ordens de proteção emparelhadas (stop + limite) para que os níveis de risco e recompensa solicitados sejam aplicados no lado da bolsa.

A estratégia não gera sinais automáticos. Ele foi projetado para execução discricionária onde o usuário decide quando e onde inserir ou modificar uma posição.

Fluxo de trabalho manual

  1. Certifique-se de que o instrumento conectado exponha PriceStep, StepPrice, VolumeStep, MinVolume e MaxVolume. Eles são obrigados a converter o risco de preço em tamanho do contrato e a alinhar o volume de pedidos com os limites cambiais.
  2. Antes de enviar um comando, defina StopPrice e TakePrice para os níveis de preço absolutos que deseja usar.
  3. Altere Command para Buy ou Sell. A estratégia:
    • Verifica se nenhuma outra posição está aberta.
    • Verifica se o stop-loss e o take-profit solicitados respeitam a distância mínima do tick.
    • Calcula o orçamento de risco a partir de OrderReserve × patrimônio líquido atual do portfólio e garante que a reserva de patrimônio (AccountReserve) seja respeitada.
    • Estima o volume do pedido a partir da distância de parada e do valor do tick do instrumento.
    • Envia a ordem de mercado e, em seguida, envia ordens de proteção emparelhadas (SellStop+SellLimit para posições compradas, BuyStop+BuyLimit para posições vendidas).
  4. Command é automaticamente redefinido para Wait depois que a ação é processada para evitar execuções duplicadas acidentais.

Gerenciando uma posição existente

  • Defina novos níveis de preços (use 0 para manter o valor atual) e mude Command para Modify. A estratégia cancela as ordens de proteção anteriores e as substitui por novas que correspondam aos preços atualizados.
  • Mude Command para Close para liquidar a posição ativa no mercado e cancelar quaisquer ordens de proteção.

Lógica de gestão de risco

  • AccountReserve – mantém intacta uma fração do patrimônio máximo. A negociação é bloqueada enquanto o patrimônio disponível (equity - peak_equity × (1 - AccountReserve)) for menor que o orçamento de risco solicitado.
  • OrderReserve – fração do patrimônio atual alocada para a próxima negociação. O orçamento é transformado em tamanho de contrato usando a distância do stop e o valor do tick do instrumento (PriceStep × StepPrice).
  • Se o volume calculado ficar abaixo de MinVolume ou violar VolumeStep, o comando será rejeitado e um aviso será gravado no log.

Parâmetros

Parâmetro Padrão Descrição
Command Wait Comando manual para executar (Buy, Sell, Modify, Close). Retorna automaticamente para Wait após o manuseio.
StopPrice 0 Nível absoluto de stop-loss. Deve estar abaixo do preço de entrada para posições compradas e acima do preço de entrada para posições vendidas.
TakePrice 0 Nível absoluto de lucro. Deve estar acima do preço de entrada para posições compradas e abaixo do preço de entrada para posições vendidas.
AccountReserve 0.20 Fração do patrimônio líquido mantida como reserva. Valores mais altos exigem uma almofada maior antes que novas negociações sejam aceitas.
OrderReserve 0.04 Fração do patrimônio arriscado por negociação. Usado para calcular o tamanho do contrato a partir da distância de parada.
CandleType 1 minute período de tempo Série de velas usada para observar os preços mais recentes para validação e registro.

Notas e limitações

  • Apenas uma posição é suportada por vez, correspondendo ao design original do consultor especialista.
  • Comandos que violam a distância mínima de preço, reserva de capital ou restrições de volume são ignorados e um aviso é registrado no log de estratégia.
  • As ordens de proteção são substituídas a cada modificação ou novo preenchimento para manter os volumes sincronizados com o tamanho real da posição.
  • A estratégia depende de dados de mercado precisos para PriceStep/StepPrice. Instrumentos que não fornecem esses campos não podem ser negociados com segurança nesta porta.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AIS Trade Machine: EMA crossover strategy with ATR-based risk management.
/// Entry on EMA cross confirmed by RSI, exit on reversal or ATR stop.
/// </summary>
public class AisTradeMachineStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _stopMultiplier;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private decimal _stopPrice;

	public AisTradeMachineStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 30)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

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

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

		_stopMultiplier = Param(nameof(StopMultiplier), 2.0m)
			.SetDisplay("Stop Multiplier", "ATR multiplier for stop.", "Risk");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal StopMultiplier
	{
		get => _stopMultiplier.Value;
		set => _stopMultiplier.Value = value;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_stopPrice = 0;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_stopPrice = 0;

		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;
		var stopDist = atrVal * StopMultiplier;

		var bullishCross = _prevFast <= _prevSlow && fastVal > slowVal;
		var bearishCross = _prevFast >= _prevSlow && fastVal < slowVal;

		// Stop management
		if (Position > 0 && _stopPrice > 0 && close <= _stopPrice)
		{
			SellMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}
		else if (Position < 0 && _stopPrice > 0 && close >= _stopPrice)
		{
			BuyMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}

		// Exit on opposite cross
		if (Position > 0 && bearishCross)
		{
			SellMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}
		else if (Position < 0 && bullishCross)
		{
			BuyMarket();
			_entryPrice = 0;
			_stopPrice = 0;
		}

		// Trail stop
		if (Position > 0)
		{
			var trail = close - stopDist;
			if (trail > _stopPrice) _stopPrice = trail;
		}
		else if (Position < 0 && _stopPrice > 0)
		{
			var trail = close + stopDist;
			if (trail < _stopPrice) _stopPrice = trail;
		}

		// Entry on cross + RSI confirmation
		if (Position == 0)
		{
			if (bullishCross && rsiVal > 50)
			{
				_entryPrice = close;
				_stopPrice = close - stopDist;
				BuyMarket();
			}
			else if (bearishCross && rsiVal < 50)
			{
				_entryPrice = close;
				_stopPrice = close + stopDist;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}