Estrategia de canal de precios de veinte pips
Descripción general
La estrategia de canal de precios de veinte pips es una conversión del asesor experto original de MetaTrader 20 pips que combina un canal de precios de estilo Donchian con filtros de media móvil a corto plazo. El algoritmo abre operaciones solo cuando la vela actual se abre opuesta a la anterior, filtra la dirección con promedios móviles calculados sobre precios típicos y gestiona las salidas a través de un objetivo fijo de veinte pips respaldado por un trailing stop dinámico basado en canales.
La versión StockSharp mantiene el espíritu del enfoque original al tiempo que adapta la gestión de pedidos al API de alto nivel. Las órdenes de mercado se utilizan para entradas y salidas, los objetivos de ganancias se monitorean internamente y los niveles de parada se emulan con las condiciones del canal de precios.
Lógica de trading
Pila de indicadores
- Una media móvil simple de un período del precio típico (H+L+C)/3 actúa como una línea de base rápida que refleja el precio típico de la vela anterior.
- Un promedio móvil simple lento configurable (predeterminado 20) calculado sobre los precios de cierre desempeña el papel del filtro
MA_Low del EA.
- Los indicadores más altos y más bajos con el mismo período que el canal de precios (predeterminado 20) emulan los buffers de indicadores personalizados originales.
Condiciones de entrada
- Configuración larga: el precio típico rápido anterior está por encima del promedio móvil lento anterior y la vela actual se abre por debajo de la apertura anterior. Después de una operación perdedora, el volumen se multiplica por el factor de recuperación (predeterminado 2). El precio de entrada se registra para realizar un seguimiento de pérdidas y ganancias.
- Configuración corta: el precio típico rápido anterior está por debajo del promedio móvil lento anterior y la vela actual se abre por encima de la apertura anterior. El escalado de volumen sigue la misma lógica de recuperación que para las operaciones largas.
Gestión de salida
- Cuando se abre la posición, se coloca un objetivo fijo de obtención de beneficios igual a
TakeProfitPips multiplicado por el paso del precio del instrumento.
- Un trailing stop impulsado por canal imita la llamada
OrderModify original. Cuando la barra anterior supera el canal de precios (desplazamiento de dos barras de la lógica MT4), el stop de protección se mueve al extremo anterior menos/más el desplazamiento final en pips. Si la siguiente vela supera ese extremo, la posición sale inmediatamente al precio de apertura.
- Las salidas de take-profit, trailing stop y gap se ejecutan a través de órdenes de mercado mientras se rastrea el precio de salida real para actualizar el indicador de ganancias/pérdidas para la escala estilo martingala.
Martingale recuperación
- Después de cada posición perdedora cerrada, el siguiente tamaño de entrada se multiplica por
RecoveryMultiplier. Las operaciones rentables restablecen la bandera y vuelven al volumen base.
Parámetros
| Nombre |
Descripción |
Predeterminado |
CandleType |
Periodo de tiempo principal utilizado para los cálculos. |
velas de 1 hora |
ChannelPeriod |
Período retroactivo para el canal de estilo Donchian. |
20 |
SlowMaPeriod |
Longitud del filtro de media móvil lenta. |
20 |
TakeProfitPips |
Distancia en pips para el objetivo de beneficio fijo. |
20 |
TrailingOffsetPips |
Desplazamiento utilizado al apretar el tope hasta el extremo anterior. |
10 |
RecoveryMultiplier |
Multiplicador de volumen aplicado después de una pérdida. |
2 |
Volume |
Volumen de operaciones base antes del escalado de recuperación. |
0.1 |
Notas de uso
- La estrategia espera que
Security.PriceStep refleje el valor del pip del instrumento negociado. Ajuste TakeProfitPips y TrailingOffsetPips si el símbolo usa una definición de pip diferente.
- Debido a que StockSharp utiliza órdenes de mercado para las salidas, las pruebas retrospectivas pueden mostrar un deslizamiento en comparación con las órdenes de parada y límite MT4 originales. La lógica sigue reproduciendo los mismos umbrales de precios.
- Los valores del canal se cambian para emular las llamadas
iCustom(..., shift=2); tenga esto en cuenta al modificar el comportamiento de seguimiento.
- El multiplicador de recuperación se puede establecer en 1 para desactivar la escala estilo martingala.
using System;
using System.Linq;
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>
/// Converted "20 pips" price channel strategy.
/// Uses Donchian channel breakouts with MA filter, trailing stop, and recovery multiplier.
/// </summary>
public class TwentyPipsPriceChannelStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private decimal? _prevChannelUpper;
private decimal? _prevChannelLower;
/// <summary>
/// Candle type to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Donchian channel lookback period.
/// </summary>
public int ChannelPeriod
{
get => _channelPeriod.Value;
set => _channelPeriod.Value = value;
}
/// <summary>
/// Slow moving average length.
/// </summary>
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
/// <summary>
/// Stop loss distance in absolute price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit distance in absolute price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="TwentyPipsPriceChannelStrategy"/>.
/// </summary>
public TwentyPipsPriceChannelStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary candle type", "General");
_channelPeriod = Param(nameof(ChannelPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Channel Period", "Donchian channel lookback", "Parameters");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Slow MA Period", "Slow moving average length", "Parameters");
_stopLoss = Param(nameof(StopLoss), 500m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss distance", "Risk");
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit distance", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return new[] { (Security, CandleType) };
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_prevChannelUpper = null;
_prevChannelLower = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
var slowMa = new SMA { Length = SlowMaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
// Use StartProtection for SL/TP
var tp = TakeProfit > 0 ? new Unit(TakeProfit, UnitTypes.Absolute) : null;
var sl = StopLoss > 0 ? new Unit(StopLoss, UnitTypes.Absolute) : null;
StartProtection(tp, sl);
base.OnStarted2(time);
}
private void ProcessCandle(ICandleMessage candle, decimal slowMaValue)
{
if (candle.State != CandleStates.Finished)
return;
// Track highs and lows for manual Donchian channel
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
while (_highs.Count > ChannelPeriod)
_highs.RemoveAt(0);
while (_lows.Count > ChannelPeriod)
_lows.RemoveAt(0);
if (_highs.Count < ChannelPeriod)
{
_prevChannelUpper = null;
_prevChannelLower = null;
return;
}
var channelUpper = _highs.Max();
var channelLower = _lows.Min();
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevChannelUpper = channelUpper;
_prevChannelLower = channelLower;
return;
}
// Channel breakout with MA filter
if (_prevChannelUpper.HasValue && _prevChannelLower.HasValue)
{
// Breakout above the previous channel high -> buy signal
if (candle.ClosePrice > _prevChannelUpper.Value && candle.ClosePrice > slowMaValue && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
}
// Breakout below the previous channel low -> sell signal
else if (candle.ClosePrice < _prevChannelLower.Value && candle.ClosePrice < slowMaValue && Position >= 0)
{
if (Position > 0)
SellMarket(Position);
SellMarket(Volume);
}
}
_prevChannelUpper = channelUpper;
_prevChannelLower = channelLower;
}
}
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, UnitTypes, Unit
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class twenty_pips_price_channel_strategy(Strategy):
"""Donchian channel breakout with SMA filter and StartProtection SL/TP."""
def __init__(self):
super(twenty_pips_price_channel_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 20).SetGreaterThanZero().SetDisplay("Channel Period", "Donchian channel lookback", "Parameters")
self._slow_ma_period = self.Param("SlowMaPeriod", 20).SetGreaterThanZero().SetDisplay("Slow MA Period", "Slow MA length", "Parameters")
self._sl = self.Param("StopLoss", 500).SetNotNegative().SetDisplay("Stop Loss", "SL distance", "Risk")
self._tp = self.Param("TakeProfit", 500).SetNotNegative().SetDisplay("Take Profit", "TP distance", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(twenty_pips_price_channel_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._prev_upper = None
self._prev_lower = None
def OnStarted2(self, time):
super(twenty_pips_price_channel_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._prev_upper = None
self._prev_lower = None
slow_ma = SimpleMovingAverage()
slow_ma.Length = self._slow_ma_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(slow_ma, self.OnProcess).Start()
sl_val = float(self._sl.Value)
tp_val = float(self._tp.Value)
tp = Unit(tp_val, UnitTypes.Absolute) if tp_val > 0 else None
sl = Unit(sl_val, UnitTypes.Absolute) if sl_val > 0 else None
self.StartProtection(tp, sl)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, slow_ma_val):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
ma_val = float(slow_ma_val)
period = self._channel_period.Value
self._highs.append(high)
self._lows.append(low)
while len(self._highs) > period:
self._highs.pop(0)
while len(self._lows) > period:
self._lows.pop(0)
if len(self._highs) < period:
self._prev_upper = None
self._prev_lower = None
return
ch_upper = max(self._highs)
ch_lower = min(self._lows)
if self._prev_upper is not None and self._prev_lower is not None and self.IsFormedAndOnlineAndAllowTrading():
if close > self._prev_upper and close > ma_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(abs(self.Position))
self.BuyMarket(self.Volume)
elif close < self._prev_lower and close < ma_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket(self.Position)
self.SellMarket(self.Volume)
self._prev_upper = ch_upper
self._prev_lower = ch_lower
def CreateClone(self):
return twenty_pips_price_channel_strategy()