Ver no GitHub

Estratégia de Operação Contrária MA da Segunda-feira

Esta estratégia recria o assessor especialista MetaTrader "Contrarian trade MA" usando a API de alto nível do StockSharp. Combina contexto semanal com um filtro de entrada apenas na segunda-feira para operar contra extremos. O sistema aguarda uma nova semana de trading, mede o quanto a semana anterior fechou em relação ao maior máximo e ao menor mínimo ao longo da janela de lookback, e verifica se o preço abriu a nova semana no lado oposto de uma média móvel deslocada. Se o mercado terminar a primeira vela diária da semana fora desses limites, uma posição contrária é aberta.

A lógica depende apenas de velas completadas. Uma série diária (padrão) impulsiona entradas e saídas, enquanto uma série semanal fornece os níveis extremos e o sinal de média móvel. Cada vez que uma vela de segunda-feira é completada, a estratégia avalia se a semana anterior terminou acima da faixa de máximos recentes ou abaixo da faixa de mínimos recentes, ou se o valor anterior da média móvel está do outro lado do preço de abertura semanal atual. A suposição é que tais movimentos sobreextendidos tendem a reverter à média durante a semana.

Como funciona

  1. Velas semanais alimentam dois indicadores:
    • Highest/Lowest encontram o máximo e mínimo extremos ao longo de CalcPeriod semanas.
    • Uma média móvel configurável (MaPeriod, MaMethod, MaShift, AppliedPrice) processa as mesmas velas semanais.
  2. Velas diárias (ou qualquer TradeCandleType selecionado) acionam decisões de trading assim que se completam.
  3. Na primeira vela completada cujo OpenTime.DayOfWeek == Monday, a estratégia avalia as condições de entrada:
    • Comprado se o fechamento semanal anterior estiver acima do maior máximo do lookback ou se o valor anterior da MA for maior que o preço de abertura semanal atual (significando que o preço abriu abaixo da MA).
    • Vendido se o fechamento semanal anterior estiver abaixo do menor mínimo do lookback ou se o valor anterior da MA for menor que o preço de abertura semanal atual (preço abriu acima da MA).
  4. As ordens são enviadas com BuyMarket ou SellMarket usando o volume da estratégia sem médias. Apenas uma posição pode estar aberta de cada vez.

Gestão de saída

  • Uma distância de stop-loss fixa é calculada como StopLossPips * Security.PriceStep. Quando habilitado (> 0), a estratégia monitora máximas e mínimas de velas diárias; se o preço tocar o nível de stop dentro do dia, a posição é fechada a mercado.
  • Uma saída baseada em tempo fecha qualquer posição aberta após sete dias desde a entrada (604800 segundos no EA original). A verificação é realizada em cada vela diária completada.
  • A estratégia nunca abre uma nova operação até que a anterior esteja completamente fechada.

Indicadores e dados

  • Extremos semanais: indicadores Highest e Lowest anexados à série MaCandleType (padrão: velas de 1 semana).
  • Média móvel semanal: os métodos Simple, Exponential, Smoothed ou LinearWeighted estão disponíveis. A média móvel pode ser deslocada para frente por MaShift barras para imitar a configuração do MetaTrader e pode consumir diferentes fontes de preço (AppliedPrice).
  • Período primário: TradeCandleType define quais velas impulsionam o timing das operações; o padrão são velas diárias para que as entradas sejam avaliadas após o primeiro dia da semana de trading ser fechado.

Parâmetros

Nome Tipo Padrão Descrição
CalcPeriod int 4 Número de velas de período de tempo superior usadas para calcular o maior máximo e menor mínimo.
StopLossPips int 300 Distância de stop-loss em passos de preço. Defina como 0 para desabilitar o stop de proteção.
MaPeriod int 7 Comprimento da média móvel semanal.
MaShift int 0 Deslocamento para frente da média móvel em barras. Espelha o parâmetro de deslocamento de MA do MetaTrader.
MaMethod MovingAverageMethod LinearWeighted Método de cálculo da média móvel (Simple, Exponential, Smoothed, LinearWeighted).
AppliedPrice AppliedPriceType Weighted Fonte de preço alimentada à média móvel (Close, Open, High, Low, Median, Typical, Weighted).
TradeCandleType DataType TimeSpan.FromMinutes(5).TimeFrame() Período primário que aciona entradas e gerencia stops/saídas.
MaCandleType DataType TimeSpan.FromDays(7).TimeFrame() Período superior usado para a média móvel e para calcular os extremos.

Notas

  • A distância de stop-loss adapta-se ao instrumento multiplicando a contagem de pips por Security.PriceStep. Instrumentos sem um passo definido desativarão efetivamente o stop.
  • Como a estratégia avalia velas completadas, as entradas ocorrem no fechamento da barra de segunda-feira e não no primeiro tick da semana. Isso mantém o comportamento determinístico entre os backtests.
  • A lógica assume apenas uma posição aberta; qualquer operação aberta é fechada pelo stop-loss ou pelo timeout de sete dias antes que um novo sinal seja considerado.
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;

/// <summary>
/// Contrarian Trade MA Monday strategy using SMA crossover with mean reversion.
/// Buys when fast SMA crosses above slow SMA, sells on reverse.
/// </summary>
public class ContrarianTradeMaMondayStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private SimpleMovingAverage _fast;
	private SimpleMovingAverage _slow;

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

	/// <summary>
	/// Fast SMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow SMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="ContrarianTradeMaMondayStrategy"/> class.
	/// </summary>
	public ContrarianTradeMaMondayStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast SMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow SMA 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");
	}

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

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

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_fast = new SimpleMovingAverage { Length = FastPeriod };
		_slow = new SimpleMovingAverage { 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;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

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

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// SMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

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

			SellMarket();
			_entryPrice = close;
			_cooldown = 80;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}