Un sistema contrario semanal convertido del asesor experto MQL "Contrarian_trade_MA" original. La estrategia analiza los extremos de las velas semanales junto con una media móvil simple para atenuar los movimientos extendidos al comienzo de una nueva semana.
Lógica de trading
Fuente de datos: velas semanales proporcionadas por el parámetro CandleType (el valor predeterminado es un período de tiempo de 7 días).
Extremos históricos: Los indicadores Highest y Lowest rastrean los máximos y mínimos de las CalcPeriod semanas completas anteriores, excluyendo la vela evaluada actualmente.
Filtro de media móvil: una media móvil simple de longitud MaPeriod aplicada a los cierres semanales actúa como un filtro direccional.
Reglas de entrada:
Compre cuando el cierre de la semana anterior sea superior al máximo registrado (highest < previousClose) o cuando el promedio móvil esté por encima de la apertura semanal actual.
Vender cuando el cierre de la semana anterior sea inferior al mínimo registrado (lowest > previousClose) o cuando el promedio móvil esté por debajo de la apertura semanal actual.
Sólo puede haber una posición abierta en cualquier momento; Las señales opuestas se ignoran hasta que se cierra la operación existente.
Reglas de salida:
La posición se cierra después de mantenerse durante siete días (604.800 segundos) independientemente de la dirección.
Se evalúa una parada protectora en cada vela semanal completa. La distancia de parada se calcula a partir de StopLossPoints * PriceStep (vuelve a 1 si los metadatos del instrumento no especifican un paso).
Parámetros
Nombre
Predeterminado
Descripción
CalcPeriod
4
Número de semanas completadas utilizadas para calcular el máximo más alto y el mínimo más bajo.
MaPeriod
7
Periodo de la media móvil simple aplicada a los cierres semanales.
StopLossPoints
300
Distancia desde el precio de entrada hasta el stop-loss, medida en incrementos de precio. Establezca en 0 para desactivar la parada.
Volume
0.5
Tamaño del pedido en lotes enviados por BuyMarket/SellMarket.
CandleType
7 days
Plazo de las velas que impulsan todos los cálculos.
Notas adicionales
La estrategia recupera automáticamente el paso de precio de Security.PriceStep. Proporcione este valor en los metadatos del instrumento para una colocación precisa del stop-loss.
StartProtection() está habilitado para rastrear cambios de posición inesperados realizados fuera de la estrategia.
Debido a que la lógica opera en velas semanales completadas, los llenados se simulan en el cierre semanal de la barra de señal cuando se ejecuta en modo de prueba.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Contrarian MA strategy - mean reversion around SMA.
/// Buys when price crosses below SMA (contrarian dip buy).
/// Sells when price crosses above SMA (contrarian top sell).
/// Uses Highest/Lowest as extreme confirmation.
/// </summary>
public class ContrarianTradeMaWeeklyStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevSma;
private bool _hasPrev;
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ContrarianTradeMaWeeklyStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 14)
.SetDisplay("SMA Period", "SMA period", "Indicators");
_channelPeriod = Param(nameof(ChannelPeriod), 10)
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevSma = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, highest, lowest, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal sma, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevClose = close;
_prevSma = sma;
_hasPrev = true;
return;
}
var mid = (highest + lowest) / 2;
// Contrarian buy: price crosses below SMA and is near the low
if (_prevClose >= _prevSma && close < sma && close < mid && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Contrarian sell: price crosses above SMA and is near the high
else if (_prevClose <= _prevSma && close > sma && close > mid && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevClose = close;
_prevSma = sma;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class contrarian_trade_ma_weekly_strategy(Strategy):
def __init__(self):
super(contrarian_trade_ma_weekly_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 14) \
.SetDisplay("SMA Period", "SMA period", "Indicators")
self._channel_period = self.Param("ChannelPeriod", 10) \
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
@property
def ma_period(self):
return self._ma_period.Value
@property
def channel_period(self):
return self._channel_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(contrarian_trade_ma_weekly_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(contrarian_trade_ma_weekly_strategy, self).OnStarted2(time)
self._has_prev = False
sma = SimpleMovingAverage()
sma.Length = self.ma_period
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, highest, lowest, self.process_candle).Start()
def process_candle(self, candle, sma, highest, lowest):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma_val = float(sma)
high_val = float(highest)
low_val = float(lowest)
if not self._has_prev:
self._prev_close = close
self._prev_sma = sma_val
self._has_prev = True
return
mid = (high_val + low_val) / 2.0
if self._prev_close >= self._prev_sma and close < sma_val and close < mid and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close <= self._prev_sma and close > sma_val and close > mid and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_close = close
self._prev_sma = sma_val
def CreateClone(self):
return contrarian_trade_ma_weekly_strategy()