Estratégia de MultiTrader Currency Strength (3253)
Visão geral
Esta estratégia é um porte de alto nível StockSharp do painel público "MultiTrader" de MQL (base de código #24786). O Consultor Especialista original era um painel discricionário que exibia a força relativa das oito principais moedas, acionava alertas visuais/sonoros quando uma moeda se tornava extremamente forte ou fraca, e sugeria qual par de Forex negociar. A versão StockSharp automatiza o mesmo fluxo de trabalho analítico e opcionalmente executa trades no par mais forte vs. mais fraco.
A lógica calcula uma posição percentual do fechamento de cada símbolo dentro de sua faixa de candle atual. A média dos cruzamentos relevantes produz uma pontuação de força para AUD, CAD, CHF, EUR, GBP, JPY, NZD e USD. Quando uma moeda sobe acima do limiar de compra configurável e outra cai abaixo do limiar de venda, a estratégia recomenda o par construído a partir dessas moedas. Se o par existir no universo configurado, a estratégia pode colocar automaticamente uma ordem de mercado nessa direção.
Modelo de força de moeda
A pontuação percentual de um símbolo é calculada como:
percent = 100 * (Close - Low) / (High - Low)
A força de cada moeda é derivada de sete cruzamentos, espelhando a implementação MQL. Uma inversão 100 - percent é aplicada quando a moeda aparece como moeda de cotação no par:
A estratégia armazena o último candle completado por par, mantém o percentual mais recente e atualiza as forças das moedas após cada atualização.
Trading e alertas
Quando todas as oito moedas têm dados válidos, a estratégia registra um snapshot (da mais forte para a mais fraca).
Se o valor mais forte é ≥ BuyLevel e o valor mais fraco é ≤ SellLevel, uma sugestão de trading é gerada.
A estratégia tenta encontrar o par direto (moeda forte como base, moeda fraca como cotação). Se não existir, verifica a orientação inversa e finalmente recorre a pares envolvendo USD.
O par detectado e a direção são registrados. Se EnableAutoTrading for true e OrderVolume for positivo, a estratégia emite uma ordem de mercado na direção sugerida. Posições opostas são zeradas automaticamente aumentando o tamanho da ordem.
Os sinais são limitados lembrando o último par sugerido e o lado, evitando alertas duplicados até que o mercado saia da zona de limiar.
Parâmetros
Nome
Descrição
Padrão
Universe
Lista de objetos Security representando os pares de FX (28 principais recomendados).
Obrigatório
CandleType
Especificação de candle para os cálculos (Diário, Semanal, Mensal, etc.).
Candles diários
BuyLevel
Limiar acima do qual uma moeda é tratada como sobrecomprada.
90
SellLevel
Limiar abaixo do qual uma moeda é tratada como sobrevendida.
10
EnableAutoTrading
Habilita ou desabilita a colocação automática de ordens.
false
OrderVolume
Volume para enviar com ordens de mercado quando o auto trading está habilitado.
1
SymbolPrefix
Prefixo opcional usado pelo broker/exchange (ex., m.).
""
SymbolSuffix
Sufixo opcional usado pelo broker/exchange (ex., .FX).
""
Passos de configuração
Configuração do universo. Adicione os 28 principais cruzamentos de Forex ao universo da estratégia. Os códigos devem corresponder aos nomes canônicos dos pares (ex., EURUSD). Use SymbolPrefix/SymbolSuffix se seu broker adicionar decorações.
Seleção de período. Escolha o CandleType desejado. Candles diários, semanais e mensais reproduzem os modos originais do painel.
Ajuste de limiar. Ajuste BuyLevel/SellLevel para controlar quão extrema a força precisa ser antes de gerar um sinal.
Auto trading (opcional). Defina EnableAutoTrading como true e defina OrderVolume. Deixe o sinalizador como false para receber apenas logs informativos.
Notas de migração
A camada GUI completa do painel MQL original é intencionalmente omitida. Toda a saída está disponível através do log da estratégia.
Alertas são emitidos como entradas LogInfo; notificações push/email/desktop não foram portadas.
Cálculos automáticos de stop-loss/alvo da versão MQL não são suportados; os traders devem gerenciar o risco usando os módulos de proteção do StockSharp ou controles de risco externos.
O helper de licenciamento baseado em DES incorporado no script MQL foi removido.
Uso recomendado
Implante a estratégia dentro de uma sessão de conector que fornece candles em tempo real e históricos para todos os pares relevantes.
Combine com um widget de gráfico para visualizar o par sugerido e monitorar as séries de candles subjacentes.
Use os parâmetros StartProtection do StockSharp ou estratégias de risco separadas para impor stops/alvos globais.
Considerações de teste
Verifique que sua fonte de dados entrega candles completados para o período selecionado; a estratégia ignora barras não terminadas.
Se alguns pares estiverem faltando no universo, a moeda correspondente não pode ser calculada e nenhum sinal será produzido.
Ao avaliar o desempenho histórico, certifique-se de que o universo permaneça estático durante todo o backtest para evitar lacunas de força.
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 MultiTraderStrategy : 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 MultiTraderStrategy()
{
_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 multi_trader_strategy(Strategy):
def __init__(self):
super(multi_trader_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(multi_trader_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(multi_trader_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 multi_trader_strategy()