Esta estratégia é uma conversão direta do expert advisor MetaTrader "HTH Trader". Opera uma cesta forex de quatro pernas e tenta capturar a reversão à média diária entre EURUSD e uma cesta espelhada de USDCHF, GBPUSD e AUDUSD. O port para StockSharp mantém os controles de risco originais e as regras de tempo, usando a API de alto nível para negociação multi-ativo.
Características principais:
Abre uma cesta hedgeada uma vez por dia entre 00:05 e 00:12 horário do terminal.
Usa os dois fechamentos diários anteriores do EURUSD para decidir a direção da cesta.
Gerencia quatro instrumentos simultaneamente: EURUSD (ativo principal), USDCHF, GBPUSD e AUDUSD.
Rastreia o lucro aberto em pips e suporta alvos de lucro e perda para toda a cesta.
Inclui uma função de duplicação de emergência que adiciona às pernas lucrativas quando o drawdown da cesta ultrapassa um limiar.
Fecha todas as operações às 23:00 horário do terminal ou quando a cesta atinge os limites configurados de lucro/perda.
Requisitos de dados
Candles intradiários: Todos os quatro símbolos devem entregar candles intradiários para o período configurado em IntradayCandleType (padrão 5 minutos). Esses candles fornecem o preço mais recente e o relógio de sessão.
Candles diários: Cada símbolo deve fornecer candles diários para que a estratégia possa monitorar os dois últimos fechamentos diários completos.
Lógica de negociação
Ao final de cada candle intradiário finalizado, a estratégia verifica o lucro aberto atual:
Se AllowEmergencyTrading estiver habilitado e o lucro aberto total ≤ -EmergencyLossPips, a estratégia duplica cada perna que estiver atualmente lucrativa e desabilita mais operações de emergência para aquele dia.
Se UseProfitTarget estiver habilitado e o lucro aberto total ≥ ProfitTargetPips, a cesta é fechada imediatamente.
Se UseLossLimit estiver habilitado e o lucro aberto total ≤ -LossLimitPips, a cesta é fechada imediatamente.
Quando o relógio chega às 23:00, a cesta é fechada independentemente do lucro.
Quando não há posições abertas e o relógio está dentro da janela 00:05–00:12, a estratégia verifica os dois últimos fechamentos diários completos do símbolo primário (EURUSD por padrão):
Se a variação percentual dia a dia for positiva, a estratégia abre: comprado EURUSD, comprado USDCHF, vendido GBPUSD, comprado AUDUSD.
Se a variação for negativa, abre: vendido EURUSD, vendido USDCHF, comprado GBPUSD, vendido AUDUSD.
Se a variação for zero ou qualquer fechamento diário estiver faltando, a estratégia pula a negociação para aquele dia.
Todas as posições são fechadas usando ordens a mercado via ClosePosition.
Parâmetros
Nome
Descrição
Padrão
TradeEnabled
Habilita ou desabilita a colocação de ordens.
true
ShowProfitInfo
Registra o lucro da cesta em pips a cada atualização enquanto há posições abertas.
true
UseProfitTarget
Habilita o fechamento automático quando ProfitTargetPips é atingido.
false
UseLossLimit
Habilita o fechamento automático quando LossLimitPips é atingido.
false
AllowEmergencyTrading
Permite a função de duplicação de emergência.
true
EmergencyLossPips
Drawdown da cesta (em pips) que aciona a duplicação de emergência.
60
ProfitTargetPips
Lucro da cesta (em pips) que aciona o fechamento quando UseProfitTarget está habilitado.
80
LossLimitPips
Perda da cesta (em pips) que aciona o fechamento quando UseLossLimit está habilitado.
40
TradingVolume
Volume da ordem para cada perna.
0.01
Symbol2
Segundo ativo (USDCHF por padrão).
null
Symbol3
Terceiro ativo (GBPUSD por padrão).
null
Symbol4
Quarto ativo (AUDUSD por padrão).
null
IntradayCandleType
Período intradiário usado para agendamento e atualizações de preço.
Candles de 5 minutos
Notas de uso
Atribua o ativo principal (Strategy.Security) ao EURUSD (ou ao par líder desejado) e mapeie Symbol2, Symbol3, Symbol4 para os instrumentos correlacionados antes de iniciar.
Certifique-se de que cada ativo tenha um PriceStep válido; caso contrário, os cálculos de lucro em pips não podem ser realizados e a lógica de emergência permanecerá inativa.
A função de duplicação de emergência só adiciona às pernas que estão atualmente lucrativas; pernas perdedoras são deixadas intactas para evitar amplificar o drawdown.
A implementação assume que ordens a mercado são executadas próximo ao último fechamento de candle. Para contabilização precisa, conecte a estratégia a um feed de dados que entregue candles intradiários oportunos.
Como a lógica é impulsionada por uma única barra por minuto (ou período escolhido), o comportamento tick a tick original do MQL pode diferir ligeiramente no tempo de execução, mas o sequenciamento e as condições de operação correspondem ao expert advisor de referência.
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>
/// Hedge strategy simplified from HTH Trader. Trades based on daily close deviation.
/// </summary>
public class HthTraderStrategy : Strategy
{
private readonly StrategyParam<bool> _tradeEnabled;
private readonly StrategyParam<bool> _useProfitTarget;
private readonly StrategyParam<bool> _useLossLimit;
private readonly StrategyParam<int> _profitTargetPips;
private readonly StrategyParam<int> _lossLimitPips;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose1;
private decimal _prevClose2;
private decimal _entryPrice;
private decimal _priceStep;
/// <summary>
/// Enable automated trading.
/// </summary>
public bool TradeEnabled
{
get => _tradeEnabled.Value;
set => _tradeEnabled.Value = value;
}
/// <summary>
/// Enable closing by reaching the profit target.
/// </summary>
public bool UseProfitTarget
{
get => _useProfitTarget.Value;
set => _useProfitTarget.Value = value;
}
/// <summary>
/// Enable closing by reaching the loss limit.
/// </summary>
public bool UseLossLimit
{
get => _useLossLimit.Value;
set => _useLossLimit.Value = value;
}
/// <summary>
/// Profit target in pips.
/// </summary>
public int ProfitTargetPips
{
get => _profitTargetPips.Value;
set => _profitTargetPips.Value = value;
}
/// <summary>
/// Loss limit in pips.
/// </summary>
public int LossLimitPips
{
get => _lossLimitPips.Value;
set => _lossLimitPips.Value = value;
}
/// <summary>
/// Candle type for monitoring.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public HthTraderStrategy()
{
_tradeEnabled = Param(nameof(TradeEnabled), true)
.SetDisplay("Trade Enabled", "Allow the strategy to submit orders", "General");
_useProfitTarget = Param(nameof(UseProfitTarget), true)
.SetDisplay("Use Profit Target", "Close when profit target is reached", "Risk");
_useLossLimit = Param(nameof(UseLossLimit), true)
.SetDisplay("Use Loss Limit", "Close when loss limit is reached", "Risk");
_profitTargetPips = Param(nameof(ProfitTargetPips), 80)
.SetDisplay("Profit Target (pips)", "Profit target in pips", "Risk");
_lossLimitPips = Param(nameof(LossLimitPips), 40)
.SetDisplay("Loss Limit (pips)", "Loss limit in pips", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for monitoring", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose1 = 0m;
_prevClose2 = 0m;
_entryPrice = 0m;
_priceStep = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_priceStep = Security?.PriceStep ?? 0.0001m;
if (_priceStep <= 0m)
_priceStep = 0.0001m;
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!TradeEnabled)
return;
// Check exit conditions
if (Position != 0 && _entryPrice > 0m)
{
var priceDiff = Position > 0
? candle.ClosePrice - _entryPrice
: _entryPrice - candle.ClosePrice;
var pipsDiff = priceDiff / _priceStep;
if (UseProfitTarget && pipsDiff >= ProfitTargetPips)
{
if (Position > 0) SellMarket();
else BuyMarket();
_entryPrice = 0m;
return;
}
if (UseLossLimit && pipsDiff <= -LossLimitPips)
{
if (Position > 0) SellMarket();
else BuyMarket();
_entryPrice = 0m;
return;
}
}
// Entry logic based on daily close deviation
if (Position == 0 && _prevClose1 > 0m && _prevClose2 > 0m)
{
var deviation = (100m * _prevClose1 / _prevClose2) - 100m;
if (deviation > 0.1m)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (deviation < -0.1m)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
_prevClose2 = _prevClose1;
_prevClose1 = candle.ClosePrice;
}
}
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 hth_trader_strategy(Strategy):
def __init__(self):
super(hth_trader_strategy, self).__init__()
self._trade_enabled = self.Param("TradeEnabled", True)
self._use_profit_target = self.Param("UseProfitTarget", True)
self._use_loss_limit = self.Param("UseLossLimit", True)
self._profit_target_pips = self.Param("ProfitTargetPips", 80)
self._loss_limit_pips = self.Param("LossLimitPips", 40)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._entry_price = 0.0
self._price_step = 0.0
@property
def TradeEnabled(self):
return self._trade_enabled.Value
@TradeEnabled.setter
def TradeEnabled(self, value):
self._trade_enabled.Value = value
@property
def UseProfitTarget(self):
return self._use_profit_target.Value
@UseProfitTarget.setter
def UseProfitTarget(self, value):
self._use_profit_target.Value = value
@property
def UseLossLimit(self):
return self._use_loss_limit.Value
@UseLossLimit.setter
def UseLossLimit(self, value):
self._use_loss_limit.Value = value
@property
def ProfitTargetPips(self):
return self._profit_target_pips.Value
@ProfitTargetPips.setter
def ProfitTargetPips(self, value):
self._profit_target_pips.Value = value
@property
def LossLimitPips(self):
return self._loss_limit_pips.Value
@LossLimitPips.setter
def LossLimitPips(self, value):
self._loss_limit_pips.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(hth_trader_strategy, self).OnStarted2(time)
self._price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0001
if self._price_step <= 0.0:
self._price_step = 0.0001
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._entry_price = 0.0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if not self.TradeEnabled:
return
close = float(candle.ClosePrice)
# Check exit conditions
if self.Position != 0 and self._entry_price > 0.0:
if self.Position > 0:
price_diff = close - self._entry_price
else:
price_diff = self._entry_price - close
pips_diff = price_diff / self._price_step
if self.UseProfitTarget and pips_diff >= int(self.ProfitTargetPips):
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_price = 0.0
return
if self.UseLossLimit and pips_diff <= -int(self.LossLimitPips):
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_price = 0.0
return
# Entry logic
if self.Position == 0 and self._prev_close1 > 0.0 and self._prev_close2 > 0.0:
deviation = (100.0 * self._prev_close1 / self._prev_close2) - 100.0
if deviation > 0.1:
self.BuyMarket()
self._entry_price = close
elif deviation < -0.1:
self.SellMarket()
self._entry_price = close
self._prev_close2 = self._prev_close1
self._prev_close1 = close
def OnReseted(self):
super(hth_trader_strategy, self).OnReseted()
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._entry_price = 0.0
self._price_step = 0.0
def CreateClone(self):
return hth_trader_strategy()