Allow the rounding a broker's own printed figures propagate
A real Scalable export refused to import at record 148: "buy gross -808.5599 does not equal quantity 6 times price 134.76, which is -808.56". Six NVIDIA shares settled at 808.5599 against a printed price of 134.76, because the fill was 134.759983 and the export printed the price to two places. One ten-thousandth out, and the whole file was rejected. The check held a gross to its own stated precision, which is only half the story: the price is rounded too, and the file never says by how much. So the allowance is now half a unit of the gross's stated precision plus one part in a hundred thousand of the gross, compared against a product kept exact at 1e-16 rather than rounded first. Measured over the complete export - 88 security rows - exactly one deviates at all, by one part in eight million, eighty times inside the new bound. What the bound still refuses is unchanged in kind: a price taken from the wrong share class, and the misplaced decimal separator the check exists for, which misses by four orders of magnitude. What it now accepts is the broker's own rounding, including a whole cent once a gross stated to the cent passes about five hundred euro, where a genuine one-cent error cannot be told from that rounding anyway. The row is kept as a regression test alongside four grosses that must still be refused: a cent, a euro, a wrong instrument's price, and a factor of ten.
This commit is contained in:
@@ -228,6 +228,38 @@ func TestSingleShareRowCatchesOnlyInconsistentArithmetic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A broker's own gross can disagree with its own printed shares times price,
|
||||
// because the price is printed to fewer places than the fill actually had.
|
||||
// Six NVIDIA shares settled at 808.5599 against a printed 134.76, whose
|
||||
// product is 808.56: one ten-thousandth out, and the whole file was refused.
|
||||
// The rounding the printed figures propagate is allowed; anything above one
|
||||
// part in a hundred thousand still is not.
|
||||
func TestRoundedPriceDoesNotRejectTheBrokersOwnGross(t *testing.T) {
|
||||
const row = `2025-01-09;10:37:32;Executed;SCALixkS3TomjQv;NVIDIA;Security;Buy;US67066G1040;6;134,76;-808,5599;0,00;0,00;EUR`
|
||||
result := readBroker(t, row)
|
||||
inv := result.Facts[0].Investment
|
||||
if inv.Gross != "-808.5599" || inv.Price != "134.76" || inv.Quantity != "6" {
|
||||
t.Fatalf("trade read as %+v", inv)
|
||||
}
|
||||
if got := result.Facts[0].Amount; got != "-808.5599" {
|
||||
t.Errorf("settled %s, want -808.5599", got)
|
||||
}
|
||||
for name, gross := range map[string]string{
|
||||
"one cent out": "-808,5699",
|
||||
"factor of ten": "-8.085,599",
|
||||
"a euro out": "-809,5599",
|
||||
"wrong instrument": "-908,5599",
|
||||
} {
|
||||
file, err := ReadCSV(strings.NewReader(scalableHeader + strings.Replace(row, ";-808,5599;", ";"+gross+";", 1) + "\n"))
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", name, err)
|
||||
}
|
||||
if _, err := ParseScalableCSV(file, brokerAccount(), nil); err == nil {
|
||||
t.Errorf("%s: accepted a gross its own shares times price does not support", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A reinvested distribution settles shares times price, so it carries as many
|
||||
// decimal places as the two together need. A real export reinvests to nine,
|
||||
// which is past what money holds and past what a share count holds, so reading
|
||||
|
||||
Reference in New Issue
Block a user