La estrategia de Canal Karpenko construye un canal de precio dinámico utilizando dos medias móviles. La línea base es un promedio de precios de cierre, mientras que los límites superior e inferior se derivan del rango promedio alto-bajo escalado por la razón áurea 1.618. El canal se expande hasta envolver la barra actual.
Una señal para ir largo aparece cuando el límite superior, previamente por encima de la línea base, cruza por debajo de ella. Una señal corta surge cuando el límite superior cruza por encima de la línea base después de permanecer por debajo. Las posiciones existentes en dirección opuesta se cierran cuando cambia el régimen.
Solo se procesan las velas completadas. Niveles fijos de stop-loss y take-profit protegen cada operación.
Detalles
Criterios de entrada:
Largo: El límite superior anterior estaba por encima de la línea base y el valor actual está por debajo o igual a ella.
Corto: El límite superior anterior estaba por debajo de la línea base y el valor actual está por encima o igual a ella.
Criterios de salida:
Cerrar largo cuando el límite superior anterior estaba por debajo de la línea base.
Cerrar corto cuando el límite superior anterior estaba por encima de la línea base.
Stops: Distancias fijas de stop-loss y take-profit en unidades de precio.
Valores predeterminados:
Base MA = 144
History = 500
Stop Loss = 1000
Take Profit = 2000
Candle Type = 4 hour
Filtros:
Categoría: Seguimiento de tendencia
Dirección: Ambos
Indicadores: Custom
Stops: Sí
Complejidad: Intermedio
Marco temporal: Medio plazo
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>
/// 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()