Esta estrategia recrea el asesor experto de MetaTrader "Contrarian trade MA" usando la API de alto nivel de StockSharp. Combina el contexto semanal con un filtro de entrada solo los lunes para operar contra los extremos. El sistema espera una nueva semana de trading, mide qué tan lejos cerró la semana anterior en relación con el máximo más alto y el mínimo más bajo durante la ventana de lookback, y verifica si el precio abrió la nueva semana en el lado opuesto de una media móvil desplazada. Si el mercado termina la primera vela diaria de la semana fuera de esos umbrales, se abre una posición contraria.
La lógica depende únicamente de velas completadas. Una serie diaria (predeterminada) impulsa las entradas y salidas, mientras que una serie semanal proporciona los niveles extremos y la señal de media móvil. Cada vez que se completa una vela del lunes, la estrategia evalúa si la semana anterior terminó por encima de la banda de máximos recientes o por debajo de la banda de mínimos recientes, o si el valor anterior de la media móvil está en el otro lado del precio de apertura semanal actual. La suposición es que tales movimientos sobreextendidos tienden a revertirse a la media durante la semana.
Cómo funciona
Las velas semanales alimentan dos indicadores:
Highest/Lowest encuentran el máximo y mínimo extremos durante CalcPeriod semanas.
Una media móvil configurable (MaPeriod, MaMethod, MaShift, AppliedPrice) procesa las mismas velas semanales.
Las velas diarias (o cualquier TradeCandleType seleccionado) desencadenan decisiones de trading una vez que se completan.
En la primera vela completada cuyo OpenTime.DayOfWeek == Monday, la estrategia evalúa las condiciones de entrada:
Largo si el cierre semanal anterior está por encima del máximo más alto del lookback o si el valor anterior de la MA es mayor que el precio de apertura semanal actual (lo que significa que el precio abrió por debajo de la MA).
Corto si el cierre semanal anterior está por debajo del mínimo más bajo del lookback o si el valor anterior de la MA es menor que el precio de apertura semanal actual (precio abrió por encima de la MA).
Las órdenes se envían con BuyMarket o SellMarket usando el volumen de la estrategia sin promediación. Solo puede haber una posición abierta a la vez.
Gestión de salidas
Una distancia de stop-loss fija se calcula como StopLossPips * Security.PriceStep. Cuando está habilitado (> 0), la estrategia monitorea los máximos y mínimos de las velas diarias; si el precio toca el nivel de stop dentro del día, la posición se cierra a mercado.
Una salida basada en tiempo cierra cualquier posición abierta una vez que han pasado siete días desde la entrada (604800 segundos en el EA original). La comprobación se realiza en cada vela diaria completada.
La estrategia nunca abre una nueva operación hasta que la anterior esté completamente cerrada.
Indicadores y datos
Extremos semanales: indicadores Highest y Lowest adjuntos a la serie MaCandleType (velas de 1 semana por defecto).
Media móvil semanal: se dispone de los métodos Simple, Exponential, Smoothed o LinearWeighted. La media móvil puede desplazarse hacia adelante por MaShift barras para imitar la configuración de MetaTrader y puede consumir diferentes fuentes de precio (AppliedPrice).
Marco temporal primario:TradeCandleType define qué velas impulsan el timing de las operaciones; el valor predeterminado son las velas diarias para que las entradas se evalúen después del cierre del primer día de la semana de trading.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
CalcPeriod
int
4
Número de velas de marco temporal superior usadas para calcular el máximo y el mínimo.
StopLossPips
int
300
Distancia de stop-loss en pasos de precio. Establezca en 0 para deshabilitar el stop de protección.
MaPeriod
int
7
Longitud de la media móvil semanal.
MaShift
int
0
Desplazamiento hacia adelante de la media móvil en barras. Refleja el parámetro de desplazamiento de MA de MetaTrader.
MaMethod
MovingAverageMethod
LinearWeighted
Método de cálculo de la media móvil (Simple, Exponential, Smoothed, LinearWeighted).
AppliedPrice
AppliedPriceType
Weighted
Fuente de precio alimentada a la media móvil (Close, Open, High, Low, Median, Typical, Weighted).
TradeCandleType
DataType
TimeSpan.FromMinutes(5).TimeFrame()
Marco temporal primario que desencadena las entradas y gestiona los stops/salidas.
MaCandleType
DataType
TimeSpan.FromDays(7).TimeFrame()
Marco temporal superior usado para la media móvil y para calcular los extremos.
Notas
La distancia de stop-loss se adapta al instrumento multiplicando el recuento de pips por Security.PriceStep. Los instrumentos sin un paso definido deshabilitarán efectivamente el stop.
Dado que la estrategia evalúa velas completadas, las entradas ocurren al cierre de la barra del lunes en lugar del primer tick de la semana. Esto mantiene el comportamiento determinista entre los backtests.
La lógica asume solo una posición abierta; cualquier operación abierta se cierra por el stop-loss o por el timeout de siete días antes de que se considere una nueva señal.
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;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class contrarian_trade_ma_monday_strategy(Strategy):
def __init__(self):
super(contrarian_trade_ma_monday_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 100) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(contrarian_trade_ma_monday_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(contrarian_trade_ma_monday_strategy, self).OnStarted2(time)
self._fast = SimpleMovingAverage()
self._fast.Length = self.fast_period
self._slow = SimpleMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return contrarian_trade_ma_monday_strategy()