Can MetaTrader 5 Detect a Cent Account?

Can MetaTrader 5 Detect a Cent Account?

10 August 2026, 11:54
Ivan Privalov
0
35

Can MetaTrader 5 Detect a Cent Account?

A practical investigation, an appealing theory, and the measurement that killed it.

The problem

You are writing an MT5 indicator or EA that displays money — profit, drawdown, expectancy, anything denominated in the deposit currency. A user runs it on a cent account. Every figure you show is now wrong by a factor of 100.

Not crashed. Not obviously broken. Just wrong, confidently, in a way that looks entirely plausible. A $12.34 profit renders as +1234 . A trader glancing at your panel sees a good week where there was a mediocre one.

The obvious fix is a setting — "Account Type: Standard / Cents" — and let the user tell you. But that invites the obvious question: why can't the software just work it out?

This article documents an attempt to do exactly that, the several approaches that seemed reasonable, the one that seemed mathematically airtight, and the empirical result that ended the effort. The conclusion is negative, which is precisely why it is worth writing down: the failure modes here are silent, and a plausible-but-wrong detector is considerably more dangerous than no detector at all.


First, a correction: cent accounts do not scale prices

A common misconception, and one worth clearing before any code is written.

On a cent account, prices are identical. XAUUSD quotes the same on a cent account as on a standard one. Contract sizes are typically identical too — 100 troy ounces for gold, 100,000 units for a forex major.

What differs is the denomination of the deposit currency. The broker holds your money in cents. Deposit $100 and the platform shows 10000 . The broker's back office applies a ×100 factor to the account balance; MetaTrader simply reports whatever the server tells it.

The practical consequence for a developer:

// Money values are scaled. These need adjusting on a cent account: HistoryDealGetDouble(ticket, DEAL_PROFIT) HistoryDealGetDouble(ticket, DEAL_SWAP) HistoryDealGetDouble(ticket, DEAL_COMMISSION) AccountInfoDouble(ACCOUNT_BALANCE) AccountInfoDouble(ACCOUNT_EQUITY) PositionGetDouble(POSITION_PROFIT) // These are NOT money and must never be scaled: // Profit Factor, Risk:Reward, Sharpe, win rate (ratios — units cancel) // Lot volumes (not currency) // Prices, SL/TP distances, points (not currency)

If a ×100 factor ever reaches your price arithmetic, that is a bug regardless of account type. Confine the scaling to a single choke point that every monetary display routes through:

double MoneyScale(double v)
  {
   return (Account_Type == ACC_CENTS) ? v / 100.0 : v;
  }

string FormatMoney(double v)
  {
   double d = MoneyScale(v);
   return ((d >= 0) ? "+" : "") + DoubleToString(d, 2);
  }

One function to audit, one place to be wrong.


Approach 1: contract size (the popular wrong answer)

This one circulates widely, so it deserves dismantling first. The reasoning: a standard forex lot is 100,000 units, cent lots are smaller, therefore a small contract size implies a cent account.

// DO NOT USE — this is broken double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); if(SymbolInfoInteger(_Symbol, SYMBOL_DIGITS) >= 2 && contractSize <= 1000.0) return true; // "cent account"

SYMBOL_TRADE_CONTRACT_SIZE is a symbol property. It says nothing about the account. Run that check across a normal instrument list on a perfectly ordinary standard account:

Symbol Digits Contract size Verdict Reality
EURUSD 5 100,000 standard standard ✔
XAUUSD 2 100 CENT standard ✘
XAGUSD 3 1,000 CENT standard ✘
BTCUSD 2 1 CENT standard ✘
SPX500 2 1 CENT standard ✘
AAPL (CFD) 2 1 CENT standard ✘

Gold, silver, indices, crypto and share CFDs all trigger it. The DIGITS >= 2 guard excludes almost nothing, since nearly every instrument qualifies.

The related trap is treating micro accounts as cent accounts. They are different products: a micro account trades micro lots but is denominated in ordinary currency. Conflating them misdetects both.

Worth noting that this exact confusion appears in an MQL5 forum thread from 2013 asking the same question. A poster claimed 0.01 lots would not open on a cent account; a moderator corrected him — that is SYMBOL_VOLUME_MIN , a symbol property, unrelated to denomination. The thread's overall conclusion, from a moderator, was that detection is probably not possible. Thirteen years later that holds up.


Approach 2: the currency code

Some brokers denominate cent accounts in a distinct ISO-style code: USC, EUC, GBC, JPC.

string cur = AccountInfoString(ACCOUNT_CURRENCY);
StringToUpper(cur);
bool looksCent = (StringLen(cur) == 3 && StringSubstr(cur, 2, 1) == "C");

This one genuinely works — when it is present. Several brokers do report USC on cent accounts, and this is the single most reliable positive signal available.

The limitation: many brokers report plain USD on a cent account. A USC reading is strong evidence for a cent account; a USD reading is no evidence at all in either direction. Treat absence as unknown, never as "standard confirmed".


Approach 3: string matching on account fields — and a landmine

The next instinct is to look for the word "cent" in the account's text fields. MT5 offers three:

AccountInfoString(ACCOUNT_SERVER) // "Broker-Pro", "Broker-MT5Real7" AccountInfoString(ACCOUNT_COMPANY) // "Broker Ltd" AccountInfoString(ACCOUNT_NAME) // <-- the landmine

Never search ACCOUNT_NAME

ACCOUNT_NAME is the account holder's personal name. The MQL5 reference is explicit that the identification strings are the server name, the broker's company, and the name of the client.

And so:

Vincent contains "cent".

So does Vincenzo, and Millicent. A naive substring search on ACCOUNT_NAME misclassifies people according to their parents' choices, then silently divides their P/L by 100. Do not read that field for this purpose at all.

The false-friend problem generally

English is unhelpfully full of words containing those four letters. For broker and server names, the realistic collisions are:

Central · Centre · Center · Centro · Crescent · Ascent · Accent · Percent · Adjacent · Innocent · Lucent

A broker called Central Markets or Crescent FX would be misdetected by StringFind(name, "cent") .

The tempting fix is a blacklist — strip the known false friends, then search. It works, but only for as long as the list stays complete, and it fails silently the first time a broker with an unanticipated name appears.

Boundary matching is better: require "cent" to be bounded by a separator or a string edge. No maintenance, and it rejects the whole class rather than enumerated members.

//+------------------------------------------------------------------+
//| Is "cent" present as a standalone token?                          |
//| Bounded by separator or string edge on both sides — distinguishes |
//| a denomination flag from a word that merely contains the letters. |
//+------------------------------------------------------------------+
bool HasCentToken(const string s)
  {
   string t = s;
   StringToLower(t);
   int len = StringLen(t);
   int pos = 0;

   while(pos < len)
     {
      int i = StringFind(t, "cent", pos);
      if(i < 0) break;

      bool leftOK = (i == 0);
      if(!leftOK)
        {
         string c = StringSubstr(t, i - 1, 1);
         leftOK = (c == "-" || c == "." || c == "_" || c == " " || c == "/");
        }

      int  after   = i + 4;
      bool rightOK = (after >= len);
      if(!rightOK)
        {
         string c = StringSubstr(t, after, 1);
         rightOK = (c == "-" || c == "." || c == "_" || c == " " || c == "/" ||
                    (c >= "0" && c <= "9"));
        }

      if(leftOK && rightOK) return true;
      pos = i + 1;
     }
   return false;
  }

Tested against real server-name shapes:

Input Matches Correct
Broker-Cent yes
Broker-Cent2 yes
Broker-Standard Cent yes
CentralMarkets-Live no
CrescentFX-Live no
AccentForex-Server no
Ascent-Live01 no

Concatenated product names ( ProCent , StandardCent ) fail boundary matching, so pair it with an explicit list of known product names:

const string g_centProducts[] = { "procent", "pro-cent", "standardcent", "standard cent", "centstandard", "cent.standard", "centeurica", "cent.eurica", "centaccount", "cent account" };

But the server often does not carry the product at all

Here is the structural problem with the whole string approach. Many brokers run numbered server pools shared across every account type — Broker-MT5Real , Broker-MT5Real2 , Broker-MT5Real25 . You choose Standard, Cent, Pro or Raw when opening the account; the server label reflects none of it.

Which means an exact-server whitelist is not merely incomplete — it is actively harmful. Whitelisting Broker-MT5Real4 as a cent server would misclassify every standard account on that same server. A silent false positive in the expensive direction.


Approach 4: the tick-value argument

This is the interesting one, and the reason this article exists.

SYMBOL_TRADE_TICK_VALUE is understood to be quoted in the deposit currency. Meanwhile, contract size × tick size gives the same quantity in the symbol's profit currency. On a cent-denominated account, the deposit currency is cents — so the reported value should be 100× the computed one.

double contract = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickVal  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);

double theoretical = contract * tickSize;   // in profit currency
double ratio       = tickVal / theoretical; // ~1 standard, ~100 cent?

The arithmetic checks out cleanly. XAUUSD: 100 × 0.01 = 1.00. EURUSD: 100,000 × 0.00001 = 1.00. Both should report tickValue ≈ 1.00 on a standard account, ≈ 100 on a cent account.

Numeric, broker-independent, no string matching. It looks like the right answer.

Two caveats that must be handled first

Cross-currency contamination. When the profit currency differs from the deposit currency, an FX rate is folded into the reported tick value. A JPY-denominated standard account trading EURUSD yields a ratio near 150 — which a naive threshold reads as a cent account. Restrict the test to same-currency-root pairs:

string profCur = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_PROFIT); bool sameRoot = (StringLen(cur) == 3 && StringLen(profCur) == 3 && StringSubstr(cur, 0, 2) == StringSubstr(profCur, 0, 2));

Calculation mode. ENUM_SYMBOL_CALC_MODE defines a different profit formula per instrument class. Only the contract-size family yields profit per tick per lot = contract × tickSize:

Calc mode Profit formula Ratio valid?
FOREX, CFD, CFDINDEX, CFDLEVERAGE, EXCH_STOCKS (close−open) × ContractSize × Lots yes
FUTURES, EXCH_FUTURES, FORTS (close−open) × TickPrice / TickSize × Lots no — circular
EXCH_BONDS face-value formula no
SERV_COLLATERAL no profit at all no

For futures, tick price is a primary input, not something derived from contract size, so the ratio is meaningless there. Whitelist the safe modes.

With both guards in place the test looked solid. So it was time to measure.


The experiment

The methodology matters more than the code. Two accounts at the same broker — one cent, one standard — probed with an identical script, on identical symbols, minutes apart. Same broker eliminates configuration differences between firms; the only variable is account type.

Confirmation of the cent account came from outside MetaTrader: the broker's own dashboard showed a balance of roughly $460, while MT5 reported 46,000. Exactly ×100. Not inferred — observed.

A minimal probe:

#property script_show_inputs

void OnStart()
  {
   string cur = AccountInfoString(ACCOUNT_CURRENCY);

   PrintFormat("SERVER   : %s", AccountInfoString(ACCOUNT_SERVER));
   PrintFormat("COMPANY  : %s", AccountInfoString(ACCOUNT_COMPANY));
   PrintFormat("CURRENCY : %s", cur);
   PrintFormat("BALANCE  : %.2f", AccountInfoDouble(ACCOUNT_BALANCE));

   string syms[] = {"XAUUSD", "EURUSD", "EURGBP"};
   for(int i = 0; i < ArraySize(syms); i++)
     {
      string s = syms[i];
      if(!SymbolSelect(s, true)) continue;

      double contract = SymbolInfoDouble(s, SYMBOL_TRADE_CONTRACT_SIZE);
      double tickSize = SymbolInfoDouble(s, SYMBOL_TRADE_TICK_SIZE);
      double tickVal  = SymbolInfoDouble(s, SYMBOL_TRADE_TICK_VALUE);
      double theo     = contract * tickSize;

      PrintFormat("%-8s contract=%.2f tickSize=%.6f tickValue=%.6f ratio=%.4f profitCur=%s",
                  s, contract, tickSize, tickVal,
                  (theo > 0 ? tickVal / theo : 0.0),
                  SymbolInfoString(s, SYMBOL_CURRENCY_PROFIT));
     }
  }

Save to MQL5\Scripts\ , compile with F7, drag onto a chart, read the Experts tab.


The result

Field Cent account Standard account
ACCOUNT_CURRENCY USD USD
ACCOUNT_SERVER Broker-Pro Broker-ECN
ACCOUNT_COMPANY identical identical
ACCOUNT_NAME (client name) (client name)
TERMINAL_NAME / _COMPANY identical identical
XAUUSD contract 100.00 100.00
XAUUSD tickSize 0.010000 0.010000
XAUUSD tickValue 1.000000 1.000000
tick ratio 1.0000 1.0000
EURUSD tickValue 1.000000 1.000000
volMin / volStep 0.01 / 0.01 0.01 / 0.01
profit currency USD USD

Not one discriminating field differs.

The tick-value theory was wrong. This broker quotes tick value in USD terms while denominating the balance in cents — an internal inconsistency from MT5's perspective, but exactly what the data shows. The ratio reads 1.0000 on both account types.

Worse than useless: the "ratio ≈ 1 ⟹ standard" branch was actively voting for the wrong answer on a genuine cent account. A detector that stays silent is harmless. One that confidently says "standard" about a cent account is how a user ends up reading a 100× overstatement.

The currency code showed USD on both. The server differed only by a product suffix — and hard-coding Broker-Pro ⟹ cent would misfire on the broker's standard "Pro" account, which shares that server.

One incidental result did land as predicted: EURGBP returned ratio 1.3498 on a USD account — the GBP/USD rate, exactly the FX contamination described earlier. The same-root guard correctly refused to score it. That part of the reasoning survived; the headline claim did not.


What survives

Signal Verdict
Currency code ends in C Works when present. Strong positive; absence proves nothing.
Known product name in ACCOUNT_SERVER Works for brokers with dedicated cent servers. Many have none.
Boundary-matched cent token in server Same. Useful, incomplete.
Cent-suffixed symbol names Occasionally useful.
ACCOUNT_NAME string search Never. Personal names. Vincent.
Contract size / volume min Never. Symbol properties, not account properties.
Exact server whitelist Never. Shared server pools cause false positives.
Tick-value ratio Disproved by measurement.

Every survivor is a positive-only signal: it can raise confidence that an account is cent-denominated, but none can establish that one is not.


The recommendation

Make it an explicit setting, and let it be authoritative.

enum ENUM_ACCOUNT_TYPE { ACC_CENTS = 1, // Cents 1:100 ACC_STANDARD = 0 // Standard 1:1 }; input ENUM_ACCOUNT_TYPE Account_Type = ACC_STANDARD; // Account Type

Two notes for anyone shipping this:

Freeze the enum integers. MT5 caches enum inputs by value, not by name. Renumbering members silently changes the setting on every saved chart. Append new members with new integers; never renumber existing ones.

If you offer auto-detection anyway, distinguish identification from assumption in your logging. "No cent indicators found, assuming Standard" is honest. "Detected: Standard" is a claim the data does not support.

A useful middle path: keep the explicit setting authoritative, but if the currency code says USC while the user has selected Standard, log a mismatch warning. Cheap, no false positives, and it catches the common misconfiguration without pretending to know.


Summary

  1. Cent accounts scale the deposit currency, not prices. Confine the ÷100 to money display; never let it reach price or ratio arithmetic.
  2. Contract size does not indicate account type. It is a symbol property. Gold's contract size of 100 triggers naive checks on every standard account.
  3. ACCOUNT_NAME is the client's personal name. Never string-search it. Vincent.
  4. ACCOUNT_CURRENCY == "USC" is the best available signal — but many cent accounts report plain USD .
  5. Server names frequently do not encode account type, and whitelisting shared servers creates false positives worse than the miss.
  6. The tick-value ratio does not work. Measured across two accounts at one broker, cent and standard, the ratio was 1.0000 on both.
  7. On at least one major broker, MT5 exposes nothing that distinguishes a cent account from a standard one.
  8. Ship the setting. A silent 100× error is worse than one checkbox.

Postscript on method

The tick-value argument was not a guess. The arithmetic was correct, the guards for cross-currency and calculation mode were correct, and the reasoning survived several rounds of scrutiny. It was still wrong, because it rested on an unverified assumption about what a broker reports in a field the documentation does not fully specify — the MQL5 reference defines SYMBOL_TRADE_TICK_VALUE only as "value of SYMBOL_TRADE_TICK_VALUE_PROFIT " and never states a currency.

Two probe runs, ten minutes apart, settled what no amount of further reasoning would have.

If you take one thing from this: when your detection logic rests on what a broker ought to report, go and read what it does report, on both sides of the distinction you are trying to make. The negative result is cheap to obtain and expensive to skip.