Esta estrategia es una conversión directa del asesor experto MetaTrader "HTH Trader". Opera una cesta forex de cuatro tramos e intenta capturar la reversión a la media diaria entre EURUSD y una cesta espejo de USDCHF, GBPUSD y AUDUSD. El puerto de StockSharp mantiene los controles de riesgo originales y las reglas de tiempo, usando la API de alto nivel para el trading multi-activo.
Características clave:
Abre una cesta cubierta una vez al día entre las 00:05 y las 00:12 hora terminal.
Usa los dos cierres diarios anteriores de EURUSD para decidir la dirección de la cesta.
Gestiona cuatro instrumentos simultáneamente: EURUSD (seguridad primaria), USDCHF, GBPUSD y AUDUSD.
Rastrea la ganancia abierta en pips y soporta objetivos de ganancia y pérdida para toda la cesta.
Incluye una función de duplicación de emergencia que agrega a las patas rentables cuando el drawdown de la cesta supera un umbral.
Cierra todas las operaciones a las 23:00 hora terminal o cuando la cesta alcanza los límites configurados de ganancia/pérdida.
Requisitos de datos
Velas intradía: Los cuatro símbolos deben entregar velas intradía para el marco temporal configurado en IntradayCandleType (por defecto 5 minutos). Estas velas proporcionan el precio más reciente y el reloj de sesión.
Velas diarias: Cada símbolo debe proporcionar velas diarias para que la estrategia pueda monitorear los últimos dos cierres diarios completados.
Lógica de trading
Al final de cada vela intradía finalizada, la estrategia verifica la ganancia abierta actual:
Si AllowEmergencyTrading está habilitado y la ganancia abierta total ≤ -EmergencyLossPips, la estrategia duplica cada pata que esté actualmente en ganancia y deshabilita más operaciones de emergencia para ese día.
Si UseProfitTarget está habilitado y la ganancia abierta total ≥ ProfitTargetPips, la cesta se cierra inmediatamente.
Si UseLossLimit está habilitado y la ganancia abierta total ≤ -LossLimitPips, la cesta se cierra inmediatamente.
Una vez que el reloj alcanza las 23:00, la cesta se cierra independientemente de la ganancia.
Cuando no hay posiciones abiertas y el reloj está dentro de la ventana 00:05–00:12, la estrategia verifica los últimos dos cierres diarios completados del símbolo primario (EURUSD por defecto):
Si el cambio porcentual día a día es positivo, la estrategia abre: largo EURUSD, largo USDCHF, corto GBPUSD, largo AUDUSD.
Si el cambio es negativo, abre: corto EURUSD, corto USDCHF, largo GBPUSD, corto AUDUSD.
Si el cambio es cero o falta algún cierre diario, la estrategia omite el trading por ese día.
Todas las posiciones se cierran usando órdenes de mercado a través de ClosePosition.
Parámetros
Nombre
Descripción
Predeterminado
TradeEnabled
Habilita o deshabilita la colocación de órdenes.
true
ShowProfitInfo
Registra la ganancia de la cesta en pips en cada actualización mientras hay posiciones abiertas.
true
UseProfitTarget
Habilita el cierre automático cuando se alcanza ProfitTargetPips.
false
UseLossLimit
Habilita el cierre automático cuando se alcanza LossLimitPips.
false
AllowEmergencyTrading
Permite la función de duplicación de emergencia.
true
EmergencyLossPips
Drawdown de la cesta (en pips) que activa la duplicación de emergencia.
60
ProfitTargetPips
Ganancia de la cesta (en pips) que activa el cierre cuando UseProfitTarget está habilitado.
80
LossLimitPips
Pérdida de la cesta (en pips) que activa el cierre cuando UseLossLimit está habilitado.
40
TradingVolume
Volumen de orden para cada pata.
0.01
Symbol2
Segunda seguridad (USDCHF por defecto).
null
Symbol3
Tercera seguridad (GBPUSD por defecto).
null
Symbol4
Cuarta seguridad (AUDUSD por defecto).
null
IntradayCandleType
Marco temporal intradía usado para programación y actualizaciones de precio.
Velas de 5 minutos
Notas de uso
Asigne la seguridad primaria (Strategy.Security) a EURUSD (o el par líder deseado) y mapee Symbol2, Symbol3, Symbol4 a los instrumentos correlacionados antes de iniciar.
Asegúrese de que cada seguridad tenga un PriceStep válido; de lo contrario, los cálculos de ganancia en pips no pueden realizarse y la lógica de emergencia permanecerá inactiva.
La función de duplicación de emergencia solo agrega a las patas que actualmente son rentables; las patas perdedoras se dejan intactas para evitar amplificar el drawdown.
La implementación asume que las órdenes de mercado se ejecutan cerca del último cierre de vela. Para una contabilización precisa, conecte la estrategia a un feed de datos que entregue velas intradía oportunas.
Dado que la lógica es impulsada por una sola barra por minuto (o marco temporal elegido), el comportamiento original tick a tick de MQL puede diferir ligeramente en el tiempo de ejecución, pero la secuencia y condiciones de operación coinciden con el asesor experto de referencia.
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()