Esta estrategia replica al experto CompassLine al fusionar dos filtros complementarios:
Seguir línea: una ruta de ruptura de Bollinger bandas desplazada opcionalmente en ATR. Cuando el precio cierra fuera de las bandas, la trayectoria se extiende en la dirección de ruptura y nunca retrocede mientras persista la tendencia.
Brújula: una transformación logística del precio medio en relación con el máximo más alto y el mínimo más bajo durante la ventana de promedio móvil. La señal bruta se suaviza dos veces (promedio triangular) para producir un estado alcista/bajista estable.
Una posición se abre sólo cuando ambos filtros coinciden en la tendencia. El filtrado de tiempo opcional y las paradas de protección reflejan la lógica MQL.
Detalles
Criterios de entrada:
La línea de seguimiento debe apuntar hacia arriba (cierre reciente por encima de la banda superior) para posiciones largas o hacia abajo (cierre reciente por debajo de la banda inferior) para posiciones cortas. El desplazamiento ATR se puede alternar con UseAtrFilter.
El estado de la brújula (basado en CompassPeriod) debe ser positivo para posiciones largas o negativo para posiciones cortas después de la fase de doble suavizado.
La negociación se ejecuta solo cuando el filtro de sesión opcional (UseTimeFilter con Session en HHmm-HHmm) lo permite.
Largo/Corto: Se admiten ambas direcciones.
Criterios de salida:
CloseMode = None mantiene la posición hasta que se produce una entrada opuesta o una parada de protección.
CloseMode = BothIndicators se cierra cuando tanto Seguir línea como Brújula invierten la dirección simultáneamente.
CloseMode = FollowLineOnly sale cuando Seguir línea cambia contra la posición.
CloseMode = CompassOnly sale cuando Compass cambia de polaridad.
Paradas: Las distancias TakeProfit y StopLoss (en pasos de seguridad) se aplican después de cada ingreso cuando sean mayores a cero.
Valores predeterminados:
FollowBbPeriod = 21
FollowBbDeviation = 1
FollowAtrPeriod = 5
UseAtrFilter = falso
CompassPeriod = 30 (longitud de suavizado = redondo(CompassPeriod / 3))
CloseMode = Ninguno
UseTimeFilter = falso
Session = "0000-2400"
TakeProfit = 0
StopLoss = 0
CandleType = Intervalo de tiempo.DesdeMinutos(15)
Filtros:
Categoría: Tendencia
Dirección: Ambos
Indicadores: Bollinger Bandas, ATR, Media móvil triangular
Paradas: Opcionales
Complejidad: Intermedia
Plazo: Intradiario
Estacionalidad: No
Redes neuronales: No
Divergencia: No
Nivel de riesgo: medio
Notas adicionales
El suavizado de Compass utiliza una ventana triangular igual a round(CompassPeriod / 3), que coincide estrechamente con la implementación del indicador original.
Las cadenas de sesión como 0930-1600 restringen las operaciones a la ventana especificada y al mismo tiempo actualizan los estados de los indicadores fuera de la sesión.
Las órdenes de protección reutilizan los ayudantes de alto nivel de StockSharp para que la lógica sea compatible con los módulos de gestión de riesgos de cartera.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Compass Line strategy: BB + RSI trend filter.
/// Buys when close < lower BB and RSI < 45.
/// Sells when close > upper BB and RSI > 55.
/// </summary>
public class CompassLineStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<int> _rsiPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BbPeriod
{
get => _bbPeriod.Value;
set => _bbPeriod.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public CompassLineStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_bbPeriod = Param(nameof(BbPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bb = new BollingerBands { Length = BbPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
decimal? prevClose = null;
decimal? prevLower = null;
decimal? prevUpper = null;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bb, rsi, (candle, bbVal, rsiVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bbv = (BollingerBandsValue)bbVal;
if (bbv.UpBand is not decimal upper || bbv.LowBand is not decimal lower)
return;
if (rsiVal.IsEmpty)
return;
var rsiDec = rsiVal.GetValue<decimal>();
var close = candle.ClosePrice;
if (prevClose.HasValue && prevLower.HasValue && prevUpper.HasValue)
{
var crossBelowLower = prevClose.Value > prevLower.Value && close <= lower;
var crossAboveUpper = prevClose.Value < prevUpper.Value && close >= upper;
if (crossBelowLower && rsiDec < 45m && Position <= 0)
BuyMarket();
else if (crossAboveUpper && rsiDec > 55m && Position >= 0)
SellMarket();
}
prevClose = close;
prevLower = lower;
prevUpper = upper;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bb);
DrawOwnTrades(area);
}
}
}
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 BollingerBands, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class compass_line_strategy(Strategy):
def __init__(self):
super(compass_line_strategy, self).__init__()
self._bb_period = self.Param("BbPeriod", 20) \
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._bb = None
self._rsi = None
self._prev_close = None
self._prev_lower = None
self._prev_upper = None
@property
def bb_period(self):
return self._bb_period.Value
@property
def rsi_period(self):
return self._rsi_period.Value
def OnReseted(self):
super(compass_line_strategy, self).OnReseted()
self._bb = None
self._rsi = None
self._prev_close = None
self._prev_lower = None
self._prev_upper = None
def OnStarted2(self, time):
super(compass_line_strategy, self).OnStarted2(time)
self._bb = BollingerBands()
self._bb.Length = self.bb_period
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.BindEx(self._bb, self._rsi, self._process_candle)
subscription.Start()
def _process_candle(self, candle, bb_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not self._bb.IsFormed or not self._rsi.IsFormed:
return
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
rsi = float(rsi_value)
close = float(candle.ClosePrice)
if self._prev_close is not None and self._prev_lower is not None and self._prev_upper is not None:
cross_below_lower = self._prev_close > self._prev_lower and close <= lower
cross_above_upper = self._prev_close < self._prev_upper and close >= upper
if cross_below_lower and rsi < 45.0 and self.Position <= 0:
self.BuyMarket()
elif cross_above_upper and rsi > 55.0 and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_lower = lower
self._prev_upper = upper
def CreateClone(self):
return compass_line_strategy()