Estrategia AI Volume
La Estrategia AI Volume busca estallidos repentinos de participación. Un pico de volumen ocurre cuando el volumen actual supera su EMA por un multiplicador dado. Si el pico se alinea con la EMA de precio de 50 períodos y el color de la vela, la estrategia entra en esa dirección. Cada operación se cierra después de un número fijo de barras.
Detalles
- Criterios de entrada: Volumen > VolumeEMA * VolumeMultiplier y precio por encima/debajo de la EMA 50 con color de vela coincidente.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Posición cerrada después de
ExitBarsvelas. - Stops: Ninguno.
- Valores predeterminados:
VolumeEmaLength= 20VolumeMultiplier= 2.0ExitBars= 5CandleType= TimeSpan.FromMinutes(1)
- Filtros:
- Categoría: Ruptura de volumen
- Dirección: Ambos
- Indicadores: EMA, Volume EMA
- Stops: No
- Complejidad: Básico
- Marco temporal: Cualquiera
- 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>
/// AI Volume Strategy - trades volume spikes in trend direction.
/// Uses EMA for trend and volume EMA for spike detection.
/// </summary>
public class AiVolumeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _priceEmaLength;
private readonly StrategyParam<int> _volumeEmaLength;
private readonly StrategyParam<decimal> _volumeMultiplier;
private readonly StrategyParam<int> _exitBars;
private readonly StrategyParam<int> _cooldownBars;
private SimpleMovingAverage _volumeSma;
private int _barsInPosition;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int PriceEmaLength { get => _priceEmaLength.Value; set => _priceEmaLength.Value = value; }
public int VolumeEmaLength { get => _volumeEmaLength.Value; set => _volumeEmaLength.Value = value; }
public decimal VolumeMultiplier { get => _volumeMultiplier.Value; set => _volumeMultiplier.Value = value; }
public int ExitBars { get => _exitBars.Value; set => _exitBars.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AiVolumeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_priceEmaLength = Param(nameof(PriceEmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Price EMA Length", "Length for price EMA", "Parameters");
_volumeEmaLength = Param(nameof(VolumeEmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Volume EMA Length", "Length for volume EMA", "Parameters");
_volumeMultiplier = Param(nameof(VolumeMultiplier), 1.0m)
.SetDisplay("Volume Multiplier", "Multiplier for volume spike detection", "Parameters");
_exitBars = Param(nameof(ExitBars), 20)
.SetGreaterThanZero()
.SetDisplay("Exit Bars", "Exit position after this many bars", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 15)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_volumeSma = null;
_barsInPosition = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var priceEma = new ExponentialMovingAverage { Length = PriceEmaLength };
_volumeSma = new SimpleMovingAverage { Length = VolumeEmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(priceEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, priceEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal priceEmaValue)
{
if (candle.State != CandleStates.Finished)
return;
var volumeResult = _volumeSma.Process(new DecimalIndicatorValue(_volumeSma, candle.TotalVolume, candle.ServerTime));
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Time-based exit
if (Position != 0)
{
_barsInPosition++;
if (_barsInPosition >= ExitBars)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
else
BuyMarket(Math.Abs(Position));
_barsInPosition = 0;
_cooldownRemaining = CooldownBars;
return;
}
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
var avgVolume = _volumeSma.IsFormed ? volumeResult.ToDecimal() : 0m;
var volumeSpike = avgVolume > 0 && candle.TotalVolume > avgVolume * VolumeMultiplier;
// If no volume data, use price action only
var useVolumeFilter = avgVolume > 0;
var trendUp = candle.ClosePrice > priceEmaValue;
var trendDown = candle.ClosePrice < priceEmaValue;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
var longOk = trendUp && isBullish && (!useVolumeFilter || volumeSpike);
var shortOk = trendDown && isBearish && (!useVolumeFilter || volumeSpike);
if (longOk && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_barsInPosition = 0;
_cooldownRemaining = CooldownBars;
}
else if (shortOk && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_barsInPosition = 0;
_cooldownRemaining = 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, Math
from StockSharp.Messages import CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class ai_volume_strategy(Strategy):
"""
AI Volume Strategy - trades volume spikes in trend direction.
Uses EMA for trend and volume SMA for spike detection.
"""
def __init__(self):
super(ai_volume_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(30)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._price_ema_length = self.Param("PriceEmaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Price EMA Length", "Length for price EMA", "Parameters")
self._volume_ema_length = self.Param("VolumeEmaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Volume EMA Length", "Length for volume EMA", "Parameters")
self._volume_multiplier = self.Param("VolumeMultiplier", 1.0) \
.SetDisplay("Volume Multiplier", "Multiplier for volume spike detection", "Parameters")
self._exit_bars = self.Param("ExitBars", 20) \
.SetGreaterThanZero() \
.SetDisplay("Exit Bars", "Exit position after this many bars", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 15) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._volume_sma = None
self._bars_in_position = 0
self._cooldown_remaining = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def PriceEmaLength(self):
return self._price_ema_length.Value
@PriceEmaLength.setter
def PriceEmaLength(self, value):
self._price_ema_length.Value = value
@property
def VolumeEmaLength(self):
return self._volume_ema_length.Value
@VolumeEmaLength.setter
def VolumeEmaLength(self, value):
self._volume_ema_length.Value = value
@property
def VolumeMultiplier(self):
return self._volume_multiplier.Value
@VolumeMultiplier.setter
def VolumeMultiplier(self, value):
self._volume_multiplier.Value = value
@property
def ExitBars(self):
return self._exit_bars.Value
@ExitBars.setter
def ExitBars(self, value):
self._exit_bars.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
def OnReseted(self):
super(ai_volume_strategy, self).OnReseted()
self._volume_sma = None
self._bars_in_position = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ai_volume_strategy, self).OnStarted2(time)
price_ema = ExponentialMovingAverage()
price_ema.Length = self.PriceEmaLength
self._volume_sma = SimpleMovingAverage()
self._volume_sma.Length = self.VolumeEmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(price_ema, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, price_ema)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, price_ema_value):
if candle.State != CandleStates.Finished:
return
volume_result = process_float(self._volume_sma, candle.TotalVolume, candle.ServerTime, True)
# Time-based exit
if self.Position != 0:
self._bars_in_position += 1
if self._bars_in_position >= self.ExitBars:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
else:
self.BuyMarket(Math.Abs(self.Position))
self._bars_in_position = 0
self._cooldown_remaining = self.CooldownBars
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
avg_volume = float(to_decimal(volume_result)) if self._volume_sma.IsFormed else 0.0
volume_spike = avg_volume > 0 and float(candle.TotalVolume) > avg_volume * float(self.VolumeMultiplier)
use_volume_filter = avg_volume > 0
close = float(candle.ClosePrice)
ema_val = float(price_ema_value)
trend_up = close > ema_val
trend_down = close < ema_val
is_bullish = close > float(candle.OpenPrice)
is_bearish = close < float(candle.OpenPrice)
long_ok = trend_up and is_bullish and (not use_volume_filter or volume_spike)
short_ok = trend_down and is_bearish and (not use_volume_filter or volume_spike)
if long_ok and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._bars_in_position = 0
self._cooldown_remaining = self.CooldownBars
elif short_ok and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._bars_in_position = 0
self._cooldown_remaining = self.CooldownBars
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return ai_volume_strategy()