Ver no GitHub

Estratégia de Hedge HTH Trader

Visão geral

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

  1. 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.
  2. Quando o relógio chega às 23:00, a cesta é fechada independentemente do lucro.
  3. 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.
  4. 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;
	}
}