Esta estrategia implementa un sistema contrarian de cruce de medias móviles escrito originalmente en MQL como X trader.
Utiliza dos medias móviles simples y abre posiciones en dirección opuesta al cruce. El riesgo se gestiona
usando take-profit y stop-loss fijos en puntos absolutos mediante StartProtection.
Cómo funciona
Suscribirse a datos de velas del marco temporal especificado.
Calcular dos medias móviles con períodos configurables.
Rastrear los últimos dos valores de cada media para detectar un cruce.
Cuando la media rápida cruza por encima de la media lenta y permanece por encima durante dos barras mientras dos barras atrás estaba por debajo,
se abre una posición corta.
Cuando la media rápida cruza por debajo de la media lenta y permanece por debajo durante dos barras mientras dos barras atrás estaba por encima,
se abre una posición larga.
Solo puede estar abierta una posición al mismo tiempo. La protección cierra automáticamente las operaciones cuando el precio se mueve
la cantidad configurada de take-profit o stop-loss.
Parámetros
CandleType – serie de velas a utilizar.
Ma1Period – período de la primera media móvil.
Ma2Period – período de la segunda media móvil.
TakeProfitPoints – objetivo de beneficio en puntos de precio.
StopLossPoints – límite de pérdida en puntos de precio.
Indicador
SimpleMovingAverage – usado dos veces con períodos diferentes.
Gestión de riesgos
StartProtection se habilita en OnStarted y aplica los valores de take-profit y stop-loss a todas las posiciones.
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>
/// X Trader Strategy - contrarian moving average cross.
/// </summary>
public class XTraderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _ma1Period;
private readonly StrategyParam<int> _ma2Period;
private decimal _ma1Prev;
private decimal _ma1Prev2;
private decimal _ma2Prev;
private decimal _ma2Prev2;
private bool _hasPrev2;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Ma1Period { get => _ma1Period.Value; set => _ma1Period.Value = value; }
public int Ma2Period { get => _ma2Period.Value; set => _ma2Period.Value = value; }
public XTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_ma1Period = Param(nameof(Ma1Period), 16)
.SetGreaterThanZero()
.SetDisplay("MA1 Period", "Period of the first moving average", "Parameters");
_ma2Period = Param(nameof(Ma2Period), 10)
.SetGreaterThanZero()
.SetDisplay("MA2 Period", "Period of the second moving average", "Parameters");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_ma1Prev = 0;
_ma1Prev2 = 0;
_ma2Prev = 0;
_ma2Prev2 = 0;
_hasPrev2 = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma1 = new ExponentialMovingAverage { Length = Ma1Period };
var ma2 = new ExponentialMovingAverage { Length = Ma2Period };
SubscribeCandles(CandleType)
.Bind(ma1, ma2, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ma1Value, decimal ma2Value)
{
if (candle.State != CandleStates.Finished) return;
if (_ma1Prev == 0)
{
_ma1Prev = ma1Value;
_ma2Prev = ma2Value;
return;
}
if (!_hasPrev2)
{
_ma1Prev2 = _ma1Prev;
_ma2Prev2 = _ma2Prev;
_ma1Prev = ma1Value;
_ma2Prev = ma2Value;
_hasPrev2 = true;
return;
}
// Contrarian: sell when MA1 crosses above MA2, buy when MA1 crosses below
var sellSignal = ma1Value > ma2Value && _ma1Prev > _ma2Prev && _ma1Prev2 < _ma2Prev2;
var buySignal = ma1Value < ma2Value && _ma1Prev < _ma2Prev && _ma1Prev2 > _ma2Prev2;
if (sellSignal && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
else if (buySignal && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
_ma1Prev2 = _ma1Prev;
_ma2Prev2 = _ma2Prev;
_ma1Prev = ma1Value;
_ma2Prev = ma2Value;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class x_trader_strategy(Strategy):
def __init__(self):
super(x_trader_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._ma1_period = self.Param("Ma1Period", 16) \
.SetDisplay("MA1 Period", "Period of the first moving average", "Parameters")
self._ma2_period = self.Param("Ma2Period", 10) \
.SetDisplay("MA2 Period", "Period of the second moving average", "Parameters")
self._ma1_prev = 0.0
self._ma1_prev2 = 0.0
self._ma2_prev = 0.0
self._ma2_prev2 = 0.0
self._has_prev2 = False
@property
def candle_type(self):
return self._candle_type.Value
@property
def ma1_period(self):
return self._ma1_period.Value
@property
def ma2_period(self):
return self._ma2_period.Value
def OnReseted(self):
super(x_trader_strategy, self).OnReseted()
self._ma1_prev = 0.0
self._ma1_prev2 = 0.0
self._ma2_prev = 0.0
self._ma2_prev2 = 0.0
self._has_prev2 = False
def OnStarted2(self, time):
super(x_trader_strategy, self).OnStarted2(time)
ma1 = ExponentialMovingAverage()
ma1.Length = self.ma1_period
ma2 = ExponentialMovingAverage()
ma2.Length = self.ma2_period
self.SubscribeCandles(self.candle_type).Bind(ma1, ma2, self.process_candle).Start()
def process_candle(self, candle, ma1_value, ma2_value):
if candle.State != CandleStates.Finished:
return
m1 = float(ma1_value)
m2 = float(ma2_value)
if self._ma1_prev == 0.0:
self._ma1_prev = m1
self._ma2_prev = m2
return
if not self._has_prev2:
self._ma1_prev2 = self._ma1_prev
self._ma2_prev2 = self._ma2_prev
self._ma1_prev = m1
self._ma2_prev = m2
self._has_prev2 = True
return
sell_signal = m1 > m2 and self._ma1_prev > self._ma2_prev and self._ma1_prev2 < self._ma2_prev2
buy_signal = m1 < m2 and self._ma1_prev < self._ma2_prev and self._ma1_prev2 > self._ma2_prev2
if sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._ma1_prev2 = self._ma1_prev
self._ma2_prev2 = self._ma2_prev
self._ma1_prev = m1
self._ma2_prev = m2
def CreateClone(self):
return x_trader_strategy()