Esta estrategia porta el asesor experto 1 MINUTE SCALPER de MetaTrader 4 a la API de alto nivel de StockSharp. Mantiene la
confirmación de tendencia multicapa, el momentum multi-temporal y el filtro MACD a largo plazo del robot original mientras adapta
los controles de riesgo al modelo centrado en posiciones de StockSharp.
Lógica Central
Pila de Tendencia – trece medias móviles linealmente ponderadas (LWMA 3/5/8/10/12/15/30/35/40/45/50/55/200) deben
alinearse en orden estricto. Las operaciones largas requieren que cada media más corta imprima por encima de la siguiente,
mientras que los cortos invierten la condición.
Puerta de Tendencia Principal – una LWMA rápida adicional (por defecto 6) debe mantenerse por encima de la LWMA lenta
(por defecto 85) para largos y por debajo para cortos, reflejando la comprobación rápido-vs-lento del EA.
Estructura de Vela – las entradas solo se activan cuando los patrones de superposición del script están presentes: para
largos el mínimo de dos barras atrás debe estar por debajo del máximo anterior; para cortos el mínimo anterior debe caer bajo
el máximo de dos barras atrás.
Filtro de Momentum – un indicador de momentum de 14 períodos calculado en un marco temporal superior (por defecto velas
de 15 minutos) debe desviarse de 100 al menos en los umbrales configurados en cualquiera de los últimos tres valores. Esto
reproduce las comparaciones MomLevelB/MomLevelS.
Sesgo MACD Mensual – un MACD construido en el marco temporal MACD seleccionado (por defecto velas de 30 días como proxy
de datos mensuales) debe mostrar la línea principal por encima de la señal para largos o por debajo para cortos.
Gestión de Operaciones
Protección Inicial – las distancias de stop-loss y take-profit se expresan en pasos del instrumento (puntos). Cuando
abre una posición, la estrategia convierte estos recuentos de pasos a precios absolutos usando Security.PriceStep.
Movimiento de Punto de Equilibrio – después de que el precio se mueve BreakEvenTriggerSteps a favor, el stop se mueve
a la entrada más BreakEvenOffsetSteps (para cortos se aplica la lógica espejada). El indicador se activa una vez por posición.
Trailing por Pasos – cuando TrailingStopSteps es positivo, el stop sigue el precio más alto (o más bajo) desde la
entrada en el número especificado de pasos.
Trailing Monetario – una vez que el beneficio flotante supera MoneyTrailTarget (divisa), la estrategia rastrea el PnL
flotante máximo y cierra la posición si el retroceso es igual a MoneyTrailStop.
Objetivos Monetarios/Porcentuales – objetivos de take-profit absolutos u opcionales cierran toda la exposición cuando el
PnL flotante cruza los umbrales configurados. El objetivo porcentual usa el valor inicial de la cartera capturado cuando
comienza la estrategia.
Stop de Capital – la estrategia monitoriza el capital máximo (valor de cartera más PnL abierto). Si el drawdown desde ese
pico supera EquityRiskPercent, todas las posiciones se cierran, replicando la salvaguarda AccountEquityHigh() del EA.
Parámetros
Parámetro
Descripción
Volume
Volumen de orden para nuevas entradas. Se añade a la posición actual absoluta para que las reversiones cambien la exposición inmediatamente.
FastMaPeriod / SlowMaPeriod
Longitudes de LWMA para el filtro de tendencia principal.
MomentumPeriod
Longitud del indicador de momentum en marco temporal superior.
MomentumBuyThreshold / MomentumSellThreshold
Desviación absoluta mínima de 100 requerida para la confirmación de momentum largo/corto.
Configurar la lógica de trailing del beneficio flotante.
UseEquityStop, EquityRiskPercent
Habilitar el stop de drawdown y definir el porcentaje máximo de drawdown.
CandleType
Velas de trabajo principales (por defecto 1 minuto).
MomentumCandleType
Velas de marco temporal superior para el indicador de momentum (por defecto 15 minutos).
MacdCandleType
Velas usadas para el filtro de tendencia MACD (por defecto 30 días ≈ mensual).
Diferencias vs. el Experto de MT4
StockSharp utiliza posiciones netas, por lo que la estrategia siempre mantiene una única posición agregada en lugar de
múltiples tickets hasta Max_Trades. Las reversiones cierran la exposición existente antes de abrir en la dirección opuesta.
PercentTakeProfit hace referencia al valor de cartera capturado al inicio en lugar del AccountBalance() en constante
cambio usado por MetaTrader, lo que evita objetivos ruidosos cuando las operaciones externas modifican el saldo.
La lógica de salida basada en dinero (Take_Profit_In_Money y TRAIL_PROFIT_IN_MONEY2) opera sobre el PnL flotante en vivo
calculado desde el precio de entrada promedio de la estrategia. Esto coincide con el comportamiento del EA pero dentro del
marco de protección de StockSharp.
La plataforma debe suministrar feeds de velas para los marcos temporales seleccionados (CandleType, MomentumCandleType,
MacdCandleType). Asegúrese de que los adaptadores que utiliza admitan las resoluciones solicitadas.
Ajuste los umbrales para adaptarse a su instrumento y sesión. Los pares con spreads estrechos o muy volátiles pueden requerir
distancias de pasos más amplias o umbrales de momentum más grandes para reducir el ruido.
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>
/// One-minute scalper strategy using fast/slow WMA crossover.
/// Buys on bullish crossover, sells on bearish crossover.
/// </summary>
public class OneMinuteScalperStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private WeightedMovingAverage _fast;
private WeightedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast WMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow WMA 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="OneMinuteScalperStrategy"/> class.
/// </summary>
public OneMinuteScalperStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast WMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 85)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow WMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 500)
.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 WeightedMovingAverage { Length = FastPeriod };
_slow = new WeightedMovingAverage { 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 = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// WMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 60;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 60;
}
_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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class one_minute_scalper_strategy(Strategy):
def __init__(self):
super(one_minute_scalper_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20).SetGreaterThanZero().SetDisplay("Fast Period", "Fast WMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 85).SetGreaterThanZero().SetDisplay("Slow Period", "Slow WMA period", "Indicator")
self._sl_points = self.Param("StopLossPoints", 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._tp_points = self.Param("TakeProfitPoints", 500).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
def OnReseted(self):
super(one_minute_scalper_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
def OnStarted2(self, time):
super(one_minute_scalper_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
self._fast = WeightedMovingAverage()
self._fast.Length = self._fast_period.Value
self._slow = WeightedMovingAverage()
self._slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(tf(5))
sub.Bind(self._fast, self._slow, self.OnProcess).Start()
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
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 = candle.ClosePrice
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
step = float(self.Security.PriceStep)
# Check SL/TP
if self.Position > 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close <= self._entry_price - self._sl_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close >= self._entry_price + self._tp_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close >= self._entry_price + self._sl_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close <= self._entry_price - self._tp_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
# WMA crossover
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 = 60
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 = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return one_minute_scalper_strategy()