How to Write System Rules

In System Selector's new "Expert Mode" a system rule is what we call a logical expression that returns the value true if the horse meets the desired criteria or false if it falls outside the desired criteria. For example in plain English a rule might be "age of horse must be between 3 and 6".

Note that each line in an Expert Mode system is a separate rule and you can have as many or as few as you like. It only takes one rule however to return a FALSE value and the horse will be discarded from consideration (in other words all rules must return a TRUE value for the horse to be selected).

In the "Expert Mode" editor there are separate drop down lists for selecting race data, horse data and past form data for creating rules. To use the horses age in a rule you look for it in the Horse Data drop down, select it and click on the Insert button. This is how you insert an item into your system. In the case of the horse's age it will appear as "H:AGE". You do not really have to understand this "code" but to give you an idea the "H:" indicates that the data is coming from the Horse file, and the word "AGE" indicates the name of the column in the Horsefile that the data will be extracted from.

Note if you remember the codes you can type them in directly and even copy and paste them from other systems. To see a list of all available item codes click the "List All Examples" button on the far right of the editor window. You will note the V: prefix is used for venue data, the R: prefix for race data, the H: prefix for horse data and the F: prefix for last start data (plus F2:, F3: and F4: for second, third and fourth last start data).

If you insert a numeric or character based form item (or calculation) into a system by itself then you will get the following error:

DATA TYPE ERROR: If you run an Expert Mode system and get this error and it includes the details "CALCEVALX (Line 21)" then it is because you have included a numeric or character based variable or calculation that is not compared to anything.

It is very important that you understand the requirement that form items (or calculations) have to be compared to one or more other values to create a rule (the only exception are variables that already return TRUE or FALSE which are discussed later). 

NEW in 2022: The Expert Mode editor window now includes a "Rule Builder" which pops up a template when you click the "Insert" button. It allows you to type a minimum value in a min box and/or a maximum value in a max box - and have the program create the rule for you (for character based rules it allows you to enter the desired character strings you are targeting).

The remainder of this post explains what the rules look like in case you want to know more about how they work.

Numeric Min-Max Rules


The majority of rules you are likely to use are numeric ones where you specify a minimum and maximum value. Indeed most of the original System Selector rules were set in this way by entering your desired minimum bound in a "min" box and your desired maximum bound in a "max" box.

In Expert Mode there is a special "Within" function for setting min-max rules. For picking horses aged between 3 and 6 it is used as follows:

Within(H:AGE,3,6)

Basically the syntax is Within() with three things inside it namely what you want to test followed by the desired minimum value followed by the desired maximum value. Note the function returns a "true" value (and keeps the horse in contention) if the value is anywhere between the minimum and maximum values or indeed is equal to the minimum or maximum.

What if you don't want to set a minimum and only want to set a maximum? Let's say you want to code the rule "age of horse must be below 6". Mathematically this means the maximum age for the horse has an upper limit of 5 so we can just use the Within function with a minimum value of zero and maximum of 5 as follows:

Within(H:AGE,0,5)

In most cases zero is the lowest minimum you are likely to use so it is recommended you use the Within rule as above even when you don't need to set a minimum. There are a couple of advantages to doing it this way. First you don't have to learn a different way to code the rule and second if you ever need to change the minimum from zero then it is quick and easy to edit.

There will be other examples where you want to set a minimum value but are not fussed about the maximum. For example you might want to only select races with more than 8 starters. This can still be coded using the Within() function as we know the maximum can never be more than 24. So basically we test for a range starting at 8 and ending at 24 as follows:

Within(NUMSTR,8,24)

Note the special variable NUMSTR gives the adjusted number of starters after accounting for scratchings (if you use R:NFLD you get the number of starters before scratchings which would not normally be of much use). Note also that when using the Within function 24 is the maximum number of horses you can have in a race and therefore it is also the maximum for things like TAB numbers, barrier positions and all rankings.

Advanced Numeric Rules


Our earlier example requiring the "age of horse must be below 6" can be coded more literally using the "less than" relational operator sign as follows:

H:AGE<6

There are a couple of reasons why you might still want to use the Within() function as in Within(H:AGE,0,5). First you don't have to learn a different way to code the rule and second if you ever need to change the minimum from zero then it is quick and easy to edit.

There may however be occasions when you will need to use relational operators instead of using the Within() function. For example if picking distances of 1200m or above, the problem with using the Within function is that if you set a maximum bound of 3200 then it may not represent the highest distance you come across. If the program comes across 3400m or 3500m races for example and you want to include them then the best approach is to not test for the upper bound at all.

To just test the lower bound requires a simple numeric comparison using the "greater than or equals" operator as follows:

R:DIST>=1200

Of course you could still use a very high maximum bound like in Within(R:DIST,1200,9999) where the maximum bound is set to 9999.

If you find you need to use relational operators, a full list of them is as follows:
  • < (less than)
  • > (greater than)
  • <= (less than or equal)
  • >= (greater than or equal)
  • = (equal)
  • <> (not equal)
The equals operator (=) provides a quick way to test for exact equality. For example to test for 1200m races only you can use the following:

R:DIST=1200

Note however you can till use the Within() function to test for 1200m races as follows:

Within(R:DIST,1200,1200)

As you can see you just set the minimum and maximum values to the same number. Using the Within() function is more readable and much easier to change if you suddenly decide to reduce the lower bound or increase the upper bound.

One case where you definitely can't use the Within() function is where you want to test for discrete values that are not within a range. For example the following uses the logical operator .OR.to test for distances 1200, 1400 and 1600 with nothing in between permissible:

R:DIST=1200.OR.R:DIST=1400.OR.R:DIST=1600

Following is another example that returns TRUE if a horse has won on a slow track or won on a heavy track:

H:SW>0.OR.H:HW>0

So while using the Within() function is easy to read and easier to understand it sometimes has limitations - limitations which can be overcome using the more advanced relational operators.

Character Based Rules


So far we have looked at rules using numbers but there are also character based form items that need to be tested for. The special LOCATION variable for example contains a single upper case character identifying the track location. S indicates Sydney, M Melbourne, B Brisbane, A Adelaide, W Perth and so on.

You could just use the equals operator like in LOCATION="S" to test for Sydney but it is better to get in the habit of using the dollar sign operator like in LOCATION$"S" which means "is the LOCATION character contained in the character string S". The reason for this is that the dollar sign operator can be used to test for multiple locations at once. For example to test for Sydney, Melbourne and Brisbane tracks the rule would be:

LOCATION$"SMB"

Just remember the dollar sign operator means "contained in" so the above rule means "is the single character location string for the track in question "contained in" the character string "SMB".

Another popular character variable is GCODE which contains a single upper case character identifying the track condition. For example G indicates Good, S indicates Soft and H indicates Heavy.

Similar to our discussion above about LOCATION, you could just use the equals operator like in GCODE="G" to test for Good tracks but it is better to get in the habit of using the dollar sign operator like in GCODE$"G" which means is the GCODE character "contained in" the character string "G". Then it is easy to modify the rule to test for multiple conditions where you can't use the equals operator. For example to test for Slow or Heavy tracks the rule would be:

GCODE$"SH"

Another character variable that comes in handy is DAYCODE which is a single character based representation of the day of the week from 1 for Sunday through to 7 for Saturday. The rule for Saturday only can be written as DAYCODE="7" but as previously discussed is better written as DAYCODE$"7" which means is the day code character contained in the character string "7". To test for Wednesdays or Saturdays the rule would be:

DAYCODE$"47"

Important: Please note that while DAYCODE looks like a number it is actually a character representation of the day number. The reason for this is that it makes is easy to test for multiple days as above. If for some reason you want the day of the week as an actual number then Val(DAYCODE) or Dow(V:MDATE) will return the numeric equivalent.

Logical Variables and Negation


There are three special variables in the drop downs that return a TRUE or FALSE value by themselves. They are as follows:

  • HOME for "trained at home track"
  • SAME for "same track as last start
  • LSFAV for "favourite last start"

Because these variables already return a TRUE or FALSE value they are rules by themselves. As a result they can be inserted as Expert Mode rules without the need to compare them to anything. For example the rule to test for "favourite last start" is simply:

LSFAV

Now suppose you want to test for the opposite of one of the above. The solution is to negate the rule using a special negation operator which is represented by an exclamation mark. For example the rule to test for "not favourite at last start" is:

!LSFAV

It is important to understand that the negation operator can be used with any Expert Mode rule, not just the ones discussed above. For example we previously used the rule LOCATION$"SMB" to test for Sydney, Melbourne or Brisbane. If you want to test for the opposite, namely any track other than these three, the rule is as follows:

!LOCATION$"SMB"

Similarly to pick any track condition that is not Soft or Heavy the rule would be:

!GCODE$"SH"

As you can see the approach is to just put the negation operator at the beginning of the rule. It can be applied to any rule for example in front of a Within() function or in front of a logical comparison. Note however that it has a high order of precedence (meaning it gets applied before other operators) so you may need to use it in conjunction with parentheses if a rule actually contains multiple comparisons. Here's an example:

!(R:DIST=1200.OR.R:DIST=1400.OR.R:DIST=1600)

The above selects any distance that is not 1200 or 1400 or 1600. Note if you did not use the parentheses then the negation would only apply to the first part of the rule.

The last example introduces the .OR. operator which can be used to string rules together when you need only one of them to apply. There is also an .AND. operator but you are much less likely to need it as if you have multiple rules that you all want to be met you simply put them on separate lines.

Expert Mode System Examples


Below is an example of our Best Bet system as written using the new "Expert Mode":

DAYCODE$"7"
LOCATION$"SMBAW"
Within(R:DIST,800,2200)
Within(NUMSTR1,12)
Within(AVBASE,56,99)
Within(NUMLSW,1,24)
Within(H:RUNS,1,99)
Within(H:POS,1,1)
Within(BOOK2DIV(H:PRICE),1,3)

Using the Within() function as previously described makes the above very readable but you have to be careful setting limits which may not be required by your system but are required by the Within() function. For example this Best Bet system does not have a maximum limit for the AVBASE race class but the Within() function requires one to be present so a figure of 99 has been used.

Below is an example of our Best Bet system as written using the new "Expert Mode" but without using the Within() function:

DAYCODE$"7"
LOCATION$"SMBAW"
R:DIST<=2200
NUMSTR<=12
AVBASE>=56
NUMLSW>=1
H:RUNS>=1
H:POS=1
BOOK2DIV(H:PRICE)<=3

The above example avoids you having to set limits where none are actually used in your rules and may also be fractionally faster to run. So by all means code your systems this way if you are comfortable doing so or use a mixture of the above two examples.

Remember all the form items used in the above examples are defined in the editor drop down lists where you can easily select them and click the Insert button to insert them into your system code. As a result, there is no need to remember them but please note that as an alternative to using the drop down lists and insert buttons you can type items in directly or copy them from other systems.