Wildcard masks

How the comma-separated lists with * and ! in the settings of CPlugin plugins and apps select groups, logins and symbols: the rules, worked examples, what was inconsistent before and what changes when every product follows one standard.

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.

This page describes the masks inside CPlugin settings. The settings fields of the MetaTrader 4 or MetaTrader 5 server itself follow the platform's own rules, which are described in the platform documentation.

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?"

  1. * stands for any text, including no text at all. demo* fits demo, demo-ecn and demo1. It does not fit real-ecn.
  2. The whole name must fit the entry, not just its beginning. ab*cd fits abXcd and abcd, but not ab: the name has to start with ab and end with cd. In the same way EUR*USD does not fit EUR.
  3. Entries are separated by commas. demo*,real-ecn means "everything that starts with demo, and also real-ecn". The semicolon is not a separator.
  4. Spaces around an entry and around the name are ignored. demo , real is the same list as demo,real. In the same way, a name that reaches the check with spaces before or after it (for example demo followed by a space) is read as demo. A space inside an entry or a name is an ordinary character: ab does not fit a b. A name made only of spaces counts as an empty name, and only * fits it.
  5. ! in front of an entry means "except". !USDJPY is an exclusion: a name that fits it is left out.
  6. 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, while USD*,!USDJPY does not, because USD* already says "yes" to USDJPY and the list is never read any further.
  7. Capital and small letters are different. usd* does not fit USDJPY.
  8. 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.

MaskNameFits?Why
demo*demo-ecnyesstarts with demo, * stands for -ecn
demo*real-ecnnodoes not start with demo
demo*demoyes* may stand for no text
ab*cdabXcdyesstarts with ab, ends with cd
ab*cdabnothe name ends before cd appears
EUR*USDEURUSDyes* stands for no text
EUR*USDEURnothe name has no USD at the end
*a*a*aXayestwo separate letters a are found one after another
*ab*bc*abcnothe two parts would have to share the letter b; each part needs its own letters
usd*USDJPYnoletter case is different
100*1005yesa login is compared as text, * stands for 5
demo , realrealyesspaces around entries are ignored
abc followed by two spacesabc followed by one spaceyesspaces are ignored on both sides, both are read as abc
demodemo with two spaces before and after ityesspaces around the name are ignored
aba bnothe space inside the name is a character
a*ba b with a space before and after ityesthe outer spaces are ignored, * stands for the inner space
*a name made only of spacesyesit counts as an empty name, and * fits everything
!demo,*demo followed by a spacenothe exclusion fits the name without its space
USD*,!USDJPYUSDJPYyesUSD* fits first and decides, the exclusion is never reached
!USDJPY,USD*USDJPYnothe exclusion fits first and decides
!USDJPY,USD*USDCHFyesthe exclusion does not fit, USD* does
!demo*realnoa list of exclusions only never says "yes"
!demo*,*realyesno exclusion fits, then * fits everything
!demo*,*demo-1nothe exclusion fits first
(empty field)valuenoan empty field fits nothing
*valueyes* fits everything

How to write a mask

You wantWrite
all groups that start with demodemo*
all groups that end with -ecn*-ecn
all groups that contain vip anywhere*vip*
two named groups onlyreal-ecn,real-std
a list of logins12345,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*
nothingleave 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.

  1. 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 !x never excluded anything: the name stayed in the list however the exclusion was written.
  2. A real bug, found in 2026. A mask such as *test* found nothing in one of our apps, even though a group called test1k obviously contains test. It was fixed on 6 May 2026.
  3. 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.
  4. 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.
  5. A rare pattern in older sample code. The older MetaTrader 4 sample code read ab*cd as matching the name ab: 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.

WhatRead differently by the older sample codeThe reading on this page
ab*cd and the name abfitsdoes not fit
*ab*bc* and the name abcfitsdoes not fit
*a*a* and the name aXadoes not fitfits
a long list with many * entries, for example *a*b*c*d*e*f*g*h*i*j* and the name abcdefghijdoes not fit: reading gave up after ten checksfits
spaces around entries, for example x , y and the name y, or abc with a space at the end and the name abcthe space could make the entry failthe spaces are ignored, fits
spaces around the name, for example the entry abc and the name abc followed by a spacedoes not fit: the space was part of the namethe 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*,!USDJPY does not exclude USDJPY. Move the exclusion to the front.
  • Patterns with * in the middle such as abc*def that you expect to fit a shorter name like abc. The name has to start with abc and end with def, so abc alone does not fit.
  • Patterns where two parts share a letter, such as *ab*bc* that you expect to fit abc. 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

  1. Open each mask field of the plugin or app and read it against the rules above.
  2. Replace every empty field you meant as "everybody" with *.
  3. Put every ! entry before the entries it cuts from.
  4. 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.
  5. Read the History notes of the version you are updating to.