FAILURE MAP
← Case archive

FA-62091 / Currency rounding and FX conversion / Open access

The outright forward is rounded to whole pips · case 01

The forward rate loses the fractional pip that the dealing screen shows.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The forward is quantized at the pip exponent instead of one decimal beyond it.

VERIFIED REPAIR

Quantize to exp+1 decimals.

Unsuccessful approach: A fixed five-decimal precision is right only for four-decimal pip pairs.

Case contract

solve(pair, spot, points, notional): pair is BASEQUOTE. The pip exponent depends on the quote currency: JPY 2, HUF 2, KRW 0, otherwise 4 (pip = 10**-exp). points is a decimal string of pips (fractional pips allowed, negative for discount). forward = spot + points*pip rounded half-even to exp+1 decimals (one fractional pip). The pip value in quote currency is notional*pip rounded half-even to 2 decimals. Return [forward, pip_value] as plain strings.

Why this case matters

Currency amounts must be rounded at the right stage and in the right unit, or ledgers, quotes and settlements drift by minor units.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(pair, spot, points, notional):
    pip_exp = {'JPY': 2, 'HUF': 2, 'KRW': 0}
    quote = pair[3:]
    pe = pip_exp.get(quote, 4)
    pip = Decimal(1).scaleb(-pe)
    fwd = Decimal(spot) + Decimal(points) * pip
    fwd = fwd.quantize(Decimal(1).scaleb(-pe), rounding=ROUND_HALF_EVEN)
    pv = (Decimal(notional) * pip).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)
    return [format(fwd, 'f'), format(pv, 'f')]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression forward-precision 1', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('regression forward-precision 2', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00']),
  ('partial repair guard 2', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('control: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00'])],
 [('regression forward-precision 1', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('regression forward-precision 2', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('partial repair guard 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00'])],
 [('regression forward-precision 1', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('regression forward-precision 2', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('partial repair guard 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
  ('partial repair guard 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('control: yen discount', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00']),
  ('control: won pips', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00'])],
 [('regression forward-precision 1', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('regression forward-precision 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
  ('partial repair guard 1', ('EURHUF', '4938.658', '-58.12', 1000000), ['4938.077', '10000.00']),
  ('partial repair guard 2', ('EURJPY', '12323.166', '18282', 1000), ['12505.986', '10.00']),
  ('control: won pips', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('control: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33'])],
 [('regression forward-precision 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
  ('regression forward-precision 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
  ('partial repair guard 1', ('EURJPY', '145.95899', '-6845', 333333), ['77.509', '3333.33']),
  ('partial repair guard 2', ('EURHUF', '1299.5246', '197.88', 100000), ['1301.503', '1000.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('control: yen discount', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00'])]]
for label, args, expected in cases[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression forward-precision 1['1.0847', '100.00']['1.08470', '100.00']Failed
regression forward-precision 2['150.78', '10000.00']['150.782', '10000.00']Failed
partial repair guard 2['1356', '1000000.00']['1355.7', '1000000.00']Failed
control: forint['395.12', '2500.00']['395.116', '2500.00']Failed
control: yen base pair['0.0703', '500.00']['0.07027', '500.00']Failed
control: sterling['1.2649', '33.33']['1.26494', '33.33']Failed
control: zero points['1.0988', '10.00']['1.09876', '10.00']Failed

SHA-256 / a4dfb88ef4986d132c60c383960382d3b5ab3a40e094d2615bb6f441c9eb910d

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(pair, spot, points, notional):
    pip_exp = {'JPY': 2, 'HUF': 2, 'KRW': 0}
    quote = pair[3:]
    pe = pip_exp.get(quote, 4)
    pip = Decimal(1).scaleb(-pe)
    fwd = Decimal(spot) + Decimal(points) * pip
    fwd = fwd.quantize(Decimal(1).scaleb(-5), rounding=ROUND_HALF_EVEN)
    pv = (Decimal(notional) * pip).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)
    return [format(fwd, 'f'), format(pv, 'f')]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression forward-precision 1', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('regression forward-precision 2', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00']),
  ('partial repair guard 2', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('control: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00'])],
 [('regression forward-precision 1', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('regression forward-precision 2', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('partial repair guard 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00'])],
 [('regression forward-precision 1', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('regression forward-precision 2', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('partial repair guard 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
  ('partial repair guard 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('control: yen discount', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00']),
  ('control: won pips', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00'])],
 [('regression forward-precision 1', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('regression forward-precision 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
  ('partial repair guard 1', ('EURHUF', '4938.658', '-58.12', 1000000), ['4938.077', '10000.00']),
  ('partial repair guard 2', ('EURJPY', '12323.166', '18282', 1000), ['12505.986', '10.00']),
  ('control: won pips', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('control: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33'])],
 [('regression forward-precision 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
  ('regression forward-precision 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
  ('partial repair guard 1', ('EURJPY', '145.95899', '-6845', 333333), ['77.509', '3333.33']),
  ('partial repair guard 2', ('EURHUF', '1299.5246', '197.88', 100000), ['1301.503', '1000.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('control: yen discount', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00'])]]
for label, args, expected in cases[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression forward-precision 1['1.08470', '100.00']['1.08470', '100.00']Passed
regression forward-precision 2['150.78150', '10000.00']['150.782', '10000.00']Failed
partial repair guard 2['1355.70000', '1000000.00']['1355.7', '1000000.00']Failed
control: forint['395.11650', '2500.00']['395.116', '2500.00']Failed
control: yen base pair['0.07027', '500.00']['0.07027', '500.00']Passed
control: sterling['1.26494', '33.33']['1.26494', '33.33']Passed
control: zero points['1.09876', '10.00']['1.09876', '10.00']Passed

SHA-256 / 5d70dfc3de0bf1ba271c53d16907ee8dcbb7a8a21baa0bea6da917a63900a0b6

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
from decimal import Decimal, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_DOWN, ROUND_UP, ROUND_HALF_DOWN
N = 1
observations = []
def solve(pair, spot, points, notional):
    pip_exp = {'JPY': 2, 'HUF': 2, 'KRW': 0}
    quote = pair[3:]
    pe = pip_exp.get(quote, 4)
    pip = Decimal(1).scaleb(-pe)
    fwd = Decimal(spot) + Decimal(points) * pip
    fwd = fwd.quantize(Decimal(1).scaleb(-(pe + 1)), rounding=ROUND_HALF_EVEN)
    pv = (Decimal(notional) * pip).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)
    return [format(fwd, 'f'), format(pv, 'f')]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression forward-precision 1', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('regression forward-precision 2', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00']),
  ('partial repair guard 2', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('control: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00'])],
 [('regression forward-precision 1', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('regression forward-precision 2', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('partial repair guard 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00'])],
 [('regression forward-precision 1', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('regression forward-precision 2', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('partial repair guard 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
  ('partial repair guard 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('control: yen discount', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00']),
  ('control: won pips', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00'])],
 [('regression forward-precision 1', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('regression forward-precision 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
  ('partial repair guard 1', ('EURHUF', '4938.658', '-58.12', 1000000), ['4938.077', '10000.00']),
  ('partial repair guard 2', ('EURJPY', '12323.166', '18282', 1000), ['12505.986', '10.00']),
  ('control: won pips', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
  ('control: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
  ('control: yen base pair', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33'])],
 [('regression forward-precision 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
  ('regression forward-precision 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
  ('partial repair guard 1', ('EURJPY', '145.95899', '-6845', 333333), ['77.509', '3333.33']),
  ('partial repair guard 2', ('EURHUF', '1299.5246', '197.88', 100000), ['1301.503', '1000.00']),
  ('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33']),
  ('control: zero points', ('AUDNZD', '1.09876', '0', 100000), ['1.09876', '10.00']),
  ('control: euro premium', ('EURUSD', '1.08345', '12.5', 1000000), ['1.08470', '100.00']),
  ('control: yen discount', ('USDJPY', '151.234', '-45.25', 1000000), ['150.782', '10000.00'])]]
for label, args, expected in cases[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression forward-precision 1['1.08470', '100.00']['1.08470', '100.00']Passed
regression forward-precision 2['150.782', '10000.00']['150.782', '10000.00']Passed
partial repair guard 2['1355.7', '1000000.00']['1355.7', '1000000.00']Passed
control: forint['395.116', '2500.00']['395.116', '2500.00']Passed
control: yen base pair['0.07027', '500.00']['0.07027', '500.00']Passed
control: sterling['1.26494', '33.33']['1.26494', '33.33']Passed
control: zero points['1.09876', '10.00']['1.09876', '10.00']Passed

SHA-256 / dd875044619c351f130600ecafb542c916580ee2e771a16291ff22eaa656758c

Verification & scope

A deterministic, bounded teaching model with a stipulated toy contract; it makes no claim of conformance to any real regulation, standard, or institution's rules. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:47:01.369132+00:00.

Case digest / 916db43154ecf068844a22870fcef8e6de5467add0e0e6411e6a988d53ddfb5c