Checking if value is in a set

Hi AFL Community,

I'm new to AFL, so I apologize if this is a basic question, but I had trouble figuring out an efficient way of doing it and didn't see anything explaining it in the docs. I am attempting to place trades only on certain days of the year. I've already gotten the date value. Essentially, I would like to place trades when that value is contained in a user defined set of arbitrary numbers. In Pseudocode, it would be:

set = {3, 5, 6, 14, 28, 32, 42}
Buy = (IndicatorValueToday is contained in set)

I'm sure I could brute force my way through it (indicator ==x[0] or indicator==x[1] or...), but is there a more streamlined way of writing this expression, particularly for larger sets? If I missed an obvious explanation, please let me know what I should be looking for.

Best,
Thomas

http://www.amibroker.com/kb/2014/09/30/gen-backtest-from-a-file/

1 Like

Hi @tdmtrader,

Can you pls provide more info/ clarity on what you're attempting to achieve.

I think what you’re trying to do, is to open a trade on particular days of the year.

For example:
You’ve done some analysis of the data for a particular stock, and found that certain days of the year are more favourable than others. So you ‘ve made a list of those special days/ dates, using something like a DayOfYear() function, where day number is 1..366, and now want AmiBroker to highlight on chart the quote/s that fall on those special days.

When the latest quote is displayed, at the right-hand-edge of the chart, and is highlighted as a special day, that’s your alert to open a trade.

Or, is @Tomasz’s suggestion what you want?

Hi Phase,

Your description is what I want. I've hardcoded in a variable called set, as follows:

set[0] = 5
set[1] = 11
set[2] = 22
etc.

I've also got code that gives me the trading day of the year:
tradingDayOfYear = BarsSince(Year() > Ref(Year(), -1)) + 1;

The code:
Buy = tradingDayOfYear==set[0] OR tradingDayOfYear==set[1] OR tradingDayOfYear==set[2] OR...

does what I want, but seems very repetitious, especially for large sets. Is there a better way to write that? I've tried a loop, but if conditions can't be applied to the array tradingDayOfYear. IIf looks like it would work if I had just one condition (e.g., IIf (tradingDayOfYear == 22), but again I'm not sure how to loop over several conditions into a single variable.

@tdmtrader, if the loop performance is acceptable, you could use a string as a convenient way to represent your set.

but if conditions can't be applied to the array tradingDayOfYear.

When you decide to use a loop you need to use the [ ] to access the individual array elements.

// define your set as a comma separated string (no spaces) with a comma before and at the end
good_days = ",22,34,45,78,126,192,"; // you'll search for ",xxx,"
tdy = BarsSince( Year() > Ref( Year(), -1 ) ) + 1;

bir = Status( "BarInRange" );
for( i = 0; i < BarCount; i++ )
{
	// iterates only bars within the Analysis selected range
    if( bir[i] ) 
        Buy[i] = ( StrFind( good_days, StrFormat( ",%1.0f,", tdy[i] ) ) > 0 );
}

Filter = 1;
AddColumn( tdy, "Trading Day Of Year", 1 );
AddColumn( IIf( Buy, Buy, Null ), "Buy", 1 );
3 Likes

/// @link [https://forum.amibroker.com/t/checking-if-value-is-in-a-set/10694/5](https://forum.amibroker.com/t/checking-if-value-is-in-a-set/10694/5)

Buy = 0;
doy = DayOfYear();

set = MxFromString("[3;5;6;14;28;32;42]");

for( i = 0; i < MxGetSize(set,0); i++ )
    Buy ||= (doy == set[i][0]);

Plot( C, "Price", colorDefault, styleBar );
PlotShapes( Buy*shapeCircle, colorOrange, 0, L, -12 );

7 Likes

@fxshrat code is nice, with just one small improvement. For what it is worth, you will get slightly better performance if you don't call MxGetSize() function in each iteration step. Use temporary variable in init part of loop instead.

for( i = 0, size = MxGetSize( set, 0 ); i < size; i++ )
    Buy ||= (doy == set[i][0]);

But actually it may be much faster just to use the simplest way, single StrFind call without any loops:

set = "3, 5, 6, 14, 28, 32, 42,"; // make sure to place comma also after LAST item

Lookup_value = ...

present_in_set = StrFind( set, StrFormat( "%g,", Lookup_value ) );

We just look for number and comma that comes at the end. It is simple and just requires to make sure to have comma also after last number in the set.

StrFind is very fast as it hugely benefits from CPU on-chip cache and linear sequential access.

9 Likes

@fxshrat and @Tomasz thanks for sharing this!

2 Likes

Thanks. The Buy ||= works well.

Regarding the second, more optimized version:

present_in_set = StrFind(set, StrFormat("%g," Lookup_value))

Can that work if Lookup_value is an array? It works fine if I specify a specific a value (Lookup_value=5), or if I specify text, but it does not work for my array:

Lookup_value = BarsSince(Year() > Ref(Year(), -1)) + 1;

I've tried referencing a specific value (Lookup_value[0]), as well as a positional reference (Ref(Lookup_value, 0), and neither seem to get me any matches.

Thanks. I appreciate your help understanding how this works.

Is that example (without any loop) meant to be for single numbers/elements only (so e.g LastValue(Buy), SelectedValue(Buy)), since there is not array of strings in AFL?

So e.g. this one would look for validity at last bar.

set = "3, 5, 6, 14, 28, 32, 42,"; // make sure to place comma also after LAST item

Lookup_value = LastValue(DayOfYear());// SelectedValue(DayOfYear());

present_in_set = StrFind( set, StrFormat( "%g,", Lookup_value ) );

Buy = present_in_set;

Currently scratching my head how to check for validity within entire array without loop since he seems to be looking for Buy array.


that's valid objection (as usual). Thank you. And normally I do assign row and column sizes outside of looping (also since they may be reused in further code after loop). But here I admit for the sake of example via mail I have been too lazy doing it that way. I'm going to say the rosary...asking for forgiveness...

Saved as formula editor code snippet.

// not related to thread topic
// just generic mat example....
mat = Matrix( 5, 5 ); 

rownum = MxGetSize( mat, 0 );
colnum = MxGetSize( mat, 1 );

for ( i = 0; i < rownum; i++ ) {
    for ( j = 0; j < colnum; j++ ) {
        // do something
    }
}

@tdmtrader - really good question. I'm also a bit stumped.

...

For illustrative purposes only, this is how you might do it in R:

# Create a list of days in a year
dayOfYear <- 1:366

# Define a set of special days, eg. public holidays
set <- c(3, 5, 6, 14, 28, 32, 42)

# Is the day of the year in the list of special days
isSpecial <- dayOfYear %in% set

# Combine the two vectors into a single matrix, so that we can see which days are special
doyIsSpecial <- cbind(dayOfYear, isSpecial)

# Display the first 30 rows
head(doyIsSpecial, 30)

Which produces:

      dayOfYear isSpecial
 [1,]         1         0
 [2,]         2         0
 [3,]         3         1
 [4,]         4         0
 [5,]         5         1
 [6,]         6         1
 [7,]         7         0
 [8,]         8         0
 [9,]         9         0
[10,]        10         0
[11,]        11         0
[12,]        12         0
[13,]        13         0
[14,]        14         1
[15,]        15         0
[16,]        16         0
[17,]        17         0
[18,]        18         0
[19,]        19         0
[20,]        20         0
[21,]        21         0
[22,]        22         0
[23,]        23         0
[24,]        24         0
[25,]        25         0
[26,]        26         0
[27,]        27         0
[28,]        28         1
[29,]        29         0
[30,]        30         0

@phase21 - To be clear, fxshrat's answer above does exactly what I've described (and what you've coded in R). The key was looping over the set with the Buy ||= condition. The %in% operator from R (or similar in other language- I'm used to python) is essentially what I was trying to replicate, and fxsrhat provided a streamlined way to do it.

By the way, thanks for the clear code fxsrhat, and the improvement on it Tomasz. That's exactly what I needed.

My follow up was regarding the alternative, more optimized version with the StrFind function, not because I need the additional optimization at this point, but because I think the answer will give me some additional clarity on the mechanics of AFL.

Out of curiosity, what does “ | |= “ represent? I went through the manual and found “ | “ which is described as bitwise OR. Google bitwise OR and AND and the wiki page was way over my comprehension.

Its similar to the increment operator, i += 1, if you're familiar with that.

Writing

Buy ||= NewSignal;

is equivalent to writing

Buy = (Buy OR NewSignal);

It's helpful for looping over conditions, as demonstrated by fxshrat earlier in this thread. I'm also finding it makes some of my code a bit cleaner to write. If I've got a set of rules, and I want to experiment with adding a new buy rule, I can just add Buy ||= FinalSignal; to the end of my code, without touching my existing code.

2 Likes

Hi @Meatamega,

Same here. Like @Tomasz said, @fxshrat's "code is nice".

What I think is going on, is that you add a bitwise "Or" ("|") in front of the "|=" operator. But why the extra leading "|"?

I'm still working out the logic of it in my own head - will have to do some experimenting to understand it properly.

| is bitwise OR, while || is logical OR, see http://www.amibroker.com/guide/a_language.html for the details

All those +=, -=, |= are compound-assignment statements that are composed of operator and assignment like this:

Y op= X;

where op is operator like +, -, *, /, %, | (bitwise or), & (bitwise and), || (logical or), && (logical and)

Compound assignments provide shorter form of typical operations with assignment of the result to left-hand operand.

So

Y |= X;

is the same as

Y = Y | X;

while

Y ||= X;

is the same as

Y = Y || X;

which in fact is the same as

Y = Y OR X;

because || is a "C-style" equivalent of OR

3 Likes

@tdmtrader has explained meaning already.

"OR" is logical Operator and equivalent is "||".

Buy ||= condition;
is just short for
Buy = Buy || condition;
or
Buy = Buy OR condition;

https://www.amibroker.com/guide/a_language.html
https://forum.amibroker.com/t/operators-for-chaining-conditions-or/1592/4

1 Like

@Tomasz and @fxshrat, thank you both for the explanations/ clarifications – they’re a big help.

@Tomasz – I can’t find any reference to “|| (logical or), && (logical and)” in the link to the language guide (http://www.amibroker.com/guide/a_language.html), however logical “AND” and “OR” are listed, but not their synonyms.

Also, the compound logical operators "||=" and "&&=" aren't documented, even though their bitwise cousins are.

So, in Operators for chaining conditions: "+" or "||", where is says:

, we could go looking for C-like “easter eggs” in AB, even though they’re not documented in the manual?

Yes, I am sorry, they aren't documented since frankly I prefer people to use more verbose "AND" and "OR" instead of "cryptic" && and ||. And for similar reasons I prefer people to use verbose

X = X AND Y; // readable by anyone

than little cryptic:

X &&= Y; // tech-nerd version :-)

AFL supports && and || for one reason: C/C++ programmers. As habits are strong it is habitual thing for C/C++ programmers to write && and ||, so I decided to support C-like syntax as well,
without "advertising" it too much.

7 Likes

Thanks @Tomasz, understood, and much appreciated.:grinning::grinning: