Forex currency exposure: find the USD risk hidden across pairs

6 min readMetaKit

Buying EURUSD and buying GBPUSD creates two different symbol positions, but both are short the dollar economically. A report grouped only by symbol can hide that common exposure. Adding their lot sizes tells you little about the currency amounts involved.

Forex currency exposure analysis splits each position into signed base and quote legs, then aggregates by currency. This article builds a simple, explicitly limited decomposition for verified linear forex contracts. It is different from aggregating account P&L, and different from calculating how many lots to trade from a stop distance.

Translate a position into two currency legs

For a pair quoted as base/quote, the exchange rate states how much quote currency corresponds to one unit of base. Buying the pair is economically long the base and short the quote; selling reverses those signs. OANDA explains this base/quote convention.

For an illustrative EURUSD contract of 100,000 EUR per lot, a 0.5-lot buy at a reference rate of 1.1000 corresponds to +50,000 EUR and −55,000 USD. A 0.5-lot sell gives −50,000 EUR and +55,000 USD under the same reference-rate convention.

These are exposure representations, not literal cash balances or a promise of physical currency delivery. A leveraged retail contract can produce this economic exposure without a $55,000 cash withdrawal from the account. Fees, financing, settlement terms and broker margin rules are separate.

Use each broker symbol's contract size. MetaQuotes documents contract size, currencies and calculation modes; “one lot” does not prove the same units across instruments. A gold, index or inverse contract needs its own model. Currency-looking names are not sufficient evidence that the simple forex formula applies.

A three-position example

Assume all three synthetic positions have verified 100,000-base-unit forex contracts. Use synchronized reference rates of EURUSD 1.1000, GBPUSD 1.2500 and USDJPY 150.00. Ignore spread and costs for this decomposition.

PositionBase legQuote leg
Buy 0.50 EURUSD+50,000 EUR−55,000 USD
Buy 0.40 GBPUSD+40,000 GBP−50,000 USD
Sell 0.20 USDJPY−20,000 USD+3,000,000 JPY

Aggregating the native-currency buckets produces +50,000 EUR, +40,000 GBP, −125,000 USD and +3,000,000 JPY. The USD short is shared by all three trades. A symbol table would show three rows; the currency table makes the common direction visible.

For comparison only, translate each bucket into USD using the same reference rates. The EUR leg is +$55,000, GBP is +$50,000, JPY is +$20,000 and USD is −$125,000. Their signed sum is zero by construction. That does not mean the portfolio has zero risk: the underlying currency legs can change value differently.

Do not add the absolute values of every leg and call the result unique portfolio notional without defining your convention. Doing so counts both sides of every represented trade. A risk report can show gross long and short currency amounts, but should explain what was counted.

Read the right fields from MT5

MetaKit exposes open positions through /v1/accounts/{id}/positions and symbol specifications through /v1/accounts/{id}/symbols/{symbol}. The current position data includes symbol, type and volume. Symbol detail includes contract_size, currency_base, currency_profit, bid and ask, alongside other specification fields.

For a confirmed forex contract, currency_base identifies the base units and currency_profit identifies the quote/profit currency used by this model. Check the broker specification before treating those fields as a universal pair definition. The current public symbol response does not expose every native calculation-mode property, so this sample requires an explicitly approved forex instrument set.

Cache specifications per account and exact symbol, not just a suffix-stripped name. The symbol differences guide explains why two similarly named contracts can have different sizes. Fetch all position pages and attach a common observation time before aggregating multiple accounts.

A pure aggregation function

The following JavaScript operates on normalized rows you create after those checks. price is a consistent reference quote per symbol, and verifiedForex is your explicit instrument-class validation, not a field returned by MetaKit.

function currencyLegs(rows) {
  const exposure = new Map();
  const add = (currency, amount) => {
    const total = (exposure.get(currency) ?? 0) + amount;
    if (!Number.isFinite(total)) throw new Error("Exposure overflow");
    exposure.set(currency, total);
  };
  for (const row of rows) {
    const { base, quote, lots, contractSize, price, side } = row;
    if (row.verifiedForex !== true || !/^[A-Z]{3}$/.test(base) ||
        !/^[A-Z]{3}$/.test(quote) || base === quote ||
        ![lots, contractSize, price].every(Number.isFinite) ||
        lots <= 0 || contractSize <= 0 || price <= 0 ||
        !["buy", "sell"].includes(side)) {
      throw new Error("Unverified or invalid forex exposure row");
    }
    const units = (side === "buy" ? 1 : -1) * lots * contractSize;
    add(base, units);
    add(quote, -units * price);
  }
  return Object.fromEntries(exposure);
}

This deliberately does not guess the base currency from the first three characters of a symbol. Nor does it accept every CFD simply because its profit currency is USD. Reject an unknown instrument and show an incomplete-report marker. Silently omitting an unsupported contract can make exposure look safer than the full portfolio.

Use one shared reference price for offsetting positions in the same instrument when assessing current directional exposure. Using each trade's entry price instead leaves a residual that includes historical economics, which answers a different question. Retain entry data separately for P&L analysis and clearly label whether the table is entry notional or current-reference exposure.

What the exposure table does not measure

Netting currency buckets does not net broker margin across separate accounts. A long and a short at different brokers can leave small net economic exposure while each account still faces its own margin requirements, liquidation rules, funding costs and counterparty risk. Read margin level and stop-out separately.

A bucket also does not predict loss from a specified market shock. That needs a defined scenario and an appropriate repricing model. For example, if EURUSD rises by 0.01 while the other reference rates are held fixed, the 50,000-EUR linear long contributes approximately +$500 before costs. The scenario is illustrative, and independently moving all pairs can violate cross-rate consistency. Real scenario engines should define coherent underlying currency changes.

The aggregation function and three-position arithmetic were checked with synthetic fixtures, including sell signs, nonstandard contract sizes and rejection of unsupported instruments. No live portfolio was queried. Use the MetaKit API documentation to connect your own read-only collector, retain observation times, and report native currency units before adding any conversion layer.

Sources and verification

Verified 18 October 2026. All prices, positions and contract sizes in the worked portfolio are synthetic assumptions, not broker specifications or trading recommendations.