EMpulse Street FX Index — Methodology
The EMpulse Street FX Index publishes daily "street" exchange rates of emerging-market currencies against USDT (treated as 1 USD): the rate people actually pay, not the official one. The index combines transaction-based market data with quotes from public market sources; each series carries a confidence class, and the limits of the data are published with it.
Components
| component | what it is | where it appears |
|---|---|---|
| transaction-based | a weighted average of the day's market transactions, per currency and market side. Only aggregates enter the index: no individual transactions or counterparties are stored or published | GET /v1/rates/*, the live table |
| multi-source | rate quotes published by independent market sources (exchangers' public channels, the rates sections of local publishers, forums), weighted by the reliability each source has earned | GET /v1/street-index/* (product "Street Rate Index — multi-source"); the live table for currencies the index covers through public sources only |
All rates are quoted as fiat per 1 USDT.
Coverage and activity
The index of a currency is one number per day, built from the components above:
- when only one component publishes, the index is that component;
- when both publish, the fresher day wins; when both publish the same day, the index is their mean weighted by
confidence: the transaction-based component with weight 1 / 0.6 / 0.3 for a
high/medium/limitedday, the multi-source component with its confidence score (0–1, at least 0.1).
A currency is active when the index has a day within the last 14 days (/v1/currencies: status =
"active", active_since = the first day of the current run of data without a break longer than 14 days). A
currency without a recent day is coverage in progress: we are adding market sources for it, its published
history stays available, and it returns to the active list on its own once a recent day exists. The live table of
the website shows active currencies only; the number of currencies quoted in the plans is the number of active
currencies.
For every active currency the latest day carries:
- sources — the number of independent market sources behind the day (the transaction-based component counts
as one). It is published only when it is two or more; otherwise it is empty (
nullin the API). - confidence — the class of the latest day (
high/medium/limited, see below); when the day is three or more days old it is shown one class lower.
Daily index fields (GET /v1/rates/{cur})
- buy — the day's weighted average rate at which a client buys USDT for fiat. A side with too few data
points for the day is
null. - sell — the same for the side where the client sells USDT.
- mid —
(buy + sell) / 2when bothbuyandsellexist. If only one side of the market exists,midequals that side's rate (seeside). If neither side exists,midisnull. - side — what
midis based on:both,buy(onlybuyexists) orsell(onlysellexists);nullwhen there is nomid. Amidbased on one side of the market is shifted by roughly half a spread against a two-sided one, so takesideinto account when comparing days. - spread_pct —
(buy − sell) / mid × 100, only whenside = "both"; otherwisenull. It is usually positive; a negative spread can occur on days with limited data and is published as is. - confidence —
high,mediumorlimited. Every currency has a base class from its coverage (GET /v1/currencies:highfor the best-covered series,mediumfor the rest); a day with fewer data points than usual is one class lower (high → medium,medium → limited).limitedmeans fewer data points: more noise, possible gaps. - in_progress —
truefor the latest day of a currency while the day is still being collected and its data points are clearly below the norm of the previous 7 calendar days (days with data only). Such a day will be revised. All other days arefalse. - mid_7d — arithmetic mean of the valid
midvalues over the last 7 calendar days, including the day itself. Days without a validmidare skipped;nullif there are none. - chg_1d_pct, chg_7d_pct, chg_30d_pct —
(mid / mid_N_days_ago − 1) × 100, comparing with exactly 1, 7 or 30 calendar days earlier.nullif either day has no validmid(no carry-forward over gaps) or if the two days have a differentside(for example,bothagainstbuy): a one-sidedmidis shifted by roughly half a spread, so such a comparison would show a move that did not happen.
"Valid mid" means a non-null mid that is not flagged as an outlier.
A side that does not move
Sometimes one side of the market stops moving while the other keeps trading at changing prices: over several days
(five, for the daily index) its median rate stays within 0.05 %, or every day on that side clears at a single
price. Such a price is quoted rather than traded, and a mid halfway between it and the live side would be
meaningless. In that case the index is based on the moving side alone: mid equals that side's rate, side names
it (buy or sell) although both buy and sell are published, and spread_pct is null. The rule is applied
day by day and lifts itself as soon as the quiet side starts moving again; when both sides are equally still (a
flat market) nothing changes. The hourly index applies the same rule over a window of 24 hours.
Outliers
A day is flagged flag = "outlier" when all of the following hold:
- it has a
mid; - it has few data points (a large move on a well-covered day is never flagged: it is treated as a genuine market move);
- its
middeviates by more than 10% from the medianmidof the surrounding window of ±15 calendar days (the day itself excluded); - the window contains at least 3 other days with a
mid(otherwise there is not enough data to judge, and the day is not flagged).
Outlier days keep their own buy, sell, mid and spread_pct values so that clients can see them, but
they are excluded from mid_7d and all chg_* calculations (for the outlier day itself and for the
days that would reference it).
Revisions and timing
- Days are UTC calendar days. The current day is incomplete until 00:00 UTC and is revised during the day;
it is marked
in_progress = truewhile its data points are clearly below normal (see above). - Because the outlier window looks 15 days forward as well as back, flags (and therefore
mid_7dandchg_*) of the most recent 15 days can change when new data arrives. The whole history is recalculated on every update. - Free API keys receive data up to the previous day; pro and enterprise keys include the current day.
- Series delivered to each API key may contain insignificant individual differences.
Hourly index (GET /v1/rates/{cur}/hourly, Pro and Enterprise)
The hourly index is the daily index at hourly granularity: for every UTC hour and currency, buy and sell are
the weighted average rates of the hour's market transactions on each side, mid, side and spread_pct follow
the daily rules, and confidence is the currency's base class.
- Hours and sides. A row exists for every hour with market data. A side needs at least 3 data points in the
hour to be published; with fewer it is
null, and when only one side existsmidis that side (sidetells which). For currencies with fewer data points, hours with an index value are correspondingly rarer: the hourly series has gaps rather than noise, by design. - Outliers within the hour. The hour's aggregates are built from transactions within ±20 % of the hour's own
median rate; nothing else is filtered afterwards, and hourly rows carry no
flag. The rule on a side that does not move (above) is applied over a window of 24 hours. - Daily summary. Each day of a per-currency response carries
vwap(the weighted average rate of the whole day, both sides together),highandlow(the highest and lowest hourlymid),hours(hours with an index value) andpeak_hour(the hour with the most market activity).highandloware taken from the hours with enough data points (at least 5 in the hour) and, when the day has them, from two-sided hours only: amidbased on one side of the market sits about half a spread away from a two-sided one, and mixing the two would show a range the market never traded.hoursstill counts every hour with an index value. - In progress. The current UTC hour is
in_progress = truewhile it is being collected and will be revised;GET /v1/rates/hourly/latestreturns only complete hours. - Timing. Hourly rows arrive with the daily update, which covers the last three days (late transactions and recalculations are picked up then): updated daily, hourly granularity — not a real-time feed. Hourly rows older than the history horizon count against the history quota at 1/24 of a daily row. The public website shows the hourly chart of the previous day for the best-covered currencies, with the same 1-day delay as the free tier.
Multi-source street index (GET /v1/street-index, product "Street Rate Index — multi-source")
For every currency where public market sources publish street rates — next to the transaction-based series where one exists, on its own where it does not — the index publishes the rate quoted in open sources: exchangers' public Telegram channels, the rates sections of local publishers, forums. A published rate is a fact; we keep the number, the time and the link to the post, never the text.
- Quote — one number from one public post: quoted currency (ISO 4217), base (
USD;USDTis counted as USD and noted;EURquotes are kept apart and not converted), side, rate per 1 unit of base, time of the post (UTC), link. A snippet of at most 200 characters around the number is stored for audit only and never shown. A post may yield several quotes (buy and sell, several currencies, several cities). - Side convention.
sideis the client's action, as in the index: a client buys USD at the exchanger's selling price (the higher number) and sells USD at the exchanger's buying price. A post that labels the pair the other way round is swapped and marked with a lower confidence.midis a quote without sides. - Extraction. Regular expressions for the common formats first ("USD 1650/1665", "Покупка: 11810 / Продажа: 11860", "$ = ₦1,650", "1 USDT = 41.25 TRY", "Dolar: 49,72", Dari / Arabic / Urdu / Bengali / Persian forms; thousands written with spaces, apostrophes or dots; toman = 10 rials). Lines about official, central-bank, exchange-listed and forecast rates are skipped. Posts with a currency word and a number but no match go to a language model in batches, as data; every number it returns is validated like a regex match.
- Plausible range per currency and base: the transaction-based reference of the last 30 days, otherwise
the median of the last 14 days of quotes (≥ 5), otherwise a seed value; the range is the reference ± 60 %.
Quotes outside it are kept as
outlierand never enter the index; quotes stored before a range existed are re-checked when one appears. - Source trust (
reliability, 0–1, recomputed every day over the last 30 days): - accuracy =
max(0, 1 − dev / 5 %),dev= median |quote / reference − 1|; the reference is the transaction-based reference of the same UTC day where one exists (a calibrated source), otherwise the leave-one-out median of at least two other sources for that currency and day; with no reference at all, 0.5. For a calibrated source the shift that the whole group of that currency's sources shares against the reference (the median deviation of the other sources on that day, at least two of them) is taken out first: a cash market that quotes a few percent away from the digital-dollar market is a property of that market, not an error of the source that reports it. Accuracy therefore measures how far a source sits from the market consensus; dispersion and outliers are penalised as before; - the shared shift itself is published with the day as
basis_gap_pct= (multi-sourcemid/ transaction-based reference − 1) × 100: the premium (negative: discount) of the quoted street rate to the transaction-based component on that day — in practice the gap between the cash market and the digital-dollar market. It is a component of the index, not a correction: neither series is adjusted by it; - cleanliness = 1 − share of outliers; stability =
max(0, 1 − dispersion / 3 %), dispersion = median within-day (max − min) / median of the source's quotes; regularity = days with quotes / 20, at most 1; reliability = 0.5·accuracy + 0.2·cleanliness + 0.15·stability + 0.15·regularity, within [0.05, 1]. A source with fewer than 5 quotes keeps the starting value 0.3. Three daily scores in a row below 0.2 pause the source for 7 days; it then returns as a candidate and has to earnactiveagain (≥ 5 valid quotes in 3 days). History is kept per day.- The day (
GET /v1/street-index/{cur}/history):mid= reliability-weighted median of post mids (the buy and sell of one post averaged; a lone quote counts as is),buy/sell= weighted medians of the respective quotes,spread_pct = (buy − sell) / mid × 100. A day is published only with ≥ 3 quotes from ≥ 2 sources; otherwise there is no row.n_sources,n_quotes,confidence(0–1: half from the number of sources, 1 at four or more; half from agreement, 1 when the median deviation of mids is 0 and 0 at 5 %) andconfidence_class(high: ≥ 3 sources agreeing within 2 %;medium: ≥ 3 sources;limited: fewer sources) describe the day. An hourly series exists only for hours with ≥ 3 quotes. - Access. Free keys: the free-tier currencies with a 1-day delay; Pro: all currencies; Enterprise: also the
quotes behind each day with their source links (
GET /v1/street-index/{cur}/quotes). Days are recomputed daily for the last 45 days and can change when sources are re-scored or re-flagged. - Limitations. Posts are what sources chose to publish; a source may be late, wrong or promotional — that
is what the trust score is for. Coverage per currency is in
n_sources. Not investment advice.
Currency pages and site statistics
- Each currency has a public page (
/rates/{CUR}) with the same 1-day delay as the free tier: the latest day, changes, 90-day and 1-year charts, the official-vs-street comparison where the product covers the currency, the last 30 days, and the same figures the API serves. The pages,/sitemap.xml,/llms.txtand/llms-full.txtare generated from the index; when citing a figure, name the source as "EMpulse Street FX Index". - The website keeps first-party visit statistics without cookies or external trackers: page views, countries and referrers are aggregated from the server's access log once an hour; IP addresses are not stored (visitors are counted through a hash with a daily rotating salt). The country of a visit is derived from the IP address with the DB-IP Lite database — IP Geolocation by DB-IP (https://db-ip.com), licence CC BY 4.0.
Limitations
- Street rates reflect what people actually pay on the street market; they are not official, central-bank or interbank rates and may differ from them significantly.
- Series with
confidence = limitedhave gaps (days without a value or without amid) and higher noise. Checkconfidenceandflagbefore using a value. - Coverage starts at different dates for different currencies (
coverage_sinceinGET /v1/currencies), and a currency can becoverage_in_progressfor a while (see "Coverage and activity"). - The index is provided for information purposes only and is not investment advice.