PriceChannel Stop
Estrategia basada en el indicador Price Channel Stop.
El indicador calcula el máximo más alto y el mínimo más bajo en el período dado para formar un canal de Donchian. Los niveles de stop se construyen dentro del canal usando el factor Risk. Cuando el precio cierra por encima del stop superior, la tendencia cambia a alcista; al cerrar por debajo del stop inferior, la tendencia cambia a bajista. La estrategia abre posiciones en estas reversiones y opcionalmente cierra posiciones opuestas.
Detalles
- Criterios de entrada: El precio cruza los niveles de stop.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Señal opuesta.
- Stops: No.
- Valores predeterminados:
ChannelPeriod= 5Risk= 0.10CandleType= TimeSpan.FromHours(1)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: Canal de Donchian
- Stops: No
- Complejidad: Básico
- Marco temporal: Intradía (1h)
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Strategy based on Price Channel Stop.
/// The indicator builds stop levels from Donchian Channel and switches trend when price crosses them.
/// </summary>
public class PriceChannelStopStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<decimal> _risk;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _sellPosOpen;
private readonly StrategyParam<bool> _buyPosClose;
private readonly StrategyParam<bool> _sellPosClose;
private decimal _prevBsmax;
private decimal _prevBsmin;
private int _trend;
private bool _isFirst = true;
/// <summary>
/// Period for channel calculation.
/// </summary>
public int ChannelPeriod
{
get => _channelPeriod.Value;
set => _channelPeriod.Value = value;
}
/// <summary>
/// Risk factor for stop calculation.
/// </summary>
public decimal Risk
{
get => _risk.Value;
set => _risk.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyPosOpen
{
get => _buyPosOpen.Value;
set => _buyPosOpen.Value = value;
}
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellPosOpen
{
get => _sellPosOpen.Value;
set => _sellPosOpen.Value = value;
}
/// <summary>
/// Allow closing long positions on opposite signal.
/// </summary>
public bool BuyPosClose
{
get => _buyPosClose.Value;
set => _buyPosClose.Value = value;
}
/// <summary>
/// Allow closing short positions on opposite signal.
/// </summary>
public bool SellPosClose
{
get => _sellPosClose.Value;
set => _sellPosClose.Value = value;
}
/// <summary>
/// Initialize the strategy.
/// </summary>
public PriceChannelStopStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 5)
.SetDisplay("Channel Period", "Period for Price Channel calculation", "Indicators")
.SetOptimize(5, 30, 1);
_risk = Param(nameof(Risk), 0.10m)
.SetDisplay("Risk", "Risk factor for stop levels", "Indicators")
.SetOptimize(0.05m, 0.3m, 0.05m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_buyPosOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Buy Position Open", "Allow opening long positions", "Trading");
_sellPosOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Sell Position Open", "Allow opening short positions", "Trading");
_buyPosClose = Param(nameof(BuyPosClose), true)
.SetDisplay("Buy Position Close", "Allow closing long positions", "Trading");
_sellPosClose = Param(nameof(SellPosClose), true)
.SetDisplay("Sell Position Close", "Allow closing short positions", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevBsmax = default;
_prevBsmin = default;
_trend = 0;
_isFirst = true;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var donchian = new DonchianChannels { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(donchian, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, donchian);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
var dc = value as IDonchianChannelsValue;
if (dc?.UpperBand is not decimal upper || dc?.LowerBand is not decimal lower)
return;
var range = upper - lower;
var dPrice = range * Risk;
var bsmax = upper - dPrice;
var bsmin = lower + dPrice;
if (_isFirst)
{
_prevBsmax = bsmax;
_prevBsmin = bsmin;
_isFirst = false;
return;
}
var trend = _trend;
if (candle.ClosePrice > _prevBsmax)
trend = 1;
else if (candle.ClosePrice < _prevBsmin)
trend = -1;
if (trend > 0 && bsmin < _prevBsmin)
bsmin = _prevBsmin;
if (trend < 0 && bsmax > _prevBsmax)
bsmax = _prevBsmax;
var isBuySignal = _trend <= 0 && trend > 0;
var isSellSignal = _trend >= 0 && trend < 0;
if (IsFormedAndOnlineAndAllowTrading())
{
if (isBuySignal && Position <= 0)
BuyMarket();
else if (isSellSignal && Position >= 0)
SellMarket();
}
_trend = trend;
_prevBsmax = bsmax;
_prevBsmin = bsmin;
}
}
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 DonchianChannels
from StockSharp.Algo.Strategies import Strategy
class price_channel_stop_strategy(Strategy):
def __init__(self):
super(price_channel_stop_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 5) \
.SetDisplay("Channel Period", "Period for Price Channel calculation", "Indicators")
self._risk = self.Param("Risk", 0.10) \
.SetDisplay("Risk", "Risk factor for stop levels", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._buy_pos_open = self.Param("BuyPosOpen", True) \
.SetDisplay("Buy Position Open", "Allow opening long positions", "Trading")
self._sell_pos_open = self.Param("SellPosOpen", True) \
.SetDisplay("Sell Position Open", "Allow opening short positions", "Trading")
self._buy_pos_close = self.Param("BuyPosClose", True) \
.SetDisplay("Buy Position Close", "Allow closing long positions", "Trading")
self._sell_pos_close = self.Param("SellPosClose", True) \
.SetDisplay("Sell Position Close", "Allow closing short positions", "Trading")
self._prev_bsmax = 0.0
self._prev_bsmin = 0.0
self._trend = 0
self._is_first = True
@property
def channel_period(self):
return self._channel_period.Value
@property
def risk(self):
return self._risk.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def buy_pos_open(self):
return self._buy_pos_open.Value
@property
def sell_pos_open(self):
return self._sell_pos_open.Value
@property
def buy_pos_close(self):
return self._buy_pos_close.Value
@property
def sell_pos_close(self):
return self._sell_pos_close.Value
def OnReseted(self):
super(price_channel_stop_strategy, self).OnReseted()
self._prev_bsmax = 0.0
self._prev_bsmin = 0.0
self._trend = 0
self._is_first = True
def OnStarted2(self, time):
super(price_channel_stop_strategy, self).OnStarted2(time)
donchian = DonchianChannels()
donchian.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(donchian, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, donchian)
self.DrawOwnTrades(area)
def process_candle(self, candle, value):
if candle.State != CandleStates.Finished:
return
if not value.IsFormed:
return
upper = value.UpperBand
lower = value.LowerBand
if upper is None or lower is None:
return
upper = float(upper)
lower = float(lower)
rng = upper - lower
d_price = rng * float(self.risk)
bsmax = upper - d_price
bsmin = lower + d_price
if self._is_first:
self._prev_bsmax = bsmax
self._prev_bsmin = bsmin
self._is_first = False
return
trend = self._trend
close = float(candle.ClosePrice)
if close > self._prev_bsmax:
trend = 1
elif close < self._prev_bsmin:
trend = -1
if trend > 0 and bsmin < self._prev_bsmin:
bsmin = self._prev_bsmin
if trend < 0 and bsmax > self._prev_bsmax:
bsmax = self._prev_bsmax
is_buy = self._trend <= 0 and trend > 0
is_sell = self._trend >= 0 and trend < 0
if is_buy and self.Position <= 0:
self.BuyMarket()
elif is_sell and self.Position >= 0:
self.SellMarket()
self._trend = trend
self._prev_bsmax = bsmax
self._prev_bsmin = bsmin
def CreateClone(self):
return price_channel_stop_strategy()