Market Trading Days vs Stock Trading Days

Hi All,

first of all apologies for such basic question , but i am rather unable to perform the folowing. for my exploration.

Count the Market Trading Days YTD which is different from a Stock Trading Days if the stock listed ,say, 3 months ago from current date. i.e
Market Trading Days 215 days YTD
Stock Trading Days 90 days YTD

MarketIndex = "COMPOSITE"
SetForeign(MarketIndex);
marketTradeDaysYTD =  LastValue(BarsSince(Year() != Ref(Year(),-1)));
RestorePriceArrays();


//stock days
//stock trading days and active days
stockTradeDaysYTD = LastValue(BarsSince(Year() != Ref(Year(),-1)));
if(stockTradeDaysYTD == 0) {
	newIPO = 1;
	stockTradeDaysYTD = BarCount;
}

the code gave the same value for both Market and Trading days which not what I need.
please help me with some pointers.

Thanks In Advance

@fourier here is a sample exploration that shows the number of traded days, inferring that the traded stocks that have bars only in the current year are IPOs

// Code sample to be used as an exploration
// Select "1 recent bar" and apply to "All Symbols"
tradedDays = BarCount;
years = Year();
firstYear = years[0];
lastYear = years[BarCount - 1];
sIPO = "";
isIPO = ( lastYear == firstYear ) AND( lastYear == Now( 8 ) );
if( isIPO ) {
    tradedDaysYTD = tradedDays;
    sIPO = "IPO";
} else {
	// Counter start at zero - adding 1
    tradedDaysYTD = 1 + LastValue( BarsSince( Year() != Ref( Year(), -1 ) ) );
}
// Exclude tickers that starts with a certain char like ^ or ~  
charTicker = StrLeft(Name(), 1);
isSpecialTicker = StrFind("~^", charTicker); 
Filter = 1 AND !(isSpecialticker); 
AddTextColumn( sIPO, "IPO" );
AddTextColumn( StrFormat( "%5.0f", tradedDays ), "Traded days" );
AddTextColumn( StrFormat( "%5.0f", tradedDaysYTD ), "Traded days YTD" );
SetSortColumns( -3, 4); // order by IPOs and least number of traded days

I’m not sure that It wil work properly in every case but it may be a starting point.
And as usual, probably there are other smarter/faster ways to achieve it!

@beppe thanks for the code, but I think I didn’t make my self clear, what I am after is the following information. , not necesssarily if the ticker is ipo or not, i am more interested in gathering
1 Ticker Traded Days YTD
2 Market Traded Days YTD , (should be a constant number for each ticker, since this is counting the days the Market is Traded YTD)

[Ticker] [ TickerTradedDaysYTD] [ MarketTradedDaysYTD]
   aa              100                      230

thanks

@fourier, unfortunately, I do not have a solution for you.

Anyway, today I reworked a little the exploration since I think it is still useful to understand how some things work in Amibroker

Actually, my code using the BarCount seems to work properly only in exploration mode.

I found this note of TJ in an old post in the AmiBrokerYahooGroup (message #114917)

BarCount does not represent the number of bars in the DATABASE,
but in the ARRAYs in the current execution.
The arrays have DYNAMIC size depending on zoom factor. That’s why BarCount may and will vary.

And it DOES NOT change when you call the SetForeign function so I had to discard its use (see the commented out code).

So, using my code, in order to set properly the “marketTradedDaysYTD” number you are forced to include the composite/market ticker in the list of the symbols used for the exploration (in my sample code it is hard-coded as “SPY”). It is actually set - with a specific conditional if - only when the exploration will reach/process this ticker. Far from an optimal solution…

To show its value, I simply use a sort at the end of the exploration to bring the only line with a value to the top; all the other tickers will have an empty column.

So, in the end, my sample exploration is only providing some “stats” about the current database tickers.
For instance, it could be used to see recent IPOs, not recently traded tickers (delisted?), infrequently traded names (with missing bars) and the long-standing ones.

// This is a code sample to be used as an exploration:
// Select "1 recent bar" and apply to "All Symbols" in Daily Interval
// Use it to see what tickers were recently introduced in the database (IPOs)
// and/or are no longer trading and to see the long-standing ones

Version(6.27); 
SetOption("RequireDeclarations", True);

SetBarsRequired( sbrAll, sbrAll );

// Pass parameters by reference
procedure tradedDaysStats(tradedDays, tradedDaysYTD, IPO, firstTradedDate, lastTradedDate) {
	local firstYear, lastYear, years, dt;
	
	tradedDays = BarCount;
	years = Year();
	firstYear = years[0];
	lastYear = years[BarCount - 1];
	IPO = False;
	dt = DateTime();
	firstTradedDate = dt[0];
	lastTradedDate = dt[BarCount-1];
	if ((lastYear == firstYear ) AND( lastYear == Now( 8 ))) {
		IPO = True;
		tradedDaysYTD = tradedDays;
	} else {
		// Counter start at zero - we need to add 1
		tradedDaysYTD = 1 + LastValue( BarsSince( Year() != Ref( Year(), -1 ) ) );
	}
}

local IPO, marketTradedDaysYTD, firstTradedDate, lastTradedDate, tradedDays, tradedDaysYTD;
local chartTicker, isSpecialTicker;
global Filter;

IPO = False;
marketTradedDaysYTD = Null;
firstTradedDate = Null;
lastTradedDate = Null;
tradedDays = Null; // Total days in the database
tradedDaysYTD = Null; 


if (Name() == "SPY") {  // set it for your composite
	tradedDaysStats(&tradedDays, &marketTradedDaysYTD, &IPO, &firstTradedDate, &lastTradeDate);
}

/* THIS DOES NOT WORK..... 
// Cannot call it since BarCount will be the one of the currently processed ticker
SetForeign("SPY");
tradedDaysStats(&tradedDays, &marketTradedDaysYTD, &IPO, &firstTradedDate, &lastTradeDate);
RestorePriceArrays();
*/

// Explore sample
tradedDaysStats(&tradedDays, &tradedDaysYTD, &IPO, &firstTradedDate, &lastTradedDate);
chartTicker = StrLeft(Name(), 1); // Skip index some tickers / composites, etc.
isSpecialTicker = StrFind("~^", chartTicker); 
Filter = 1 AND !(isSpecialticker); 
AddTextColumn( StrFormat( "%3.0f", tradedDaysYTD), "Traded YTD days");	
if (IsNull(marketTradedDaysYTD))
	AddTextColumn( "", "Market YTD days" );	
else
	AddTextColumn( StrFormat( "%3.0f", marketTradedDaysYTD), "Market YTD days" );	
AddTextColumn( StrFormat( "%1.0f", IPO), "IPO" );	
AddTextColumn( StrFormat( "%5.0f", tradedDays), "Traded days" );	
AddTextColumn( DateTimeToStr(firstTradedDate, 4), "First trade Date");
AddTextColumn( DateTimeToStr(lastTradedDate, 4), "Last trade Date");
SetSortColumns(-4, -5, 3); // order market YTD, then IPOs, then least traded

Note that in my user-definede procedure named “tradedDaysStats()” I use the pass by reference variables so it requires a recent version of AmiBroker.

I wish some true expert will join us to give you the proper solution and/or to further clarify how to properly accomplish what you want.

@fourier I don't have an answer for your problem but wanted to point you to what I think is a mistake in your code. In calculating the number of trading days this year, you need to review how the BarsSince function does it calculations (need to +1).

The accurate count can be debugged with an Exploration that does not use LastValue.

// the proper way
TradeDayOfYear = BarsSince( year() > Ref(year(),-1) )+1;

// vs.
marketTradeDaysYTD =  BarsSince(Year() != Ref(Year(),-1));

image

And I suspect your output finding equal numbers for your ipo stock and market index being equal may have something to do with checking the box "Pad & align" in the Backtester settings
image

I tried couple of experimental codes and reread SetForeign and Foreign function exmple and notes and I think when using SetForeign(ticker) , even when the foreign ticker contains
data , "longer" than currently selected symbol , whats made available are overlapping bars
Explained by TJ , in the notes under Foreign function.(see image)
with that , I think I will need to hardcode the market trading days baseline.

Thank you @beppe @portfoliobuilder i.e
pseudo code

BarCount 0 =  200;
BarCount 1 =  50; 
//whats available is 
Bars 0 : 150 - 199; //using foreign function whats available
Bar 1   , 0-49  ; //(currently selected)

code to test my hypothesis.


//set at the begining to ensure 
//all price arrays is the compsites
MarketIndex = "COMPOSITE";
SetForeign(MarketIndex);

secondsInDay = 60*60*24;
myYear = NumToStr(LastValue(Year()),1.0,False);
StartDateStr = myYear+"-01-01";//YYYY-MM-DD
StartDate= _DT(StartDateStr);// I am following examples of lookup in reference,I use a string;
EndDate = _DT(Now(1));
TimeDiff = DateTimeDiff(endDate,StartDate); // returns Seconds
TimeDiffDays = Ceil(TimeDiff/secondsInDay)+1; // ConvertToDays days rounding eror add 2

marketTradeDaysYTD = 0;
nextDay = StartDate;
firstNonNullVolumeDate = 0; // store date hitting first non null volume
hitNonNull = 0; // latch to avoid nonNull being reassigned
closeFirstDay = 0;
for(i=0 ; i < TimeDiffDays ; i++){
	volOnDate = LookUp(Volume,nextDay,0);
	if(volOnDate > 0){
		marketTradeDaysYTD+=1;
		if(hitNonNull == 0){
			closeFirstDay = Lookup(Close,nextDay,0);
			firstNonNullVolumeDate = nextDay;
			hitNonNull = 1;
		}
	}
	nextDay = DateTimeAdd(nextDay,1,inDaily);
}

RestorePriceArrays();
Filter =1 ;

AddColumn(TimeDiffDays,"#DaysYTD",1.0);
AddColumn(firstNonNullVolumeDate,"1stNonNullVolumeDate",formatDateTime);
AddColumn(closeFirstDay,"CloseOnDate",1.2);
AddColumn(marketTradeDaysYTD,"marketTradedDays",1.0);

Notes From TJ about Foreign and Synchronization

image

rPlease Ignore Previous Reply/Post
I tried couple of experimental codes and reread SetForeign and Foreign function exmple and notes and I think when using SetForeign(ticker) , even when the foreign ticker contains
data , "longer" than currently selected symbol , whats made available are overlapping bars
Explained by TJ , in the notes under Foreign function.(see image)
with that , I think I will need to hardcode the market trading days baseline.

Thank you @beppe @portfoliobuilder for spending your precious time
pseudo code

BarCount 0 =  200;
BarCount 1 =  50; 
//whats available is 
Bars 0 : 150 - 199; //using foreign function whats available
Bar 1   , 0-49  ; //(currently selected)

Notes From TJ about Foreign and Synchronization

image

code to test my hypothesis.


//set at the begining to ensure 
//all price arrays is the compsites
MarketIndex = "COMPOSITE";
SetForeign(MarketIndex);

secondsInDay = 60*60*24;
myYear = NumToStr(LastValue(Year()),1.0,False);
StartDateStr = myYear+"-01-01";//YYYY-MM-DD
StartDate= _DT(StartDateStr);// I am following examples of lookup in reference,I use a string;
EndDate = _DT(Now(1));
TimeDiff = DateTimeDiff(endDate,StartDate); // returns Seconds
TimeDiffDays = Ceil(TimeDiff/secondsInDay)+1; // ConvertToDays days rounding eror add 2

marketTradeDaysYTD = 0;
nextDay = StartDate;
firstNonNullVolumeDate = 0; // store date hitting first non null volume
hitNonNull = 0; // latch to avoid nonNull being reassigned
closeFirstDay = 0;
for(i=0 ; i < TimeDiffDays ; i++){
	volOnDate = LookUp(Volume,nextDay,0);
	if(volOnDate > 0){
		marketTradeDaysYTD+=1;
		if(hitNonNull == 0){
			closeFirstDay = Lookup(Close,nextDay,0);
			firstNonNullVolumeDate = nextDay;
			hitNonNull = 1;
		}
	}
	nextDay = DateTimeAdd(nextDay,1,inDaily);
}

RestorePriceArrays();
Filter =1 ;

AddColumn(TimeDiffDays,"#DaysYTD",1.0);
AddColumn(firstNonNullVolumeDate,"1stNonNullVolumeDate",formatDateTime);
AddColumn(closeFirstDay,"CompositeCloseOnDate",1.2);
AddColumn(marketTradeDaysYTD,"marketTradedDays",1.0);

exploration Result
image

1 Like

@fourier interesting solution!

Unfortunately, on my system (AB 6.27.1 beta - 64 bits) the formula did not work properly since the Lookup function always returned Null values.
I used this snippet to find the reason (probably a bug or something else changed in the last betas)

StartDateStr1 = "2017-01-01 00:00:00";
StartDate1 = _DT( StartDateStr1 );
// This will result in "2016-12-31 23:00:00"; // AB 6.27.1 64Bits - OK on 6.10.0 32 bits 
StartDateStr2 = DateTimeToStr( StartDate1, 3 );
StartDate2 =  _DT( StartDateStr2 );
sameDateStr = StartDateStr1 == StartDateStr2; // False
sameDate = StartDate1 == StartDate2; // False
printf( "StartDateStr1 : " + StartDateStr1 + "\n");
printf( "StartDateStr2 : " + StartDateStr2 + "\n");
printf( "StartDate1    : %g\n", StartDate1 );
printf( "StartDate2    : %g\n", StartDate2 );
printf( "Same date str : %2.0f\n", sameDateStr );
printf( "Same date     : %2.0f\n", sameDate );

The above works properly in version 6.10.0 32 bits - NOT in 6.27.1 64 bits

Just in case someone else wants to test your code using the above beta version, I modified a bit the first part of your code in this way:

MarketIndex = "SPY";
SetForeign(MarketIndex);

// replace the next 7 lines of code with this:
	dt = DateTime();
	dy = DayOfYear();
	daysYTD = LastValue(dy); // This returns the day of the YEAR of the last bar not absolute....
	endDate = dt[BarCount-1];
	startDate = DateTimeAdd(endDate, (-daysYTD+1), inDaily);
	TimeDiffDays = DaysYTD; 

// from here your original code
marketTradeDaysYTD = 0;
nextDay = StartDate;

In any case, at least for me, this was a good exercise to explore some functions I never used before!

1 Like

good Deal @beppe , it sure was an interesting exercise, corrected my assumptions about Foreign function, at least in my version of AM , i had always though Foreign made available all data points
of the foreign ticker, but this exercise proved otherwise.

thanks again