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 |
| exchange-based | the open order books of crypto exchanges where the digital dollar is quoted against the currency, read once an hour through the exchanges' documented public APIs: the best offers of each side, their prices and the amount on offer, aggregated per exchange and across exchanges with the reliability each exchange has earned. Every exchange counts as one source; names are not attached to figures | the live table and GET /v1/rates/* for currencies the index covers through exchanges; exchange_spread_pct (Pro); the external term of USDT Liquidity |
All rates are quoted as fiat per 1 USDT.
The exchange-based component
- Which exchanges. Only exchanges that publish a documented public API for their offers, readable without an
account, whose terms do not forbid automated reading or redistribution, and whose
robots.txtallows the endpoint. Everything else is left out, and the terms of every exchange are re-read every 30 days: an exchange whose terms change drops out on its own. The exchanges behind a figure appear only as a count (sources); the list of exchanges is published here with their consent and is otherwise not attached to any figure. - The hour. Per exchange, currency and side the best offers by price (up to 20) give the best price, the median,
the depth-weighted price and the depth (the amount on offer, in USDT). Sides follow the index convention:
buyis the side where a client buys the digital dollar. An exchange that quotes bitcoin rather than a dollar token enters through its own US-dollar book of the same hour (price in the currency ÷ price in dollars), so every figure is a dollar rate. Across exchanges the hour'smid,buyandsellare reliability-weighted medians;exchange_spread_pct= (highest − lowest exchange mid) / median × 100 with at least two exchanges. The day takes the medians of its hours. - Reliability of an exchange (0–1, recomputed daily over 30 days): accuracy =
max(0, 1 − dev / 5 %),dev= median distance of the exchange's mid from the median of the other exchanges on the same hour (with fewer than two others, from the transaction-based reference of the day); a depth term (depth against a norm) and a freshness term (hours with data in the last day);0.5·accuracy + 0.25·depth + 0.25·freshness. An exchange whose prices sit away from the others — a stale or manipulated book — loses weight at once; three daily scores below 0.2 pause it for a week. - Confidence. A day is
highwith at least three exchanges within 2 % of each other,mediumwith at least three exchanges,limitedwith fewer. The day enters the one public index of the currency by the rule below; a currency the exchanges quote and no other component covers becomes active through this component alone.
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 several publish, the freshest day wins; the components that publish that same day are averaged with
weights by confidence: the transaction-based component with weight 1 / 0.6 / 0.3 for a
high/medium/limitedday, the multi-source and the exchange-based components with their 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, every exchange of the exchange-based component as one). It is published when it is two or more,
and always for a
limitedday ("1 source, limited confidence"); 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.
A currency is published from its first market source on: a day with one source is limited and carries the
note coverage improving (website, API, MCP) — the number is real, its uncertainty is stated, and more sources
are being added. medium needs two independent sources, high three agreeing within 2 %. The exchange-based
component enters the activity rule only once its order books show at least three independent offers in an hour
and two days of data; a currency without a single data point stays coverage_in_progress.
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. -
exchange_spread_pct (Pro and Enterprise) — the cross-exchange spread of the exchange-based component on the day (see above);
nullwith fewer than two exchanges.
"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.
City boards and exchangers' cards
- A city page (
/rates/{CUR}/{city}) shows the country-wide index and, below it, the boards of the exchange offices of the city that publish their rates openly (their public pages and channels): the median of those boards, the number of exchangers behind it and a confidence class —limitedwith fewer than three boards,mediumfrom three,highwhen the boards agree within 2 %. A board older than three days is not shown. - Every exchanger with a public board gets a card built from that board: the public name of the business, its city, its site or channel and the rates it publishes. No person is recorded: no names, phone numbers or documents; the only contact kept is an open address of the business on its own site.
- A business claims its card without any human step: a code placed in a file or a meta tag on its site, in a post of its channel, in a message to the EMpulse bot from an account that administers the channel, or a link in a letter to the open address. A verified card carries a badge, comes first on the city board and may publish its own rates (a bot command, its channel or its own key).
- Own rates are shown apart from the index, marked as the exchanger's own. They enter the index only as one more source with the reliability it earns (the same trust rules as every other source); a rate that disagrees with the city board by more than the plausible distance is not accepted and lowers the card's standing, never raises it. Repeated rejections pause quoting. Through that source, verified exchangers' own rates also count towards the activity of a currency, like any other source of the multi-source component.
Corridors (GET /v1/corridors/{a}/{b}, /corridors/{A}-{B}, Pro and Enterprise)
Many emerging-market currencies are exchanged against each other directly — som for tenge, hryvnia for dirhams, rupees for riyals — in public forums, channels and classified boards, without a dollar leg. The index reads those public listings in the languages of the countries and publishes the corridor rate of a pair next to the cross rate its own dollar legs imply.
- A listing is one public post that names two non-dollar currencies and a price or two amounts ("куплю сумы за тенге, 100 000 сум за 4 000 тенге", "1 KZT = 25.3 UZS", "vendo pesos por reales"). We keep the pair, the rate (units of the second currency per 1 unit of the first), the side of the author, the amounts when stated, the city and the payment method class (cash / transfer / card), the time and the link. Phone numbers, user names, e-mail addresses and links inside the text are removed before anything is stored; authors are never recorded. Rules for the common forms come first; a post with two currencies the rules did not understand goes to a language model as data, and every number it returns is validated like a rule match.
- Plausibility. A listing is checked against the rate implied by the two dollar legs of the index
(
b per USD ÷ a per USD) ± 60 %: an inverted number is flipped and noted, a number outside the range is an outlier and never enters the rate. Dollar offers in the same post are left to the dollar index. - The day (
rate) is the trust-weighted median of the day's valid listings of the pair, with the day'slowandhigh;sourcescounts the independent public sources behind it;confidenceishighwith three or more sources agreeing within 3 %,mediumwith two,limitedwith one or when a dollar leg of the pair has no reference yet. The pair is published from its first listing on; a single listing islimited. implied_crossis the same rate computed from the two dollar rates of the index on the same day (each at most three days old);corridor_premium_pct= (rate / implied_cross − 1) × 100 — the price of going directly instead of through the dollar: positive means the corridor is dearer, negative cheaper. The two directions of a pair are one series:b per aanda per bare each other's inverse, and so are the premiums.- Currencies without a dollar market. When a currency has no dollar component of its own but is quoted
against a currency that has one, the index derives its dollar rate through the corridor (
a per USD = b per USD ÷ b per a, the weighted median across such corridors of the day). That derived series enters the activity rule like any other component, one confidence class lower than the corridor it came from, and is never used as a dollar leg of another corridor. - Access. Public pages carry the 24-hour delay of the site; Pro and Enterprise keys get the current day,
the history and the list of the dearest and cheapest corridors (
/v1/corridors/top). Listings are read only from public sources whose robots.txt and terms allow it; sources that forbid automated reading or reuse are left out.
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.