Jenkin's Cycle

Link to TOS Study

#hint: <b>Jenkins Cycle Volume</b> \n This study indicates a possible trend reversal after the cumulative volume range specified by the user is reached.\n

#hint jenkins_range: The number of shares traded for a trend reversal.

#hint length: The number of trading days per year.

##################################################################################

##

## This code is based on some of the cyclic and harmonic methodology theory developed

## by Michael S. Jenkins. Ken Hodor modified the cyclic indicator based on trade volume

## for the SPY and implemented his approach in TradeStation. This is an implementation

## of the approach Ken presented at the August 22, 2015 San Diego Investools Meeting.

##

## Every 1.1 Billion shares of SPY traded represents a possible trend reversal.

##

## Original Implementation: Ken Hodor - TradeStation Easy Language

## 09/16/2015 Implemented by Tim Sayre - ThinkorSwim thinkScript

##

## NO ACTUAL OR IMPLIED WARRANTIES OFFERED - USE AT YOUR OWN DISCRETION

##################################################################################

declare lower;

input jenkins_range = 1100000000; ## Ken Hodor determined 1.1 billion shares traded fit the data set.

def length = 252; ## Trading days per year.

input showApproxDaysRemainLabel = yes; ## Shows/Hide Label with approximate number of bars until reversal.

def cycle_dir;

def cumulative_vol;

def debug_label;

def bar_vol = GetValue( volume, 0 );

## Only need these values at the first bar, but not sure how else to keep this tidy except by

## using the Compound Value function w/o thus preventing the calculation for every bar.

## The variable isn't needed afterwards so just copying the previous bar value into the current bar value.

def avg_daily = ( fold i = 0 to length with vol_sum = 0 do vol_sum + GetValue( volume, i ) ) / length;

def lo_idx = GetMinValueOffset( low, length );

def vol_to_low = CompoundValue( 1, vol_to_low[1], fold j = 0 to length - lo_idx with vol = 0 do vol + GetValue( volume, j ) );

## Checks to see if odd or even number to determine initial direction.

def cur_dir = CompoundValue( 1, cycle_dir[1], If( floor( vol_to_low / jenkins_range ) % 2 == 0, 1, -1 ) );

## If it is first bar then we need to calculate initial volume based on location of lowest low bar.

## Otherwise we use the previous bars value for cumulative volume.

def hist_vol = CompoundValue( 1, cumulative_vol[1], vol_to_low % jenkins_range );

## Check to see if we need to reverse direction or not.

if ( hist_vol + bar_vol > jenkins_range ) then {

## Account for cycle reversal.

cumulative_vol = hist_vol + bar_vol - jenkins_range;

cycle_dir = cur_dir * (-1);

## debug

debug_label = no;

} else {

cumulative_vol = hist_vol + bar_vol;

cycle_dir = cur_dir;

## debug

debug_label = no;

}

plot JenkinsVol = If ( cycle_dir == 1, 0, 1 ) + cycle_dir * cumulative_vol / jenkins_range;

JenkinsVol.SetPaintingStrategy( PaintingStrategy.LINE_VS_POINTS );

JenkinsVol.SetLineWeight( 2 ); ## Range from 1 to 5 ThinkScript imposed.

## Display for approximate number of bars remaining in the cycle.

def remain = ( jenkins_range - cumulative_vol ) / avg_daily;

AddLabel( showApproxDaysRemainLabel, "Approx. Days Remaining - " + Round( remain, 2 ), Color.WHITE );

#### debug ####

## Just change this out for other variables.

AddChartBubble( debug_label, 1.0, AsText( cycle_dir, NumberFormat.TWO_DECIMAL_PLACES ), Color.WHITE );