Check for empty bars in last N days

I'm trying to come up with a filter where you could reject trading any stock that has too many recent empty bars or days that it didn't trade. In my first attempt, I tried to compare the number of bars in the last N days to the number of bars of an index or in this case I used an ETF named SPY. The trouble is when you do that, as far as I understand, Amibroker does the opposite of Pad and Align in that for the foreign stock SPY, it will align the bars for it, to the stock. It removes any bars for SPY when the bars for the stock are empty.

For my second attempt, what I did was to detect the number of days passed between each bar of the stock. For weekdays, you'll normally have 1 day between each bar, but on weekends you'll get 3 days between bars. Then if there is a holiday or two, you can get as many as 5 days between bars. To get around this, I averaged the days between bars, then I would look at the highest high for that average for the last N days and my filter was equal to 1 as long as the highest high didn't exceed a certain amount.

If anyone has a better or cleaner method to do this, I'd love to see it. Below here are examples of my two attempts:

Comparing the number bars in the last N days between a stock and an ETF (doesn't work):

//ADXN is good stock for testing
Lookback = 100;//how many days to look back
LookBackDay = IIf( Now( 10 ) - lookback < 0, Now( 10 ) - lookback + 365, Now( 10 ) - lookback ); //adds 365 days if you cross back into the previous year
DayNumber = DayOfYear();
CrossDay = cross( DayNumber, LookBackDay );
StockCheck = BarsSince( CrossDay );

SetForeign( "SPY" );
ETFCheck = BarsSince( CrossDay );
RestorePriceArrays();

Filter = 1;
AddColumn( Close, "Close", 1.2 );
AddColumn( LookBackDay, "LookBackDay", 1.0 );
AddColumn( DayNumber, "DayNumber", 1.0 );
AddColumn( CrossDay, "CrossDay", 1.0 );
AddColumn( StockCheck, "StockCheck", 1.0 );
AddColumn( ETFCheck, "ETFCheck", 1.0 );

Switch based on the average number days between bars in the last N days (works):

//ADXN is good stock for testing
//Make sure that pad and align is turned off
Today = DateTime();
PreviousBar = Ref( Today, -1 );
DateChange = DateTimeDiff( Today, PreviousBar ) / 60 / 60 / 24;//will go up as high as 5 for Easter
ChangeinNDays = Sum( DateChange, 8 );//adds up the day changes in the last 8 bars
ChangeHigh = HHV( ChangeinNDays, 8 );//takes the highest sum over the last two weeks so you're not turning off on Mondays etc
IndexFilterOn = ChangeHigh < 15;
Swti
Plot( DateChange, "Date Change", colorWhite, styleLine );

Filter = 1;
AddColumn( DateChange, "DateChange", 1.0 );
AddColumn( ChangeinNDays, "ChangeinNDays", 1.0 );
AddColumn( ChangeHigh, "ChangeHigh", 1.0 );
AddColumn( IndexFilterOn, "IndexFilterOn", 1.0 );


//Can be shortened to a single line as such:
//IndexFilterOn = HHV( Sum( DateTimeDiff( DateTime(), Ref( DateTime(), -1 ) ) / 60 / 60 / 24, 8 ), 8 ) < 15;

Is there a reason you don't want to turn on Pad & Align? Then you could just count the number of bars where volume is 0 (no trades).

1 Like

@Marcel It looks like @mradtke beat me while I was typing, I may not understand what you are really trying to do but you wrote

To me that means insufficient VOLUME. And yet I look at your codes and do not see any attempt to quantify Volume.

Also I don't think you are correct with your thoughts on Pad & Align

Pad & Align will not remove bars for your reference symbol.

Run an Exploration with Pad & Align off and the try again with it on. You will see how the bar index is changing to either match your reference symbol data (when Pad & Align is on) or your current symbol data with Pad & Align off.

VolumeFilter = 1; // 1 share traded
lbPeriods 	 = 100; // look back periods, how far back do you want to look

Illiquid 	 = V < VolumeFilter;
TotalNoTrade =  Sum(V < VolumeFilter, lbPeriods) ; // total the number of days with no Volume

Filter=1;
AddColumn(C, "Close");

AddColumn(V, "Volume");
AddColumn(Illiquid, "Illiquid", 1.0, colorDefault, IIf(Illiquid, colorRed, colorDefault));
AddColumn(TotalNoTrade, "Days with too low V", 1.0);
AddColumn(BarIndex(), "Bar Index");

Pad an Align on (with my database's SPY)
image

Pad & Align off
image

2 Likes

I think what Marcel was trying to explain is that when you call SetForeign(), the foreign symbol essentially gets padded and aligned to the current symbol.

1 Like

@mradtke thanks for clarifying Matt. Can we reassure @Marcel that he is misinformed? The foreign symbol will not have any bars "removed" just because his stock is not trading.

// Marcel is worried about Pad & Align removing data from P&A symbol
SetForeign( "SPY" );
PadClose = Close;
RestorePriceArrays();

image

1 Like

Yes, that's correct Matt, the foreign symbol gets padded and aligned to the current symbol.

Larry, I'm seeing different results when I run an exploration or your code, the top with Pad and Align on referring to SPY, the bottom one off. I'm still trying to figure out the difference.
Annotation 2020-07-20 164232

@Marcel I'm stumped. I have a work related zoom meeting in a few minutes, but happy to talk to you later if you haven't figured it out.

1 Like

Larry, you're not also using Norgate's Date Padding functionality, are you?

3 Likes

Matt, yes, I was trying to see if I can avoid using pad and align. If a stock is missing a long period of bars, pad and align will add a number of artificial bars to that stock. Those artificial bars can change the indicators for that stock and you can end up with false signals. On the other hand, when you use an index filter, without pad and align, if the stock is missing bars, the index will miss those bars too and the index filter can be calculated incorrectly. In both instances, the problem is related to missing bars for the stock. If I can detect a number of missing bars for a stock, I can simply make a filter to turn off trades for that particular stock. Then I won't need to use pad and align.

For my own trading, I have the dollar volume filters set high enough that it would be rare for me to see missing bars of data. This is more for some Australian trading friends that I work with who will trade some of the more thinly traded stocks on their market.

I'm using Metastock data so it might be the difference between Metastock and Norgate that our results don't match.

@mradtke yes Matt that was probably the cause of the difference. I have the Norgate database padding on. Thanks for reminding me of that as I had not looked at that functionality in a long time. With that padding I do get days on Marcel's suggested symbol (ADXN) that have only a few shares trading Volume, appear. Without the Norgate padding on and only with the AmiBroker Pad & Align, those dates disappear.

1 Like

@Marcel: If you don't intend to trade stocks with lots of missing bars anyway, then it doesn't really matter if the indicator calculations are a bit off because you've used P&A, does it?

1 Like

That's true Matt, for my own trading, I typically have to set the dollar volume filter to 1 million or more so I won't likely see any empty bars. It's more I'm trying to help my friends that trade the Australian markets where they'll work with more thinly traded stocks. The filter that measures the average number of days between bars that I posted at the start does work, I was just trying to think of something a little neater.

1 Like

Here's another variant where I measure the days between bars, except for Monday, I correct for 2 days. For Tuesday, if the days between bars is greater than 3, I correct for 3 days in case it was a long weekend. Then if the 10 day average change exceeds 1.3 it would have to be due to empty bars and the filter turns off.

//ADXN is good stock for testing
//Make sure that pad and align is turned off
Today = DateTime();
PreviousBar = Ref( Today, -1 );
Change = DateTimeDiff( Today, PreviousBar ) / 60 / 60 / 24;//will go up as high as 5 for Easter
DateChange = IIf(DayOfWeek()==1,Change-2,Change);//remove weekend
DateChange = IIf(DayOfWeek()==2 AND Change > 3,Change-3,DateChange);//remove long weekend with a Monday
AverageChange = MA( DateChange, 10 );//average day change for the last 10 bars
IndexFilterOn = AverageChange < 1.3;
Plot( DateChange, "Date Change", colorWhite, styleLine );

Filter = 1;
AddColumn( Change, "Change", 1.0 );
AddColumn( DateChange, "DateChange", 1.0 );
AddColumn( AverageChange, "AverageChange", 1.2 );
AddColumn( IndexFilterOn, "IndexFilterOn", 1.0 );
1 Like

This is a line I've come up with to check for recent empty bars. It appears to work. I'm just posting it to share and to invite any opinions:

NoRecentEmptyBars = MA( DaysSince1900() - Ref(DaysSince1900(),-1) - IIf(DayOfWeek() == 1,2,0) - IIf( DayOfWeek() == 2 AND DaysSince1900() - Ref(DaysSince1900(),-1) > 3,3,0,10) < 1.3;

Basically, the line is measuring the 10 day average difference of the days since 1900 between two successive bars. Every Monday, that difference will be 3 days, so it subtracts 2. If a holiday falls on a Monday, the difference will be 4 days between the Tuesday and Friday, so it subtracts 3. You can have other holidays in that 10 day period that can increase the difference so it looks if the average difference is more than not just 1, but 1.3. If it sees an average difference greater than 1.3, that should indicate that there are a lot of empty bars in the last two weeks and so you wouldn't want to trade that particular stock.

1 Like