#include in an if statement

I'm trying to make an AFL that others could use with either Norgate or Metastock data without having to make changes in the code. I tried coding it like in the example below but I get errors because of the line with #include. Would anyone know how I could do it? Thank you.

dbname = GetDatabaseName();
if (StrFind(dbname, "Norgate"))
{
Index = Foreign("$SPX","C",True); 
#include_once "Formulas\Norgate Data\Norgate Data Functions.afl"
}
else
{
Index = Foreign(".SPX","C",True); 
}

As per Norgate Data document, put the #Include_once statement at the top of your AFL file (unconditionally).

2 Likes

First and foremost: DO NOT INCLUDE STUFF that you do NOT use.

If you are not using Norgate functions, don't #include them.

As to placing #include inside if() - it is not really good idea.

All commands that begin with # (hash) are pre-processor commands. That means that they are processed BEFORE formula is executed. The same happens with #include.
#include causes the text of the formula to be literally included (like "copy-paste here") in the place where you have #include command.
This means that it is included BEFORE evaluation of any if() statements happens.
Also keep in mind that if included file contains function definitions they can't be inside if() statements, like this:

if( condition )
{
   // INVALID CODE - you can not have function definition inside if()  
   // the definition placed here would generate Error 30 (syntax error)
   function test() 
   {
      return 0;
   }
}

So as @TrendSurfer wrote, assuming that Norgate Data Functions.afl contains function definitions you should place the #include outside.

3 Likes

Thank you so much for your input. I was hoping to write it so that someone with either Norgate or Metastock data could use it without making code changes but that's not possible. I'll just write it like this:

#include_once "Formulas\Norgate Data\Norgate Data Functions.afl"//comment line out if using Metastock data

dbname = GetDatabaseName();
if (StrFind(dbname, "Norgate"))
{
Index = Foreign("$SPX","C",True); 
}
else
{
Index = Foreign(".SPX","C",True); 
}

@Marcel Not sure what else your formula is doing, but you should only need that #include if you're calling a Norgate-specific function like NorgateIndexConstituentTimeSeries(). You don't need the #include to just use OHLCV data from a Norgate database.

3 Likes

Without if-else

//comment line out if using Metastock data
//#include_once "Formulas\Norgate Data\Norgate Data Functions.afl"

dbname = GetDatabaseName();
ticker = StrExtract(".SPX,$SPX",StrFind(dbname, "Norgate"));
Index = Foreign(ticker,"C",True); 
3 Likes

@Marcel, why are you #including something that you are NOT using?
If you don't call Norgate* functions why do you want to include those functions? It is pointless and uses resources for no purpose.

I don't know from where you got those #include bits but if you got them from Norgate docs, it means that they need to be updated to say NOT to include functions that are NOT used.
Also if you want to have your formula independent of data source you should AVOID using any 3rd party plugins and functions exposed by those plugins.

Our documentation is clear that you need to add #inlcude_once line to use Norgate-provided functions, plus a list of the functions is provided:

image

The full document is here:
https://norgatedata.com/amibroker-usage.php

1 Like

Folks...

He's trying to write code that could be given to EITHER Norgate users or Metastock users, without them having to change the code, and the code "just works" (just like Apple). I happen to know the target users, and making the code work with as little change as possible is a good thing.

Most of these "answers" seem off topic, although the explanation about how #include works is useful.

Marcel, if you instructed all the Metastock users to 1) create a Norgate Data directory under Include, and 2) put an empty Norgate Data Functions.afl file in that directory, the #include should not fail. You'd then have to wrap the Norgate function calls in if statements, and dummy up the return values for Metastock users.

Otherwise just instruct them to comment out the #include statement as you have done. That solution is in fact probably the simplest approach.

That's just best practice. The #include could in fact be anywhere before the calls to the Norgate functions.

Yes I know, but as @Tomasz wrote you don't wont to activate resources that you don't need and you should call #include statements unconditionally.

So I would create two separate versions of the code, a Norgate Data version and a Metastock version or as you wrote, one version and users comments out non-required command lines.

Yes.

Yes but as you wrote the best practice is to place it at the top of the file (and unconditionally).

@LinusVanPelt, yes, that's correct, I'm trying to write code so it can be given to either Norgate or Metastock users without them having to change the code. The #include line will produce an error for someone using Metastock as they won't have the file for it to refer to.

Matt's comment where he questioned if the #include is necessary made me think. I need to have #include line when I'm creating a trading system with Norgate and I want to refer to delisted stocks, index constituents etc. However, when someone else uses the code for the actual trading of a system, there's no need to use anything that's in the #include file. I can omit that line.

I can shorten the code even more by using the line suggested by fxshrat.

Thank you all for your help!

That won't work, because when AmiBroker does its syntax check it follows every path in the code whether it can actually be reached or not. See the example below where the function "ris" cannot possibly be executed, yet still causes AB to report the error. So all the non-existent Norgate functions would fail the syntax check on a system without Norgate installed.

image

1 Like

The point is that in that case you simply can NOT use Norgate* specific functions, because Metastock does NOT HAVE them.

What you should do is to have ONE separate include file that is vendor-specific.
For example, create a file that is named vendor_support.afl and that would be the ONLY file that is different depending on given database vendor. Other vendors that don't have specific features would return say 0. So

// vendor specific code for Norgate
function MySupportFunction( void )
{
  // use NORGATE functions 
 return NorgateWhateverFunction(....);
}
// vendor specific code for Metastock (does nothing)
function MySupportFunction( void )
{
  return 0;
}

And then in your code you would only include that single

#include_once <vendor_support.afl>
... your formula

So every file would be the same except a single vendor-specific file and you will tell the users to install correct one.

Generally speaking, to do what you are after in a SINGLE file, you would need a pre-processor conditional statements such as C-like #ifdef/#endif but at the moment AFL's pre-processor is super simple (but fast) and does not offer #ifdef Over the course of 20 years I have only heard one person who wanted that, so it is pretty low on priorities list.
Pre-processor conditionals are fundamentally different than regular if() because they are processed BEFORE formula is executed and actually transform the source file so parser does NOT see them (the same way as parser does not see any #include or other # preprocessor commands). See: C preprocessor - Wikipedia
Full-blown C preprocessor is slower than AFL simple one and I am not very keen to slow down the system for some rarely-used stuff.

3 Likes

I tried taking the functions out of the Norgate Include file and into the regular AFL so that I could share the same code with my trading group that use both Metastock and Norgate data. It works with both Norgate and Metastock. It's an interesting challenge to figure it out and a good way to learn how things work, but in reality, as I wrote in an earlier post, it's not really necessary. I only need to use the Norgate functions in the include file, when I'm creating and testing a system using historical index constituents and delisted stocks. When I share the complete system with someone else and they just want to trade it, they only need to use the current data and all those functions won't be needed. Here's my example code to share just in case anyone else is interested:

function NorgateIndexConstituentTimeSeries(indexname)
{
	local norgateresult;
	norgateresult = zzzNorgateDataIndexConstituentTimeSeries(indexname);
	return norgateresult;
}
function NorgateOriginalCloseTimeSeries()
{
	local norgateresult;
	norgateresult = zzzNorgateDataOriginalCloseTimeSeries();
	return norgateresult;
}
function NorgateOriginalVolumeTimeSeries()
{
	local norgateresult;
	norgateresult = zzzNorgateDataOriginalVolumeTimeSeries(); 
	return norgateresult;
}

dbname = GetDatabaseName();
if (StrFind(dbname, "Norgate"))
{
IndexTicker = "$SPX";
OrdinaryFilter =StrFind( FullName()," Common");
OC = NorgateOriginalCloseTimeSeries(); 		// NDU datafeed function for original unadjusted Closing price
OV = NorgateOriginalVolumeTimeSeries(); 	// NDU datafeed function for original unadjusted Volume
OnSecondLastBarOfDelistedSecurity = !IsNull( GetFnData( "DelistingDate" ) ) AND( BarIndex() == ( LastValue( BarIndex() ) - 1 ) OR DateTime() >= GetFnData( "DelistingDate" ) ) ;
OnLastTwoBarsOfDelistedSecurity = !IsNull( GetFnData( "DelistingDate" ) ) AND( BarIndex() >= ( LastValue( BarIndex() ) - 1 ) OR DateTime() >= GetFnData( "DelistingDate" ) );
Constituent = NorgateIndexConstituentTimeSeries( IndexTicker ) ;
}
else
{
IndexTicker = ".SPX";
OrdinaryFilter = StrFind( FullName()," ORD");
OC = Close; 		
OV = Volume;
OnSecondLastBarOfDelistedSecurity = 0;
OnLastTwoBarsOfDelistedSecurity = 0;
Constituent = 1;
} 
MinimumTurnover = 1;//in millions
DollarVolume = ( OC * OV );
Liquid = EMA( DollarVolume, 20 ) > MinimumTurnover * 1000000
         AND Constituent 
         AND NOT OnLastTwoBarsOfDelistedSecurity;  
Buy = Cross(30, RSI(5)) AND OrdinaryFilter AND Liquid;
Sell = Cross(RSI(5), 70) OR OnSecondLastBarOfDelistedSecurity;
Short = Cover = 0;
PositionScore = 100 - RelStrength( IndexTicker, 1 );

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