A estratégia de Força de Moedas v1.1 replica o consultor especialista do MetaTrader Currency Strength v1.1. Ela mede a força relativa das oito principais moedas (USD, EUR, JPY, CAD, AUD, NZD, GBP, CHF) usando mudanças percentuais diárias de 26 pares FX líquidos. Sempre que a força de duas moedas diverge além de um limiar configurável, a estratégia abre uma posição no par de moedas correspondente na direção da moeda mais forte.
Mercado e dados
Universo de instrumentos: 26 pares FX principais e cruzados (USDJPY, USDCAD, AUDUSD, USDCHF, GBPUSD, EURUSD, NZDUSD, EURJPY, EURCAD, EURGBP, EURCHF, EURAUD, EURNZD, AUDNZD, AUDCAD, AUDCHF, AUDJPY, CHFJPY, GBPCHF, GBPAUD, GBPCAD, GBPJPY, CADJPY, NZDJPY, GBPNZD, CADCHF).
Frequência de dados: Velas diárias (D1). Apenas velas concluídas são processadas para manter cálculos consistentes.
Campos necessários: Preços de abertura, máxima, mínima e fechamento de cada vela.
Cálculo da força das moedas
A mudança percentual diária para cada par é calculada como:
(change) = (Close − Open) / Open × 100
Essas mudanças específicas de par são então combinadas em índices de força de moedas:
Força EUR = média de EURJPY, EURCAD, EURGBP, EURCHF, EURAUD, EURUSD, EURNZD
Força USD = média de USDJPY, USDCAD, –AUDUSD, USDCHF, –GBPUSD, –EURUSD, –NZDUSD
Força JPY = média negativa de USDJPY, EURJPY, AUDJPY, CHFJPY, GBPJPY, CADJPY, NZDJPY
Força CAD = média de CADCHF, CADJPY, –GBPCAD, –AUDCAD, –EURCAD, –USDCAD
Força AUD = média de AUDUSD, AUDNZD, AUDCAD, AUDCHF, AUDJPY, –EURAUD, –GBPAUD
Força NZD = média de NZDUSD, NZDJPY, –EURNZD, –AUDNZD, –GBPNZD
Força GBP = média de GBPUSD, –EURGBP, GBPCHF, GBPAUD, GBPCAD, GBPJPY, GBPNZD
Força CHF = média de CHFJPY, –USDCHF, –EURCHF, –AUDCHF, –GBPCHF, –CADCHF
Cada média usa o mesmo número de componentes que no consultor especialista original para preservar o esquema de ponderação.
Lógica de trading
Após todos os 26 pares reportarem uma nova vela diária finalizada, as forças são recalculadas.
Para cada par a estratégia compara as duas forças de moedas relevantes. Se a diferença absoluta exceder o parâmetro DifferenceThreshold, um sinal de operação é gerado.
A direção do sinal segue a moeda mais forte:
Se força da moeda base > força da moeda cotada → comprar o par.
Se força da moeda base < força da moeda cotada → vender o par.
As operações só são permitidas quando a vela diária do par concorda com o sinal (fechamento acima da abertura para compras, fechamento abaixo para vendas), refletindo o filtro de tendência do EA original.
As posições líquidas existentes são respeitadas. Se um sinal de reversão aparece enquanto uma posição contrária está aberta, a estratégia fecha a posição atual e inverte para a nova direção com uma única ordem a mercado.
Quando TradeOncePerDay está habilitado, cada par pode entrar comprado no máximo uma vez por dia de trading e entrar vendido no máximo uma vez por dia de trading.
Gestão de risco e saídas
O indicador opcional UseSlTp habilita a lógica de stop-loss e take-profit executada na vela diária de cada par. As distâncias são definidas em pips (StopLossPips, TakeProfitPips).
A lógica protetora avalia a máxima/mínima diária da vela mais recente. Se esses extremos atingirem os alvos respectivos, a posição é fechada ao preço de mercado na próxima etapa de avaliação.
Sem SL/TP, as posições permanecem abertas até que um sinal oposto force uma reversão ou a estratégia seja parada manualmente, refletindo o comportamento do EA fonte.
Parâmetros da estratégia
Parâmetro
Descrição
CandleType
Período para velas (padrão: diário).
DifferenceThreshold
Diferença mínima de força (em pontos percentuais) necessária para acionar uma operação.
TradeOncePerDay
Se true, limita cada par a uma entrada comprada e uma vendida por dia.
UseSlTp
Habilita a avaliação diária dos níveis de stop-loss e take-profit.
TakeProfitPips
Distância do take-profit medida em pips.
StopLossPips
Distância do stop-loss medida em pips.
Parâmetros de pares
Entradas individuais Security para os 26 pares FX. Cada um deve ser atribuído antes de iniciar a estratégia.
Volume
Propriedade da classe base que define o tamanho da operação (padrão 0.01 lotes).
Notas de implementação
A estratégia se inscreve em cada par separadamente usando a API de subscrição de velas de alto nível (SubscribeCandles).
O tratamento de velas ignora estritamente velas incompletas, satisfazendo as diretrizes de conversão do StockSharp.
Os cálculos de força e a geração de sinais só funcionam quando todos os pares reportam a mesma data de trading, garantindo cestas de moedas sincronizadas.
Dicionários internos rastreiam as últimas datas de operação por direção e armazenam informações de entrada para saídas protetoras.
Dicas de uso
Atribuir todos os 26 instrumentos antes de iniciar a estratégia; entradas ausentes lançam uma exceção para evitar cálculos parciais.
Garantir que o provedor de dados forneça velas diárias para cada par configurado para que as forças das moedas permaneçam sincronizadas.
Ajustar DifferenceThreshold para controlar a frequência de sinais. Limiares menores levam a operações mais frequentes, mas também mais reversões.
Calibrar os stops baseados em pip para a precisão de cotação do seu corretor; o padrão assume preços de pip fracionário.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that trades based on percentage change momentum of candles.
/// Simplified from the original multi-pair currency strength approach to single-security.
/// </summary>
public class CurrencyStrengthV11Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _differenceThreshold;
private decimal? _prevChange;
private decimal? _prevMomentum;
private decimal _entryPrice;
private DateTimeOffset? _lastTradeTime;
/// <summary>
/// Candle type for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Minimum percentage change to trigger trades.
/// </summary>
public decimal DifferenceThreshold
{
get => _differenceThreshold.Value;
set => _differenceThreshold.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public CurrencyStrengthV11Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strength calculation", "General");
_differenceThreshold = Param(nameof(DifferenceThreshold), 0.2m)
.SetDisplay("Threshold", "Minimum percentage change to trigger trade", "Parameters")
.SetOptimize(0.05m, 1m, 0.05m);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevChange = null;
_prevMomentum = null;
_entryPrice = 0m;
_lastTradeTime = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormed)
return;
var change = candle.OpenPrice != 0m
? (candle.ClosePrice - candle.OpenPrice) / candle.OpenPrice * 100m
: 0m;
if (_prevChange == null)
{
_prevChange = change;
return;
}
var momentum = change - _prevChange.Value;
var cooldownPassed = _lastTradeTime is null || candle.CloseTime - _lastTradeTime >= TimeSpan.FromHours(24);
var longSignal = _prevMomentum is decimal prevMomentum && prevMomentum <= DifferenceThreshold && momentum > DifferenceThreshold;
var shortSignal = _prevMomentum is decimal prevMomentum2 && prevMomentum2 >= -DifferenceThreshold && momentum < -DifferenceThreshold;
if (cooldownPassed && longSignal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume > 0m ? Volume : 1m);
_entryPrice = candle.ClosePrice;
_lastTradeTime = candle.CloseTime;
}
else if (cooldownPassed && shortSignal && Position >= 0)
{
if (Position > 0)
SellMarket(Position);
SellMarket(Volume > 0m ? Volume : 1m);
_entryPrice = candle.ClosePrice;
_lastTradeTime = candle.CloseTime;
}
_prevChange = change;
_prevMomentum = momentum;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class currency_strength_v11_strategy(Strategy):
def __init__(self):
super(currency_strength_v11_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._difference_threshold = self.Param("DifferenceThreshold", 0.2)
self._prev_change = None
self._prev_momentum = None
self._entry_price = 0.0
self._last_trade_time = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def DifferenceThreshold(self):
return self._difference_threshold.Value
@DifferenceThreshold.setter
def DifferenceThreshold(self, value):
self._difference_threshold.Value = value
def OnStarted2(self, time):
super(currency_strength_v11_strategy, self).OnStarted2(time)
self._prev_change = None
self._prev_momentum = None
self._entry_price = 0.0
self._last_trade_time = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
open_price = float(candle.OpenPrice)
close = float(candle.ClosePrice)
change = (close - open_price) / open_price * 100.0 if open_price != 0.0 else 0.0
if self._prev_change is None:
self._prev_change = change
return
momentum = change - self._prev_change
threshold = float(self.DifferenceThreshold)
long_signal = (self._prev_momentum is not None and
self._prev_momentum <= threshold and
momentum > threshold)
short_signal = (self._prev_momentum is not None and
self._prev_momentum >= -threshold and
momentum < -threshold)
cooldown_passed = (self._last_trade_time is None or
candle.CloseTime - self._last_trade_time >= TimeSpan.FromHours(24))
if cooldown_passed and long_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(abs(self.Position))
vol = float(self.Volume) if float(self.Volume) > 0 else 1.0
self.BuyMarket(vol)
self._entry_price = float(candle.ClosePrice)
self._last_trade_time = candle.CloseTime
elif cooldown_passed and short_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket(self.Position)
vol = float(self.Volume) if float(self.Volume) > 0 else 1.0
self.SellMarket(vol)
self._entry_price = float(candle.ClosePrice)
self._last_trade_time = candle.CloseTime
self._prev_change = change
self._prev_momentum = momentum
def OnReseted(self):
super(currency_strength_v11_strategy, self).OnReseted()
self._prev_change = None
self._prev_momentum = None
self._entry_price = 0.0
self._last_trade_time = None
def CreateClone(self):
return currency_strength_v11_strategy()