Inicio
/
Ejemplos de estrategias
Ver en GitHub
Estrategia Bruno
El asesor experto Bruno es un sistema de seguimiento de tendencia escrito originalmente para MetaTrader 5. El puerto mantiene la misma cadena de confirmación: Average Directional Index (ADX) con movimiento direccional, un par de medias móviles exponenciales (EMA 8/21), MACD (13, 34, 8), un Oscilador Estocástico (21, 3, 3) y la pendiente de un Parabolic SAR (0.055, 0.21). Cada filtro que coincide con la dirección multiplica el tamaño de la orden por un factor configurable. Si tanto las señales largas como las cortas se amplían en la misma vela, el trading se omite para evitar órdenes en conflicto.
Lógica de trading
Sesgo direccional
La presión larga se fortalece cuando +DI > -DI y +DI > 20.
La presión corta se fortalece cuando +DI < -DI y +DI < 40.
Alineación de momentum
La preferencia larga requiere EMA(8) por encima de EMA(21), Estocástico %K por encima de %D y %K por debajo del umbral de sobrecompra (predeterminado 80).
La preferencia corta requiere EMA(8) por debajo de EMA(21), Estocástico %K por debajo de %D y %K por encima del umbral de sobreventa (predeterminado 20).
Filtro MACD
Sesgo largo: histograma MACD por encima de cero y línea principal MACD por encima de la línea de señal.
Sesgo corto: histograma MACD por debajo de cero y línea principal MACD por debajo de la línea de señal.
Pendiente del Parabolic SAR
El sesgo largo se refuerza cuando los valores anteriores del SAR están subiendo mientras EMA(8) > EMA(21).
El sesgo corto se refuerza cuando los valores anteriores del SAR están bajando mientras EMA(8) < EMA(21).
Cada condición satisfecha multiplica el tamaño del lote base por SignalMultiplier (predeterminado 1.6). Solo un lado puede estar activo a la vez. Cuando se genera una señal final, la estrategia cierra cualquier posición opuesta, envía la orden de mercado con el volumen multiplicado y almacena el cierre actual como precio de entrada.
Gestión de posiciones
Stop-loss / take-profit – distancias fijas expresadas en pips ajustados, coincidiendo con la versión de MetaTrader. Si cualquiera de los niveles es alcanzado intrabarra, la posición se cierra inmediatamente.
Trailing stop – se activa una vez que el beneficio flotante supera TrailingStop + TrailingStep pips. Entonces el stop se coloca detrás del precio por TrailingStop pips y solo avanza cuando la ganancia aumenta al menos TrailingStep pips más.
Gestión de conflictos – si tanto los filtros largos como los cortos se activan en la misma vela, no se toma ninguna nueva operación.
Parámetros
Grupo
Nombre
Descripción
Trading
BaseVolume
Tamaño inicial del lote antes de los multiplicadores.
Trading
SignalMultiplier
Multiplicador de volumen aplicado por cada filtro coincidente.
Gestión del riesgo
StopLossPips / TakeProfitPips
Distancias de protección en pips ajustados. Establezca en cero para deshabilitar.
Gestión del riesgo
TrailingStopPips / TrailingStepPips
Distancia de trailing y paso mínimo en pips ajustados.
Indicadores
AdxPeriod, AdxPositiveThreshold, AdxNegativeThreshold
Longitud del ADX y umbrales del DI.
Indicadores
FastEmaPeriod, SlowEmaPeriod
Longitudes de EMA usadas en la confirmación de tendencia.
Indicadores
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod
Configuración del MACD.
Indicadores
StochasticPeriod, StochasticKsmoothing, StochasticDsmoothing, StochasticOverbought, StochasticOversold
Configuración del oscilador estocástico.
General
CandleType
Marco temporal usado para toda la cadena de señales (predeterminado 1 hora).
Notas
El tamaño de pip ajustado sigue la convención de MetaTrader: los instrumentos con 3 o 5 dígitos decimales se multiplican por 10.
El Parabolic SAR opera con paso de aceleración 0.055 y máximo 0.21, reflejando los valores predeterminados del asesor experto.
El puerto mantiene el estilo de gestión del dinero original (apilamiento de volumen) pero agrega la exposición en una única posición de StockSharp.
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;
/// <summary>
/// Bruno trend-following strategy using EMA crossover with trend filter.
/// Buys when fast EMA crosses above slow EMA with price above trend EMA.
/// Sells on reverse conditions.
/// </summary>
public class BrunoStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _trendPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private ExponentialMovingAverage _trend;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Trend EMA period.
/// </summary>
public int TrendPeriod
{
get => _trendPeriod.Value;
set => _trendPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="BrunoStrategy"/> class.
/// </summary>
public BrunoStrategy()
{
_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");
_trendPeriod = Param(nameof(TrendPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("Trend Period", "Trend 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");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_trend = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
_trend = new ExponentialMovingAverage { Length = TrendPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, _trend, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue, decimal trendValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed || !_trend.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// EMA crossover with trend filter
if (_prevFast <= _prevSlow && fastValue > slowValue && close > trendValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && close < trendValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_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 bruno_strategy(Strategy):
def __init__(self):
super(bruno_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._trend_period = self.Param("TrendPeriod", 200) \
.SetDisplay("Trend Period", "Trend EMA 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._trend = 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 trend_period(self):
return self._trend_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(bruno_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._trend = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(bruno_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
self._trend = ExponentialMovingAverage()
self._trend.Length = self.trend_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._trend, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value, trend_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
trend_val = float(trend_value)
if not self._fast.IsFormed or not self._slow.IsFormed or not self._trend.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 = 80
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 = 80
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 = 80
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 = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and close > trend_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and close < trend_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return bruno_strategy()