Guides

How to calculate position size in MetaTrader — pips, lots, leverage and the formula

How to calculate position size in MetaTrader — pips, lots, leverage and the formula

Every trade needs an answer to one question before anything else: how big? Get it wrong in one direction and a good strategy makes no money. Get it wrong in the other and a normal run of losses takes the account.

A position size calculator answers that question with arithmetic. It is not a prediction and not a strategy. It takes three things you already decided — how much you are willing to lose, where your stop goes, and what you are trading — and returns the only lot size consistent with them.

This article explains every term in that calculation, derives the formula, shows how to compute it in MQL5 properly, and ends with a free calculator you can use right now.

What the calculator actually decides

It does not decide where your stop goes. It does not decide whether the trade is good. Those come first, and they come from your strategy.

What it decides is the one number that connects them:

If I am willing to lose X, and my stop is Y away, then my position can only be Z big.

Change any of the three and the third follows. That is the whole idea. The rest of this article is about getting each term right, because each one has a trap in it.

The vocabulary, properly

Most of the confusion in position sizing is vocabulary, not mathematics. Four terms do all the work.

Point

The smallest price change the symbol can make. On a 5-digit EURUSD quote, one point is 0.00001. In MQL5 this is SYMBOL_POINT.

Pip

The traditional unit traders talk in — and not the same as a point on most modern brokers. A pip is the fourth decimal on a normal FX pair and the second decimal on a JPY pair.

Point versus pip on a 5-digit and a 3-digit quote

When your broker quotes 5 digits, one pip is ten points. This single mismatch is responsible for more oversized positions than any other mistake in retail trading: the trader sizes for a 20-pip stop, the code divides by 20 points, and the position comes out ten times too large.

If you only remember one thing from this article, remember that the unit you think in and the unit your code receives are usually not the same unit.

Lot and contract size

A lot is a quantity of the instrument. The contract size says how much.

Standard lot Units per 1.00 lot
Forex 1.00 100,000 units of the base currency
Gold (XAUUSD) 1.00 100 troy ounces
Silver (XAGUSD) 1.00 5,000 troy ounces
Indices, crypto 1.00 usually 1 contract — but check

Those last two words matter. Contract sizes for gold, indices and crypto differ between brokers more than they have any right to. In MQL5, read it rather than assume it: SYMBOL_TRADE_CONTRACT_SIZE.

Pip value

What one pip of movement is worth, for one lot, in your account currency. This is where the money enters the calculation:

TEXT
pip value per lot = pip size  x  contract size  x  (quote currency -> account currency)

For EURUSD on a USD account: 0.0001 x 100,000 x 1 = $10. The conversion is 1 because the pair is already quoted in USD.

For USDJPY on a USD account the pair is quoted in yen, so the yen have to be converted back. At a price of 147.12:

TEXT
0.01 x 100,000 x (1 / 147.12) = $6.80 per pip, per lot

This is why a 50-pip stop is not the same amount of money on every symbol, and why "I always trade 0.10 lots" is not a risk policy.

Leverage is not in the formula

This surprises people, so it is worth being blunt about it.

Leverage does not affect how much you lose when your stop is hit. If you buy 0.10 lots of EURUSD and it moves 50 pips against you, you lose $50 on 1:30 leverage, on 1:100 leverage, and on 1:500 leverage. The loss is set by position size and distance, nothing else.

What leverage decides is whether the broker will let you open the position — that is margin, a different question:

TEXT
margin required = (lots x contract size x price) / leverage

Higher leverage means the same position ties up less of your money. It does not make the position safer, and it does not belong anywhere in a risk calculation. Treat it as a constraint to check at the end, not an input at the start.

The formula

Everything above collapses into one line:

TEXT
                        risk money
lots =  ------------------------------------------
         stop loss in pips  x  pip value per lot

Where risk money is either a percentage of your balance or a fixed amount:

TEXT
risk money = balance x risk%  /  100          or just a number you choose

Worked, three times

EURUSD, $10,000 account, 1% risk, 50-pip stop

TEXT
risk money = 10,000 x 1% = $100
pip value  = 0.0001 x 100,000 x 1 = $10
lots       = 100 / (50 x 10) = 0.20

USDJPY, same account, same risk, price 147.12

TEXT
pip value  = 0.01 x 100,000 x (1/147.12) = $6.80
lots       = 100 / (50 x 6.80) = 0.29

XAUUSD, same account, 30-pip stop (pip = 0.1, contract = 100 oz)

TEXT
pip value  = 0.1 x 100 x 1 = $10
lots       = 100 / (30 x 10) = 0.33

Three different symbols, three different lot sizes, the same $100 at risk. That is the point of sizing at all.

What the formula looks like

For a fixed amount of money at risk, position size falls away as the stop gets wider. It is a hyperbola, and it has a practical consequence: a stop twice as wide means a position half the size, not a loss twice as large.

Position size against stop-loss distance at three risk levels

Why a percentage, and not a fixed lot size

Risking a percentage of the current balance has one property a fixed lot size can never have: it shrinks automatically when you are losing.

Balance remaining after consecutive losses at 1, 2, 5 and 10 percent risk

Twenty losses in a row at 1% leaves 82% of the account. At 10% it leaves 12%. Both are the same number of losses; only the sizing rule differs.

Twenty losses in a row sounds absurd, and for a strategy that wins half its trades it very nearly is: across a thousand trades you would expect about two runs of eight, and a run of twenty essentially never.

Now take a trend-following system that wins 35% of the time and makes its money from a few large winners. Runs of eight become routine — roughly fifty of them in five thousand trades — and a run of twenty stops being a freak event. A low win rate is not a flaw in that kind of system; it is the design. The sizing rule has to survive it anyway.

That is the thing to take from the chart: a sizing rule is judged by the tail, not by the average.

Here is the same idea on a fixed sequence of trades — identical results in both lines, only the sizing differs:

The same 24 trades sized with a fixed lot and with 2 percent of balance

Read that chart honestly: the fixed-lot line finishes higher. Larger size amplifies both directions, and in this particular sequence it ended up ahead.

The difference is what happened on the way. The fixed-lot account fell to $6,000 — a 40% drawdown — while the percentage account never went below $8,508. And the fixed-lot account was risking $500 a trade from a $10,000 start, so twenty consecutive losses would have left it at exactly zero. The percentage account would still have had $6,676 and a way back.

A sizing rule is not judged by the run it survived. It is judged by whether it can survive the run it has not had yet.

Calculating it in MQL5

Now the part that matters if you are writing an Expert Advisor. The naive version looks like this, and it is wrong in three separate ways:

MQL5
// Do not use this.
double lots = (AccountInfoDouble(ACCOUNT_BALANCE) * 0.01) / (stopPips * 10.0);

It assumes a $10 pip value, which is only true for USD-quoted pairs on a USD account. It ignores the broker's volume step, so it will send 0.2873 to a server that only accepts multiples of 0.01. And it never checks the minimum.

The reliable way: let the terminal do the conversion

MetaTrader already knows every contract size, every tick value and every exchange rate your account needs. OrderCalcProfit() asks it directly: if I opened this position here and closed it there, what would it do to my balance?

Point it at your entry and your stop, and the answer is your loss per lot — converted into your account currency, for any symbol, with no assumptions:

MQL5
//+------------------------------------------------------------------+
//| Loss in account currency if volume is stopped out at sl.          |
//| Returns a positive number, or 0.0 if it cannot be calculated.     |
//+------------------------------------------------------------------+
double LossForVolume(const string symbol, ENUM_ORDER_TYPE type,
                     double entry, double sl, double volume)
  {
   double money = 0.0;
   if(!OrderCalcProfit(type, symbol, volume, entry, sl, money))
      return(0.0);                       // unknown symbol, or no quotes yet
   return(MathAbs(money));
  }

With that, the position size is three lines of arithmetic and a lot of care about the edges:

MQL5
//+------------------------------------------------------------------+
//| Volume that risks riskMoney if price travels from entry to sl.    |
//| Returns 0.0 when the trade cannot be taken within that risk.      |
//+------------------------------------------------------------------+
double PositionSize(const string symbol, ENUM_ORDER_TYPE type,
                    double entry, double sl, double riskMoney)
  {
   if(riskMoney <= 0.0 || entry <= 0.0 || sl <= 0.0 || entry == sl)
      return(0.0);

   //--- what one full lot would lose on this exact stop
   double lossPerLot = LossForVolume(symbol, type, entry, sl, 1.0);
   if(lossPerLot <= 0.0)
      return(0.0);

   double volume = riskMoney / lossPerLot;

   //--- the broker's volume grid
   double vmin = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double vmax = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   if(step <= 0.0)
      step = vmin > 0.0 ? vmin : 0.01;

   //--- always DOWN: rounding up risks more than was asked for
   volume = MathFloor(volume / step) * step;

   //--- kill the binary dust that MathFloor leaves behind
   int decimals = (int)MathMax(0, MathCeil(-MathLog10(step)));
   volume = NormalizeDouble(volume, decimals);

   if(volume > vmax)
      volume = NormalizeDouble(MathFloor(vmax / step) * step, decimals);

   //--- Below the broker's minimum, the honest answer is "no trade".
   //--- Rounding up to vmin here is the bug that quietly doubles your risk.
   if(volume < vmin)
      return(0.0);

   return(volume);
  }

Risk as a percentage

MQL5
double RiskMoneyPercent(double percent)
  {
   //--- Equity, not balance: it already includes what open trades are doing.
   //--- Sizing off balance while sitting on a large floating loss will size
   //--- the next trade off money you no longer really have.
   return(AccountInfoDouble(ACCOUNT_EQUITY) * percent / 100.0);
  }

Converting pips to a stop price

If your stop is expressed in pips rather than as a price, convert it once, in one place:

MQL5
//+------------------------------------------------------------------+
//| Pip size for a symbol. The 3/5-digit rule covers Forex; indices   |
//| and crypto are quoted in whole points, so pip == point there.     |
//+------------------------------------------------------------------+
double PipSize(const string symbol)
  {
   int    digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
   double point  = SymbolInfoDouble(symbol, SYMBOL_POINT);
   return((digits == 3 || digits == 5) ? point * 10.0 : point);
  }

This heuristic is standard and it is also only a heuristic. It is right for Forex and metals and right for most indices by accident. If you trade exotic symbols, make the pip size an input and stop guessing.

Check the margin before you send it

Correct size and affordable size are different questions. Leverage shows up here and only here:

MQL5
bool CanAfford(const string symbol, ENUM_ORDER_TYPE type,
               double volume, double price)
  {
   double margin = 0.0;
   if(!OrderCalcMargin(type, symbol, volume, price, margin))
      return(false);
   //--- leave headroom: free margin at the moment of sending is not
   //--- free margin a second later
   return(margin < AccountInfoDouble(ACCOUNT_MARGIN_FREE) * 0.5);
  }

Putting it together

MQL5
void OpenRiskedTrade(const string symbol, double riskPercent, double slPrice)
  {
   double entry = SymbolInfoDouble(symbol, SYMBOL_ASK);
   double risk  = RiskMoneyPercent(riskPercent);
   double lots  = PositionSize(symbol, ORDER_TYPE_BUY, entry, slPrice, risk);

   if(lots <= 0.0)
     {
      Print("Trade skipped: ", risk, " is not enough to open the smallest ",
            "position this symbol allows with that stop.");
      return;
     }
   if(!CanAfford(symbol, ORDER_TYPE_BUY, lots, entry))
     {
      Print("Trade skipped: not enough free margin for ", lots, " lots.");
      return;
     }

   CTrade trade;
   trade.Buy(lots, symbol, 0.0, slPrice, 0.0);
  }

The alternative, when you cannot use OrderCalcProfit

Some contexts — a custom indicator, a quick script — make OrderCalcProfit() awkward. The tick-value route gives the same answer:

MQL5
double LossPerLotFromTicks(const string symbol, double entry, double sl)
  {
   //--- TICK_VALUE_LOSS, not TICK_VALUE. On symbols where the two differ,
   //--- the loss value is the one that applies to a stop-out.
   double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE_LOSS);
   double tickSize  = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tickValue <= 0.0 || tickSize <= 0.0)
      return(0.0);
   return((MathAbs(entry - sl) / tickSize) * tickValue);
  }

Note SYMBOL_TRADE_TICK_SIZE, not SYMBOL_POINT. They are equal on most Forex symbols and not equal on plenty of others, and using the wrong one scales every position you ever open.

What this calculation does not include

Position sizing is necessary and it is not sufficient. Four things sit outside the formula and all four cost real money:

Commission. A round-turn commission is part of the cost of a losing trade. If you pay $7 per lot per side, a 0.20-lot trade costs about $2.80 on top of the loss. Subtract it from your risk budget before sizing.

Swap. Positions held overnight are charged or credited. On a multi-day trade this can exceed the commission.

Slippage and gaps. Your stop is where you intend to leave, not a promise of the price you get. A weekend gap or a news spike can fill well past it. This is the single best argument for keeping the percentage small.

Correlation. Six positions at 1% each are not six independent 1% risks if they are all short the dollar. They are closer to one 6% risk wearing six hats.

Use the calculator

Rather than doing this by hand, the site has a calculator that implements exactly the formula above:

Open the position size calculator

It handles percentage and fixed-amount risk, Forex, metals, indices and crypto, accounts in any currency, and it rounds to your broker's volume step — always downwards.

When the symbol is not quoted in your account currency it fetches the exchange rate for you, shows you which rate it used and when it was published, and lets you overwrite it if you would rather use your broker's live number. Your figures never leave your browser: the only request the page makes carries two currency codes and nothing else.


Nothing in this article is financial advice. It is arithmetic, and the inputs are yours. Trading involves substantial risk of loss.