Estrategia de Compra en Nube Ichimoku con Salida EMA Personalizada
Implementación de la estrategia de compra en la Nube Ichimoku con salida EMA personalizada y filtro de volumen. La estrategia compra cuando el precio está por encima de la nube y el volumen supera su promedio. Opcionalmente requiere que el precio se mantenga por encima de la EMA. La posición se cierra cuando el precio cae por debajo de la EMA o cuando se activa el stop-loss.
Detalles
- Criterios de entrada:
- Largo:
Price > Cloud && Volume > AvgVolume && (Price > EMA if enabled)
- Largo:
- Largo/Corto: Solo largos
- Criterios de salida:
Price < EMA
- Stops: Basado en porcentaje usando
StopLossPercent - Valores predeterminados:
TenkanPeriod= 9KijunPeriod= 26SenkouSpanPeriod= 52EmaLength= 44VolumeAvgPeriod= 10StopLossPercent= 2CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Solo largos
- Indicadores: Nube Ichimoku, EMA, Volumen
- Stops: Sí
- Complejidad: Principiante
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- 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>
/// Ichimoku cloud buy with custom EMA exit strategy.
/// Long only. A long opens when the close is above the Ichimoku cloud and the candle volume exceeds its VolumeAvgPeriod average,
/// optionally also requiring the close above the EmaLength EMA. The position closes when the close falls below the EMA,
/// and a percent stop loss limits the loss.
/// </summary>
public class IchimokuCloudBuyCustomEmaExitStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanPeriod;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _volumeAvgPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<bool> _useEmaFilter;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _volumeSma;
/// <summary>
/// Tenkan-sen period.
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Kijun-sen period.
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Senkou Span B period.
/// </summary>
public int SenkouSpanPeriod
{
get => _senkouSpanPeriod.Value;
set => _senkouSpanPeriod.Value = value;
}
/// <summary>
/// Exit EMA period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Volume average period.
/// </summary>
public int VolumeAvgPeriod
{
get => _volumeAvgPeriod.Value;
set => _volumeAvgPeriod.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Require the close above the EMA for entries.
/// </summary>
public bool UseEmaFilter
{
get => _useEmaFilter.Value;
set => _useEmaFilter.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public IchimokuCloudBuyCustomEmaExitStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Tenkan-sen period", "Ichimoku");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Kijun-sen period", "Ichimoku");
_senkouSpanPeriod = Param(nameof(SenkouSpanPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span Period", "Senkou Span B period", "Ichimoku");
_emaLength = Param(nameof(EmaLength), 44)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Exit EMA period", "Exit");
_volumeAvgPeriod = Param(nameof(VolumeAvgPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Volume Avg Period", "Volume average period", "Volume");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_useEmaFilter = Param(nameof(UseEmaFilter), true)
.SetDisplay("Use EMA Filter", "Require the close above the EMA for entries", "Exit");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_volumeSma = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_volumeSma = new SimpleMovingAverage { Length = VolumeAvgPeriod };
var ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanPeriod },
};
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, ema, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ichimoku);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
var volume = candle.TotalVolume;
var volumeValue = _volumeSma.Process(new DecimalIndicatorValue(_volumeSma, volume, candle.OpenTime) { IsFinal = true });
if (!_volumeSma.IsFormed || volumeValue.IsEmpty || !emaValue.IsFormed)
return;
if (!ichimokuValue.IsFormed || ichimokuValue is not IchimokuValue { SenkouA: decimal senkouA, SenkouB: decimal senkouB })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var averageVolume = volumeValue.GetValue<decimal>();
var ema = emaValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (Position > 0)
{
if (close < ema)
SellMarket(Position);
return;
}
var aboveCloud = close > Math.Max(senkouA, senkouB);
var emaOk = !UseEmaFilter || close > ema;
if (aboveCloud && volume > averageVolume && emaOk)
BuyMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import Ichimoku, ExponentialMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class ichimoku_cloud_buy_custom_ema_exit_strategy(Strategy):
"""
Ichimoku cloud buy with custom EMA exit strategy.
Long only. A long opens when the close is above the Ichimoku cloud and the candle volume exceeds its VolumeAvgPeriod average,
optionally also requiring the close above the EmaLength EMA. The position closes when the close falls below the EMA,
and a percent stop loss limits the loss.
"""
def __init__(self):
super(ichimoku_cloud_buy_custom_ema_exit_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9).SetGreaterThanZero().SetDisplay("Tenkan Period", "Tenkan-sen period", "Ichimoku")
self._kijun_period = self.Param("KijunPeriod", 26).SetGreaterThanZero().SetDisplay("Kijun Period", "Kijun-sen period", "Ichimoku")
self._senkou_span_period = self.Param("SenkouSpanPeriod", 52).SetGreaterThanZero().SetDisplay("Senkou Span Period", "Senkou Span B period", "Ichimoku")
self._ema_length = self.Param("EmaLength", 44).SetGreaterThanZero().SetDisplay("EMA Length", "Exit EMA period", "Exit")
self._volume_avg_period = self.Param("VolumeAvgPeriod", 10).SetGreaterThanZero().SetDisplay("Volume Avg Period", "Volume average period", "Volume")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._use_ema_filter = self.Param("UseEmaFilter", True).SetDisplay("Use EMA Filter", "Require the close above the EMA for entries", "Exit")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._volume_sma = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ichimoku_cloud_buy_custom_ema_exit_strategy, self).OnReseted()
self._volume_sma = None
def OnStarted2(self, time):
super(ichimoku_cloud_buy_custom_ema_exit_strategy, self).OnStarted2(time)
self._volume_sma = SimpleMovingAverage()
self._volume_sma.Length = self._volume_avg_period.Value
ichimoku = Ichimoku()
ichimoku.Tenkan.Length = self._tenkan_period.Value
ichimoku.Kijun.Length = self._kijun_period.Value
ichimoku.SenkouB.Length = self._senkou_span_period.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ichimoku, ema, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ichimoku)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ichimoku_value, ema_value):
if candle.State != CandleStates.Finished:
return
volume = candle.TotalVolume
volume_value = process_value(self._volume_sma, volume, candle.OpenTime, True)
if not self._volume_sma.IsFormed or volume_value.IsEmpty or not ema_value.IsFormed:
return
if not ichimoku_value.IsFormed or ichimoku_value.SenkouA is None or ichimoku_value.SenkouB is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
average_volume = to_decimal(volume_value)
ema = ema_value.GetValue[Decimal](None)
close = candle.ClosePrice
if self.Position > 0:
if close < ema:
self.SellMarket(self.Position)
return
above_cloud = close > max(ichimoku_value.SenkouA, ichimoku_value.SenkouB)
ema_ok = not self._use_ema_filter.Value or close > ema
if above_cloud and volume > average_volume and ema_ok:
self.BuyMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return ichimoku_cloud_buy_custom_ema_exit_strategy()