Esta estrategia es una adaptación directa del asesor experto MetaTrader 4 up3x1_Investor. Opera con un solo instrumento utilizando velas completadas desde un período de tiempo configurable (H1 por defecto). El puerto replica la lógica original con StockSharp API de alto nivel y agrega parámetros claros de gestión de riesgos.
Lógica de trading
La estrategia evalúa la última vela completamente cerrada y comprueba que:
El rango de velas (máximo menos mínimo) supera las 0.0060 unidades de precio.
El cuerpo de la vela (diferencia absoluta entre apertura y cierre) supera las 0.0050 unidades de precio.
Si la vela cerró alcista y se cumplen las condiciones anteriores, la estrategia abre una posición de mercado larga.
Si la vela cerró bajista y se cumplen las condiciones, la estrategia abre una posición de mercado corta.
El comercio está completamente deshabilitado los lunes (para reflejar la protección DayOfWeek()==1 del código MQL).
Gestión de Puestos
Al ingresar, la estrategia establece objetivos internos utilizando las distancias configuradas basadas en pasos:
TakeProfitPoints → distancia al objetivo de ganancias.
StopLossPoints → distancia de parada de protección.
TrailingStopPoints → distancia utilizada para rastrear el stop una vez que el precio se mueve a favor.
Las paradas y los objetivos se evalúan en cada vela terminada:
Si el precio alcanza el objetivo, la posición se cierra al precio objetivo.
Si el precio alcanza el tope, la posición se cierra para limitar la pérdida.
Una vez que el precio avanza más allá de la distancia de seguimiento, el stop se acerca al precio de mercado para asegurar las ganancias.
Además, si las medias móviles simples de 24 y 60 períodos calculadas sobre las mismas velas se igualan (dentro de un paso del precio), la posición se cierra inmediatamente. Esto imita la lógica MQL donde la orden se cierra cuando ambos promedios coinciden exactamente.
Gestión de volumen y riesgos
BaseVolume define el tamaño del lote alternativo cuando no se puede calcular ningún ajuste basado en la cuenta.
MaximumRisk replica la fórmula original AccountFreeMargin()*MaximumRisk/1000. Si el valor de la cartera está disponible, la estrategia dimensiona la posición como value * MaximumRisk / 1000, redondeada a un decimal.
DecreaseFactor imita la reducción de la racha de pérdidas: después de más de una pérdida consecutiva, el volumen disminuye proporcionalmente a losses / DecreaseFactor.
MinimumVolume garantiza que el volumen nunca caiga por debajo del tamaño negociable más pequeño utilizado en el script MQL (0,1 lotes).
Parámetros
Nombre
Predeterminado
Descripción
BaseVolume
0.1
Tamaño de la posición base en lotes cuando no se aplica ningún ajuste de riesgo.
MaximumRisk
0.2
Factor de riesgo utilizado para derivar el volumen del capital de la cuenta (igual que el EA original).
DecreaseFactor
3
Reduce el tamaño de la posición después de pérdidas consecutivas.
MinimumVolume
0.1
Volumen mínimo permitido.
TakeProfitPoints
20
Distancia objetivo de beneficio medida en pasos de precio.
StopLossPoints
50
Distancia de stop-loss medida en pasos de precio.
TrailingStopPoints
10
Distancia del trailing stop medida en incrementos de precio.
SkipMondays
true
Desactive toda la actividad comercial los lunes.
CandleType
1 hour
Plazo de suscripción de velas.
Notas
La estrategia solo mantiene una posición abierta a la vez, igualando la guardia original CalculateCurrentOrders.
El seguimiento de pérdidas consecutivas es puramente interno porque los corredores StockSharp no exponen el historial de pedidos MetaTrader.
No se utilizan órdenes pendientes; todas las operaciones se envían como órdenes de mercado a través de BuyMarket y SellMarket.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Up3x1InvestorRangeFilterStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Up3x1InvestorRangeFilterStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14).SetDisplay("ATR Period", "ATR lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevClose = close; _prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 6;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 6;
}
_prevClose = close; _prevEma = ema;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class up3x1_investor_range_filter_strategy(Strategy):
def __init__(self):
super(up3x1_investor_range_filter_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14).SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(up3x1_investor_range_filter_strategy, self).OnReseted()
self._prev_close = 0.0; self._prev_ema = 0.0; self._has_prev = False; self._cooldown = 0
def OnStarted2(self, time):
super(up3x1_investor_range_filter_strategy, self).OnStarted2(time)
self._has_prev = False; self._cooldown = 0
ema = ExponentialMovingAverage(); ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ema):
if candle.State != CandleStates.Finished: return
if not self.IsFormedAndOnlineAndAllowTrading(): return
close = float(candle.ClosePrice); ema_val = float(ema)
if not self._has_prev:
self._prev_close = close; self._prev_ema = ema_val; self._has_prev = True; return
if self._cooldown > 0:
self._cooldown -= 1; self._prev_close = close; self._prev_ema = ema_val; return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume); self._cooldown = 6
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume); self._cooldown = 6
self._prev_close = close; self._prev_ema = ema_val
def CreateClone(self): return up3x1_investor_range_filter_strategy()