Estrategia Ichimoku Volume
Implementación de estrategia - Ichimoku + Volume. Compra cuando el precio está por encima de la nube Kumo, Tenkan-sen está por encima de Kijun-sen y el volumen está por encima del promedio. Vende cuando el precio está por debajo de la nube Kumo, Tenkan-sen está por debajo de Kijun-sen y el volumen está por encima del promedio.
Las pruebas indican un retorno anual promedio de aproximadamente 40%. Funciona mejor en el mercado de criptomonedas.
Los componentes de Ichimoku definen el sesgo direccional mientras que el aumento de volumen confirma el interés. Las operaciones se abren cuando el precio se alinea con la nube y el volumen aumenta.
Es adecuado para traders que siguen rupturas de nubes con participación. El riesgo está limitado por un stop basado en ATR.
Detalles
- Criterios de entrada:
- Largo:
Price > Cloud && Tenkan > Kijun && Volume > AvgVolume - Corto:
Price < Cloud && Tenkan < Kijun && Volume > AvgVolume
- Largo:
- Largo/Corto: Ambos
- Criterios de salida:
- Ruptura de la nube en dirección opuesta
- Stops: Basado en porcentaje usando
StopLoss - Valores predeterminados:
TenkanPeriod= 9KijunPeriod= 26SenkouSpanPeriod= 52VolumeAvgPeriod= 20StopLoss= new Unit(2, UnitTypes.Percent)CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: Ichimoku Cloud, Volume
- Stops: Sí
- Complejidad: Intermedio
- 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>
/// Strategy combining Ichimoku (manual Tenkan/Kijun) with volume filter.
/// Buys when price above Kumo, Tenkan above Kijun, volume above average.
/// Sells when price below Kumo, Tenkan below Kijun, volume above average.
/// </summary>
public class IchimokuVolumeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _volumeAvgPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private readonly List<decimal> _vols = new();
private int _cooldown;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <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>
/// Volume average period.
/// </summary>
public int VolumeAvgPeriod
{
get => _volumeAvgPeriod.Value;
set => _volumeAvgPeriod.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize strategy.
/// </summary>
public IchimokuVolumeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetRange(5, 20)
.SetDisplay("Tenkan Period", "Tenkan-sen period (fast)", "Ichimoku");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetRange(15, 40)
.SetDisplay("Kijun Period", "Kijun-sen period (slow)", "Ichimoku");
_volumeAvgPeriod = Param(nameof(VolumeAvgPeriod), 20)
.SetRange(10, 50)
.SetDisplay("Volume Average Period", "Period for volume moving average", "Volume");
_cooldownBars = Param(nameof(CooldownBars), 100)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_vols.Clear();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = KijunPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
var vol = candle.TotalVolume;
_highs.Add(high);
_lows.Add(low);
_vols.Add(vol);
var tenkanPrd = TenkanPeriod;
var kijunPrd = KijunPeriod;
var volPrd = VolumeAvgPeriod;
var minBars = Math.Max(kijunPrd, volPrd);
if (_highs.Count < minBars)
{
if (_cooldown > 0) _cooldown--;
return;
}
// Manual Tenkan-sen: (highest high + lowest low) / 2 over tenkan period
var count = _highs.Count;
decimal tenkanHH = decimal.MinValue, tenkanLL = decimal.MaxValue;
for (int i = count - tenkanPrd; i < count; i++)
{
if (_highs[i] > tenkanHH) tenkanHH = _highs[i];
if (_lows[i] < tenkanLL) tenkanLL = _lows[i];
}
var tenkan = (tenkanHH + tenkanLL) / 2m;
// Manual Kijun-sen
decimal kijunHH = decimal.MinValue, kijunLL = decimal.MaxValue;
for (int i = count - kijunPrd; i < count; i++)
{
if (_highs[i] > kijunHH) kijunHH = _highs[i];
if (_lows[i] < kijunLL) kijunLL = _lows[i];
}
var kijun = (kijunHH + kijunLL) / 2m;
// Senkou Span A = (Tenkan + Kijun) / 2
var senkouA = (tenkan + kijun) / 2m;
// Senkou Span B = (highest high + lowest low) / 2 over 2*kijun period (use kijun period for simplicity)
var senkouB = kijun; // simplified: use Kijun as proxy for Senkou B
var upperKumo = Math.Max(senkouA, senkouB);
var lowerKumo = Math.Min(senkouA, senkouB);
// Volume average
decimal sumVol = 0;
for (int i = count - volPrd; i < count; i++)
sumVol += _vols[i];
var avgVol = sumVol / volPrd;
var highVolume = vol > avgVol;
// Trim lists
var maxKeep = minBars * 3;
if (_highs.Count > maxKeep)
{
var trim = _highs.Count - minBars * 2;
_highs.RemoveRange(0, trim);
_lows.RemoveRange(0, trim);
_vols.RemoveRange(0, trim);
}
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Buy: price above cloud + Tenkan above Kijun + high volume
if (close > upperKumo && tenkan > kijun && highVolume && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Sell: price below cloud + Tenkan below Kijun + high volume
else if (close < lowerKumo && tenkan < kijun && highVolume && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long: price drops below kijun
if (Position > 0 && close < kijun)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short: price rises above kijun
else if (Position < 0 && close > kijun)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class ichimoku_volume_strategy(Strategy):
"""
Strategy combining Ichimoku (manual Tenkan/Kijun) with volume filter.
"""
def __init__(self):
super(ichimoku_volume_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._tenkan_period = self.Param("TenkanPeriod", 9) \
.SetRange(5, 20) \
.SetDisplay("Tenkan Period", "Tenkan-sen period (fast)", "Ichimoku")
self._kijun_period = self.Param("KijunPeriod", 26) \
.SetRange(15, 40) \
.SetDisplay("Kijun Period", "Kijun-sen period (slow)", "Ichimoku")
self._volume_avg_period = self.Param("VolumeAvgPeriod", 20) \
.SetRange(10, 50) \
.SetDisplay("Volume Average Period", "Period for volume moving average", "Volume")
self._cooldown_bars = self.Param("CooldownBars", 100) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General") \
.SetRange(5, 500)
self._highs = []
self._lows = []
self._vols = []
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(ichimoku_volume_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._vols = []
self._cooldown = 0
ema = ExponentialMovingAverage()
ema.Length = self._kijun_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
vol = float(candle.TotalVolume)
self._highs.append(high)
self._lows.append(low)
self._vols.append(vol)
tenkan_prd = self._tenkan_period.Value
kijun_prd = self._kijun_period.Value
vol_prd = self._volume_avg_period.Value
min_bars = max(kijun_prd, vol_prd)
if len(self._highs) < min_bars:
if self._cooldown > 0:
self._cooldown -= 1
return
count = len(self._highs)
# Manual Tenkan-sen
tenkan_hh = max(self._highs[count - tenkan_prd:count])
tenkan_ll = min(self._lows[count - tenkan_prd:count])
tenkan = (tenkan_hh + tenkan_ll) / 2.0
# Manual Kijun-sen
kijun_hh = max(self._highs[count - kijun_prd:count])
kijun_ll = min(self._lows[count - kijun_prd:count])
kijun = (kijun_hh + kijun_ll) / 2.0
# Senkou Span A and B
senkou_a = (tenkan + kijun) / 2.0
senkou_b = kijun # simplified
upper_kumo = max(senkou_a, senkou_b)
lower_kumo = min(senkou_a, senkou_b)
# Volume average
sum_vol = sum(self._vols[count - vol_prd:count])
avg_vol = sum_vol / vol_prd
high_volume = vol > avg_vol
# Trim lists
max_keep = min_bars * 3
if len(self._highs) > max_keep:
trim = len(self._highs) - min_bars * 2
self._highs = self._highs[trim:]
self._lows = self._lows[trim:]
self._vols = self._vols[trim:]
if self._cooldown > 0:
self._cooldown -= 1
return
cd = self._cooldown_bars.Value
# Buy: price above cloud + Tenkan above Kijun + high volume
if close > upper_kumo and tenkan > kijun and high_volume and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
elif close < lower_kumo and tenkan < kijun and high_volume and self.Position == 0:
self.SellMarket()
self._cooldown = cd
# Exit long: price drops below kijun
if self.Position > 0 and close < kijun:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > kijun:
self.BuyMarket()
self._cooldown = cd
def OnReseted(self):
super(ichimoku_volume_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._vols = []
self._cooldown = 0
def CreateClone(self):
return ichimoku_volume_strategy()