FA-62081 / Currency rounding and FX conversion / Open access
The pip size is chosen by searching the pair text for JPY · case 01
JPY-base pairs get yen pips while forint and won pairs get four-decimal pips.
ROOT CAUSE
Pip exponent is 2 whenever 'JPY' appears anywhere in the pair, and 4 otherwise.
VERIFIED REPAIR
Look up the pip exponent from the quote currency using the full table.
Unsuccessful approach: Checking only JPY and HUF in the quote position still gives won pairs a 0.0001 pip.
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 = 2 if 'JPY' in pair else 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 pip-scale-lookup 1', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
('regression pip-scale-lookup 2', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
('partial repair guard 2', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.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: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33'])],
[('regression pip-scale-lookup 1', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
('regression pip-scale-lookup 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
('partial repair guard 1', ('USDKRW', '224.7871', '-8932', 1000), ['-8707.2', '1000.00']),
('partial repair guard 2', ('USDKRW', '1580.721', '-1057.4', 100000), ['523.3', '100000.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 pip-scale-lookup 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
('regression pip-scale-lookup 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
('partial repair guard 1', ('USDKRW', '187.33709', '-11198', 2500000), ['-11010.7', '2500000.00']),
('partial repair guard 2', ('USDKRW', '56.05528', '-16375', 1000000), ['-16318.9', '1000000.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 pip-scale-lookup 1', ('EURHUF', '4938.658', '-58.12', 1000000), ['4938.077', '10000.00']),
('regression pip-scale-lookup 2', ('JPYSEK', '183.37113', '-118.52', 1000), ['183.35928', '0.10']),
('partial repair guard 1', ('USDKRW', '1861.3659', '-46.8', 1000), ['1814.6', '1000.00']),
('partial repair guard 2', ('USDKRW', '17311.063', '184.26', 333333), ['17495.3', '333333.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 pip-scale-lookup 1', ('EURHUF', '1299.5246', '197.88', 100000), ['1301.503', '1000.00']),
('regression pip-scale-lookup 2', ('USDKRW', '224.7871', '-8932', 1000), ['-8707.2', '1000.00']),
('partial repair guard 1', ('USDKRW', '48.91428', '15066', 1000000), ['15114.9', '1000000.00']),
('partial repair guard 2', ('USDKRW', '516.5786', '17023', 333333), ['17539.6', '333333.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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression pip-scale-lookup 1 | ['1352.50032', '100.00'] | ['1355.7', '1000000.00'] | Failed |
| regression pip-scale-lookup 2 | ['0.085', '50000.00'] | ['0.07027', '500.00'] | Failed |
| partial repair guard 2 | ['16115.27670', '100.00'] | ['29061.0', '1000000.00'] | Failed |
| control: euro premium | ['1.08470', '100.00'] | ['1.08470', '100.00'] | Passed |
| control: yen discount | ['150.782', '10000.00'] | ['150.782', '10000.00'] | Passed |
| control: forint | ['395.11996', '25.00'] | ['395.116', '2500.00'] | Failed |
| control: sterling | ['1.26494', '33.33'] | ['1.26494', '33.33'] | Passed |
SHA-256 / 86d0c33a3a4d8fda89b1649fd22e8d980cbb6778fb84fd5a7273f33bb7535f5d
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 = 2 if quote in ('JPY', 'HUF') else 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 pip-scale-lookup 1', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
('regression pip-scale-lookup 2', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
('partial repair guard 2', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.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: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33'])],
[('regression pip-scale-lookup 1', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
('regression pip-scale-lookup 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
('partial repair guard 1', ('USDKRW', '224.7871', '-8932', 1000), ['-8707.2', '1000.00']),
('partial repair guard 2', ('USDKRW', '1580.721', '-1057.4', 100000), ['523.3', '100000.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 pip-scale-lookup 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
('regression pip-scale-lookup 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
('partial repair guard 1', ('USDKRW', '187.33709', '-11198', 2500000), ['-11010.7', '2500000.00']),
('partial repair guard 2', ('USDKRW', '56.05528', '-16375', 1000000), ['-16318.9', '1000000.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 pip-scale-lookup 1', ('EURHUF', '4938.658', '-58.12', 1000000), ['4938.077', '10000.00']),
('regression pip-scale-lookup 2', ('JPYSEK', '183.37113', '-118.52', 1000), ['183.35928', '0.10']),
('partial repair guard 1', ('USDKRW', '1861.3659', '-46.8', 1000), ['1814.6', '1000.00']),
('partial repair guard 2', ('USDKRW', '17311.063', '184.26', 333333), ['17495.3', '333333.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 pip-scale-lookup 1', ('EURHUF', '1299.5246', '197.88', 100000), ['1301.503', '1000.00']),
('regression pip-scale-lookup 2', ('USDKRW', '224.7871', '-8932', 1000), ['-8707.2', '1000.00']),
('partial repair guard 1', ('USDKRW', '48.91428', '15066', 1000000), ['15114.9', '1000000.00']),
('partial repair guard 2', ('USDKRW', '516.5786', '17023', 333333), ['17539.6', '333333.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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression pip-scale-lookup 1 | ['1352.50032', '100.00'] | ['1355.7', '1000000.00'] | Failed |
| regression pip-scale-lookup 2 | ['0.07027', '500.00'] | ['0.07027', '500.00'] | Passed |
| partial repair guard 2 | ['16115.27670', '100.00'] | ['29061.0', '1000000.00'] | Failed |
| control: euro premium | ['1.08470', '100.00'] | ['1.08470', '100.00'] | Passed |
| control: yen discount | ['150.782', '10000.00'] | ['150.782', '10000.00'] | Passed |
| control: forint | ['395.116', '2500.00'] | ['395.116', '2500.00'] | Passed |
| control: sterling | ['1.26494', '33.33'] | ['1.26494', '33.33'] | Passed |
SHA-256 / b8133ad9000a6b14a0152628c84c084d48410b2bee1311f4ae6aa58515d18923
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 pip-scale-lookup 1', ('USDKRW', '1352.5', '3.2', 1000000), ['1355.7', '1000000.00']),
('regression pip-scale-lookup 2', ('JPYSEK', '0.07012', '1.5', 5000000), ['0.07027', '500.00']),
('partial repair guard 2', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.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: forint', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
('control: sterling', ('GBPUSD', '1.26501', '-0.75', 333333), ['1.26494', '33.33'])],
[('regression pip-scale-lookup 1', ('EURHUF', '395.12', '-0.35', 250000), ['395.116', '2500.00']),
('regression pip-scale-lookup 2', ('EURHUF', '698.3982', '193.71', 1000), ['700.335', '10.00']),
('partial repair guard 1', ('USDKRW', '224.7871', '-8932', 1000), ['-8707.2', '1000.00']),
('partial repair guard 2', ('USDKRW', '1580.721', '-1057.4', 100000), ['523.3', '100000.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 pip-scale-lookup 1', ('USDKRW', '16113.982', '12947', 1000000), ['29061.0', '1000000.00']),
('regression pip-scale-lookup 2', ('EURHUF', '138.28482', '-67.66', 1000), ['137.608', '10.00']),
('partial repair guard 1', ('USDKRW', '187.33709', '-11198', 2500000), ['-11010.7', '2500000.00']),
('partial repair guard 2', ('USDKRW', '56.05528', '-16375', 1000000), ['-16318.9', '1000000.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 pip-scale-lookup 1', ('EURHUF', '4938.658', '-58.12', 1000000), ['4938.077', '10000.00']),
('regression pip-scale-lookup 2', ('JPYSEK', '183.37113', '-118.52', 1000), ['183.35928', '0.10']),
('partial repair guard 1', ('USDKRW', '1861.3659', '-46.8', 1000), ['1814.6', '1000.00']),
('partial repair guard 2', ('USDKRW', '17311.063', '184.26', 333333), ['17495.3', '333333.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 pip-scale-lookup 1', ('EURHUF', '1299.5246', '197.88', 100000), ['1301.503', '1000.00']),
('regression pip-scale-lookup 2', ('USDKRW', '224.7871', '-8932', 1000), ['-8707.2', '1000.00']),
('partial repair guard 1', ('USDKRW', '48.91428', '15066', 1000000), ['15114.9', '1000000.00']),
('partial repair guard 2', ('USDKRW', '516.5786', '17023', 333333), ['17539.6', '333333.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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression pip-scale-lookup 1 | ['1355.7', '1000000.00'] | ['1355.7', '1000000.00'] | Passed |
| regression pip-scale-lookup 2 | ['0.07027', '500.00'] | ['0.07027', '500.00'] | Passed |
| partial repair guard 2 | ['29061.0', '1000000.00'] | ['29061.0', '1000000.00'] | Passed |
| control: euro premium | ['1.08470', '100.00'] | ['1.08470', '100.00'] | Passed |
| control: yen discount | ['150.782', '10000.00'] | ['150.782', '10000.00'] | Passed |
| control: forint | ['395.116', '2500.00'] | ['395.116', '2500.00'] | Passed |
| control: sterling | ['1.26494', '33.33'] | ['1.26494', '33.33'] | Passed |
SHA-256 / 9009ef5ff950f6c4afe4dd135df55eb2d7a534e30ecdea520474fdfce461d2b8
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.345274+00:00.
Case digest / cf604889de82e33e2135c781a5a113f1f2b76f9373a598985e9c14032722df60