Esta estratégia porta o consultor especialista MetaTrader personal_assistant_codeBase_MNS para o StockSharp. Atua como um assistente de trading manual: em vez de gerar sinais autônomos, expõe métodos C# que replicam as ações acionadas por teclas de atalho do EA original (abrir/fechar operações, ajustar volume ou liquidar posições lucrativas). O assistente também registra métricas informativas sobre o símbolo, ordens ativas e níveis de risco configurados em cada vela concluída.
Como funciona
A estratégia subscreve a uma série de velas configurável (CandleType, 1 minuto por padrão).
Cada vela concluída aciona uma atualização que imprime: posição atual, PnL, número de ordens stop/take ativas, spread, valor do tick e o número mágico configurado.
Comandos manuais (ex.: PressBuy() ou PressSell()) enviam ordens de mercado com o volume do assistente atual. Níveis opcionais de stop-loss e take-profit são traduzidos de distâncias em pip e armazenados internamente na estratégia.
Os níveis protetores são emulados em dados de velas: se o preço tocar o stop ou alvo armazenado, a estratégia emite saídas de mercado.
Uma regra opcional de mover para break-even (UseTrailingStop) é armada após o preço avançar BreakEvenTriggerPips; uma vez armada, liquida a posição se o preço recuar para o preço de entrada mais BreakEvenOffsetPips.
Funcionalidades
Replica os botões 1–8 do assistente MQL via métodos públicos:
PressBuy() / PressSell() – abrir ordens de mercado com níveis protetores opcionais.
PressCloseAll() – zerar toda a exposição.
IncreaseVolume() / DecreaseVolume() – ajustar o volume do assistente em 0,01 lotes.
CloseLongPositions() / CloseShortPositions() – fechar apenas um lado.
CloseProfitablePositions() – fechar a posição quando o PnL flutuante é positivo.
Registra uma legenda de ação detalhada no início quando DisplayLegend está habilitado.
Converte distâncias de risco baseadas em pip em preços absolutos usando o passo de preço e a precisão decimal do instrumento.
Suporta trailing de break-even para posições compradas e vendidas, imitando a rotina original MOVETOBREAKEVEN().
Mantém níveis stop/take armazenados independentes para operações compradas e vendidas para que ao mudar de direção os níveis obsoletos sejam descartados automaticamente.
Parâmetros
Parâmetro
Descrição
MagicNumber
Identificador informacional copiado do input MagicNo do MQL.
DisplayLegend
Habilitar para imprimir a legenda de controle e mensagens de status por vela.
OrderVolume
Volume base da ordem de mercado (lotes) reutilizado por todas as ações manuais.
Slippage
Slippage máximo tolerado (em ticks), armazenado como referência.
TakeProfitPips
Distância de pip para o nível de take-profit armazenado (0 o desabilita).
StopLossPips
Distância de pip para o nível de stop-loss armazenado (0 o desabilita).
UseTrailingStop
Habilitar ou desabilitar a lógica de trailing de break-even.
BreakEvenTriggerPips
Distância de lucro (em pips) necessária antes de o stop de break-even ser armado.
BreakEvenOffsetPips
Offset (em pips) adicionado ao preço de entrada quando o stop é armado.
CandleType
Série de velas usada para monitoramento e emulação de níveis.
Dicas de uso
Chamar os métodos auxiliares a partir de ações do Designer, scripts ou controles de UI para imitar pressionamentos de teclas do painel original do MetaTrader.
Os níveis protetores e distâncias de break-even dependem do instrumento fornecer PriceStep, StepPrice e Decimals. Para instrumentos exóticos sem esses metadados, ajustar as distâncias de pip manualmente ou desabilitar as funcionalidades definindo-as como 0.
Como os níveis stop/take são reproduzidos usando máximas e mínimas de velas, picos intra-barra muito rápidos podem não ser capturados a menos que o período da vela seja pequeno. Reduzir o período se uma granularidade maior for necessária.
CloseProfitablePositions() replica o comportamento do "botão 8": verifica o PnL flutuante e fecha toda a posição apenas quando o valor é estritamente positivo.
Diferenças em relação à versão MetaTrader
As etiquetas do gráfico são substituídas por entradas de log porque o StockSharp não expõe as mesmas primitivas de desenho dentro das estratégias.
As ordens de stop-loss e take-profit são simuladas através de saídas de mercado em eventos de velas em vez de ordens pendentes imediatas.
O gerenciamento de break-even é implementado com ordens de mercado do StockSharp; não modifica ordens protetoras existentes.
O slippage é mantido como parâmetro informacional; a execução real é tratada pelo conector 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()