# Taiwan Odd-Lot Trading (TWSE + TPEx) — Both Sessions (`chamarix/taiwan-odd-lot-trading`) Actor

Both of Taiwan's odd-lot sessions on one row for every listed and OTC security: the intraday session carrying 98% of the flow and the after-hours auction, with shares, trade count, turnover, OHLC and closing quotes. Back to 2004, reconciled against the exchanges' own totals.

- **URL**: https://apify.com/chamarix/taiwan-odd-lot-trading.md
- **Developed by:** [chris](https://apify.com/chamarix) (community)
- **Categories:** AI, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Taiwan Odd-Lot Trading (上市櫃零股交易) API

Get structured JSON access to **both of Taiwan's odd-lot trading sessions** for every listed and OTC security — share volume, trade count, turnover, open/high/low/close and the closing bid and offer, for the intraday session and the after-hours auction, on the same row.

**About 2,400 securities per session across both boards.** The intraday session (盤中零股) reaches back to 2020-10-26, the day it opened; the after-hours auction (盤後零股) reaches back to **2004-12-24** on TWSE.

### Why this data matters

- **The session most datasets silently miss.** Taiwan has run *two* odd-lot sessions since October 2020 and publishes them as two unrelated reports. The older after-hours auction is the one that shows up in odd-lot feeds — and it is now a rounding error. On 2026-09-11 the TWSE intraday session traded **NT$36,807,891,797 against the after-hours auction's NT$504,416,597**: the intraday session was **98.65%** of the board's odd-lot turnover, and 98.78% on the OTC board. A feed carrying only 盤後零股 is showing you 1.35% of the market and calling it odd-lot trading. This Actor fetches all four reports and puts both sessions on one row, with `intraday_share_of_value_pct` already computed.
- **Odd-lot is how Taiwanese retail actually buys.** A board lot is 1,000 shares, which at 台積電's NT$2,420 is NT$2.42 million a trade. Retail buys the odd lots instead — 152,482 intraday odd-lot trades in 台積電 alone on 2026-09-11, against 2,830,341 shares. That makes odd-lot volume the cleanest available read on retail participation per stock, per day.
- **An odd-lot close that is not the board-lot close.** The two sessions price independently, so `intraday_last_price`, `after_hours_price` and the regular close are three different numbers. All three are published here as published, along with the intraday first price, high, low and a `intraday_vwap` computed from turnover — and the VWAP is verified to land inside the session's own high-low range on every row.
- **Turnover checked against the prices it came from, not against a tolerance.** The after-hours auction fills at a single price, so turnover would be shares × price except that the exchange floors every matched order to whole dollars before adding them up. That leaves a shortfall of under one dollar per order and never a cent over: `0 ≤ shares × price − turnover < trades`. That is a bound derived from how the figure is computed, not a fudge factor — and it held on **every priced row of ten sessions sampled from 2008 to 2026, 6,663 rows, zero exceptions**. The intraday session gets the two-sided form of the same bound against its own high and low.
- **Reconciled against the exchanges' own totals, every run.** TWSE closes each odd-lot report with a 合計 line; five independent sums are compared against it per report. The OTC board publishes a *separate* statistics report (零股交易成交統計) repeating both sessions' volume, trade count and turnover per security — 2,012 security-sessions compared figure by figure on a single day, zero disagreements. Both exchanges also republish the after-hours report through open-data hosts with different column names and different decimal places; that is a third witness, and it is compared too. **A run that cannot reconcile them publishes nothing.**
- **A holiday and an outage are told apart.** The OTC board answers a public holiday, a date before its archive and next January alike with `stat: ok` and an empty table. Taking that at face value turns a total outage into a successful run reporting zero trading. This Actor reads the exchange's own trading calendar first, asks only for sessions that were actually held, and **fails loudly** if a session the calendar lists comes back empty.

### Output

One record per security per trading day, both sessions on the row. A real record, as published:

```json
{
  "record_type": "STOCK",
  "date": "2026-09-11",
  "market": "TWSE",
  "code": "2330",
  "name": "台積電",
  "sessions_covered": ["INTRADAY", "AFTER_HOURS"],
  "intraday_shares": 2830341,
  "intraday_transactions": 152482,
  "intraday_value": 6838539090,
  "intraday_first_price": 2425,
  "intraday_last_price": 2420,
  "intraday_high": 2425,
  "intraday_low": 2405,
  "intraday_vwap": 2416.1537744038615,
  "intraday_price_change": null,
  "intraday_reference_price": null,
  "intraday_best_bid_price": 2420,
  "intraday_best_bid_volume": 733,
  "intraday_best_ask_price": 2425,
  "intraday_best_ask_volume": 26078,
  "after_hours_shares": 43016,
  "after_hours_transactions": 1620,
  "after_hours_value": 104313800,
  "after_hours_price": 2425,
  "after_hours_best_bid_price": 2420,
  "after_hours_best_bid_volume": 200937,
  "after_hours_best_ask_price": 2425,
  "after_hours_best_ask_volume": 18385,
  "total_shares": 2873357,
  "total_transactions": 154102,
  "total_value": 6942852890,
  "intraday_share_of_value_pct": 98.4975,
  "identity_ok": true
}
```

And one `MARKET_SUMMARY` row per board per session, summed from the rows above it and checked against the exchange's own published totals:

```json
{
  "record_type": "MARKET_SUMMARY",
  "date": "2026-09-11",
  "market": "TWSE",
  "securities_listed": 1364,
  "securities_traded_intraday": 1312,
  "securities_traded_after_hours": 1050,
  "intraday_shares": 111506452,
  "intraday_transactions": 2076279,
  "intraday_value": 36807891797,
  "after_hours_shares": 2174052,
  "after_hours_transactions": 29192,
  "after_hours_value": 504416597,
  "total_value": 37312308394,
  "intraday_share_of_value_pct": 98.6481,
  "official_totals_match": true
}
```

#### The two sessions

| | `intraday_` fields | `after_hours_` fields |
|---|---|---|
| Chinese name | 盤中零股 | 盤後零股 |
| How it matches | call auction every three minutes, alongside the regular board | one call auction after the close |
| Prices published | first, last, high, low | one auction price |
| Opened | 2020-10-26 | long before the archive |
| Archive reaches | 2020-10-26 (both boards) | 2004-12-24 TWSE, 2011-01-03 TPEx |
| Share of odd-lot turnover (2026-09-11) | 98.65% TWSE, 98.78% TPEx | 1.35% / 1.22% |

The two reports cover slightly different universes — TWSE listed 1,253 securities in its after-hours report and 1,234 in its intraday report on 2025-01-03 — so the join is an outer one. A security in only one report still gets a row, with the other session's columns **null rather than zero**.

### Input

```json
{
  "startDate": "2026-09-01",
  "endDate": "2026-09-11",
  "markets": ["TWSE", "TPEX"],
  "sessions": ["INTRADAY", "AFTER_HOURS"],
  "stockCodes": ["2330", "6182"],
  "includeMarketSummary": true,
  "crossCheck": true,
  "maxSessions": 60
}
```

Leave both dates empty for the most recent session. `stockCodes` takes exchange codes as strings — they are **not always numeric**: active ETFs use forms like `00400A`, index ETFs `006201`, preferred shares `1101A`.

`maxSessions` defaults to 60 because a session is about 2,400 securities and a full year of both boards is over half a million rows. Set it to 0 to lift the cap.

Turn `useApifyProxy` on for a backfill longer than a few months: TWSE's firewall refuses by address and will cut a long run off partway through.

### Things worth knowing about this data

- **The unit is shares, not lots.** Everything on the board-lot reports is in 1,000-share lots; odd-lot reports are in single shares. `intraday_shares: 2830341` is 2,830,341 shares, not lots.
- **The OTC board's two odd-lot reports disagree about which calendar to use.** The after-hours report accepts a ROC year (`115/09/11`) or a Western one; the intraday report accepts only Western and answers `參數輸入錯誤` to a ROC year. Both are normalised here, and dates come out as ISO `YYYY-MM-DD` regardless of which of the three shapes the exchange used.
- **`intraday_price_change` is TPEx-only, and it is not measured from the previous odd-lot price.** The OTC board prints a change column; TWSE does not. It is measured from the previous session's **regular-board close** — checked both ways, and 921 of 922 securities agreed with the regular close against 108 with the previous odd-lot close. `intraday_reference_price` backs that base out, which equals the previous regular close except on ex-dividend and ex-rights days, where it is the adjusted reference price.
- **TWSE's early after-hours rows carry volume only.** Before **2005-12-19**, 成交筆數 and 成交金額 are literally `--` on every row. They come through as **null, not zero**, and the turnover check simply has nothing to check on those sessions.
- **`identity_ok: false` rows are published, not dropped.** A handful of rows failing a derived bound is the exchange's own arithmetic and is flagged; a systematic failure means a report changed shape, and that fails the whole run rather than publishing anything.
- **Column layouts are read by name, never by position.** All four reports have carried the same columns since their first session — twenty-one years for the TWSE after-hours report — so a column name that stops existing means the layout changed, and the run stops rather than reading the wrong column.

### Data source

TWSE 盤中零股交易行情單 (`TWTC7U`) and 盤後零股交易行情單 (`TWT53U`); TPEx 盤中零股每日收盤行情 (`oddQuote`), 盤後零股每日收盤行情 (`odd`) and 零股交易成交統計 (`oddSummary`); TWSE 每日市場成交資訊 (`FMTQIK`) as the trading calendar; and the TWSE and TPEx open-data editions of the after-hours report for independent verification. Public official data, no login required.

### Taiwan Market Data Suite

This Actor is part of a suite of 31 Taiwan market data APIs by [chamarix](https://apify.com/chamarix) — official sources only, cross-validated against independent official endpoints, clean JSON out. Code samples for the whole suite: [GitHub](https://github.com/cc77556/taiwan-market-data-actors).

**Market data:**

- [taiwan-stock-daily-quotes](https://apify.com/chamarix/taiwan-stock-daily-quotes) — Daily OHLCV, VWAP, P/E, price-to-book & dividend yield for every listed/OTC stock since 2004
- [taiwan-index-history](https://apify.com/chamarix/taiwan-index-history) — Daily TAIEX & TPEx index history since 1990 with market turnover, the total-return index & all 273 TWSE indices
- [twse-institutional-trades](https://apify.com/chamarix/twse-institutional-trades) — Daily institutional buy/sell (foreign, investment trust, dealer) per stock — TWSE listed
- [tpex-institutional-trades](https://apify.com/chamarix/tpex-institutional-trades) — Daily institutional buy/sell per stock — TPEx OTC market
- [taiwan-monthly-revenue](https://apify.com/chamarix/taiwan-monthly-revenue) — Monthly revenue of 1,900+ listed & OTC companies, MoM/YoY
- [taiwan-financial-statements](https://apify.com/chamarix/taiwan-financial-statements) — Quarterly income statement, balance sheet & cash flow back to 2013
- [taiwan-director-compensation](https://apify.com/chamarix/taiwan-director-compensation) — Board pay for 1,950+ companies, parent vs consolidated scope, with EPS, ROE & profit on the same row
- [taiwan-esg-disclosures](https://apify.com/chamarix/taiwan-esg-disclosures) — 21 ESG topics for 1,950+ companies — Scope 1/2/3 emissions, energy, water, waste, pay, board & climate risk
- [taiwan-dividend-calendar](https://apify.com/chamarix/taiwan-dividend-calendar) — Ex-dividend / ex-rights dates, reference prices & payouts back to 2003
- [taiwan-margin-trading](https://apify.com/chamarix/taiwan-margin-trading) — Daily margin trading & short sale balances per stock
- [taiwan-sbl-short-sale-balance](https://apify.com/chamarix/taiwan-sbl-short-sale-balance) — Securities-lending short sale balances per stock
- [taiwan-day-trading-stats](https://apify.com/chamarix/taiwan-day-trading-stats) — Day-trading volume, value & ratio per stock since 2014
- [taiwan-odd-lot-trading](https://apify.com/chamarix/taiwan-odd-lot-trading) — Both odd-lot sessions per stock — intraday & after-hours, shares, turnover, OHLC & quotes since 2004
- [tdcc-shareholding-dispersion](https://apify.com/chamarix/tdcc-shareholding-dispersion) — Weekly TDCC shareholding dispersion (retail vs whale structure)
- [taiwan-foreign-shareholding](https://apify.com/chamarix/taiwan-foreign-shareholding) — Foreign ownership percentage & remaining quota per stock
- [taiwan-futures-daily](https://apify.com/chamarix/taiwan-futures-daily) — Daily bars, settlement price & open interest for all 384 TAIFEX futures contracts since 1998, with the large-trader report
- [taifex-institutional-derivatives](https://apify.com/chamarix/taifex-institutional-derivatives) — Institutional futures & options positions (TAIFEX), incl. put/call ratio
- [taifex-options-chain](https://apify.com/chamarix/taifex-options-chain) — Full options chain by strike & expiry, both sessions, with the exchange's own Delta, since 2001
- [taiwan-warrants-daily](https://apify.com/chamarix/taiwan-warrants-daily) — Daily quotes, strike, expiry & moneyness for every listed/OTC warrant since 2004
- [taiwan-government-bonds](https://apify.com/chamarix/taiwan-government-bonds) — Central government bond benchmark yields, the full yield curve & issuance master, with staleness stated
- [taiwan-stock-alerts](https://apify.com/chamarix/taiwan-stock-alerts) — Watch-list, disposition & short-sale suspension alerts
- [taiwan-insider-share-transfers](https://apify.com/chamarix/taiwan-insider-share-transfers) — Insider share-transfer filings (directors, officers, 10% holders) since 2002
- [taiwan-director-shareholdings](https://apify.com/chamarix/taiwan-director-shareholdings) — Monthly director/officer shareholdings & share-pledge ratio since 1999
- [taiwan-block-trades](https://apify.com/chamarix/taiwan-block-trades) — Every block trade (鉅額交易) with price, size & basket constituents since 2005
- [taiwan-shareholder-meetings](https://apify.com/chamarix/taiwan-shareholder-meetings) — Shareholder meeting dates, book closure periods & e-voting since 2005
- [taiwan-emerging-stock-quotes](https://apify.com/chamarix/taiwan-emerging-stock-quotes) — Emerging (興櫃) board quotes, pre-IPO register & history since 2003
- [taiwan-etf-regular-investment](https://apify.com/chamarix/taiwan-etf-regular-investment) — Monthly regular savings plan (定期定額) rankings for stocks & ETFs since 2020
- [taiwan-treasury-stock-buybacks](https://apify.com/chamarix/taiwan-treasury-stock-buybacks) — Every treasury-stock buyback (庫藏股) filing, plan vs execution, since 2000

**Property market:**

- [taiwan-real-estate-transactions](https://apify.com/chamarix/taiwan-real-estate-transactions) — Actual registered sale, presale & lease prices (實價登錄) for all 22 cities since 2012

**Government & civic data:**

- [taiwan-legislator-monitor](https://apify.com/chamarix/taiwan-legislator-monitor) — Legislative Yuan bills, legislators & meetings
- [taiwan-tender-monitor](https://apify.com/chamarix/taiwan-tender-monitor) — Government e-procurement tenders (open calls, awards, failures)

# Actor input Schema

## `startDate` (type: `string`):

First session to fetch. Leave both dates empty for the most recent session. Each report has its own archive floor and a range that starts before one simply begins where that data does: the TWSE after-hours report reaches 2004-12-24, the TPEx after-hours report 2011-01-03, and both intraday reports 2020-10-26, the day the intraday odd-lot session opened.

## `endDate` (type: `string`):

Last session to fetch, inclusive. Defaults to today. Holidays cost nothing: the run reads the exchange's own trading calendar first and only asks for days the market actually held.

## `markets` (type: `array`):

TWSE is the main board, TPEX the over-the-counter board. Both are fetched by default; together they are about 2,400 securities a session.

## `sessions` (type: `array`):

Taiwan runs two odd-lot sessions and publishes them as separate reports. INTRADAY (盤中零股) matches every three minutes alongside the regular board and is where nearly all the flow is — 98.6% of TWSE odd-lot turnover on 2026-09-11. AFTER\_HOURS (盤後零股) is a single call auction after the close and is the older series. Both are on by default and land on the same row.

## `stockCodes` (type: `array`):

Keep only these securities, by exchange code. Leave empty for every security. Codes are not always numeric — ETFs and active ETFs use forms like 00400A and 006201, and preferred shares add a letter, so pass them as strings exactly as the exchange prints them. Filtering happens after the market summary is computed, so the summary still covers the whole market.

## `includeMarketSummary` (type: `boolean`):

One extra row per market per session carrying the whole board's odd-lot share volume, trade count and turnover for both sessions, how many securities traded in each, and whether the run's own sums matched the exchange's printed totals. Two rows a session.

## `crossCheck` (type: `boolean`):

The OTC board publishes a separate statistics report that repeats both sessions' volume, trade count and turnover for every security, and both exchanges republish the after-hours report through open-data hosts with different column names and different decimal places. With this on, every overlap is compared figure by figure and a disagreement fails the run rather than being published. Costs one extra request per session plus two at the end.

## `useApifyProxy` (type: `boolean`):

Turn this on for a backfill longer than a few months. TWSE's firewall refuses by address, so a long range gets cut off partway and stays refused for minutes; with this on, TWSE requests take a different exit address each time they are refused. Off by default because a direct run is faster for an ordinary range. The OTC board always goes direct either way — it resets proxied connections.

## `maxSessions` (type: `integer`):

A safety cap, because a session is about 2,400 securities and a year of both boards is over half a million rows. When a range exceeds the cap the most recent sessions are kept and the rest skipped, with a warning saying which. Set to 0 to lift it.

## Actor input object example

```json
{
  "startDate": "",
  "endDate": "",
  "markets": [
    "TWSE",
    "TPEX"
  ],
  "sessions": [
    "INTRADAY",
    "AFTER_HOURS"
  ],
  "stockCodes": [],
  "includeMarketSummary": true,
  "crossCheck": true,
  "useApifyProxy": false,
  "maxSessions": 60
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

One record per security per trading day, carrying the intraday and after-hours odd-lot sessions side by side, plus a market summary row per board per day.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startDate": "",
    "endDate": "",
    "markets": [
        "TWSE",
        "TPEX"
    ],
    "sessions": [
        "INTRADAY",
        "AFTER_HOURS"
    ],
    "stockCodes": [],
    "includeMarketSummary": true,
    "crossCheck": true,
    "useApifyProxy": false,
    "maxSessions": 60
};

// Run the Actor and wait for it to finish
const run = await client.actor("chamarix/taiwan-odd-lot-trading").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startDate": "",
    "endDate": "",
    "markets": [
        "TWSE",
        "TPEX",
    ],
    "sessions": [
        "INTRADAY",
        "AFTER_HOURS",
    ],
    "stockCodes": [],
    "includeMarketSummary": True,
    "crossCheck": True,
    "useApifyProxy": False,
    "maxSessions": 60,
}

# Run the Actor and wait for it to finish
run = client.actor("chamarix/taiwan-odd-lot-trading").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startDate": "",
  "endDate": "",
  "markets": [
    "TWSE",
    "TPEX"
  ],
  "sessions": [
    "INTRADAY",
    "AFTER_HOURS"
  ],
  "stockCodes": [],
  "includeMarketSummary": true,
  "crossCheck": true,
  "useApifyProxy": false,
  "maxSessions": 60
}' |
apify call chamarix/taiwan-odd-lot-trading --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,chamarix/taiwan-odd-lot-trading"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/C2q13VtcJZZo0HDc9/builds/zMaNUJTB7Yxt7itk9/openapi.json
