Filtro de Volumen ZPF
El Filtro de Volumen ZPF combina dos medias móviles con una media de volumen. El valor del indicador es la diferencia entre la media rápida y la lenta, suavizada por el volumen. Cuando este valor cruza por encima de cero, se asume presión alcista; un cruce por debajo de cero señala presión bajista.
La estrategia opera en ambas direcciones. Las entradas ocurren cuando el indicador ZPF cruza la línea de cero. Las posiciones se cierran cuando ocurre un cruce opuesto.
Detalles
- Criterios de entrada: ZPF cruza por encima o por debajo de cero.
- Largo/Corto: Ambos.
- Criterios de salida: Cruce opuesto de la línea de cero.
- Stops: No.
- Valores predeterminados:
Length= 12CandleType= TimeSpan.FromHours(4)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: Moving Average, Volume
- Stops: No
- Complejidad: Básico
- Marco temporal: Swing
- 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>
/// ZPF Volume Filter strategy.
/// Opens long positions when the ZPF indicator crosses above zero and
/// short positions when it crosses below.
/// </summary>
public class ZpfStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _fastMa;
private SimpleMovingAverage _slowMa;
private SimpleMovingAverage _volumeMa;
private int _barsSinceTrade;
public int Length { get => _length.Value; set => _length.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ZpfStrategy()
{
_length = Param(nameof(Length), 12)
.SetRange(5, 50)
.SetDisplay("Length", "Base moving average length", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
_fastMa = default;
_slowMa = default;
_volumeMa = default;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_barsSinceTrade = CooldownBars;
_fastMa = new SimpleMovingAverage { Length = Length };
_slowMa = new SimpleMovingAverage { Length = Length * 2 };
_volumeMa = new SimpleMovingAverage { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastMa, _slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
_barsSinceTrade++;
var volResult = _volumeMa.Process(candle.TotalVolume, candle.OpenTime, true);
if (!volResult.IsFormed)
return;
var volumeAvg = volResult.ToDecimal();
// Calculate ZPF value
var zpf = volumeAvg * (fast - slow) / 2m;
// Detect zero line cross
if (zpf > 0 && Position <= 0 && _barsSinceTrade >= CooldownBars)
{
if (Position < 0) BuyMarket();
BuyMarket();
_barsSinceTrade = 0;
}
else if (zpf < 0 && Position >= 0 && _barsSinceTrade >= CooldownBars)
{
if (Position > 0) SellMarket();
SellMarket();
_barsSinceTrade = 0;
}
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class zpf_strategy(Strategy):
def __init__(self):
super(zpf_strategy, self).__init__()
self._length = self.Param("Length", 12) \
.SetDisplay("Length", "Base moving average length", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetGreaterThanZero() \
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._volume_ma = None
self._bars_since_trade = 6
@property
def length(self):
return self._length.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(zpf_strategy, self).OnReseted()
self._volume_ma = None
self._bars_since_trade = self.cooldown_bars
def OnStarted2(self, time):
super(zpf_strategy, self).OnStarted2(time)
self._bars_since_trade = self.cooldown_bars
fast_ma = SimpleMovingAverage()
fast_ma.Length = self.length
slow_ma = SimpleMovingAverage()
slow_ma.Length = self.length * 2
self._volume_ma = SimpleMovingAverage()
self._volume_ma.Length = self.length
sub = self.SubscribeCandles(self.candle_type)
sub.Bind(fast_ma, slow_ma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
self._bars_since_trade += 1
vol_result = process_float(self._volume_ma, candle.TotalVolume, candle.OpenTime, True)
if not vol_result.IsFormed:
return
volume_avg = float(vol_result)
zpf = volume_avg * (float(fast) - float(slow)) / 2.0
if zpf > 0 and self.Position <= 0 and self._bars_since_trade >= self.cooldown_bars:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._bars_since_trade = 0
elif zpf < 0 and self.Position >= 0 and self._bars_since_trade >= self.cooldown_bars:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._bars_since_trade = 0
def CreateClone(self):
return zpf_strategy()