EZRate
Sign in

OTC Market Data Aggregation: How EZRate Cleans Unstructured Quotes in Real Time

Inside OTC market data aggregation: collecting quotes, parsing units and sides, time windows, median-based outlier removal, jump guards and source counts.

Ersan Fazli
· Web Developer
Published 27 October 2026, 20:00 · 3 min read
OTC Market Data Aggregation
Key takeaways
  • Iran’s FX market is an OTC market: quotes arrive as free-text messages and venue prices, not as a central feed.
  • EZRate parses each message into asset, side, unit and market, keeps a short time window, drops quotes beyond ±1% of the median and publishes a weighted median every 60 seconds.
  • Low-source flags and a jump guard make the output honest when data is thin or noisy.

Most price APIs sit on top of an exchange that already produces a clean ticker. Iran’s currency market has none. Prices exist as thousands of messages, board quotes and venue prices that use different units, formats and conventions. This article explains the OTC market data aggregation pipeline that turns that noise into one publishable rate per minute.

★
Short answer
EZRate collects quotes from many independent sources, parses each into a structured record (asset, buy/sell, unit, market, time), keeps only recent quotes, removes those more than about 1% away from the median and publishes a weighted median every minute. If fewer than five valid sources remain, it keeps the previous rate and flags it as low-source. A jump guard holds back sudden unconfirmed moves.

Part of the Iranian Rial exchange rate API guide. The public summary is on the methodology page.

Stage 1: collection

Sources include trading channels and groups, exchange-office boards, regional markets and crypto venues. The collector reads them continuously and stores every raw message with its source and timestamp, so any published rate can be traced back.

Stage 2: parsing unstructured text

A typical message mixes emojis, hashtags, Persian and Latin digits and abbreviations. The parser has to answer, for every line: which asset, which side, which unit, which market?

ProblemExampleHandling
Digits and separators۱۰۵٬۲۰۰ / 105,200 / 105.2Normalise to Latin digits; infer thousands
UnitRial vs Toman, sometimes per lineExplicit markers, then plausibility vs current level
Side“خرید / فروش”, arrows, one-sided linesKeyword and position rules per source
Asset namesSynonyms and nicknamesDictionary of aliases per asset
MarketTehran, Herat, Dubai…Title or hashtags of the message
NoiseChange values, ads, old repostsIgnore non-price numbers; drop forwarded old messages

Each source can have its own regex patterns with an automatic fallback, because formats change without notice.

Example of a raw Persian price message transformed into structured JSON records
From free-text message to structured quotes

Stage 3: time window

Only quotes from the last few minutes count toward the current rate. A ten-minute-old quote describes a different market. Edited messages replace their earlier values instead of counting twice.

Stage 4: outlier removal

Within the window, the pipeline computes the median per asset, market and side, and drops quotes more than about 1% away. The median is used because it is robust: a handful of fat-finger or manipulated quotes cannot move it while most sources are honest.

python
from statistics import median

def clean(quotes: list[float], band: float = 0.01) -> list[float]:
    if not quotes:
        return []
    m = median(quotes)
    return [q for q in quotes if abs(q / m - 1) <= band]

The full statistical discussion, including MAD and Z-scores, is in outlier detection in price feeds.

Stage 5: weighted median

The remaining quotes are combined with a weighted median: sources with a longer track record of agreeing with the consensus weigh more, but no single source can decide the price.

Stage 6: confidence and guards

  • Low-source flag: fewer than five valid sources → keep the previous rate, set low_sources: true.
  • Jump guard: a move far beyond normal volatility must persist before it is published.
  • Source count: sources is published with every rate.
Pipeline diagram: collect → parse → window → outliers → weighted median → guards → publish
EZRate’s aggregation pipeline

What consumers should do with these signals

  1. Display updated_at and the source count next to rates.
  2. Pause automatic repricing while low_sources is true.
  3. Treat a missing update as “unchanged”, not as zero.

Implementation patterns are in financial API failover.

US Dollar270,680Toman+0.40%

Related live rates: US dollar rate, 18K gold price, Emami coin price.

What is OTC market data aggregation?
Collecting prices from many over-the-counter sources, cleaning them and combining them into one representative rate.
Why use the median instead of the average?
The median is not moved by a few extreme or manipulated quotes, while the average is.
How often is the rate updated?
Every 60 seconds.
What does low_sources mean?
Fewer than five valid sources were available, so the previous rate was kept and flagged.
Ersan Fazli
I work in software development and programming, with a strong interest in building practical and efficient solutions. My focus is on developing new features, improving system performance, and solving technical challenges using modern technologies. I strive to create reliable, scalable, and high-quality software through continuous learning and the application of modern development practices.
All posts by author →

Comments (0)

Comments are shown after review. Sign-in is needed to send.

Related

Market data
Outlier Detection in Price Feeds: Median Bands, MAD, Z-scores and Jump Guards Compared
Market data
Iran Official vs Free Market Rate: Why Standard Forex APIs Get IRR Wrong
API & Developers
USDT to IRT API: Tracking Tether to Toman Rates in Real Time via REST