Estrategia Genie
Genie es un asesor experto basado en Parabolic SAR mejorado con el Índice Direccional Promedio (ADX) para confirmar la fuerza de la tendencia. La estrategia abre posiciones cuando el SAR cambia de dirección respecto al precio mientras los componentes +DI y -DI del ADX intercambian su dominancia. Un stop trailing y un take profit fijo gestionan el riesgo.
Las pruebas demuestran que el enfoque funciona mejor en instrumentos con tendencia y volatilidad moderada.
Detalles
- Criterios de entrada:
- Largo: SAR anterior por encima del cierre previo, SAR actual por debajo del cierre actual, +DI anterior < -DI anterior, +DI actual > -DI actual, y ADX por encima del +DI y -DI actuales.
- Corto: SAR anterior por debajo del cierre previo, SAR actual por encima del cierre actual, +DI anterior > -DI anterior, +DI actual < -DI actual, y ADX por encima del +DI y -DI actuales.
- Largo/Corto: Ambos lados.
- Criterios de salida:
- El stop trailing se activa o la vela anterior cierra contra la posición.
- Stops: Sí, stop trailing y take profit medidos en unidades de precio.
- Valores predeterminados:
TakeProfit= 500TrailingStop= 200SarStep= 0.02AdxPeriod= 14
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: Parabolic SAR, ADX
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: Sí (entre +DI y -DI)
- Nivel de riesgo: Medio
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>
/// Parabolic SAR strategy with momentum confirmation and trailing protection.
/// </summary>
public class GenieStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _rsiLongLevel;
private readonly StrategyParam<decimal> _rsiShortLevel;
private decimal _prevSar;
private ICandleMessage _prevCandle = null!;
private int _cooldownRemaining;
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
public decimal SarStep
{
get => _sarStep.Value;
set => _sarStep.Value = value;
}
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public decimal RsiLongLevel
{
get => _rsiLongLevel.Value;
set => _rsiLongLevel.Value = value;
}
public decimal RsiShortLevel
{
get => _rsiShortLevel.Value;
set => _rsiShortLevel.Value = value;
}
public GenieStrategy()
{
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetDisplay("Take Profit", "Take profit distance", "Protection");
_trailingStop = Param(nameof(TrailingStop), 200m)
.SetDisplay("Trailing Stop", "Trailing stop distance", "Protection");
_sarStep = Param(nameof(SarStep), 0.02m)
.SetDisplay("SAR Step", "Acceleration factor", "Indicator");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("Momentum Period", "Period for momentum confirmation", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
_rsiLongLevel = Param(nameof(RsiLongLevel), 55m)
.SetDisplay("RSI Long", "Minimum RSI level for long entries", "Filters");
_rsiShortLevel = Param(nameof(RsiShortLevel), 45m)
.SetDisplay("RSI Short", "Maximum RSI level for short entries", "Filters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSar = 0m;
_prevCandle = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sar = new ParabolicSar { AccelerationStep = SarStep, AccelerationMax = 0.2m };
var rsi = new RelativeStrengthIndex { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(sar, rsi, ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(TrailingStop, UnitTypes.Absolute),
isStopTrailing: true,
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sar);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue sarValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished || !sarValue.IsFinal || !rsiValue.IsFinal)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var sarCurrent = sarValue.ToDecimal();
var rsi = rsiValue.ToDecimal();
if (_prevCandle == null)
{
_prevSar = sarCurrent;
_prevCandle = candle;
return;
}
var sellCondition = _cooldownRemaining == 0 &&
_prevSar < _prevCandle.ClosePrice &&
sarCurrent > candle.ClosePrice &&
rsi <= RsiShortLevel;
var buyCondition = _cooldownRemaining == 0 &&
_prevSar > _prevCandle.ClosePrice &&
sarCurrent < candle.ClosePrice &&
rsi >= RsiLongLevel;
if (Position == 0)
{
if (sellCondition)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
else if (buyCondition)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
}
else if (Position > 0 && _prevCandle.OpenPrice > _prevCandle.ClosePrice)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
else if (Position < 0 && _prevCandle.OpenPrice < _prevCandle.ClosePrice)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
_prevSar = sarCurrent;
_prevCandle = candle;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ParabolicSar, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class genie_strategy(Strategy):
def __init__(self):
super(genie_strategy, self).__init__()
self._take_profit = self.Param("TakeProfit", 500.0) \
.SetDisplay("Take Profit", "Take profit distance", "Protection")
self._trailing_stop = self.Param("TrailingStop", 200.0) \
.SetDisplay("Trailing Stop", "Trailing stop distance", "Protection")
self._sar_step = self.Param("SarStep", 0.02) \
.SetDisplay("SAR Step", "Acceleration factor", "Indicator")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("Momentum Period", "Period for momentum confirmation", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._rsi_long_level = self.Param("RsiLongLevel", 55.0) \
.SetDisplay("RSI Long", "Minimum RSI level for long entries", "Filters")
self._rsi_short_level = self.Param("RsiShortLevel", 45.0) \
.SetDisplay("RSI Short", "Maximum RSI level for short entries", "Filters")
self._prev_sar = 0.0
self._prev_candle = None
self._cooldown_remaining = 0
@property
def take_profit(self):
return self._take_profit.Value
@property
def trailing_stop(self):
return self._trailing_stop.Value
@property
def sar_step(self):
return self._sar_step.Value
@property
def adx_period(self):
return self._adx_period.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@property
def rsi_long_level(self):
return self._rsi_long_level.Value
@property
def rsi_short_level(self):
return self._rsi_short_level.Value
def OnReseted(self):
super(genie_strategy, self).OnReseted()
self._prev_sar = 0.0
self._prev_candle = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(genie_strategy, self).OnStarted2(time)
sar = ParabolicSar()
sar.AccelerationStep = float(self.sar_step)
sar.AccelerationMax = 0.2
rsi = RelativeStrengthIndex()
rsi.Length = self.adx_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sar, rsi, self.process_candle).Start()
self.StartProtection(
Unit(float(self.take_profit), UnitTypes.Absolute),
Unit(float(self.trailing_stop), UnitTypes.Absolute),
isStopTrailing=True,
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
def process_candle(self, candle, sar_value, rsi_value):
if candle.State != CandleStates.Finished or not sar_value.IsFinal or not rsi_value.IsFinal:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
sar_current = float(sar_value)
rsi = float(rsi_value)
if self._prev_candle is None:
self._prev_sar = sar_current
self._prev_candle = candle
return
prev_close = float(self._prev_candle.ClosePrice)
close = float(candle.ClosePrice)
rsi_long = float(self.rsi_long_level)
rsi_short = float(self.rsi_short_level)
sell_condition = self._cooldown_remaining == 0 and \
self._prev_sar < prev_close and \
sar_current > close and \
rsi <= rsi_short
buy_condition = self._cooldown_remaining == 0 and \
self._prev_sar > prev_close and \
sar_current < close and \
rsi >= rsi_long
if self.Position == 0:
if sell_condition:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
elif buy_condition:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif self.Position > 0 and float(self._prev_candle.OpenPrice) > prev_close:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
elif self.Position < 0 and float(self._prev_candle.OpenPrice) < prev_close:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_sar = sar_current
self._prev_candle = candle
def CreateClone(self):
return genie_strategy()