HaramiStrategy convierte el asesor experto "Harami" de MetaTrader en la API de alto nivel de StockSharp. La estrategia combina un patrón Harami alcista/bajista detectado en un marco temporal superior con expansión de momentum y un filtro MACD de largo plazo. Solo se procesan velas completadas y toda la gestión de operaciones se realiza a través del motor de protección integrado de StockSharp.
Datos e indicadores
Marco temporal base: configurable (velas de 15 minutos por defecto) para la detección de tendencia mediante medias móviles.
Marco temporal superior: configurable (por defecto una hora) para el reconocimiento de patrones y la confirmación de momentum.
Marco temporal MACD: configurable (por defecto velas de 30 días) para emular el filtro MACD mensual original.
Indicadores:
Media Móvil Ponderada Linealmente (FastMaLength) en el marco temporal base.
Media Móvil Exponencial (SlowMaLength) en el marco temporal base.
Momentum (MomentumPeriod) en el marco temporal superior. La estrategia utiliza la distancia absoluta desde el valor neutral (100) para las últimas tres barras del marco temporal superior.
Convergencia/Divergencia de Medias Móviles (12/26/9) en el marco temporal MACD.
Configuración larga
La EMA lenta está por encima de la LWMA rápida en el marco temporal base, señalando una tendencia alcista.
El marco temporal superior forma una secuencia Harami alcista: hace dos velas fue bajista, la vela anterior fue alcista y su cuerpo es más pequeño que el cuerpo bajista anterior.
Cualquiera de las últimas tres desviaciones de momentum del marco temporal superior supera MomentumBuyThreshold.
La línea principal MACD está por encima de la línea de señal en el marco temporal MACD.
No hay posición larga abierta (Position <= 0).
La estrategia envía una orden de compra a mercado dimensionada para revertir cualquier exposición corta y añadir Volume lotes.
Configuración corta
La EMA lenta está por debajo de la LWMA rápida en el marco temporal base.
El marco temporal superior forma un Harami bajista: hace dos velas fue alcista, la vela anterior fue bajista y el último cuerpo es más pequeño.
Cualquiera de las últimas tres desviaciones de momentum del marco temporal superior supera MomentumSellThreshold.
La línea principal MACD está por debajo de la línea de señal.
No hay exposición corta abierta (Position >= 0).
La estrategia envía una orden de venta a mercado suficientemente grande para cerrar posiciones largas y abrir una nueva posición corta de tamaño Volume.
Gestión de riesgo
StartProtection instala niveles de stop-loss y take-profit (expresados en puntos). Las características adicionales de trailing, break-even y gestión monetaria del EA original se omiten intencionalmente para mantener la versión StockSharp concisa. Los cambios de dirección de operación aplanan automáticamente la exposición opuesta.
Parámetros
Nombre
Descripción
Valor predeterminado
CandleType
Marco temporal primario para medias móviles y ejecución de señales.
Velas de 15 minutos
HigherCandleType
Marco temporal utilizado para la confirmación de Harami y momentum.
Velas de 1 hora
MacdCandleType
Marco temporal para el filtro de tendencia MACD.
Velas de 30 días
FastMaLength
Longitud de la MA ponderada linealmente rápida.
6
SlowMaLength
Longitud de la MA exponencial lenta.
85
MomentumPeriod
Período de retroceso del Momentum en el marco temporal superior.
14
MomentumBuyThreshold
Desviación mínima de momentum para confirmación larga.
0.3
MomentumSellThreshold
Desviación mínima de momentum para confirmación corta.
0.3
StopLossPoints
Distancia de stop-loss en puntos.
40
TakeProfitPoints
Distancia de take-profit en puntos.
100
Consejos de uso
Alinear CandleType, HigherCandleType y MacdCandleType con los datos históricos disponibles; asegurarse de que el marco temporal superior sea más largo que el marco temporal base.
Ajustar los umbrales de momentum para que coincidan con la volatilidad del instrumento negociado.
Usar el optimizador de StockSharp a través de los rangos de parámetros proporcionados para ajustar las longitudes de MA y los umbrales de momentum.
Siempre realizar backtesting con configuraciones realistas de comisión/latencia antes de implementar en vivo.
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 HaramiStrategy : 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 HaramiStrategy()
{
_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 harami_strategy(Strategy):
def __init__(self):
super(harami_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(harami_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(harami_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 harami_strategy()