Esta estrategia porta el asesor experto de MetaTrader personal_assistant_codeBase_MNS a StockSharp. Actúa como un asistente de trading manual: en lugar de generar señales autónomas, expone métodos C# que replican las acciones impulsadas por teclas de acceso rápido del EA original (abrir/cerrar operaciones, ajustar volumen o liquidar posiciones rentables). El asistente también registra métricas informativas sobre el símbolo, órdenes activas y niveles de riesgo configurados en cada vela terminada.
Cómo funciona
La estrategia se suscribe a una serie de velas configurable (CandleType, 1 minuto por defecto).
Cada vela terminada desencadena una actualización que imprime: posición actual, PnL, número de órdenes stop/take activas, spread, valor del tick y el número mágico configurado.
Los comandos manuales (p. ej., PressBuy() o PressSell()) envían órdenes de mercado con el volumen del asistente actual. Los niveles opcionales de stop-loss y take-profit se traducen desde distancias de pip y se almacenan internamente en la estrategia.
Los niveles protectores se emulan en datos de velas: si el precio toca el stop o el objetivo almacenados, la estrategia emite salidas de mercado.
Una regla opcional de mover a break-even (UseTrailingStop) se arma después de que el precio avanza BreakEvenTriggerPips; una vez armada, liquida la posición si el precio retrocede al precio de entrada más BreakEvenOffsetPips.
Características
Replica los botones 1–8 del asistente MQL via métodos públicos:
PressBuy() / PressSell() – abrir operaciones de mercado con niveles protectores opcionales.
PressCloseAll() – aplanar toda la exposición.
IncreaseVolume() / DecreaseVolume() – ajustar el volumen del asistente en 0.01 lotes.
CloseLongPositions() / CloseShortPositions() – cerrar solo un lado.
CloseProfitablePositions() – cerrar la posición cuando el PnL flotante es positivo.
Registra una leyenda de acción detallada al inicio cuando DisplayLegend está habilitado.
Convierte las distancias de riesgo basadas en pips en precios absolutos usando el paso de precio y la precisión decimal del instrumento.
Admite trailing de break-even para posiciones largas y cortas, imitando la rutina original MOVETOBREAKEVEN().
Mantiene niveles de stop/take almacenados independientes para operaciones largas y cortas para que al cambiar de dirección se descarten automáticamente los niveles obsoletos.
Parámetros
Parámetro
Descripción
MagicNumber
Identificador informacional copiado del input MagicNo de MQL.
DisplayLegend
Habilitar para imprimir la leyenda de control y los mensajes de estado por vela.
OrderVolume
Volumen base de la orden de mercado (lotes) reutilizado por todas las acciones manuales.
Slippage
Deslizamiento máximo tolerado (en ticks), almacenado como referencia.
TakeProfitPips
Distancia de pip para el nivel de take-profit almacenado (0 lo deshabilita).
StopLossPips
Distancia de pip para el nivel de stop-loss almacenado (0 lo deshabilita).
UseTrailingStop
Habilitar o deshabilitar la lógica de trailing de break-even.
BreakEvenTriggerPips
Distancia de beneficio (en pips) requerida antes de que el stop de break-even se arme.
BreakEvenOffsetPips
Offset (en pips) añadido al precio de entrada una vez que el stop está armado.
CandleType
Serie de velas usada para monitoreo y emulación de niveles.
Consejos de uso
Llamar a los métodos helper desde acciones de Designer, scripts o controles de UI para imitar las pulsaciones de teclas del panel original de MetaTrader.
Los niveles protectores y las distancias de break-even dependen del instrumento que proporcione PriceStep, StepPrice y Decimals. Para instrumentos exóticos sin estos metadatos, ajustar las distancias de pip manualmente o deshabilitar las características estableciéndolas en 0.
Dado que los niveles stop/take se reproducen usando los máximos y mínimos de las velas, los picos intra-barra muy rápidos pueden no capturarse a menos que el marco temporal de la vela sea pequeño. Reducir el marco temporal si se requiere mayor granularidad.
CloseProfitablePositions() replica el comportamiento del "botón 8": verifica el PnL flotante y cierra toda la posición solo cuando el valor es estrictamente positivo.
Diferencias con la versión de MetaTrader
Las etiquetas del gráfico son reemplazadas por entradas de registro porque StockSharp no expone las mismas primitivas de dibujo dentro de las estrategias.
Las órdenes de stop-loss y take-profit se simulan a través de salidas de mercado en eventos de velas en lugar de órdenes pendientes inmediatas.
La gestión de break-even se implementa con órdenes de mercado de StockSharp; no modifica las órdenes protectoras existentes.
El deslizamiento se mantiene como parámetro informacional; la ejecución real es manejada por el conector de StockSharp.
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;
public class PersonalAssistantMnsStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public PersonalAssistantMnsStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA 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");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { 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;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class personal_assistant_mns_strategy(Strategy):
def __init__(self):
super(personal_assistant_mns_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA 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(personal_assistant_mns_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(personal_assistant_mns_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
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 = 100
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 = 100
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 = 100
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 = 100
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 = 100
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 = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return personal_assistant_mns_strategy()