Das PChannel-System verwendet einen Preiskanal-Ausbruch mit verzögerter Bestätigung. Es verfolgt das höchste Hoch und das niedrigste Tief über einen konfigurierbaren Zeitraum. Wenn der Preis den Kanal durchbricht und dann wieder schließt, steigt die Strategie in Richtung des Ausbruchs ein und schließt dabei gegenteilige Positionen. Optionale Stop-Loss- und Take-Profit-Levels steuern das Risiko.
Parameter
Period – Rückblicklänge für den Kanal.
Shift – Anzahl der Bars zur Verzögerung der Kanalwerte.
StopLoss – absoluter Preisabstand für den Schutz-Stop.
TakeProfit – absoluter Preisabstand für das Gewinnziel.
CandleType – Kerzenserie für Berechnungen.
Handelslogik
Kanalgrenzen aus den letzten Period Kerzen mit optionalem Shift berechnen.
Wenn die vorherige Kerze außerhalb des Kanals schloss und die aktuelle Kerze wieder zurückkehrt, eine Position in Ausbruchsrichtung eröffnen.
Die entgegengesetzte Position, falls vorhanden, vor dem Öffnen einer neuen schließen.
Aktive Trades überwachen und bei Erreichen von StopLoss oder TakeProfit aussteigen.
Diese Strategie hat noch keine Python-Implementierung.
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()