Die Karpenko-Kanal-Strategie erstellt einen dynamischen Preiskanal mithilfe zweier gleitender Durchschnitte. Die Basislinie ist ein Durchschnitt der Schlusskurse, während die oberen und unteren Grenzen aus dem durchschnittlichen Hoch-Tief-Bereich abgeleitet werden, skaliert mit dem goldenen Verhältnis 1.618. Der Kanal erweitert sich, bis er den aktuellen Balken umschließt.
Ein Signal für Long erscheint, wenn die obere Grenze, die zuvor über der Basislinie lag, diese von oben nach unten kreuzt. Ein Short-Signal entsteht, wenn die obere Grenze die Basislinie von unten nach oben kreuzt, nachdem sie darunter war. Bestehende Positionen in entgegengesetzter Richtung werden bei einem Regimewechsel geschlossen.
Es werden nur abgeschlossene Kerzen verarbeitet. Feste Stop-Loss- und Take-Profit-Niveaus schützen jeden Trade.
Details
Einstiegskriterien:
Long: Vorherige obere Grenze über der Basislinie und aktueller Wert darunter oder gleich.
Short: Vorherige obere Grenze unter der Basislinie und aktueller Wert darüber oder gleich.
Ausstiegskriterien:
Long schließen, wenn die vorherige obere Grenze unter der Basislinie war.
Short schließen, wenn die vorherige obere Grenze über der Basislinie war.
Stops: Feste Stop-Loss- und Take-Profit-Abstände in Preiseinheiten.
Standardwerte:
Base MA = 144
History = 500
Stop Loss = 1000
Take Profit = 2000
Candle Type = 4 hour
Filter:
Kategorie: Trendfolge
Richtung: Beide
Indikatoren: Custom
Stops: Ja
Komplexität: Mittel
Zeitrahmen: Mittelfristig
Saisonalität: Nein
Neuronale Netze: Nein
Divergenz: Nein
Risikolevel: Mittel
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>
/// Karpenko Channel strategy.
/// Generates signals based on dynamic channel and SMA baseline crossover.
/// Long when price is below channel baseline, short when above.
/// </summary>
public class KarpenkoChannelStrategy : Strategy
{
private readonly StrategyParam<int> _basicMa;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMa;
private bool _initialized;
private int _cooldownRemaining;
/// <summary>
/// Period for base moving average.
/// </summary>
public int BasicMa { get => _basicMa.Value; set => _basicMa.Value = value; }
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public KarpenkoChannelStrategy()
{
_basicMa = Param(nameof(BasicMa), 20)
.SetGreaterThanZero()
.SetDisplay("Base MA", "Length of base moving average", "Parameters");
_cooldownBars = Param(nameof(CooldownBars), 8)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a signal", "Signal");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = BasicMa };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0m;
_prevMa = 0m;
_initialized = false;
_cooldownRemaining = 0;
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_initialized)
{
_prevClose = candle.ClosePrice;
_prevMa = maValue;
_initialized = true;
return;
}
// Cross above MA -> buy signal
var crossUp = _prevClose <= _prevMa && candle.ClosePrice > maValue;
// Cross below MA -> sell signal
var crossDown = _prevClose >= _prevMa && candle.ClosePrice < maValue;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (crossUp && _cooldownRemaining == 0 && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (crossDown && _cooldownRemaining == 0 && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
_prevClose = candle.ClosePrice;
_prevMa = maValue;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class karpenko_channel_strategy(Strategy):
def __init__(self):
super(karpenko_channel_strategy, self).__init__()
self._basic_ma = self.Param("BasicMa", 20) \
.SetDisplay("Base MA", "Length of base moving average", "Parameters")
self._cooldown_bars = self.Param("CooldownBars", 8) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a signal", "Signal")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_close = 0.0
self._prev_ma = 0.0
self._initialized = False
self._cooldown_remaining = 0
@property
def basic_ma(self):
return self._basic_ma.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(karpenko_channel_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ma = 0.0
self._initialized = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(karpenko_channel_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.basic_ma
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def process_candle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
ma_value = float(ma_value)
close = float(candle.ClosePrice)
if not self._initialized:
self._prev_close = close
self._prev_ma = ma_value
self._initialized = True
return
cross_up = self._prev_close <= self._prev_ma and close > ma_value
cross_down = self._prev_close >= self._prev_ma and close < ma_value
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if cross_up and self._cooldown_remaining == 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif cross_down and self._cooldown_remaining == 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_close = close
self._prev_ma = ma_value
def CreateClone(self):
return karpenko_channel_strategy()