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.
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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