Intraday cumulative volume

Hi all,

I'm trying to program relative volume, ie.

"cumulative volume intraday is greater than cumulative volume of the previous day at the same time".

I know I have to show my working but it's not great.

DayOpen=TimeFrameGetPrice("O",inDaily);
bsdo = BarsSince(O==DayOpen);
Sum_vol = Sum(V,bsdo);
vol_time = ValueWhen(Sum_vol,TimeNum());
cond = TimeFrameGetPrice("vol_time", inDaily);
cond_yest = Ref(cond,-1);
...lost!

Would appreciate some help please.

You can use SumSince() instead

newDay = Day() != Ref( Day(), -1);
sum_vol_intra = SumSince( newDay, V);
// you will get running sum of volume since new day

prev_vol_day = TimeFrameGetPrice( "V", inDaily, -1 );

cond_vol = sum_vol_intra > prev_vol_day;
// what you want
1 Like

No, you have to use Sparse* functions (see AFL function reference) since he wants to shift array forward for each same time as was written in 1st post to compare with "current" day's one.

Grey line is current day.
Red one if shifted previous day.

14

3 Likes

Thanks guys. I'll try to do something with 'sparse' and my sparse coding ability.

Thanks for that @fxshrat
it was a bit confusing :slight_smile:
to rephrase, we are comparing cumulative volume for previous day at the same time for current day and see which is higher.

I do something similar, but use the average of the past 30 days instead of the prior day. Perhaps this will help anyway.

I prefer to see the intraday volume in an unconventional manner. In the 5-minute chart below, each bar is the cumulative volume for that day. The black portion of the bar represents that bar's volume. The light gray portion represents the cumulative volume prior to the current bar. The blue line represents the moving average (EMA) of the past 30 days. In the title, the relative volume is expressed as a factor of the average; in this case it was 1.8x at the end of the day which you could probably eyeball from the chart.

image

There is probably more code here than you will actually want. Calculating these averages is very slow, so I created code to save the average volume as a composite, so that it does not get calculated on every chart refresh. When working with a 1-minute DB with 24h filtering, it can take a few seconds to calculate.

Also, a big assumption in this code is that the bars are aligned to the same time every day (e.g. bar #1 of the day is always at 7:00AM), which is not accurate because some symbols trade at different times on different days of the week (outside of regular market hours, that is). I adapted code from http://www.amibroker.com/members/library/detail.php?id=1406 and that's the approach that was taken by stevencarr2 at that time, and I rolled with it.

// Intraday Cumulative Relative Volume
// AFL by Peter Deal
// @Link https://forum.amibroker.com/t/intraday-cumulative-volume/22127
// Commercial use prohibited

EnableTextOutput( 0 );
Version( 6.30 ); 
Intrvl 		= Interval( 0 );
IntraDay 	= Intrvl < inDaily;
Symbol 		= Name();
NewDay		= Day() != Ref( Day(), -1 );
//V			= V * 100; // My personal preference is for IBKR volume to be multiplied by 100 to match other data providers.

function IntraCumVol( VolMADays )
{
	// Returns intraday cumulative volume if Interval < inDaily, or just daily volume if >= inDaily.
	
	if( Intrvl < inDaily )
	{
		nd = NewDay;
		FirstBarVolume = ValueWhen( nd, V, 1 ); // Need to add this to running total because SumSince starts summing one bar after the true condition.
		Output = SumSince( nd, V ) + FirstBarVolume;
	}
	else Output = V;
	return Output;
}

function CalcIntraCumAvgVol( VolMADays )
{
	// Returns intraday cumulative average (EMA) volume over VolMADays.
	// Adapted from http://www.amibroker.com/members/library/detail.php?id=1406
	// Puts the average in a composite for future use because this is a slow function.
	// This code assumes that every day has the same number of bars, which is not true for some 
	// symbols using 24h filtering, and also holidays when the market closes early.
	// The number of bars in the average composite will not necessarily be aligned to the number
	// of bars in the following days, so this function is an approximation at best.

	HasVolume 	= LastValue( TimeFrameGetPrice( "V", inDaily, -1 ) > 0 );
	BarNum 		= BarsSince( NewDay );
	NumBars 	= LastValue( highest( BarNum ) ) + 1;
	Multiplier 	= ( 2 / ( VolMADays + 1 ) );

	for( i = 0; i < NumBars; ++i )
	{
		Ksf = IIf( i == BarNum, Multiplier, 0 );
		VarSet( "AvgVol_" + Symbol + "_" + NumBars + "_" + i, AMA( IntraCumVol( VolMADays ), Ksf ) ) ;
	}

	AvgVol = 0;

	for( i = 0; i < NumBars; ++i )
	{
		AvgVol = IIf( i == BarNum, VarGet( "AvgVol_" + Symbol + "_" + NumBars + "_" + i ), AvgVol );
	}

	StaticVarRemove( "AvgVol_" + Symbol + "_" + NumBars + "_" + i );
	AddToComposite( AvgVol, "~CumVolEMA_" + Symbol + "_" + VolMADays + "_" + Intrvl, "V", atcFlagDeleteValues | atcFlagEnableInExplore | atcFlagCompositeGroup | atcFlagEnableInIndicator ); // atcFlagResetValues | atcFlagCompositeGroup | atcFlagEnableInExplore | atcFlagEnableInIndicator | 
	Output = AvgVol;
	
	return Output;
}

function IntraCumAvgVol( VolMADays )
{
	// If intraday, returns the average intraday cumulative volume, including the current day's volume, 
	// so it needs to be recalculated when new bars are printed. Recalculates automatically when it detects
	// missing data, however it does not recalculate automatically when filtering is changed. 
	// If not intraday, returns simple MA of daily volume. 
	// See notes on CalcIntraCumAvgVol().
	
	if( Intraday )
	{
		HasVolume = LastValue( TimeFrameGetPrice( "V", inDaily, -1 ) > 0 );	
		
		if( HasVolume )
		{
			AvgVol 			= Foreign( "~CumVolEMA_" + Symbol + "_" + VolMADays + "_" + Intrvl, "V" );
			NotCalculated 	= IIf( Status( "action" ) <= 2 AND IsNull( FirstVisibleValue( AvgVol ) ), 1, 0 );
			NewData 		= IIf( LastValue( AvgVol ) == 0, 1, 0 );
			Dn 				= DateNum();
			MissingCalc 	= 0;
			
			for( i = 1; i < BarCount; i++ )
			{
				if( AvgVol[i] 	== 0 AND 		// AvgVol is zero on this bar.
					AvgVol[i-1] != 0 AND		// On the prior bar, it was not zero.
					Dn[i] 		== Dn[i-1] )	// It is the same day on both bars.
				{
					MissingCalc = 1;
				}
			}
			
			NeedsRecalc = NotCalculated OR NewData OR MissingCalc;
				
			if ( NeedsRecalc )
			{
				AvgVol = CalcIntraCumAvgVol( VolMADays );
			}	
			
			Output = HighestSince( NewDay, AvgVol, 1 );
			Output = AvgVol;
		}
		else 
		{
			Output = Null;
		}
	}
	else
	{
		Output = Ref( MA( V, VolMADays ), -1 );
	}

	return Output;
}

_SECTION_BEGIN( "Intraday volume EMA" );

	// Plots intraday cumulative volume and average intraday cumulative volume.
	// Pulls prior day average from composite to save time.
	// If prior day composite is zero, it recalculates the composite.
	// If chart is daily, it just shows daily volume and average of past days.
	// If volume is not reported for this symbol, does nothing.
	// See notes in CalcIntraCumAvgVol() regarding accuracy of this plot.

	VolMADays 	= Param( "Volume MA days", 30, 1, 270, 1 );
	Vol 		= IntraCumVol( VolMADays );
	AvgVol 		= IntraCumAvgVol( VolMADays );
	
	if( Intraday )
	{
		VolTitle = "Intraday cumulative volume";
		Plot( Vol - V, "IntraCumVol", colorLightGrey, styleHistogram | styleNoLabel | styleNoTitle, 0, Null, Null, Null, 3 );
	}
	else
	{
		VolTitle = "Volume";
	}

	RelVol = SafeDivide( Vol, AvgVol, 0 );

	Title = StrFormat( "{{NAME}} - " + VolTitle + " - {{VALUES}} - RelVol %.1fx", RelVol );
	Plot( Vol, "Volume", colorBlack, styleHistogram, 0, Null, Null, Null, 3 );
	Plot( AvgVol, "AvgVol", colorBlue, styleLine, 0, Null, Null, 1 );

_SECTION_END();

I hope this helps.

Peter

1 Like

Sorry, but that's much too complicated and too slow way (min. 10 times slower).
There isn't any BarCount loop required at all and no saving to ATC.
Also results are not correct as far as average per timenum is concerned.

As I said... using Sparse* functions is way to go.

I appreciate your feedback. I'm going to work with the sparse functions (I haven't used them before) and expect to make this code faster, simpler, and hopefully, elegant.

Peter

@fxshrat,

I made some progress on this, and you're right, Sparse* is the way to go. This works much faster now. Thank you for your guidance!

@C_M,

This isn't exactly what you're looking for but maybe it will help you figure out how to use the sparse* functions.

// Intraday Cumulative Relative Volume
// AFL by Peter Deal
// @Link https://forum.amibroker.com/t/intraday-cumulative-volume/22127
// Commercial use prohibited

EnableTextOutput( 0 );
Version( 6.30 ); 
//SetBarsRequired( sbrAll );
Intrvl 		= Interval( 0 );
IntraDay 	= Intrvl < inDaily;
NewDay		= DateNum() != Ref( DateNum(), -1 ); 
BarsIn24 	= inDaily / Intrvl; // Maximum possible number of bars in 24 hours
V			= V * 100; // My personal preference is for IBKR volume to be multiplied by 100 to match other data providers.

function TimeNumAdd( ReferenceTime, IntervalSeconds, NumIntervals )
{
	// Returns the time based on a reference time, plus or minus time.
	// Based on https://forum.amibroker.com/t/timenum-addition-subtraction/3324
	// IntervalSeconds can be negative (subtract time) or positive (add time).
	// NumIntervals must be a positive number.
	// Example usage:
	// TimeNumAdd( 160000, -Intrvl, 1 ) ); // Where Intrvl = in1Minute returns 155900
	dn 	= LastValue( DateNum() );							// any 'DateNum' will work
	dt 	= DateTimeConvert( 2, dn, ReferenceTime );   		// create 'DateTime'
	dt2 = DateTimeAdd( dt, IntervalSeconds, NumIntervals );	// do arithmetic
	nt 	= DateTimeConvert( 1, dt2 );						// grab time portion of 'dt2'
	return nt;
}

function IntraCumVol()
{
	// Returns intraday cumulative volume if Interval < inDaily, or just daily volume if >= inDaily.
	
	if( Intrvl < inDaily )
	{
		FirstBarVolume = ValueWhen( NewDay, V, 1 ); // Need to add this to running total because SumSince starts summing one bar after the true condition.
		Output = SumSince( NewDay, V ) + FirstBarVolume;
	}
	else Output = V;
	return Output;
}

function IntraCumAvgVol( VolMADays )
{
	// If intraday, returns the MA of intraday cumulative volume, including the current day's volume, 
	// If not intraday, returns MA of daily volume. 

	if( Intraday )
	{
		HasVolume = LastValue( TimeFrameGetPrice( "V", inDaily, -1 ) > 0 );	
		Output = 0;
		
		if( HasVolume )
		{
			Tn = TimeNum();
			IntraCumV = IntraCumVol();
			
			for( i = 1; i <= BarsIn24; i++ )
			{
				BarTime = TimeNumAdd( 0, Intrvl, i );
				only_when = Tn == BarTime;
				x = SparseCompress( only_when, IntraCumV );
				y = MA( x, VolMADays );
				y = SparseExpand( only_when, y );
				Output = IIf( !IsNull( y ), y, Output );
			}
		}
		else 
		{
			Output = Null;
		}
	}
	else
	{
		Output = MA( V, VolMADays );
	}

	return Output;
}

_SECTION_BEGIN( "Intraday cumulative volume" );

	// Plots intraday cumulative volume and average intraday cumulative volume.
	// If chart is daily, it just shows daily volume and average.
	// If volume is not reported for this symbol, does nothing.

	VolMADays 	= Param( "Volume MA days", 30, 1, 270, 1 );
	Vol 		= IntraCumVol();
	AvgVol 		= IntraCumAvgVol( VolMADays );
	
	if( Intraday )
	{
		VolTitle = "Intraday cumulative volume";
		Plot( Vol - V, "IntraCumVol", colorLightGrey, styleHistogram | styleNoLabel | styleNoTitle, 0, Null, Null, Null, 3 );
	}
	else
	{
		VolTitle = "Volume";
	}

	RelVol = SafeDivide( Vol, AvgVol, 0 );

	Title = StrFormat( "{{NAME}} - " + VolTitle + " - {{VALUES}} - RelVol %.1fx", RelVol );
	Plot( Vol, "Volume", colorBlack, styleHistogram, 0, Null, Null, Null, 3 );
	Plot( AvgVol, "AvgVol", colorBlue, styleLine, 0, Null, Null, 1 );

_SECTION_END();

if( Status( "action" ) == actionExplore )
{
	Filter = 1;
	AddColumn( V, "Bar volume", 1.0 );
	AddColumn( Vol, "CumVol", 1.0 );
	AddColumn( AvgVol, "AvgCumVol", 1.0 );
	AddColumn( RelVol, "RelVol", 1.1 );
}
  • Peter
1 Like

It is better but still too slow.
See difference.
16

Thanks very much Peter.

Hi fxshrat,

Thank you for helping on this. I've been trying to improve the execution speed, but haven't been able to make a drastic reduction. Would you be willing to give me a hint that would point me in the right direction? I could use a fresh perspective.

The code check & profile tool indicates a lot of time in the loop, which is not a surprise, so I am probably doing something there that could be done another way, or perhaps with less "expensive" functions.

image

Here's my most recent version:

// Intraday Cumulative Relative Volume v6
// AFL by Peter Deal
// @Link https://forum.amibroker.com/t/intraday-cumulative-volume/22127
// Commercial use prohibited

EnableTextOutput( 0 );
Version( 6.30 ); 
//SetBarsRequired( sbrAll );
Intrvl 		= Interval( 0 );
IntraDay 	= Intrvl < inDaily;
NewDay		= DateNum() != Ref( DateNum(), -1 ); 
BarsIn24 	= inDaily / Intrvl; // Maximum possible number of bars in 24 hours
V			= V * 100; // My personal preference is for IBKR volume to be multiplied by 100 to match other data providers.

function TimeNumAdd( ReferenceTime, IntervalSeconds, NumIntervals )
{
	// Returns the time based on a reference time, plus or minus time.
	// Based on https://forum.amibroker.com/t/timenum-addition-subtraction/3324
	// IntervalSeconds can be negative (subtract time) or positive (add time).
	// NumIntervals must be a positive number.
	// Example usage:
	// TimeNumAdd( 160000, -Intrvl, 1 ) ); // Where Intrvl = in1Minute returns 155900
	dn 	= LastValue( DateNum() );							// any 'DateNum' will work
	dt 	= DateTimeConvert( 2, dn, ReferenceTime );   		// create 'DateTime'
	dt2 = DateTimeAdd( dt, IntervalSeconds, NumIntervals );	// do arithmetic
	nt 	= DateTimeConvert( 1, dt2 );						// grab time portion of 'dt2'
	return nt;
}

function IntraCumVol()
{
	// Returns intraday cumulative volume if Interval < inDaily, or just daily volume if >= inDaily.
	
	if( Intraday )
	{
		FirstBarVolume = ValueWhen( NewDay, V, 1 ); // Need to add this to running total because SumSince starts summing one bar after the true condition.
		Output = SumSince( NewDay, V ) + FirstBarVolume;
	}
	else Output = V;
	return Output;
}

function IntraCumAvgVol( VolMADays )
{
	// Returns the prior day's MA of intraday cumulative volume (average volume if daily or higher timeframe). 
	
	if( Intraday )
	{
		HasVolume 	= LastValue( TimeFrameGetPrice( "V", inDaily, -1 ) > 0 );	
		Output 		= 0;
		
		if( HasVolume )
		{
			Tn 				= TimeNum();
			IntraCumV 		= IntraCumVol();
			HourAlignment 	= LastValue( Hour() ) * 10000;
			MinuteAlignment = LastValue( Minute() ) * 100;
			BarAlignment 	= IIf( Intrvl < inDaily, HourAlignment + MinuteAlignment, 0 ); // This may not work with 3- and 7-min intervals.
			
			for( i = 1; i <= BarsIn24; i++ )
			{
				BarTime 	= TimeNumAdd( BarAlignment, Intrvl, i );
				only_when 	= Tn == BarTime;
				x 			= SparseCompress( only_when, IntraCumV );
				y 			= MA( Ref( x, -1 ), VolMADays ); // This pulls MA from prior day.
				y 			= Nz( SparseExpand( only_when, y ) );
				Output		= Max( y, Output );
			}
		}
		else 
		{
			Output = Null;
		}
	}
	else
	{
		Output = Ref( MA( V, VolMADays ), -1 );
	}

	return Output;
}

_SECTION_BEGIN( "Intraday cumulative volume" );

	// Plots intraday cumulative volume and average intraday cumulative volume.
	// If chart is daily, it just shows daily volume and average.
	// If volume is not reported for this symbol, does nothing.

	VolMADays 	= Param( "Volume MA days", 30, 1, 270, 1 );
	Vol 		= IntraCumVol();
	AvgVol 		= IntraCumAvgVol( VolMADays );
	
	if( Intraday )
	{
		VolTitle = "Intraday cumulative volume";
		Plot( Vol - V, "IntraCumVol", colorLightGrey, styleHistogram | styleNoLabel | styleNoTitle, 0, Null, Null, Null, 3 );
	}
	else
	{
		VolTitle = "Volume";
	}

	RelVol = SafeDivide( Vol, AvgVol, 0 );

	Title = StrFormat( "{{NAME}} - " + VolTitle + " - {{VALUES}} - RelVol %.1fx", RelVol );
	Plot( Vol, "Volume", colorBlack, styleHistogram, 0, Null, Null, Null, 3 );
	Plot( AvgVol, "AvgVol", colorBlue, styleLine, 0, Null, Null, 1 );

_SECTION_END();

if( Status( "action" ) == actionExplore )
{
	Filter = 1;
	AddColumn( V, "Bar volume", 1.0 );
	AddColumn( Vol, "CumVol", 1.0 );
	AddColumn( AvgVol, "AvgCumVol", 1.0 );
	AddColumn( RelVol, "RelVol", 1.1 );
}

Again, I appreciate your help and guidance here.

Peter

2 Likes

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.