How do I tell whether Taiwan market data is missing something?

Separate two things: the market was closed, and the data did not arrive. For prices, the response's meta.market_status tells you which dates were open, so you can compare that against the rows you received. Some datasets carry a per-row data_gaps field; the price endpoint does not, and instead gives you price_method and price_confidence describing how each number was produced.

Two kinds of missing, handled differently

  • The market was closed: weekends, public holidays, typhoon days. Not a gap — your series should not have those dates.
  • The data did not arrive: the market traded and you have no row. That is a gap, and left unhandled it quietly distorts calculations.
  • Conflating the two usually ends in forward-filling, which invents a flat day that never happened.

Prices: use meta.market_status to separate closed from missing

The price response's meta carries last_trading_day and market_status, a list of date and status pairs where status is open or unknown. Open means the market traded; unknown means not yet determined. Compare that against the dates you received and the distinction resolves itself.

import requests

r = requests.get(
"https://api.twmarketdata.com/v2/datasets/twse-daily-price",
headers={"X-API-Key": "sk_live_your_key"},
params={"symbol": "2330", "start_date": "2026-06-01", "end_date": "2026-07-31"},
timeout=20,
)
r.raise_for_status()
payload = r.json()

print("rows", payload["count"], "data as of", payload["data_as_of"])
print("last trading day", payload["meta"]["last_trading_day"])

have = {row["date"] for row in payload["rows"]}
for entry in payload["meta"]["market_status"]:
if entry["status"] == "open" and entry["date"] not in have:
print("market traded but no row:", entry["date"])

Every price row explains how it was produced

Each row carries price_method and price_confidence. When a value looks odd or a stretch is uncertain, read those two before deciding whether to use it — they tell you more about trustworthiness than the close itself does.

for row in payload["rows"][:5]:    print(row["date"], row["close"], "method", row["price_method"], "confidence", row["price_confidence"])

Some datasets carry data_gaps, and it is per row

Margin and short balance data, for instance, carries a data_gaps field on every row. In practice it is usually an empty list, meaning no known gap for that row; when populated it describes that row's gaps. Note that it is per row, not one summary for the response.

m = requests.get(    "https://api.twmarketdata.com/v2/datasets/margin-short",    headers={"X-API-Key": "sk_live_your_key"},    params={"symbol": "2330", "limit": 20},    timeout=20,).json() for row in m["rows"]:    if row["data_gaps"]:        print(row["trade_date"], "gap markers:", row["data_gaps"])

Elsewhere data_gaps describes the dataset, not a day

On some datasets the same name holds named coverage limitations rather than dates — statements about what the dataset never covered, such as being limited to the listed market. That is not telling you a day is missing; it is telling you a scope boundary exists, which is worth reading before any cross-market analysis.

There is no single site-wide gaps field

Worth saying plainly: data_gaps is not present on every endpoint. The price endpoint has none, and offers market_status, count, data_as_of and per-row price_method and price_confidence instead. So the right move is not to hunt for data_gaps, but to check which signal the endpoint you are calling actually provides.

Whatever the signal, do not patch the hole yourself

  • Do not forward-fill. It manufactures a flat session, understating volatility and hiding drawdown.
  • Do not substitute zero. Zero is meaningful in prices and in net flow, so afterwards you cannot distinguish 'was zero' from 'was absent'.
  • Leave the gap and skip it explicitly in calculations. A series repaired to look complete will flatter a backtest — the same problem as look-ahead, one layer down.

What we do not have

  • No uniform data_gaps field across all datasets, as above.
  • No real-time quotes, so an intraday missing tick is out of scope here — the day's data exists after the close.
  • In market_status, unknown means undetermined, not closed. Do not treat it as a holiday.

Common questions

1I asked for a month and got 43 rows. Did I lose some?
Count the trading days in the window first. Twenty-odd per month is normal once weekends and holidays come out, and forty-odd across two months is consistent. Compare against the open dates in meta.market_status to know for certain.
2data_gaps is an empty list. Does that mean the data is perfect?
It means that row has no known gap markers, which is not the same as the dataset being complete. Read it alongside a trading-day comparison and price_confidence.
3Why not just fill the gaps for me?
Because a repaired series cannot be audited afterwards. Silently filled holes lower measured volatility and shallow out drawdowns, and you would have no way to tell that from real market behaviour. Better to show you the hole and leave the judgement with you.
4Why does the price endpoint have no data_gaps?
It uses a different set of signals: meta.market_status for which days traded, count and data_as_of for what you received and how current it is, and per-row price_method and price_confidence for provenance. Equivalent diagnostic power, different fields.

Want to query this data yourself? Create a free account.

Related articles