TASC 2011-05 | Volume Zone Oscillator (Khalil, Steckler)

Modified on 2011/04/15 14:07 by Eugene — Categorized as: TASC Traders Tips

Traders' Tip text

The Volume Zone Oscillator system, as presented in Walid Khalil's and David Steckler's article "In the Volume Zone", is available as a free download to Wealth-Lab users as well as many other ready-made trading Strategies. To start exploring its potential of switching gears and trading in both the trending and rangebound markets, all it takes is to click the "Download" button in Wealth-Lab's "Open Strategy" dialog.
Image

Figure 1. A Wealth-Lab Developer 6.1 chart showing the Volume Zone Oscillator strategy in action applied to Natural Gas, continuous futures contract (NG20_I0B, daily).


Although the new oscillator implementation is pretty straightforward, the accompanying system rules have to function in different market regimes and cover such events as positive and negative price/oscillator divergences. The complexity of their implementation is hidden in additional library, "Community Components", available for download to Wealth-Lab customers from our site (Extensions section). Users can see the divergence lines drawn on a chart (the traditional way) and as a "binary wave" to be used in mechanical trading systems.

WealthScript Code (C#)


using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using Community.Components; // Divergence Between Two DataSeries (Detect, Plot) 

namespace WealthLab.Strategies { public class VZOStrategy : WealthScript { private StrategyParameter paramPeriod; public VZOStrategy() { paramPeriod = CreateParameter("VZO Period", 14, 2, 252, 2); } protected override void Execute() { int period = paramPeriod.ValueInt; DataSeries R = new DataSeries( Bars, "R" ); DataSeries TV = EMA.Series( Volume, period, EMACalculation.Modern ); DataSeries VZO = new DataSeries( Bars, "VZO" ); ADX adx = ADX.Series( Bars,14 ); EMA ema = EMA.Series( Close, 60, EMACalculation.Modern ); for(int bar = period; bar < Bars.Count; bar++) { R[bar] = Math.Sign( Close[bar] - Close[bar-1] ) * Volume[bar]; } DataSeries VP = EMA.Series( R, period, EMACalculation.Modern ); for(int bar = period; bar < Bars.Count; bar++) { if( TV[bar] != 0 ) VZO[bar] = 100 * VP[bar] / TV[bar]; } ChartPane vzoPane = CreatePane( 30, true, true ); PlotSeriesOscillator( vzoPane, VZO, 60, -60, Color.Red, Color.Blue, Color.Black, LineStyle.Solid, 1 ); DrawHorzLine( vzoPane, 60, Color.DarkGreen, LineStyle.Dotted, 2 ); DrawHorzLine( vzoPane, -60, Color.Red, LineStyle.Dotted, 2 ); DrawHorzLine( vzoPane, 40, Color.DarkGreen, LineStyle.Solid, 1 ); DrawHorzLine( vzoPane, -40, Color.Red, LineStyle.Solid, 1 ); DrawHorzLine( vzoPane, 0, Color.DarkBlue, LineStyle.Solid, 1 ); ChartPane divPane = CreatePane( 30, true, true ); SeriesHelper sh = new SeriesHelper(this); DataSeries pd = sh.PlotPeakDivergence(3, PricePane, High, 4d, vzoPane, VZO, 4d); PlotSeries(divPane, pd, Color.Blue, LineStyle.Solid, 2); DataSeries td = sh.PlotTroughDivergence(3, PricePane, Low, 4d, vzoPane, VZO, 4d); PlotSeries(divPane, td, Color.Red, LineStyle.Solid, 2); ChartPane adxPane = CreatePane( 30, true, true ); PlotSeries(adxPane, adx, Color.Purple, LineStyle.Histogram, 2); DrawHorzLine(adxPane, 18, Color.Red, LineStyle.Dashed, 2 ); PlotSeries(PricePane, ema, Color.Blue, LineStyle.Solid, 1); int start = Math.Max(adx.FirstValidValue,period); start = Math.Max( start, ema.FirstValidValue ); for(int bar = start; bar < Bars.Count; bar++) { bool bull = adx[bar] > 18 && Close[bar] > ema[bar]; bool bear = adx[bar] > 18 && Close[bar] < ema[bar]; bool osc = adx[bar] < 18; if( bull ) SetBackgroundColor( bar, Color.FromArgb( 30, Color.Blue ) ); if( bear ) SetBackgroundColor( bar, Color.FromArgb( 30, Color.Red ) ); if( osc ) SetBackgroundColor( bar, Color.FromArgb( 30, Color.Green ) );

if (IsLastPositionActive) { Position p = LastPosition; if( p.PositionType == PositionType.Long ) { if( p.EntrySignal.ToLower().Contains("uptrend") ) { if( VZO[bar] > 60 && TurnDown( bar, VZO ) || ( Close[bar] < ema[bar] && VZO[bar] < 0 ) || (pd[bar] <= -1.0 && VZO[bar] < 40.0) ) SellAtMarket( bar+1, p, "trend sell" ); } else if( p.EntrySignal.ToLower().Contains("buy nontrend") ) { if( VZO[bar] > 40.0 ) { if( adx[bar] > 18 ) SellAtMarket( bar+1, p, "nontrend sell rule #1" ); } else { if( VZO[bar] < -5 ) SellAtMarket( bar+1, p, "nontrend sell rule #2" ); } } } else { if( p.EntrySignal.ToLower().Contains("downtrend") ) { if( VZO[bar] < -60 && TurnUp( bar, VZO ) || ( Close[bar] > ema[bar] && VZO[bar] > 0 ) || (td[bar] <= -1.0 && VZO[bar] > -40.0) ) CoverAtMarket( bar+1, p, "trend cover" ); } else if( p.EntrySignal.ToLower().Contains("short nontrend") ) { if( VZO[bar] < -40.0 ) { if( adx[bar] > 18 ) CoverAtMarket( bar+1, p, "nontrend cover rule #1" ); } else { if( VZO[bar] < -5 && CrossOver( bar, VZO, 15 ) ) CoverAtMarket( bar+1, p, "nontrend cover rule #2" ); } } } } else { bool buy = bull && ( CrossOver( bar, VZO, -40 ) || CrossOver( bar, VZO, 0 ) ); bool shrt = bear && ( CrossUnder( bar, VZO, 40 ) || CrossUnder( bar, VZO, 0 ) ); bool nt_buy = osc && (CrossOver( bar, VZO, -40 ) || CrossOver( bar, VZO, 15 )); bool nt_shrt = osc && (CrossUnder( bar, VZO, 40 ) || CrossUnder( bar, VZO, -5 )); if( buy ) { SetBarColor( bar, Color.Blue ); BuyAtMarket( bar+1, "buy uptrend" ); } else if( shrt ) { SetBarColor( bar, Color.Red ); ShortAtMarket( bar+1, "short downtrend" ); } else if( nt_buy ) { SetBarColor( bar, Color.Cyan ); BuyAtMarket( bar+1, "buy nontrend" ); } else if( nt_shrt ) { SetBarColor( bar, Color.Orange ); ShortAtMarket( bar+1, "short nontrend" ); } } } } } }