CompareSG

S$1,000 has bought a record amount of yen, won, ringgit and baht since 2024

Yen and won hit their records in 2026, ringgit and baht in 2024. They are the highest months in MAS's monthly series, which starts in January 1988 for yen and ringgit and January 2003 for won and baht. Pick a country to see S$1,000 month by month since January 2015.

Pick a country

Everything below changes with it.

How many yen did S$1,000 buy each month since January 2015?

Each tile is one month, from January 2015 at the top left. A darker tile means S$1,000 bought more. MAS monthly average rates.

July 2026
S$1,000 = ¥125,818
42% more than in January 2015 (¥88,488). This is the record month in the whole MAS series.
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
Fewer yenMore¥74,974 to ¥125,818

Record month. Lowest month since 2015. The top left tile is January 2015. Tap a tile to read it.

In the 141 months since January 2015, S$1,000 bought the most yen in July 2026 (¥125,818) and the fewest in October 2016 (¥74,974).

Which five months bought the most yen?

Out of the 141 months since January 2015. September 2026, the latest month, is marked.

  • 1. July 2026the highest month in the MAS series since January 1988
    ¥125,818
  • 2. June 2026
    ¥124,782
  • 3. April 2026
    ¥124,750
  • 4. August 2026
    ¥124,425
  • 5. March 2026
    ¥124,008
  • 7. September 2026 Latest
    ¥122,941

6 of the 141 months bought more yen than September 2026 (¥122,941), so the latest month ranks number 7.

How much more did S$1,000 buy in September 2026 than in January 2015?

The four currencies side by side, S$1,000 each time. Yours is marked.

  • Yen Yours¥88,488 then, ¥122,941 now
    +39%
  • Won₩814,332 then, ₩1,067,236 now
    +31%
  • RinggitRM2,679 then, RM3,198 now
    +19%
  • Baht฿24,492 then, ฿26,108 now
    +7%

S$1,000 bought ¥88,488 in January 2015 and ¥122,941 in September 2026: 39% more yen. That is the biggest change of the four.

More finds

How much more can S$1,000 buy after one year?
24% more
From ¥81,739 in Sep 2021 to ¥101,215 a year later, in Sep 2022. This is the biggest rise over twelve months since 2015.
How much less can S$1,000 buy after one year?
15% less
From ¥91,929 in Jun 2015 to ¥77,742 a year later, in Jun 2016. This is the biggest fall over twelve months since 2015.
What did a middle month buy?
¥83,977
Line up the 141 months since January 2015 from the fewest yen to the most, and the one in the middle, Jan 2018, bought this. September 2026 bought 46% more.

The short version

In MAS's monthly exchange rates, S$1,000 bought a record ¥125,818 in July 2026, a record ₩1,187,648 in June 2026, a record ฿27,158 in June 2024 and a record RM3,547 in February 2024. The yen and ringgit records are the highest of 465 months since January 1988; the won and baht records are the highest of 285 months since January 2003. In September 2026, S$1,000 bought ¥122,941, ₩1,067,236, RM3,198 and ฿26,108.

In January 2015, S$1,000 bought ¥88,488, ₩814,332, RM2,679 and ฿24,492. By September 2026 that is 39% more yen, 31% more won, 19% more ringgit and 7% more baht. At the record months the gains are 42%, 46%, 32% and 11%.

These are exchange rates only. They say nothing about prices in Japan, South Korea, Malaysia or Thailand, or about flights and hotels, and money changers, banks and cards use their own rates, not MAS's monthly averages. We give no forecast.

By Ben (opens in a new tab). Updated . Data: Monetary Authority of Singapore (through SingStat Table Builder).

Questions people ask

How much yen does S$1,000 buy?

In September 2026 it bought ¥122,941, using MAS's monthly average rate. The record month in MAS's series, which starts in January 1988, is July 2026, at ¥125,818.

How much won does S$1,000 buy?

In September 2026 it bought ₩1,067,236, using MAS's monthly average rate. The record month in MAS's series, which starts in January 2003, is June 2026, at ₩1,187,648.

How much ringgit does S$1,000 buy?

In September 2026 it bought RM3,198, using MAS's monthly average rate. The record month in MAS's series, which starts in January 1988, is February 2024, at RM3,547.

How much baht does S$1,000 buy?

In September 2026 it bought ฿26,108, using MAS's monthly average rate. The record month in MAS's series, which starts in January 2003, is June 2024, at ฿27,158.

When did S$1,000 buy the fewest since 2015?

Yen: ¥74,974 in October 2016. Won: ₩804,505 in May 2018. Ringgit: RM2,656 in February 2015. Baht: ฿22,150 in September 2019. Each is the lowest of the 141 months from January 2015 to September 2026.

How does September 2026 compare with the other months?

Counting the 141 months from January 2015, 6 bought more yen than September 2026, 18 more won, 39 more ringgit and 15 more baht. So September 2026 ranks number 7 for yen, 19 for won, 40 for ringgit and 16 for baht, where 1 is the most.

Are these the highest months on record?

In MAS's monthly series, yes. It covers 465 months for yen and ringgit, from January 1988, and 285 months for won and baht, from January 2003. We say nothing about earlier years because this table has no earlier months.

Does S$1,000 buy more than it did in January 2015?

In September 2026 it does for all four: 39% more yen, 31% more won, 19% more ringgit and 7% more baht. Counting the 141 months from January 2015, S$1,000 bought more than in January 2015 in 58 months for yen, 126 for won, 138 for ringgit and 84 for baht.

Will the Singapore dollar buy more next month?

We do not know and we give no forecast. This page shows past monthly averages only.

Does a higher number mean a cheaper holiday?

Not by itself. It means S$1,000 converts into more of that currency at MAS's average rate. Prices in each country, flights and hotels also move, and this page covers none of them.

Is this the rate a money changer gives?

No. MAS publishes the average rate for each month. Money changers, banks and cards use their own rates, so what you get for S$1,000 on the day differs.

Why does the grid start in January 2015?

It shows 141 months, close to twelve years, on one screen. The records and the gains use the whole MAS series, not only the grid.

How is the amount for S$1,000 worked out?

We divide 1,000 by MAS's rate for the month, scaled to one unit of the currency. MAS gives yen, won and baht per 100 units and ringgit per 1. We round half up to whole units. S$500 buys half as much, S$2,000 twice as much.

Where do the numbers come from?

The Monetary Authority of Singapore, through SingStat Table Builder, table M700051, Exchange Rates (Average For Period), Monthly. SingStat says it was last updated on 14 Oct 2026 and we downloaded it on 16 Oct 2026. SingStat says recent figures are provisional and may be revised.

The numbers behind it

What S$1,000 bought: record, lowest since 2015, January 2015 and latest (MAS, read 16 Oct 2026)
CurrencyRecord monthAt the recordLowest month since 2015At the lowestJanuary 2015Latest, September 2026
Yen (Japan)July 2026¥125,818October 2016¥74,974¥88,488¥122,941
Won (South Korea)June 2026₩1,187,648May 2018₩804,505₩814,332₩1,067,236
Ringgit (Malaysia)February 2024RM3,547February 2015RM2,656RM2,679RM3,198
Baht (Thailand)June 2024฿27,158September 2019฿22,150฿24,492฿26,108
How the amounts compare (per cent, rounded half up)
CurrencyRecord against January 2015September 2026 against January 2015Record against the lowest since 2015September 2026 below the recordMonths above January 2015, of 141
Yen (Japan)+42%+39%+68%2%58
Won (South Korea)+46%+31%+48%10%126
Ringgit (Malaysia)+32%+19%+34%10%138
Baht (Thailand)+11%+7%+23%4%84
Rank of the latest month, and the biggest rise and fall over twelve months
CurrencyFirst month in seriesMonths in seriesRank of September 2026, of 141 (1 is the most)Biggest rise in a yearBiggest fall in a year
Yen (Japan)January 19884657+24%, Sep 2021 to Sep 202215% less, Jun 2015 to Jun 2016
Won (South Korea)January 200328519+14%, Oct 2021 to Oct 20227% less, Feb 2016 to Feb 2017
Ringgit (Malaysia)January 198846540+13%, Jan 2015 to Jan 20167% less, Feb 2024 to Feb 2025
Baht (Thailand)January 200328516+9%, Dec 2020 to Dec 20218% less, Aug 2018 to Aug 2019

Amounts are for S$1,000, in whole units of each currency, using MAS's monthly average rate. Per cent figures are rounded half up to whole numbers.

Every month since January 2015, as a table
S$1,000 in each currency, every month since January 2015, newest first (MAS, read 16 Oct 2026)
MonthYenWonRinggitBaht
Sep 2026¥122,941₩1,067,236RM3,198฿26,108
Aug 2026¥124,425₩1,098,901RM3,184฿25,877
Jul 2026¥125,818₩1,154,734RM3,162฿25,938
Jun 2026¥124,782₩1,187,648RM3,161฿25,538
May 2026¥123,993₩1,169,591RM3,099฿25,498
Apr 2026¥124,750₩1,162,791RM3,113฿25,308
Mar 2026¥124,008₩1,164,144RM3,088฿25,212
Feb 2026¥122,399₩1,142,857RM3,090฿24,664
Jan 2026¥122,474₩1,137,656RM3,146฿24,427
Dec 2025¥120,627₩1,135,074RM3,169฿24,461
Nov 2025¥119,005₩1,121,076RM3,189฿24,864
Oct 2025¥116,822₩1,098,901RM3,255฿25,138
Sep 2025¥115,101₩1,084,599RM3,280฿24,907
Aug 2025¥114,877₩1,081,081RM3,287฿25,244
Jul 2025¥114,639₩1,075,269RM3,307฿25,329
Jun 2025¥112,511₩1,063,830RM3,304฿25,416
May 2025¥111,732₩1,075,269RM3,295฿25,460
Apr 2025¥108,956₩1,089,325RM3,334฿25,495
Mar 2025¥111,607₩1,090,513RM3,320฿25,311
Feb 2025¥112,689₩1,072,961RM3,299฿25,092
Jan 2025¥114,982₩1,067,236RM3,284฿25,187
Dec 2024¥113,714₩1,066,098RM3,304฿25,302
Nov 2024¥114,969₩1,043,841RM3,319฿25,770
Oct 2024¥114,168₩1,038,422RM3,281฿25,482
Sep 2024¥110,448₩1,027,749RM3,288฿25,725
Aug 2024¥111,198₩1,026,694RM3,355฿26,411
Jul 2024¥117,316₩1,026,694RM3,476฿26,931
Jun 2024¥116,809₩1,020,408RM3,484฿27,158
May 2024¥115,354₩1,010,101RM3,492฿27,116
Apr 2024¥113,353₩1,009,082RM3,512฿27,103
Mar 2024¥111,682₩993,049RM3,519฿26,813
Feb 2024¥111,136₩991,080RM3,547฿26,681
Jan 2024¥109,302₩993,049RM3,508฿26,345
Dec 2023¥108,143₩978,474RM3,497฿26,281
Nov 2023¥111,025₩968,992RM3,477฿26,281
Oct 2023¥109,218₩987,167RM3,467฿26,678
Sep 2023¥108,342₩977,517RM3,434฿26,310
Aug 2023¥107,250₩978,474RM3,414฿25,938
Jul 2023¥105,675₩961,538RM3,441฿25,948
Jun 2023¥104,866₩962,464RM3,441฿25,925
May 2023¥102,323₩990,099RM3,374฿25,540
Apr 2023¥100,190₩993,049RM3,322฿25,730
Mar 2023¥99,701₩972,763RM3,331฿25,727
Feb 2023¥99,800₩957,854RM3,280฿25,547
Jan 2023¥98,348₩938,967RM3,265฿25,091
Dec 2022¥99,860₩957,854RM3,265฿25,733
Nov 2022¥102,659₩978,474RM3,324฿26,243
Oct 2022¥103,146₩1,001,001RM3,294฿26,630
Sep 2022¥101,215₩987,167RM3,214฿26,210
Aug 2022¥97,580₩954,198RM3,227฿25,912
Jul 2022¥97,962₩937,207RM3,184฿26,070
Jun 2022¥96,740₩923,361RM3,181฿25,251
May 2022¥93,214₩917,431RM3,173฿24,911
Apr 2022¥92,404₩904,159RM3,125฿24,734
Mar 2022¥87,116₩898,473RM3,090฿24,459
Feb 2022¥85,558₩889,680RM3,109฿24,212
Jan 2022¥85,063₩885,740RM3,102฿24,617
Dec 2021¥83,549₩868,056RM3,088฿24,623
Nov 2021¥84,069₩872,600RM3,080฿24,377
Oct 2021¥83,710₩874,891RM3,082฿24,771
Sep 2021¥81,739₩870,322RM3,091฿24,526
Aug 2021¥81,044₩857,633RM3,114฿24,428
Jul 2021¥81,446₩845,309RM3,100฿24,093
Jun 2021¥82,583₩841,751RM3,102฿23,569
May 2021¥82,068₩844,595RM3,103฿23,518
Apr 2021¥81,693₩837,521RM3,089฿23,504
Mar 2021¥80,919₩842,460RM3,061฿22,936
Feb 2021¥79,327₩838,223RM3,048฿22,602
Jan 2021¥78,235₩828,500RM3,045฿22,635
Dec 2020¥77,906₩821,018RM3,044฿22,579
Nov 2020¥77,429₩827,815RM3,053฿22,615
Oct 2020¥77,405₩841,751RM3,054฿23,004
Sep 2020¥77,328₩862,069RM3,039฿22,963
Aug 2020¥77,429₩866,551RM3,058฿22,790
Jul 2020¥76,976₩863,558RM3,072฿22,631
Jun 2020¥77,166₩866,551RM3,067฿22,361
May 2020¥75,603₩866,551RM3,063฿22,611
Apr 2020¥75,666₩859,845RM3,059฿22,916
Mar 2020¥75,798₩861,326RM3,033฿22,660
Feb 2020¥79,183₩859,845RM2,996฿22,548
Jan 2020¥80,926₩862,813RM3,020฿22,520
Dec 2019¥80,412₩865,052RM3,056฿22,265
Nov 2019¥79,923₩858,369RM3,054฿22,213
Oct 2019¥78,839₩863,558RM3,054฿22,153
Sep 2019¥77,845₩866,551RM3,032฿22,150
Aug 2019¥76,775₩874,126RM3,024฿22,221
Jul 2019¥79,517₩864,304RM3,031฿22,637
Jun 2019¥79,271₩861,326RM3,054฿22,845
May 2019¥80,238₩863,558RM3,043฿23,201
Apr 2019¥82,305₩842,460RM3,033฿23,498
Mar 2019¥82,088₩835,422RM3,013฿23,433
Feb 2019¥81,546₩829,187RM3,011฿23,127
Jan 2019¥80,276₩827,130RM3,035฿23,463
Dec 2018¥81,967₩819,001RM3,045฿23,867
Nov 2018¥82,393₩819,672RM3,044฿23,969
Oct 2018¥81,820₩820,345RM3,016฿23,762
Sep 2018¥81,606₩816,327RM3,018฿23,785
Aug 2018¥81,162₩819,672RM2,990฿24,142
Jul 2018¥81,719₩823,723RM2,970฿24,415
Jun 2018¥81,633₩811,030RM2,968฿24,096
May 2018¥81,954₩804,505RM2,960฿23,879
Apr 2018¥81,780₩812,348RM2,955฿23,803
Mar 2018¥80,632₩814,996RM2,970฿23,784
Feb 2018¥81,820₩818,331RM2,964฿23,856
Jan 2018¥83,977₩806,452RM2,991฿24,128
Dec 2017¥83,850₩805,802RM3,028฿24,249
Nov 2017¥83,222₩813,670RM3,077฿24,282
Oct 2017¥83,029₩834,028RM3,108฿24,436
Sep 2017¥82,001₩839,631RM3,120฿24,561
Aug 2017¥80,723₩831,255RM3,149฿24,446
Jul 2017¥81,960₩825,764RM3,128฿24,601
Jun 2017¥80,122₩816,993RM3,090฿24,574
May 2017¥80,438₩807,103RM3,093฿24,706
Apr 2017¥78,734₩811,030RM3,153฿24,646
Mar 2017¥80,379₩806,452RM3,159฿24,832
Feb 2017¥79,898₩807,754RM3,142฿24,748
Jan 2017¥80,412₩826,446RM3,121฿24,818
Dec 2016¥80,717₩824,402RM3,107฿24,925
Nov 2016¥76,546₩825,083RM3,073฿25,051
Oct 2016¥74,974₩814,332RM3,018฿25,338
Sep 2016¥74,979₩815,661RM3,024฿25,547
Aug 2016¥75,143₩824,402RM2,990฿25,761
Jul 2016¥77,101₩844,595RM2,975฿25,948
Jun 2016¥77,742₩862,813RM3,014฿26,053
May 2016¥79,491₩857,633RM2,953฿25,846
Apr 2016¥81,129₩849,618RM2,890฿25,989
Mar 2016¥82,176₩862,813RM2,966฿25,627
Feb 2016¥81,599₩866,551RM2,975฿25,336
Jan 2016¥82,433₩839,631RM3,030฿25,228
Dec 2015¥86,490₩833,333RM3,040฿25,565
Nov 2015¥86,670₩815,661RM3,051฿25,326
Oct 2015¥85,631₩816,993RM3,041฿25,469
Sep 2015¥84,854₩838,223RM3,046฿25,452
Aug 2015¥87,889₩843,882RM2,913฿25,334
Jul 2015¥90,555₩841,751RM2,793฿25,223
Jun 2015¥91,929₩826,446RM2,781฿25,073
May 2015¥90,457₩819,001RM2,699฿25,122
Apr 2015¥88,543₩805,153RM2,692฿24,084
Mar 2015¥87,443₩808,407RM2,672฿23,691
Feb 2015¥87,566₩811,688RM2,656฿24,051
Jan 2015¥88,488₩814,332RM2,679฿24,492

The boring bit

Where the information comes from

Every number comes from the Monetary Authority of Singapore (MAS), taken from SingStat Table Builder: table M700051, Exchange Rates (Average For Period), Monthly (opens in a new tab). SingStat shows the table as last updated on 14 Oct 2026, and we downloaded it as an .xlsx file on 16 Oct 2026. The table gives the Singapore dollars needed for 100 Japanese yen, 100 Korean won, 1 Malaysian ringgit and 100 Thai baht. SingStat notes that data for the most recent periods are provisional and may be revised later.

What is not claimed

  • This is not a forecast and not financial advice. We do not say whether the Singapore dollar will buy more or less next month.
  • These are exchange rates only. They do not show what a holiday, a meal or a flight costs in any of the four countries.
  • MAS publishes the average for each month. It is not the rate a money changer, bank or card gives on a given day, and daily moves inside a month are hidden.
  • Amounts are not adjusted for inflation in Singapore or in the other countries.
  • Records are records within MAS's monthly series (from January 1988 for yen and ringgit, from January 2003 for won and baht). We say nothing about earlier years.
  • The months of 2020 and 2021 came during COVID, when travel was heavily restricted, so those months say little about what a trip would have felt like.
  • We do not rank the four countries.

Coverage

All 1,500 monthly rates in the table for the four currencies were used: 465 months each for yen and ringgit (January 1988 to September 2026) and 285 months each for won and baht (January 2003 to September 2026). The tiles use 141 months per currency, from January 2015 to September 2026, which is 564 tiles in all. No month is missing between the first and the last for any currency. The table also has other currencies, which we did not use.

Report an error. Corrections are logged publicly with a date.

How we did it
PythonopenpyxlSingStat Table BuilderJavaScript
  1. Downloaded the table. We saved the monthly exchange rate table (M700051) from SingStat Table Builder as an .xlsx file on 16 Oct 2026.
  2. Checked the file. The script stops if the header row, the MAS source line, the newest first month order, a duplicate month, a gap in months or a rate at or below zero is found.
  3. Kept four series. Yen, won, ringgit and baht, as published: 1,500 rows, written to a .csv file with the rate exactly as MAS gave it.
  4. Worked out S$1,000. 1,000 divided by the rate, scaled to one unit (the file gives yen, won and baht per 100 units), rounded half up to whole units with exact decimals.
  5. Found the records and gaps. The record is the highest month in the whole series (the script stops on a tie); the lowest is the lowest of the 141 months from January 2015; the climb is the biggest rise against the same month a year earlier; per cent figures are rounded half up.
  6. Drew the page. The same JavaScript writes the yen view into the page and redraws it when you pick another country, so the first view and the picker agree.
Show the code
"""Prep for the SGD to yen, won, ringgit and baht insight (demo).

Reads MAS's monthly exchange rates (SingStat Table Builder, table M700051, saved as an .xlsx on 16 Oct 2026), checks the file,
writes data/monthly_rates.csv (one row per month and currency, the rate exactly as published) and prep/data.json (every figure the page uses).

Usage: python3 -I prep/prep_fx.py        (run from the build folder; needs openpyxl for the .xlsx; the page build itself needs only the standard library)
"""
import csv, json, os, sys
from decimal import Decimal, ROUND_HALF_UP

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
SRC = os.path.join(ROOT, "data", "M700051.xlsx")
CSV = os.path.join(ROOT, "data", "monthly_rates.csv")
OUT = os.path.join(HERE, "data.json")

MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
# code: (series label in the file, units of foreign currency per published rate, name, country, symbol)
CUR = {
    "JPY": ("Japanese Yen", 100, "Yen", "Japan", "¥"),
    "KRW": ("Korean Won", 100, "Won", "South Korea", "₩"),
    "MYR": ("Malaysian Ringgit", 1, "Ringgit", "Malaysia", "RM"),
    "THB": ("Thai Baht", 100, "Baht", "Thailand", "฿"),
}
GRID_START = (2015, 1)


def half_up(x, places=0):
    return Decimal(x).quantize(Decimal(1).scaleb(-places), rounding=ROUND_HALF_UP)


def ym(label):
    s = str(label).strip()
    y, m = int(s[:4]), MON.index(s[5:8]) + 1
    assert s[4] == " " and 1988 <= y <= 2100, label
    return (y, m)


def read_xlsx():
    import openpyxl
    ws = openpyxl.load_workbook(SRC, data_only=True).active
    rows = list(ws.iter_rows(values_only=True))
    hi = [i for i, r in enumerate(rows) if r and str(r[0]).strip() == "Data Series"]
    assert len(hi) == 1, "header row not found"
    hdr = rows[hi[0]]
    assert "MONETARY AUTHORITY OF SINGAPORE" in " ".join(str(r[0]) for r in rows[:hi[0]] if r and r[0]), "source line missing"
    months = [ym(h) for h in hdr[1:] if h not in (None, "")]
    assert months == sorted(months, reverse=True), "months should run newest first"
    assert len(set(months)) == len(months), "duplicate months"
    out = []
    for code, (label, unit, *_r) in CUR.items():
        hit = [r for r in rows[hi[0] + 1:] if r and r[0] and str(r[0]).startswith(label + " (")]
        assert len(hit) == 1, (code, "series not found exactly once")
        r = hit[0]
        assert ("Per %d " % unit if unit > 1 else "Per Malaysian") in str(r[0]) or unit == 1, (code, r[0])
        for m, v in zip(months, r[1:]):
            if v in (None, "", "na", "-"):
                continue
            out.append((m, code, str(v).strip()))
    return out, months


def main():
    recs, months = read_xlsx()
    with open(CSV, "w", newline="") as f:
        w = csv.writer(f)
        w.writerow(["year", "month", "currency", "sgd_per_unit_as_published"])
        for (y, m), code, v in sorted(recs):
            w.writerow([y, m, code, v])
    rows = list(csv.DictReader(open(CSV)))
    data = {}
    for code, (label, unit, name, country, sym) in CUR.items():
        s = sorted(((int(r["year"]), int(r["month"])), Decimal(r["sgd_per_unit_as_published"])) for r in rows if r["currency"] == code)
        keys = [k for k, _ in s]
        # no gaps: every month between the first and the last is present
        n = (keys[-1][0] - keys[0][0]) * 12 + keys[-1][1] - keys[0][1] + 1
        assert n == len(keys), (code, "gap in months", n, len(keys))
        assert all(v > 0 for _, v in s)
        # what S$1,000 buys, whole units, half up. rate = SGD per `unit` units
        amt = [(k, half_up(Decimal(1000) * unit / v)) for k, v in s]
        full = dict(amt)
        grid = [(k, a) for k, a in amt if k >= GRID_START]
        last = amt[-1]
        rec = max(amt, key=lambda x: (x[1], x[0]))
        assert sum(1 for k, a in amt if a == rec[1]) == 1, (code, "tie for the record")
        g_rec = max(grid, key=lambda x: x[1])
        g_low = min(grid, key=lambda x: x[1])
        assert sum(1 for k, a in grid if a == g_low[1]) == 1 and g_rec == rec, (code, "grid extremes")
        low_all = min(amt, key=lambda x: x[1])
        jan15 = full[GRID_START]
        above = [k for k, a in grid if a > jan15]
        below_or_eq = [k for k, a in grid if a <= jan15]
        last_below = max(below_or_eq) if below_or_eq else None
        # biggest rise over 12 months (same month a year earlier)
        climb = fall = None
        for k, a in amt:
            p = (k[0] - 1, k[1])
            if p in full and k >= (GRID_START[0] + 1, 1):
                r = a / full[p] - 1
                if climb is None or r > climb[0]:
                    climb = (r, p, k, full[p], a)
                if fall is None or r < fall[0]:
                    fall = (r, p, k, full[p], a)
        # the middle month: line the grid months up by amount; with an odd count the middle one is exact
        assert len(grid) % 2 == 1
        by_amt = sorted(grid, key=lambda x: (x[1], x[0]))
        mid = by_amt[len(grid) // 2]
        # ranking within the grid: 1 + the number of months that bought strictly more; the top five, most first (later month first on a tie)
        top = sorted(grid, key=lambda x: (-x[1], [-x[0][0], -x[0][1]]))
        latest_rank = 1 + sum(1 for k, a in grid if a > last[1])
        before_rec_year = max((x for x in amt if x[0][0] < rec[0][0]), key=lambda x: x[1])
        data[code] = {
            "name": name, "country": country, "symbol": sym, "unit": unit,
            "first_month": list(keys[0]), "last_month": list(keys[-1]), "n_full": len(keys), "n_grid": len(grid),
            "grid": [[k[0], k[1], int(a)] for k, a in grid],
            "jan15": int(jan15), "latest": [last[0][0], last[0][1], int(last[1])],
            "record": [rec[0][0], rec[0][1], int(rec[1])], "grid_low": [g_low[0][0], g_low[0][1], int(g_low[1])],
            "full_low": [low_all[0][0], low_all[0][1], int(low_all[1])],
            "before_record_year": [before_rec_year[0][0], before_rec_year[0][1], int(before_rec_year[1])],
            "months_above_jan15": len(above), "last_at_or_below_jan15": list(last_below) if last_below else None,
            "months_in_2026": sum(1 for k, _ in grid if k[0] == 2026),
            "climb12": {"from": [climb[1][0], climb[1][1]], "to": [climb[2][0], climb[2][1]], "a": int(climb[3]), "b": int(climb[4]), "pct": float(half_up(climb[0] * 100, 0))},
            "fall12": ({"from": [fall[1][0], fall[1][1]], "to": [fall[2][0], fall[2][1]], "a": int(fall[3]), "b": int(fall[4]), "pct": float(half_up(-fall[0] * 100, 0))} if fall[0] < 0 else None),
            "median": [mid[0][0], mid[0][1], int(mid[1])],
            "latest_rank": latest_rank, "n_more_than_latest": latest_rank - 1,
            "top5": [[k[0], k[1], int(a)] for k, a in top[:5]],
            "gain_record_pct": float(half_up((rec[1] / jan15 - 1) * 100, 0)),
            "gain_latest_pct": float(half_up((last[1] / jan15 - 1) * 100, 0)),
            "gap_record_low_pct": float(half_up((g_rec[1] / g_low[1] - 1) * 100, 0)),
            "latest_vs_record_pct": float(half_up((1 - last[1] / rec[1]) * 100, 0)),
        }
        print(code, "months", len(keys), keys[0], "to", keys[-1], "| record", rec, "| grid low", g_low, "| jan15", jan15, "| latest", last,
              "| above jan15", len(above), "of", len(grid), "| last at/below jan15", last_below)
    D = {"asof": "16 Oct 2026", "src_updated": "14 Oct 2026", "cur": data, "order": ["JPY", "KRW", "MYR", "THB"],
         "n_rows": len(rows), "grid_start": list(GRID_START)}
    json.dump(D, open(OUT, "w"), indent=1)
    print("wrote", CSV, len(rows), "rows;", OUT)


if __name__ == "__main__":
    main()

What could make this wrong: MAS or SingStat revising recent months; a misread series in the .xlsx file (the script checks the header and the series name); rounding to whole units, which can move a per cent figure by one; and the table's months being averages, so a day's rate can sit well above or below them.

Source: Monetary Authority of Singapore, through SingStat Table Builder, table M700051, accessed 16 Oct 2026. MAS and SingStat give no guarantee of accuracy. Licence wording for this table: not yet confirmed.