The PChannel System uses a price channel breakout with delayed confirmation. It tracks the highest high and lowest low over a configurable period. When price breaks through the channel and then closes back inside, the strategy enters in the direction of the breakout while closing any opposite positions. Optional stop-loss and take-profit levels manage risk.
Parameters
Period – lookback length for the channel.
Shift – number of bars to delay channel values.
StopLoss – absolute price distance for the protective stop.
TakeProfit – absolute price distance for the profit target.
CandleType – candle series used for calculations.
Trading logic
Compute channel bounds from the last Period candles with an optional Shift.
If the previous candle closed outside the channel and the current candle returns inside, open a position in the breakout direction.
Close the opposite position, if any, before opening a new one.
Monitor active trades and exit when StopLoss or TakeProfit is reached.
This strategy has no Python implementation yet.
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>
/// Price Channel breakout system with delayed confirmation.
/// Opens a long position when price breaks above the channel and then returns inside.
/// Opens a short position on a breakout below the channel followed by a return inside.
/// </summary>
public class PChannelSystemStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<int> _shift;
private readonly StrategyParam<DataType> _candleType;
private bool _prevAbove;
private bool _prevBelow;
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
public int Shift
{
get => _shift.Value;
set => _shift.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public PChannelSystemStrategy()
{
_period = Param(nameof(Period), 20)
.SetGreaterThanZero()
.SetDisplay("Period", "Channel calculation period", "Indicator")
.SetOptimize(10, 40, 5);
_shift = Param(nameof(Shift), 2)
.SetNotNegative()
.SetDisplay("Shift", "Bars shift for channel", "Indicator")
.SetOptimize(0, 5, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAbove = false;
_prevBelow = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevAbove = false;
_prevBelow = false;
var highest = new Highest { Length = Period };
var lowest = new Lowest { Length = Period };
var upperQueue = new Queue<decimal>();
var lowerQueue = new Queue<decimal>();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, (candle, highVal, lowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
upperQueue.Enqueue(highVal);
lowerQueue.Enqueue(lowVal);
if (upperQueue.Count <= Shift || lowerQueue.Count <= Shift)
return;
var upper = upperQueue.Dequeue();
var lower = lowerQueue.Dequeue();
var isAbove = candle.ClosePrice > upper;
var isBelow = candle.ClosePrice < lower;
// Was above, now returned inside -> buy signal
if (_prevAbove && !isAbove && Position <= 0)
BuyMarket();
// Was below, now returned inside -> sell signal
else if (_prevBelow && !isBelow && Position >= 0)
SellMarket();
_prevAbove = isAbove;
_prevBelow = isBelow;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class p_channel_system_strategy(Strategy):
def __init__(self):
super(p_channel_system_strategy, self).__init__()
self._period = self.Param("Period", 20) \
.SetDisplay("Period", "Channel calculation period", "Indicator")
self._shift = self.Param("Shift", 2) \
.SetDisplay("Shift", "Bars shift for channel", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles for strategy", "General")
self._prev_above = False
self._prev_below = False
self._upper_queue = []
self._lower_queue = []
@property
def period(self):
return self._period.Value
@property
def shift(self):
return self._shift.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(p_channel_system_strategy, self).OnReseted()
self._prev_above = False
self._prev_below = False
self._upper_queue = []
self._lower_queue = []
def OnStarted2(self, time):
super(p_channel_system_strategy, self).OnStarted2(time)
self._prev_above = False
self._prev_below = False
self._upper_queue = []
self._lower_queue = []
highest = Highest()
highest.Length = self.period
lowest = Lowest()
lowest.Length = self.period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def process_candle(self, candle, high_val, low_val):
if candle.State != CandleStates.Finished:
return
high_val = float(high_val)
low_val = float(low_val)
shift = int(self.shift)
self._upper_queue.append(high_val)
self._lower_queue.append(low_val)
if len(self._upper_queue) <= shift or len(self._lower_queue) <= shift:
return
upper = self._upper_queue.pop(0)
lower = self._lower_queue.pop(0)
close_price = float(candle.ClosePrice)
is_above = close_price > upper
is_below = close_price < lower
if self._prev_above and not is_above and self.Position <= 0:
self.BuyMarket()
elif self._prev_below and not is_below and self.Position >= 0:
self.SellMarket()
self._prev_above = is_above
self._prev_below = is_below
def CreateClone(self):
return p_channel_system_strategy()