Estrategia Volume EA
Descripción general
Esta estrategia opera basándose en picos de volumen y el Índice de Canal de Materias Primas (CCI). Abre posiciones al inicio de una nueva hora cuando el volumen de la vela anterior supera al de la vela previa por un factor configurable. Los valores del CCI deben caer dentro de bandas específicas para confirmar la señal.
Reglas
- Solo hay una posición abierta a la vez.
- Al comienzo de cada hora:
- Entrada larga cuando:
- La vela anterior es alcista.
- Volumen anterior > volumen previo ×
Factor. - El CCI está entre
CciLevel1yCciLevel2.
- Entrada corta cuando:
- La vela anterior es bajista.
- Volumen anterior > volumen previo ×
Factor. - El CCI está entre
CciLevel4yCciLevel3.
- Entrada larga cuando:
- Un stop de seguimiento de
TrailingStoppasos de precio protege las ganancias. - Todas las posiciones se cierran cuando la hora es igual a 23.
Parámetros
Factor– umbral multiplicador de volumen.TrailingStop– distancia de seguimiento en pasos de precio.CciLevel1/CciLevel2– límites del CCI para operaciones largas.CciLevel3/CciLevel4– límites del CCI para operaciones cortas.CandleType– marco temporal de velas utilizado para los cálculos.
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>
/// Strategy based on volume spikes and CCI ranges.
/// </summary>
public class VolumeEaStrategy : Strategy
{
private readonly StrategyParam<decimal> _factor;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<decimal> _cciLevel1;
private readonly StrategyParam<decimal> _cciLevel2;
private readonly StrategyParam<decimal> _cciLevel3;
private readonly StrategyParam<decimal> _cciLevel4;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevVolume;
private decimal _prevPrevVolume;
private decimal _prevOpen;
private decimal _prevClose;
private decimal _longStop;
private decimal _shortStop;
/// <summary>
/// Volume multiplier threshold.
/// </summary>
public decimal Factor
{
get => _factor.Value;
set => _factor.Value = value;
}
/// <summary>
/// Trailing stop distance in price steps.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Lower CCI level for long trades.
/// </summary>
public decimal CciLevel1
{
get => _cciLevel1.Value;
set => _cciLevel1.Value = value;
}
/// <summary>
/// Upper CCI level for long trades.
/// </summary>
public decimal CciLevel2
{
get => _cciLevel2.Value;
set => _cciLevel2.Value = value;
}
/// <summary>
/// Upper CCI level for short trades.
/// </summary>
public decimal CciLevel3
{
get => _cciLevel3.Value;
set => _cciLevel3.Value = value;
}
/// <summary>
/// Lower CCI level for short trades.
/// </summary>
public decimal CciLevel4
{
get => _cciLevel4.Value;
set => _cciLevel4.Value = value;
}
/// <summary>
/// Candle type for processing.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public VolumeEaStrategy()
{
_factor = Param(nameof(Factor), 1.55m)
.SetGreaterThanZero()
.SetDisplay("Factor", "Volume multiplier", "Trading");
_trailingStop = Param(nameof(TrailingStop), 350m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Trailing distance in steps", "Risk");
_cciLevel1 = Param(nameof(CciLevel1), 50m)
.SetDisplay("CCI Level1", "Lower CCI for buys", "Trading");
_cciLevel2 = Param(nameof(CciLevel2), 190m)
.SetDisplay("CCI Level2", "Upper CCI for buys", "Trading");
_cciLevel3 = Param(nameof(CciLevel3), -50m)
.SetDisplay("CCI Level3", "Upper CCI for sells", "Trading");
_cciLevel4 = Param(nameof(CciLevel4), -190m)
.SetDisplay("CCI Level4", "Lower CCI for sells", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevVolume = 0;
_prevPrevVolume = 0;
_prevOpen = 0;
_prevClose = 0;
_longStop = 0;
_shortStop = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var cci = new CommodityChannelIndex { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, cci);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
var currentVolume = candle.TotalVolume;
{
var trailDist = TrailingStop;
var volumeOk = _prevVolume > _prevPrevVolume * Factor;
if (volumeOk)
{
if (_prevClose > _prevOpen && cciValue > CciLevel1 && cciValue < CciLevel2 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_longStop = candle.ClosePrice - trailDist;
_shortStop = 0m;
}
else if (_prevClose < _prevOpen && cciValue < CciLevel3 && cciValue > CciLevel4 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_shortStop = candle.ClosePrice + trailDist;
_longStop = 0m;
}
}
if (Position > 0)
{
var candidate = candle.ClosePrice - trailDist;
if (candidate > _longStop)
_longStop = candidate;
if (candle.ClosePrice <= _longStop)
{
SellMarket();
_longStop = 0m;
}
}
else if (Position < 0)
{
var candidate = candle.ClosePrice + trailDist;
if (_shortStop == 0m || candidate < _shortStop)
_shortStop = candidate;
if (candle.ClosePrice >= _shortStop)
{
BuyMarket();
_shortStop = 0m;
}
}
}
_prevPrevVolume = _prevVolume;
_prevVolume = currentVolume;
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
}
}
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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class volume_ea_strategy(Strategy):
def __init__(self):
super(volume_ea_strategy, self).__init__()
self._factor = self.Param("Factor", 1.55) \
.SetDisplay("Factor", "Volume multiplier", "Trading")
self._trailing_stop = self.Param("TrailingStop", 350.0) \
.SetDisplay("Trailing Stop", "Trailing distance in steps", "Risk")
self._cci_level1 = self.Param("CciLevel1", 50) \
.SetDisplay("CCI Level1", "Lower CCI for buys", "Trading")
self._cci_level2 = self.Param("CciLevel2", 190) \
.SetDisplay("CCI Level2", "Upper CCI for buys", "Trading")
self._cci_level3 = self.Param("CciLevel3", -50) \
.SetDisplay("CCI Level3", "Upper CCI for sells", "Trading")
self._cci_level4 = self.Param("CciLevel4", -190.0) \
.SetDisplay("CCI Level4", "Lower CCI for sells", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_volume = 0.0
self._prev_prev_volume = 0.0
self._prev_open = 0.0
self._prev_close = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
@property
def factor(self):
return self._factor.Value
@property
def trailing_stop(self):
return self._trailing_stop.Value
@property
def cci_level1(self):
return self._cci_level1.Value
@property
def cci_level2(self):
return self._cci_level2.Value
@property
def cci_level3(self):
return self._cci_level3.Value
@property
def cci_level4(self):
return self._cci_level4.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(volume_ea_strategy, self).OnReseted()
self._prev_volume = 0.0
self._prev_prev_volume = 0.0
self._prev_open = 0.0
self._prev_close = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
def OnStarted2(self, time):
super(volume_ea_strategy, self).OnStarted2(time)
cci = CommodityChannelIndex()
cci.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def on_process(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
current_volume = candle.TotalVolume
trail_dist = self.trailing_stop
volume_ok = self._prev_volume > self._prev_prev_volume * self.factor
if volume_ok:
if self._prev_close > self._prev_open and cci_value > self.cci_level1 and cci_value < self.cci_level2 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._long_stop = candle.ClosePrice - trail_dist
self._short_stop = 0
elif self._prev_close < self._prev_open and cci_value < self.cci_level3 and cci_value > self.cci_level4 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._short_stop = candle.ClosePrice + trail_dist
self._long_stop = 0
if self.Position > 0:
candidate = candle.ClosePrice - trail_dist
if candidate > self._long_stop:
self._long_stop = candidate
if candle.ClosePrice <= self._long_stop:
self.SellMarket()
self._long_stop = 0
elif self.Position < 0:
candidate = candle.ClosePrice + trail_dist
if self._short_stop == 0 or candidate < self._short_stop:
self._short_stop = candidate
if candle.ClosePrice >= self._short_stop:
self.BuyMarket()
self._short_stop = 0
self._prev_prev_volume = self._prev_volume
self._prev_volume = current_volume
self._prev_open = candle.OpenPrice
self._prev_close = candle.ClosePrice
def CreateClone(self):
return volume_ea_strategy()