La estrategia Karacatica es un enfoque de seguimiento de tendencia que combina la acción del precio con el Índice Direccional Promedio (ADX). Busca situaciones en las que el precio de cierre actual es mayor o menor que el precio de cierre hace un número especificado de velas y confirma el movimiento con la dominancia de la línea +DI o -DI.
Indicadores
Average Directional Index (ADX) – mide la fuerza de la tendencia y proporciona los componentes +DI y -DI.
Comparación de precios – verifica si el último cierre está por encima o por debajo del cierre hace Period velas.
Parámetros
Period – número de velas utilizado tanto para el cálculo del ADX como para el lookback de la comparación de precios. El valor predeterminado es 70.
TakeProfitPercent – take-profit expresado como porcentaje del precio de entrada. El valor predeterminado es 2%.
StopLossPercent – stop-loss expresado como porcentaje del precio de entrada. El valor predeterminado es 1%.
CandleType – marco temporal de las velas a las que suscribirse. El valor predeterminado es 1 hora.
Lógica de trading
Entrada larga: Close > Close[Period] y +DI > -DI sin señal larga existente. Cierra las posiciones cortas y abre una larga.
Entrada corta: Close < Close[Period] y -DI > +DI sin señal corta existente. Cierra las posiciones largas y abre una corta.
Protección de posición: StartProtection aplica tanto los porcentajes de take-profit como de stop-loss.
Notas de uso
Diseñada para la API de alto nivel de StockSharp; se suscribe a velas y vincula el indicador ADX.
La estrategia cierra automáticamente las posiciones opuestas cuando aparece una nueva señal.
No se proporciona implementación en Python por ahora.
Aviso legal
Este ejemplo es solo para fines educativos y no garantiza ganancias. El trading conlleva un riesgo significativo. Siempre pruebe las estrategias exhaustivamente antes de implementarlas en mercados en vivo.
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>
/// Karacatica strategy uses ADX to determine trend direction and compares
/// current close price with the close price from a specified period ago.
/// It goes long when an uptrend is detected and price is rising, and
/// goes short when a downtrend is detected and price is falling.
/// </summary>
public class KaracaticaStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<DataType> _candleType;
private AverageDirectionalIndex _adx;
private readonly Queue<decimal> _closeQueue = new();
private int _lastSignal;
/// <summary>
/// Indicator period used for ADX and price comparison.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Candle type parameter.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public KaracaticaStrategy()
{
_period = Param(nameof(Period), 30)
.SetGreaterThanZero()
.SetDisplay("Period", "ADX period and lookback for close comparison", "Indicators")
.SetOptimize(20, 50, 10);
_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 OnReseted()
{
base.OnReseted();
_adx = null;
_closeQueue.Clear();
_lastSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_adx = new AverageDirectionalIndex { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_adx, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _adx);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
_closeQueue.Enqueue(candle.ClosePrice);
if (_closeQueue.Count > Period + 1)
_closeQueue.Dequeue();
if (_closeQueue.Count <= Period)
return;
if (!adxValue.IsFormed)
return;
var pastClose = _closeQueue.Peek();
var adxTyped = adxValue as IAverageDirectionalIndexValue;
if (adxTyped?.Dx is not IDirectionalIndexValue dxVal)
return;
var plusDi = dxVal.Plus;
var minusDi = dxVal.Minus;
if (plusDi is null || minusDi is null)
return;
var buySignal = candle.ClosePrice > pastClose && plusDi > minusDi && _lastSignal != 1;
var sellSignal = candle.ClosePrice < pastClose && minusDi > plusDi && _lastSignal != -1;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (buySignal && Position <= 0)
{
BuyMarket();
_lastSignal = 1;
}
else if (sellSignal && Position >= 0)
{
SellMarket();
_lastSignal = -1;
}
}
}
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 AverageDirectionalIndex
from StockSharp.Algo.Strategies import Strategy
class karacatica_strategy(Strategy):
def __init__(self):
super(karacatica_strategy, self).__init__()
self._period = self.Param("Period", 30) \
.SetDisplay("Period", "ADX period and lookback for close comparison", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._close_queue = []
self._last_signal = 0
@property
def period(self):
return self._period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(karacatica_strategy, self).OnReseted()
self._close_queue = []
self._last_signal = 0
def OnStarted2(self, time):
super(karacatica_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self.period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, adx)
self.DrawOwnTrades(area)
def process_candle(self, candle, adx_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
self._close_queue.append(close)
if len(self._close_queue) > self.period + 1:
self._close_queue.pop(0)
if len(self._close_queue) <= self.period:
return
if not adx_value.IsFormed:
return
past_close = self._close_queue[0]
plus_di = adx_value.Dx.Plus
minus_di = adx_value.Dx.Minus
if plus_di is None or minus_di is None:
return
plus_di = float(plus_di)
minus_di = float(minus_di)
buy_signal = close > past_close and plus_di > minus_di and self._last_signal != 1
sell_signal = close < past_close and minus_di > plus_di and self._last_signal != -1
if buy_signal and self.Position <= 0:
self.BuyMarket()
self._last_signal = 1
elif sell_signal and self.Position >= 0:
self.SellMarket()
self._last_signal = -1
def CreateClone(self):
return karacatica_strategy()