Massive's forex aggregate bars use only the bid side of each quote. The forex custom bars documentation says the bars "are generated from quoted bid/ask prices rather than executed trades" and does not say which side; the bars themselves show it is the bid. The ask never enters a bar and neither does the midpoint, so one quote with a suddenly wide spread can set a bar's low far below where the market was.
Applies to
- Plans: every Currencies plan includes forex bars. Checking a bar against its quotes needs /v3/quotes, which Currencies Starter and Currencies Business include and Currencies Basic does not, per the forex quotes documentation.
- Endpoints: /v2/aggs/ticker/{forexTicker}/range/{multiplier}/{timespan}/{from}/{to} and the daily market summary, /v2/aggs/grouped/locale/global/market/fx/{date}, for bars; /v3/quotes/{fxTicker} for quotes.
- Asset classes: forex, including gold (C:XAUUSD).
What each field on a forex bar means
Each field on a forex bar from the custom bars endpoint, /v2/aggs/ticker/{forexTicker}/range/..., comes from the quotes inside the bar's window, taken in the order /v3/quotes returns them with order=asc:
| Field | On a forex bar |
|---|---|
| o | The bid of the first quote in the window |
| h | The highest bid in the window |
| l | The lowest bid in the window |
| c | The bid of the last quote in the window |
| n | The number of quotes in the window, including quotes whose bid did not change |
| v | The same number as n |
| vw | The average of the bids, each quote counted once, rounded to four decimal places |
| t | The start of the window, in Unix milliseconds |
The forex v is not a traded amount. Forex has no consolidated trade tape, so there is no traded size to add up, and v repeats the quote count in n. The sample response on the custom bars documentation shows the same, with v and n both 125329. v equals n on minute, hour, day, week and month bars and in the daily market summary.
The forex daily market summary, /v2/aggs/grouped/locale/global/market/fx/{date}, carries the same fields with one difference: its t marks the end of the day rather than the start, such as 1791417599999 (23:59:59.999 UTC) for 7 October 2026, as the daily market summary documentation describes.
The forex vw is not weighted by any size either, because quotes carry none. The WebSocket forex aggregates, CA and CAS, have no vw field at all, per the per-minute and per-second WebSocket documentation.
How to check a forex bar against its quotes
A forex bar with only a couple of quotes lets you check every field by hand. This EUR to AUD bar covers one second, 21:14:46 UTC (5:14:46 PM ET) on 29 September 2026:
curl -X GET "https://api.massive.com/v2/aggs/ticker/C:EURAUD/range/1/second/1790716486000/1790716486000?apiKey=YOUR_API_KEY"
Response
{
"ticker": "C:EURAUD",
"results": [
{ "v": 2, "vw": 1.5639, "o": 1.62233, "c": 1.50544, "h": 1.62233, "l": 1.50544, "t": 1790716486000, "n": 2 }
],
"status": "OK"
}
These are the quotes in that second:
curl -X GET "https://api.massive.com/v3/quotes/C:EURAUD?timestamp.gte=1790716486000000000×tamp.lt=1790716487000000000&order=asc&sort=timestamp&apiKey=YOUR_API_KEY"
Response
{
"results": [
{ "ask_exchange": 48, "ask_price": 1.62564, "bid_exchange": 48, "bid_price": 1.62233, "participant_timestamp": 1790716486000000000 },
{ "ask_exchange": 48, "ask_price": 1.74033, "bid_exchange": 48, "bid_price": 1.50544, "participant_timestamp": 1790716486000000000 }
],
"status": "OK"
}
The bar's open and high, 1.62233, are the first quote's bid, and its close and low, 1.50544, are the second quote's bid. n and v are both 2, one for each quote. vw is (1.62233 + 1.50544) / 2 = 1.563885, rounded to 1.5639. Neither ask, 1.62564 or 1.74033, appears anywhere in the bar. Both quotes carry the same participant_timestamp, because forex quote times are recorded to the whole second, so the order within a second is the order the endpoint returns.
Why a forex bar can show a price the market was not at
A forex bar's low can sit far below the market when one quote in the window has an unusually wide spread, because the bar takes that quote's bid and nothing else. A wide spread does not keep a quote out of a bar. The second quote above has a bid of 1.50544 and an ask of 1.74033, a spread of about 14% of its midpoint of about 1.6229, and that bid became the low of the one-second bar and of the minute bar around it:
curl -X GET "https://api.massive.com/v2/aggs/ticker/C:EURAUD/range/1/minute/1790716440000/1790716440000?apiKey=YOUR_API_KEY"
Response
{
"ticker": "C:EURAUD",
"results": [
{ "v": 55, "vw": 1.6192, "o": 1.6225, "c": 1.61984, "h": 1.6231, "l": 1.50544, "t": 1790716440000, "n": 55 }
],
"status": "OK"
}
The minute from 21:14 UTC opened at 1.6225 and closed at 1.61984, with 55 quotes, while its low is the 1.50544 bid. To build forex bars from the midpoint instead, or to leave out quotes with wide spreads, request the quotes from /v3/quotes and aggregate them yourself. The forex custom bars, daily market summary and previous day bar endpoints take no parameter that selects the ask or the midpoint.
Which exchange forex quotes come from
Forex quotes on Massive carry bid_exchange and ask_exchange 48. The exchanges endpoint lists one forex venue, with id 48:
curl -X GET "https://api.massive.com/v3/reference/exchanges?asset_class=fx&apiKey=YOUR_API_KEY"
Response
{
"results": [
{ "id": 48, "type": "exchange", "asset_class": "fx", "locale": "global", "name": "Currency Banks 1" }
],
"status": "OK"
}
Every forex quote carries this one id, so exchange 48 does not tell one bank or venue from another. Where does Massive's currencies data come from? explains why forex has quotes from banks and no exchange tape.
Why this happens
Forex bars are built from quotes because foreign exchange has no central exchange and no consolidated record of trades. With no executions to aggregate, a forex bar takes its prices from the bid of each quote and measures activity by counting quotes, which is why v and n match and why vw is an unweighted average.
If you see an error
v equal to n on every forex bar is expected: both count quotes.
A forex high or low far from the rest of the bar comes from one quote's bid. Request that window from /v3/quotes and compare bid_price with ask_price; a wide spread on one quote explains it.
A forex bar that does not match your own bars built from the midpoint is expected, because Massive's bars use the bid.

