Wildcard masks
Many settings in CPlugin plugins and apps ask for a list of groups, logins or symbols. You do not type every name; you write a short pattern such as demo*,real-ecn. That pattern is called a wildcard mask. This page explains exactly how a mask is read, why we are moving all products to one reading of it, and what that changes for you.
The rules
A mask is a list of entries separated by commas. Each entry is compared with one name (one group, one symbol or one login) and the list gives a yes or no answer: "does this name belong to the list?"
*stands for any text, including no text at all.demo*fitsdemo,demo-ecnanddemo1. It does not fitreal-ecn.- The whole name must fit the entry, not just its beginning.
ab*cdfitsabXcdandabcd, but notab: the name has to start withaband end withcd. In the same wayEUR*USDdoes not fitEUR. - Entries are separated by commas.
demo*,real-ecnmeans "everything that starts withdemo, and alsoreal-ecn". The semicolon is not a separator. - Spaces around an entry and around the name are ignored.
demo , realis the same list asdemo,real. In the same way, a name that reaches the check with spaces before or after it (for exampledemofollowed by a space) is read asdemo. A space inside an entry or a name is an ordinary character:abdoes not fita b. A name made only of spaces counts as an empty name, and only*fits it. !in front of an entry means "except".!USDJPYis an exclusion: a name that fits it is left out.- The first entry that fits decides. The list is read from left to right and reading stops at the first entry that fits the name: an ordinary entry says "yes", an entry with
!says "no". If no entry fits, the answer is "no". For this reason exclusions are written first:!USDJPY,USD*leaves out USDJPY, whileUSD*,!USDJPYdoes not, becauseUSD*already says "yes" to USDJPY and the list is never read any further. - Capital and small letters are different.
usd*does not fitUSDJPY. - An empty field fits nothing. To select everything, write
*. A list made only of exclusions also fits nothing, so "everything except demo" is written!demo*,*.
What a mask does not do: it is not a regular expression, ? is just a question mark and not "any one character", a range such as 1000-1999 is read as that exact text and not as "all logins from 1000 to 1999" (a product that accepts login ranges says so on its own page), and a mask never changes anything by itself. It only answers "belongs to the list or not" for the setting it is written in.
Examples
Every row below was checked against the shared test table described further down.
| Mask | Name | Fits? | Why |
|---|---|---|---|
demo* | demo-ecn | yes | starts with demo, * stands for -ecn |
demo* | real-ecn | no | does not start with demo |
demo* | demo | yes | * may stand for no text |
ab*cd | abXcd | yes | starts with ab, ends with cd |
ab*cd | ab | no | the name ends before cd appears |
EUR*USD | EURUSD | yes | * stands for no text |
EUR*USD | EUR | no | the name has no USD at the end |
*a*a* | aXa | yes | two separate letters a are found one after another |
*ab*bc* | abc | no | the two parts would have to share the letter b; each part needs its own letters |
usd* | USDJPY | no | letter case is different |
100* | 1005 | yes | a login is compared as text, * stands for 5 |
demo , real | real | yes | spaces around entries are ignored |
abc followed by two spaces | abc followed by one space | yes | spaces are ignored on both sides, both are read as abc |
demo | demo with two spaces before and after it | yes | spaces around the name are ignored |
ab | a b | no | the space inside the name is a character |
a*b | a b with a space before and after it | yes | the outer spaces are ignored, * stands for the inner space |
* | a name made only of spaces | yes | it counts as an empty name, and * fits everything |
!demo,* | demo followed by a space | no | the exclusion fits the name without its space |
USD*,!USDJPY | USDJPY | yes | USD* fits first and decides, the exclusion is never reached |
!USDJPY,USD* | USDJPY | no | the exclusion fits first and decides |
!USDJPY,USD* | USDCHF | yes | the exclusion does not fit, USD* does |
!demo* | real | no | a list of exclusions only never says "yes" |
!demo*,* | real | yes | no exclusion fits, then * fits everything |
!demo*,* | demo-1 | no | the exclusion fits first |
| (empty field) | value | no | an empty field fits nothing |
* | value | yes | * fits everything |
How to write a mask
| You want | Write |
|---|---|
all groups that start with demo | demo* |
all groups that end with -ecn | *-ecn |
all groups that contain vip anywhere | *vip* |
| two named groups only | real-ecn,real-std |
| a list of logins | 12345,12346,12350 |
| every symbol except one | !XAUUSD,* |
every symbol that starts with USD, except USDJPY | !USDJPY,USD* |
all demo groups except the VIP ones | !demo-vip*,demo* |
| everything | * |
| nothing | leave the field empty |
What was wrong before
Until now the same mask could be read in different ways depending on the app. The problems were found mainly in our web and desktop apps. These are the facts we found and checked.
- An exclusion could do nothing. In one of our apps every entry of the list was checked on its own instead of reading the list as a whole, so
!xnever excluded anything: the name stayed in the list however the exclusion was written. - A real bug, found in 2026. A mask such as
*test*found nothing in one of our apps, even though a group calledtest1kobviously containstest. It was fixed on 6 May 2026. - Every app handled an empty field in its own way. The check for an empty field was written separately in each app, and the apps did not all treat it alike. In some places it meant "everybody", in others "nobody". A forgotten field could silently include every client.
- Several readers had grown up. The MetaTrader 4 and MetaTrader 5 sample code that authors copy from reads masks differently from each other, and our own code carried further variants written separately. They disagreed on some cases.
- A rare pattern in older sample code. The older MetaTrader 4 sample code read
ab*cdas matching the nameab: when the name ended first, the rest of the pattern was not checked. Patterns of this shape are rare.
Why one standard everywhere
- One definition. The rules above are the single definition for every CPlugin product.
- One shared test table. We keep one table of names and masks with the correct answer for each row. Every implementation, the C++ plugins and the .NET apps alike, must give the correct answer to every row before it is released. A mask therefore means the same thing in every product.
- Fewer support questions. "Why does this mask work in one product and not in another?" stops being a question.
- Safer defaults. An empty field can no longer include everybody by accident. Selecting everybody is always a visible
*.
What changes for you
Our plugins follow this reading. Before installing an update of any CPlugin product, check your mask fields against the rules and the checklist on this page. It takes a minute, and it matters most for the rare patterns in the table below, which the older sample code read differently.
| What | Read differently by the older sample code | The reading on this page |
|---|---|---|
ab*cd and the name ab | fits | does not fit |
*ab*bc* and the name abc | fits | does not fit |
*a*a* and the name aXa | does not fit | fits |
a long list with many * entries, for example *a*b*c*d*e*f*g*h*i*j* and the name abcdefghij | does not fit: reading gave up after ten checks | fits |
spaces around entries, for example x , y and the name y, or abc with a space at the end and the name abc | the space could make the entry fail | the spaces are ignored, fits |
spaces around the name, for example the entry abc and the name abc followed by a space | does not fit: the space was part of the name | the spaces are ignored, fits |
What does not change: letter case still matters; entries are still separated by commas; * still stands for any text; the first entry that fits still decides; exclusions still have to be written first.
Our web apps. The apps are being brought to the same reading: exclusions work, and an empty field fits nothing, so selecting everybody always needs a visible *. We migrate your existing saved settings ourselves, in upcoming updates: wherever an empty field meant "everybody", we write * for you. You do not need to do anything in the apps, and nothing stops working. The History notes of each release say what changed.
If you want a field to select everything, write *: it works in every version.
What to check in your settings
- Empty mask fields. Decide for each one whether it should select everybody (write
*) or nobody. - Exclusions written after a wide entry.
USD*,!USDJPYdoes not exclude USDJPY. Move the exclusion to the front. - Patterns with
*in the middle such asabc*defthat you expect to fit a shorter name likeabc. The name has to start withabcand end withdef, soabcalone does not fit. - Patterns where two parts share a letter, such as
*ab*bc*that you expect to fitabc. They do not fit. - An exclusion with a space right after
!, such as! abc. The space belongs to the entry, and because spaces around a name are ignored, no name can start with a space: such an exclusion leaves out nothing. Write!abc.
Checklist before updating
- Open each mask field of the plugin or app and read it against the rules above.
- Replace every empty field you meant as "everybody" with
*. - Put every
!entry before the entries it cuts from. - For each pattern with
*in the middle, write down one name it must fit and one it must not fit, and check both against the examples table. - Read the History notes of the version you are updating to.

