For how to make a new version of a statistic please see this tutorial.
For how to make a new version of a statistic please see this tutorial.
PT3 allows you to create your own Custom Reports and Statistics{*} to extend the functionality already built in even further.
{*} Custom statistics are not available during the trial period - you must be registered to use them.
This tutorial is additional information to be used in conjuction with the main Custom Statistics and Reports documentation - it does not replace the documentation, and does not attempt to detail every function.
It is intended to give practical help and examples of how to create and use your own (or other people's) reports and statistics.
In addition this guide will primarily discuss cash reports and statistics but most things apply to tournaments too and the ideas should translate pretty directly.
For example, see the Tutorial on Converting a Cash Stat to Tournament use.
For a complete walkthrough of how to build a statistic see this tutorial.
Every report and statistic in PT3 is in a specific section.
Only statistics from the same section can be used in a report - e.g. if the report is in the Holdem Cash Player Statistics section then only stats from the Holdem Cash Player Statistics section can be displayed in that report.
The sections are:
Note: The only statistics you can display in the HUD are in the Holdem Cash Player Statistics section - or Holdem Tournament Player Statistics for tournament tables.
Note: There are also the same set of sections for Omaha, and these work exactly the same as for Holdem.
From now on I'm only going to refer to the holdem cash sections, but the same applies to the equivalent omaha and tournament sections.
Built-in reports
Built-in reports
Built-in reports
Built-in reports
General Tab -> Top report = Sessions stats
General Tab -> Bottom report = Player Statistics stats
Hands tab -> Top report = Player Statistics stats
Hands tab -> Bottom report = Hand stats
Sessions tab -> Top report = Session stats
Sessions tab -> Hands report = Hand stats
Positions tab -> Top report = Player Statistics stats
Positions tab -> Middle report = Player Statistics stats
Positions tab -> Bottom report = Hand stats
Winnings tab -> Top report = Sessions stats
Winnings tab -> Middle report = Sessions stats
Winnings tab -> Bottom report (when showing Hands) = Hand stats
Summary tab -> Bottom report = Player Statistics stats
Creating a custom report is simple - the Custom Reports guide explains all the elements of custom reports so I'll just explain the steps you need to take to create your own report, and then in the next section I'll create an example report with detailed steps and images to walk you through it.
Select the Cash Games > Reports tab.
The first thing to do is decide which section the report will be in and select it from the Section dropdown list.
You should then see the Available Stats list populate with stats from the selected section.
Now you need to give your report a name (by typing in the Name field) to allow you to identify it in future - you cannot run a report until you have named it.
To add statistics to your report double click them in the list of Available Stats and they will appear in the Report Stats list above.
You can filter the available stats by selecting a Category to make it easier to find the stats you want.
When you have added your stats you can change they order they will be shown in the report by selecting a stat in the Report Stats list and using the up and down arrows to move them.
That's all you really need to do, but there are further options to allow you to tweak your report contents, sorting order, etc., should you want to.
The blue Filters link allows you to filter your report in three ways.
These three methods can be combined, so for instance you can apply both simple and custom filters together. Any report filters that you apply will override any filters already active (or applied) via the main Filters button, though.
1. The most basic is the Filter on Active Player option.
This determines whether the report will show information for just the player currently selected in the Player List or for all players.
Be aware that if you uncheck this option then your report will show information for all players - this can lead to very large reports which can take a very long time to genarate. In particular you need to be really careful with reports in the Holdem Cash Hands section - showing every hand in the database from every player's point of view will mean a very big report!
However, if you want a Holdem Cash Player Statistics report (for instance) to show stats for more than one player you must uncheck this option.
2. The Simple Filters button is exactly the same as the main Filters window but filters applied here will only apply to, and will be saved with, this report.
If you have any Simple Filters applied the button text will go blue.
Note: Filtering rules apply here as described in "Sections" - if you filter for specific actions or types of hands it will only affect reports in the Holdem Cash Hands and Holdem Cash Player Statistics sections - Holdem Cash Session and Holdem Cash Sessions reports will NOT be filtered to show results from only a few hands; these will always show results from whole sessions.
3. You can also enter your own Custom Filters for situations not covered by the Simple Filters window using statistics or columns in the same section as the report.
To do this enter a Filter Expression.
The easiest way to do this is to use the Insert link to select your chosen column, statistic or operator but once you are used to the format you can just type in the expression.
This allows some quite advanced filters, but it doesn't need to be complicated.
For example, to filter a Holdem Cash Player Statistics report to only show players who have played more than 100 hands you could enter this expression:
#Hands# > 100
Statistic names are identified using # either side. To enter this same filter using the Column instead of the Statistic you would enter:
cnt_hands > 100
The above expressions for the number of hands are equivalent whether you use the statistic or column, but let's say you want to filter to only show players who have 3-bet on the river a certain number of times. The "3Bet River" stat is a percentage of times they bet when they had the opportunity (not a count of how many times they 3-bet) so we need to use the column "cnt_r_3bet" in the filter expression:
cnt_r_3bet > 10.
Note: This will only filter the report to show those players who 3-bet on the river more than 10 times - it will NOT filter their stats to only those hands where they 3-bet, it will still show their overall stats.
To see only stats from the hands where they 3-bet the river you need to use the Simple Filters button.
The Sorting window allows you to define the order in which your report will be sorted.
You can sort by more than one stat and the sorting will be applied in the order defined on the right.
For instance if you want to sort a Holdem Cash Hands report by limit then by amount won so that you see all hands from the same limit together but sorted by amount won within each limit you would double click Limit and Amount Won on the left and then make sure that Limit was listed first on the right hand side (using the up/down arrows).
Double click a stat on the right hand side to change the sort order between ascending and descending.
The Advanced link allows you to specify whether or not the report has a summary (totals) line at the bottom, and to define colours for rows within the report based on the value of a statistic or column in the same way you define filter expressions.
See the window for an example expression.
To see what RGB values to use for different colours open the Windows colour-chooser window and note the RGB values for your chosen colour - for instance you can access this by double clicking one of the colour blocks in the Settings Window.
When you are happy with your report click the Save button.
You can then run your report by clicking the Run Report button and the results will be shown in the main part of the report window.
This can take a while to populate if there is a lot of information to be retrieved for your report.
Tip! If you click Run Report before saving PT3 will ask you if you want to save - you must save in order for your changes to be used when you run the report.
Now that you have saved your report it will be available for use any time you visit the Reports tab.
To access it again select the appropriate section then choose your report name from the Report dropdown list.
You can also click the Export button to save your report as a file outside PT3 for backup or to copy to another computer or share with other PT3 users via the Repository.
If you download a report from the Repository, or want to import one of your own reports that you removed from PT3, use the Import button.
If you have a report that you no longer want to keep in PT3 click the Remove button to delete it.
If you previously exported this report then the file you exported it to will not be affected so you can re-import it later.
If you want to start over with a new empty report click the New button - if you hadn't saved the current report you will be asked whether you want to save it. As long as you have saved it you can get back to your previous report at any time by selecting it from the dropdown list but you can only have one report open at once.
The previous post explains the process of creating a custom report, and how to use the various options.
This post will walk through the steps needed to create a very simple report in the Holdem Cash Player Statistics section, with screenshots to illustrate.
This is just a basic report to show some stats for all players with more than 100 hands in the database, sorted by VP$IP.
1. Click the Reports tab.
2. Select Holdem Cash Player Statistics from the Section dropdown list.
3. Give your report a Name - my unimaginative name is "Example HCPS Report", but I'm sure you can do better than that..
4. Scroll the Available Stats list up and down to find and double-click the stats you want to see in your report.
I've added Player, Hands, VP$IP, PFR, Amount Won and BB/100.
5. These stats will then be shown in the Report Stats section above.
6. Stats will be listed in the order you add them but you can change the order using the up and down buttons.
You could save and run the report now, but it will currently only show stats for the current selected player and will sort by the player name, so we need to set Filters and Sorting information.
7. Click the blue Filters link to open the Edit Report Filter window.
7a. Untick the Filter on Active Player option so that the report will show stats for all players.
7b. Click the blue Insert link and go to the Statistics tab. This will enter into the Filter Expression field: #Hands# 7c. After this type " > 100" so that the expression now says: #Hands# > 100 |
![]() |
This will filter players based on the value of their "Hands" stat (which is also in the Holdem Cash Player Statistics section).
Tip! You can type the whole expression yourself but Insert is particularly useful when you don't know the exact name of the Statistic or Column that you want to use.
7d. Click the Save button and you should see the filter expression appear in the Filters list in the top section of the window.
Tip! At this point you could click New and enter another filter expression.
You could also click the Simple Filters button to restrict which hands are used to generate the stats.
7e. This report only needs this one filter so click OK to store the filters and return to the main report window.
8. Click the blue Sorting link to open the Edit Report Sorting window.
8a. To sort the report by VP$IP select it in the list on the left and click the right arrow in the centre.
VP$IP will move to the list on the right.
The Sort Method will be 'Ascending' by default - if you prefer to see the highest VP$IP values at the top of the report instead then double click the stat on the right hand side to change to 'Descending'.
Tip! If you want to sort by more than one column then add more and use the up and down arrows to change the priority order for sorting.
8b. Click OK to store the sorting preferences and return to the main report window.
9. The report is now ready so click Save to store it.
10. Click Run Report to populate the report.
Statistics are the values that are displayed in Reports and the HUD.
They display information taken from the Database Tables, but they cannot access this information directly - that is the job of Columns.
Statistics are usually built using one or more Columns or Variables.
They can be formatted to display information in many ways.
Columns can access the Database Tables directly and generally calculate a single (unformatted) piece of information (whereas a Statistic might use multiple pieces of information combined together to form their displayed value).
To display the value calculated by a Column you need to use a Statistic.
Variables can be used to do calculations using Columns, and then these can be used in a Statistic as well.
Database Tables >> Columns [ >> Variables ] >> Statistic
For instance, a typical stat which can be displayed in the HUD displays the percentage of times that a player did some action "A" out of the opportunites they had to do "A". (e.g. "A" might be "bet the flop" - you can only bet the flop if you see the flop and no-one bets before the action gets to you.)
Stat "A" is constructed using two columns:
These columns look up their information from the relevant tables in the database.
The Value Expression of A is then:
The statistic "3Bet Preflop" is calculated as the number of times the player 3-bet preflop divided by the number of times they had the chance to 3-bet preflop (and multiplied by 100 to give a percentage).
The Value Expression is:
(cnt_p_3bet / cnt_p_3bet_opp) * 100
This uses two columns to count the two numbers needed.
"cnt_p_3bet" counts the number of times the player 3-bet preflop, using the database table and field "holdem_hand_player_statistics.flg_p_3bet".
The Expression is:
sum(if[holdem_hand_player_statistics.flg_p_3bet, 1, 0])
I'll explain how to use the sum(if[...]]) construct later.
"cnt_p_3bet_opp" counts the number of times the player had an opportunity to 3-bet preflop, using the database table and field "holdem_hand_player_statistics.flg_p_3bet_opp".
The Expression is:
sum(if[holdem_hand_player_statistics.flg_p_3bet_opp, 1, 0])
The information about a Statistic is split across four tabs.
This section defines each field in the Edit Stat Info section.
The Definition tab is the main information about a stat - its name, description and value.
The Format tab defines how the stat will appear when it is used in reports or the HUD.
You must fill in the Title and Width fields in order to be able to use the stat in reports.
The Categories tab is simply to make finding stats easier when adding them to reports or the HUD - you can filter the list of stats to any of the available categories. Categories are different in each Section.
To add or remove a category, select it and use the < or > button to move it from one side to the other.
The Colors tab allows you to color the stat based on the value of any Column, Variable or Statistic (from the same Section), or on the value of a calculation involving combinations of these.
Color Conditions are applied in order - the first Condition Expression which is true is applied.
Each color condition can be specified to be used in either the HUD, the Tracker (reports) or both.
Change the color by double-clicking the Color block, then click Save.
For examples, please see the How To: Advanced HUD Configuration.
The blue Insert and Validate links are to help you construct valid expressions.
The option and buttons at the bottom of the window are used to manage statistics.
The Format Expression for a Statistic defines how the stat will be displayed in reports and the HUD.
If you leave this field blank then the value calculated in the Value Expression will be displayed.
This is OK, but most stats are calculated numeric values which will have a lot of decimal places, so if you leave the Format Expression blank then the value will be displayed to say 6 decimal places. e.g. "3.000000"
In most cases you won't want to see that many decimal places and the simplest way to format the value is to use the old 'c' [programming language] format expression of, for example:
/%.2f
This formats a float, or floating point (decimal) number, to 2 decimal places.
Should you wish to display 1 decimal place instead you can change this to /%.1f - other numbers can be used too, including 0 (zero).
Another way to format numbers is with the format_number function.
format_number( value, decimals, commas, color )
Example
format_number( (cnt_p_3bet / cnt_p_3bet_opp) * 100, 2, false, false )
..would display as:
12.34
The format_money function will format a numeric value as a currency amount.
format_money( value, color )
Example
format_money( amt_won, true )
..would display as:
$123.45
The format_date function displays a date and time.
format_date( date, format )
Example
format_date( date_played, 'datetime' )
..would display as:
2009/10/27 15:24
The format_bool function will display a boolean (true/false) value either as a checkbox, Yes/No or Y/N.
format_bool( value, format )
Example
format_bool( flg_f_saw, 'check' )
..would display as:
The format function is a more general formatting function which allows you to combine values together into a formatted string.
format( format_str, ... )
Example 1 (Holdem Cash Hand section)
format( 'The hand was played at {1} and the player won {2} big blinds.', format_date(date_played,'datetime'), format_number(amt_bb_won,1,false,false) )
..would display:
The hand was played at 2009/10/27 15:24 and the player won 5.5 big blinds.
Example 2 (Holdem Cash Player Statistics section)
format( '{1} had {2} steal opportunities and raised {3} times', str_player_name, format_number(cnt_steal_opp,0,false,false), format_number(cnt_steal_att,0,false,false) )
..would display as:
Player1 had 20 steal opportunities and raised 12 times
More realistically, you might use this format expression:
format( '{1}% ({2}/{3})', (cnt_steal_att / cnt_steal_opp) * 100, format_number(cnt_steal_opp,0,false,false), format_number(cnt_steal_att,0,false,false) )
..which would display as:
60% (12/20)
Note: this sort of thing is not necessary for use in the HUD as you can display times/opportunities in the HUD using the Statistic Properties.
The trim function can be used to shorten text (string) values, such as the player name.
This is normally used in conjuction with ignore_formatting so that the HUD does not alter the text.
Example
ignore_formatting(trim(str_player_name,5))
..would display the first 5 characters of the player name.
See the "Player Short" stat available for download from the "Get More" section.
The if function is used to display one thing or the other based on some criteria.
if( epxr, then, else )
Example 1
if( cnt_hands > 100, format_money(amt_won,true), '--' )
..would display -- for any players who have not played more than 100 hands.
(See above for help with format_money)
Example 2
One way I use the if function is to extend the use of format_bool (see above) to only show the checkbox if an opportunity arose, and have the tick indicate whether or not the opportunity was taken.
For instance, in the Holdem Cash Hand section I have a "Steal Attempted" stat which indicates whether or not the player made an open raise from a steal position but the checkbox is only shown for hands where the player had an opportunity to attempt a steal.
* No steal opportunity - nothing is shown
* Steal opportunity, no raise - empty checkbox shown
* Steal opportunity, raised - ticked checkbox is shown
The format expression for this is:
if ( flg_steal_opp, format_bool(flg_steal_attempt,'check'), '' )
What this does is check whether there was a steal opportunity; if so it displays a checkbox to show the value of flg_steal_attempt; if not it displays an empty string ('') which will result in nothing being shown.
Example 3
You can also use if to hide stat information until certain conditions are met, such as a minimum number of opportunites.
For instance, if you want to display the 4-bet preflop stat but censor the information until the player has had the opportunity to 4-bet 10 times (to stop you acting on information which doesn't have a significant sample) you could use an expression like:
if ( cnt_p_4bet_opp < 10, 'X', format_number( (cnt_p_4bet / cnt_p_4bet_opp) * 100, 2, false, false ) )
There are several lookup_ functions available, which allow you to access string representations of things like limits, sites and card values, amongst other things.
I am not going to detail all of the lookup functions here, but I will list a few stats that you can look at to see how they are used.
All of these are in the Holdem Cash Hands section:
Columns are used to retrieve information directly from the database and generate information to be shown in Statistics.
For information on how Statistics and Columns fit together see this section.
Exactly how this works depends on which section the column is in.
For instance the Holdem Cash Hand (HCH) section retrieves information about individual hands so the column will have a value related to a single hand – for instance it might have a boolean (true/false) value to indicate whether the player saw the flop in the hand, or a numeric value to show the size of a bet made on the flop.
Examples
The Holdem Cash Player Statistics (HCPS) section has information from all of a player’s hands. The column effectively iterates through every hand for the player, so rather than recording whether or not the player saw the flop in a specific hand you might have a column to count the hands where the player saw the flop.
Example
This expression uses the same database field as the HCH stat, but it references it for every hand, and adds up (sum) the result of the if statement for each hand.
The expression works like this:
‘SUM’ adds up the values from all the hands.
‘IF’ checks each hand.
The format is:
sum( if[ expression, then, else ] )
For each hand it evaluates the expression, and if it is true it returns the ‘then’ value, otherwise it returns the ‘else’ value.
Here ‘then’ is 1 and ‘else’ is 0 – this has the effect of counting the hands where the player saw the flop.
In English: if [ the player saw the flop, return 1, else return 0 ].
These 1s and 0s are then added up by the SUM to give the number of times the player saw the flop.
For instance, if the player played 5 hands and saw the flop in the first and third, then you would effectively have: sum( 1, 0, 1, 0, 0 ) = 1 + 0 + 1 + 0 + 0 = 2.
The values returned do not have to be 1s and 0s, but that is the most common usage.
As an example of how else it might be used the following expression will return the total amount bet on the flop as continuation bets, across all of a player’s hands, but will not count bets made when the player was not the preflop aggressor.
Example
The Holdem Cash Session section works in the same way as Holdem Cash Hand – it refers to individual sessions.
The Holdem Cash Sessions section works in the same way as Holdem Cash Player Statistics – it iterates through all sessions to calculate total values.
The tournament sections work in the same way.
We use conventional names for columns in PT3 to aid understanding and to make it easier to find things. This is not required, but is recommended.
We normally construct a name like this:
cnt_p_3bet
The first part (cnt_) means ‘count’ – how often something happened (this is in the HCPS section).
The second part (p_) means ‘preflop’.
The third part (3bet) is the situation we’re counting – so this column counts preflop 3-bets.
We may also add a 4th part (or more) - if we were counting preflop 3-bet opportunities, for instance, we’d call it cnt_p_3bet_opp.
Common types are:
Then:
p_ for preflop, f_, t_ and r_ for flop, turn or river or ttl_ for total.
This section describew some of the common elements that make up an expression in a Column.
All of the elements described here can be extended and combined to give more complex expressions, should the Statistic that you are building need that.
Expressions can consist of database fields, functions and operators, including some PostgreSQL functions.
The best ways to explore the options are to examine existing columns' Expressions and to use the blue insert link above and to the right of the Expression field.
An Expression can be as simple as retrieving a single value from a field in the database.
For instance, in the Holdem Cash Hand section you might want a stat to display a checkbox to indicate whether or not the player limped preflop in a hand.
The expression for this would just be:
holdem_hand_player_statistics.flg_p_limp
Because this is already a boolean (true/false) value we don't need to do anything else with it.
Similarly, you might want a stat to display the total amount the player bet in a hand.
The expression for this would be:
holdem_hand_player_detail.amt_bet_ttl
The IF function is used to determine whether a condition is true or false, and return a different value based on the outcome.
The structure of the IF function is described above, but it is basically:
If the expression evaluates to true the then value is used, otherwise the else value is used.
For instance, if you want a stat in the Holdem Cash Hand section to show the size of a continuation bet on the flop but to show zero for non-continuation bets you could use an expression like this:
This expression says: if the player made a continuation bet (holdem_hand_player_statistics.flg_f_cbet is true) then return the size of the bet (holdem_hand_player_detail.amt_f_bet_made), else return 0.
The IF function is also commonly used in combination with SUM to generate a count of how many times a certain situation occurs across all hands - for instance in the Holdem Cash Player Statistics section.
I will describe the use of SUM next, but for now let's see how IF can be used to turn a boolean value into a 1 (true) or 0 (false).
Using the preflop limp example from above:
if[ holdem_hand_player_statistics.flg_p_limp, 1, 0 ]
If the player limped preflop this returns a value of 1, else it returns 0.
So now let's move on to the SUM function to see how we can count the number of hands where the player limped preflop.
The SUM function is used to add up a set of values.
It is therefore only useful is sections where we are dealing with more than one item, such as Holdem Cash Player Statistics which generates statistics based on all of a player's hands.
Before we get back to counting the number of hands where a player limped preflop, here is a simple expression of the use of SUM.
To calculate the total amount of money a player bet across all hands, we could use an expression in the Holdem Cash Player Statistics section:
sum( holdem_hand_player_detail.amt_bet_ttl )
This goes through every hand the player played and adds up the total amount bet in that hand.
Compare that with the expression we saw in the Simple Expressions section above which just retrieved the amount bet in a single hand, and which would be used in the Holdem Cash Hand section.
So now we know how to use SUM and IF we can combine them to write a column expression to count the number of times a player limped preflop.
Again, this would be in the Holdem Cash Player Statistics section:
sum( if[holdem_hand_player_statistics.flg_p_limp, 1, 0] )
For each hand the IF statement returns a 1 if the player limped and a 0 if they did not.
The SUM statement then adds up these 1s and 0s to give us a count.
You can combine IF statements if you have more than 2 outcomes to generate.
As a simple example let's say we want a column to determine whether the player bet more or less than the pot size on the flop or didn't bet at all, which gives us 3 outcomes. You would normally do this sort of thing in the Format Expression of the Statistic, rather than in the Column itself, but it should illustrate the point..
if[ holdem_hand_player_detail.val_f_bet_made_pct = 0, 'Did not bet', if[ holdem_hand_player_detail.val_f_bet_made_pct > 100, 'Bet more than the pot', 'Bet less than the pot' ] ]
You can use the normal mathematical operators in expressions:
+ - / * = < > >= <=
!= (not equal: <> also works)
For example if we want to check whether the player bet more than the size of the pot (as in the Nested IFs example) we could use:
holdem_hand_player_detail.val_f_bet_made_pct > 100
There are also several other operators which are useful from time to time. Here are a few which may be useful, please see the documentation linked below for a complete list:
% - modulo (remainder) 5 % 4 = 1
^ - exponentiation (power) 2 ^ 3 = 8
abs - absolute value. abs(-5) = 5
round - round to the nearest integer (whole number) round(42.7) = 43; or you can specify the number of decimal places: round( 66.66666, 2 ) = 66.67
trunc - truncate towards zero. trunc(42.7) = 42; or you can specify the number of decimal places: trunc( 66.666666, 1 ) = 66.6
For a full list please see the PostgreSQL Documentation.
You can use the normal boolean operators in expressions:
AND OR NOT
For instance, to see if a player made a continuation bet on the flop and turn we could use the expression:
holdem_hand_player_statistics.flg_f_cbet AND holdem_hand_player_statistics.flg_t_cbet
To see if they c-bet the flop but not the turn we could use:
holdem_hand_player_statistics.flg_f_cbet AND NOT holdem_hand_player_statistics.flg_t_cbet
To see if a player made a normal bet on the flop or the turn we could use:
holdem_hand_player_statistics.flg_f_bet OR holdem_hand_player_statistics.flg_t_bet
Normal operator precedence applies in expressions in PT3.
Some operators have higher precedence than others so will always be calculated first, regardless of the order they are written in.
For example:
2 + 3 * 4 = 14
This is because multiplication has higher precedence than addition. If you want to multiply 2 + 3 by 4, you need to write (2 + 3) * 4.
Brackets over-ride precendence, and you should use them to clarify exactly what you are trying to do.
Examples:
See Table 4-1. Operator Precedence at the bottom of this page for a full list of the order operators are applied in, but when in doubt please use brackets.
For example, if you want to see if a player bet the flop and then bet the turn or river, you could use:
holdem_hand_player_statistics.flg_f_bet AND (holdem_hand_player_statistics.flg_t_bet OR holdem_hand_player_statistics.flg_r_bet)
"holdem_hand_player_statistics.flg_f_bet AND holdem_hand_player_statistics.flg_t_bet OR holdem_hand_player_statistics.flg_r_bet" would not give us what we want.
If you want to see whether a player bet and then folded on the flop, or bet and then folded on the turn, you could use:
Again, the brackets are needed to make sure we get the result we want.
The PT3 database contains some string (text) fields, and these can be read and analysed in Column Expressions.
Some typical string fields are:
Player actions are the most commonly used of these, so I'll describe them in more detail, and use them for the examples here.
Every action is represented by a single uppercase letter.
X = Check, F = Fold, C = Call, B = Bet, R = Raise
The enum_ fields consist of a single character; either one of the above action characters or a character to represent a street, depending on the type of field.
P = Preflop, F = Flop, T = Turn, R = River, N = None (e.g. if the player did not go all in then holdem_hand_player_statistics.enum_allin = 'N')
The player actions by street strings (like lookup_actions_p.action) consist of a series of characters to represent all of a player's actions on a single street.
For example, if a player limps then folds to a raise preflop the action string would be 'CF'.
If they check-raised the flop then called a 3-bet their action string would be 'XRC'.
We can compare whole strings using the normal = operator.
For example, if we want to see if a player's only action preflop was to call we could use:
lookup_actions_p.action = 'C'
Tip! Strings are always written inside single quote marks like this.
If instead of just calling once, the player called and then faced a raise and called again then their action string would be 'CC' and lookup_actions_p.action = 'C' would be false.
To check just certain parts of a string we need to use Pattern Matching functions.
To compare just part of a string the normal = operator does not work, as mentioned above.
If we want to check whether the player's first action preflop was to call, but we don't care what they did after that, then we could use the LIKE function:
lookup_actions_p.action LIKE 'C%'
This expression compares the player's preflop action string (lookup_actions_p.action) with the actions we're interested in - in this case a call ('C') followed by any other actions, including no action ('%').
The % is a token which makes up part of a pattern. % specifically represents any (zero or more) characters, which can be anything.
If we want to check for the player calling and then making exactly one more action (but we don't care what that second action is) then we would use the _ (underscore) token which represents exactly one character, which can be anything.
lookup_actions_p.action LIKE 'C_'
We can also combine tokens:
lookup_actions_p.action LIKE 'C_%'
..would match any action string which starts with a call and has at least one more action (e.g. 'CF', 'CC', 'CCF', 'CRC', etc.), but would NOT match just a call ('C').
You can use these tokens at any point in the string, so you could check for a player being the preflop aggressor by checking for their preflop action string ending with a raise:
lookup_actions_p.action LIKE '%R'
As another example, you could check whether the hand was played at a 6-max table like this:
holdem_limit.limit_name LIKE '%(6 max)'
LIKE is pretty good for this sort of thing, and can be used for virtually all cases, but there are some comparisons which can be simplified by using another pattern matching function; SIMILAR TO.
This gets a bit more complicated now, so if you find LIKE confusing enough, you might want to skip the next section for now..
If we want to check for a player's action when facing a flop bet being a call, but we don't care whether they checked before that or not (i.e. whether the call is made in or out of position) or what actions, if any, they made after calling we could do this with LIKE, but we would need two checks, like this: lookup_actions_f.action LIKE 'C%' OR lookup_actions_f.action LIKE 'XC%'
Alternatively we could use a single SIMILAR TO expression:
lookup_actions_f.action SIMILAR TO '(X|XC)%'
The brackets define a separate part of the pattern, and the | means 'OR', so this expression matches any string which starts with 'X' OR 'XC' and then has zero or more characters after that.
Please see the PostgreSQL documentation on Pattern Matching for more information.
Another way of working with strings is the SUBSTRING function.
This allows you to access any part of a string - its format is this:
SUBSTRING( string FROM x FOR y )
This returns y characters, starting from character number x.
e.g.
SUBSTRING( 'abcdefg' from 2 for 3 ) = 'bcd'
This can be used to compare strings, for instance to see if the first two preflop actions were both calls you could use:
SUBSTRING( lookup_actions_p.action from 1 for 2 ) = 'CC'
This checks whether 1 character, starting from the first character, is 'C'; in the same way that you could use lookup_actions_p.action LIKE 'C%'.
In general LIKE is easier to use than SUBSTRING for comparisons, but SUBSTRING can also be used to return part of a string.
For instance, if you want to retrieve the first character of a limit name to display the currency you could use:
SUBSTRING( holdem_limit.limit_name from 1 for 1 )
For a walkthrough of how to create a custom statistic see this tutorial.
If you have a statistic for one game type that you'd like to use for another game type you can convert it using the Convert To option on the Statistics tab.
For instance, if you want to use the "Preflop Limp Fold" Holdem Cash Player Statistics stat from the previous post for holdem tournaments you could convert it to the Holdem Tournament Player Statistics section, like this:
Select the section you want to convert to from the dropdown list and then click the Convert To button.
You can convert a stat to the relevant section of any of the other 3 game types, but not to a different type of section.
For instance, you can convert a stat from Holdem Cash Hands to Holdem Tournament Hands, Omaha Cash Hands or Omaha Tournament Hands, but not to Omaha Cash Player Statistics or Holdem Cash Sessions.
Most stats that we build are in the Holdem Cash Player Statistics (HCPS) section as these are the stats that can be used in the HUD and in reports about players' overall stats.
All of the following applies to the appropriate Omaha and Tournament sections too, but I will just describe the holdem cash case for simplicity.
When you build one of these stats you will want to check that it is giving you the correct results and I find the best way to do this is to create a matching stat in the Holdem Cash Hands (HCH) section.
This pair of stats will be based on the same expression, but the way they are constructed is slightly different.
HCPS stats are often based on the number of times a certain action or event occurred, out of the number of times it could have happened, across all of the player's hands. This is normally displayed as a percentage. (You can build other types of stats here too, but these are most common.)
The construction of these 'percentage' stats is described in detail earlier in this tutorial.
HCH stats are based on individual hands and indicate whether the action or event occurred in each specific hand. This is normally displayed as a checkbox to indicate true or false.
As an example I'm going to build a HCH stat to go with the built in HCPS "Steal Success" stat.
The HCPS stat shows the percentage of times the player's steal raises "succeeded", by which I mean the raise won with no further action.
So if the player made 10 steal raises and won immediately 3 times the stat will show "30".
You can add this statistic to the built-in reports on the Positions tab, for instance - either or both the Position Statistics and Position Hands reports.
This will show how often the steal raises succeeded, but to see which 10 specific hands were the steal raises, and which 3 succeeded we need to create a stat we can show in the Position Hands Detail report.
The HCH stat will show a checkbox if the player made a steal raise; the checkbox will be checked if the steal succeeded and unchecked if it didn't.
The HCPS "Steal Success" stat is constructed like this:
( cnt_steal_success / cnt_steal_att ) * 100
The column expressions are:
cnt_steal_success =
sum(if[holdem_hand_player_statistics.flg_steal_att AND NOT(holdem_hand_player_statistics.flg_f_saw) AND NOT(holdem_hand_player_statistics.flg_p_face_raise), 1, 0])
cnt_steal_att =
sum(if[holdem_hand_player_statistics.flg_steal_att, 1, 0])
To build the HCH stat we need to recreate these columns in the Holdem Cash Hands section, but with one crucial difference.
The HCPS stats uses the sum(if[...]) construct to count how often the steal raise occurred and how often it succeeded across all the player's hands.
The HCH section works on individual hands so we don't need the sum(if[...]) part, but the rest is the same, so we create new columns:
flg_steal_success =
holdem_hand_player_statistics.flg_steal_att AND NOT(holdem_hand_player_statistics.flg_f_saw) AND NOT(holdem_hand_player_statistics.flg_p_face_raise)
flg_steal_att =
holdem_hand_player_statistics.flg_steal_att
Note that I change the names slightly too - they are now boolean (true/false) values so by convention we name them flg_ which indicates a 'flag'.
Now to construct the actual HCH statistic we go to the Statistics tab and click New.
Enter your stat name "Steal Success".
The Value Expression can be flg_steal_success - i.e. did the steal succeed or not.
The Format tab is where the real work is done. To make it really clear which hands are the ones where a steal was attempted we're only going to show the checkbox at all if a steal was attempted (i.e. when flg_steal_att is true).
So the Format Expression is:
if ( flg_steal_att, format_bool( flg_steal_success, 'check' ), '' )
This says if flg_steal_att is true then show a checkbox based on the true/false value flg_steal_success otherwise show nothing (''=empty string). See earlier in this tutorial for more information.
Make sure you fill in the Title and Width fields as well, or the stat won't show in your reports.
The Positions tab then looks like this when I add the stats to all 3 reports:
Here you can easily see which hands were steal attempts and which succeeded.
I have included the similar "Steal Attempted" stats for clarity - you will notice that there is one hand where a steal could have been attempted but wasn't, and this isn't included in the steal success stat.
I have also added "x/y" versions of the HCPS "Steal Success" and "Steal Attempted" stats to the top two reports so that you can easily see how many attempts and successes there were.
These are built by duplicating the built in stats and just changing the Format Expression. For example the Format Expression for "Steal Success (x/y)" is:
format( '{1}% ({2}/{3})', format_number(100* cnt_steal_success / cnt_steal_att, 0, false, false), format_number(cnt_steal_success,0,false,false), format_number(cnt_steal_att,0,false,false) )
For information about "live" HUD stats click here.