logoalt Hacker News

xliiyesterday at 12:18 PM7 repliesview on HN

I glanced, and I found this handbook shallow and - in some areas - even bad advice.

E.g. If I ever see a monetary value stored in something else than integers I'm going to run away screaming (thank you Rust decimals represented as JSON floats). It's always integers unless you have a VERY good reason to do otherwise (though exported view can be in anything, even in weird bitcoded formats).

FX exchange. Resolution of FX isn't a point-in-time thing, things like buyer rate-in-time, seller rate-in-time, agreement, agreement tolerance, agreed upon resolution timestamp come in the effect.

Immutability - that's why you want to have event sourcing everywhere that touches money:

    # Resolved stream
    A -> B -> E

    # Actual stream
    A0 -> Edit(A0, A) -> B -> C -> D -> Rollback(B) -> E

Though in the end Fintech != Fintech. I worked at Fintech where money was treated like a baggage, and in other where money was a central point of everything.

Replies

oddthinkyesterday at 9:16 PM

> E.g. If I ever see a monetary value stored in something else than integers I'm going to run away screaming (thank you Rust decimals represented as JSON floats). It's always integers unless you have a VERY good reason to do otherwise (though exported view can be in anything, even in weird bitcoded formats).

That really overstates the issue. Whole domains of finance run just fine on doubles.

If you're doing Monte Carlo options pricing over interest rate paths, and you're interested in the risk metrics, like durations, convexity, vega, and so on, no one cares what your rounding convention is. doubles are just fine, thank you. How are you going to force `exp(-rt)cashflow` to be an integer? Or the normal CDF?

Yes, there are domains where ints make sense. But it's certainly not universal, you just need to make the right engineering choice.

show 3 replies
dahartyesterday at 2:52 PM

What are you referring to with the integers/floats comment? The article says clearly that the rule of thumb is not to use floats and that they’re “almost never” a good idea, that they cause unpredictable precision loss, and recommends integer or BigDecimal types in multiple places. Are you also talking about rationals? So what is the bad advice here, exactly?

For FX, it seems like you’re reinforcing what the handbook says, that there’s no canonical rate. Aside from that, it’s talking about post-resolution records and you’re talking about how to resolve, no? That’s valid nuance of a separate goal, and it’s a fine goal of yours, but doesn’t seem like a demonstration of something missing or wrong.

The article appears to make the very same point about immutability? What are you saying that’s different?

show 1 reply
shotoday at 7:43 AM

> It's always integers unless you have a VERY good reason to do otherwise

I don't really agree. This seems like one of those outdated "greybeard rules" which people love to cargo cult, but to me it just comes with its own set of trade-offs, like now you have to think about exponents everywhere and getting them wrong comes with orders-of-magnitude consequences.

If you have a modern DB and your languages handle its decimals well then I'd use them. You can translate as needed for imports and exports, but the core of your system is always sound. Switch to the more basic formats if and only if there was some perf problem with decimals, which for most fintechs will be a VERY long time with modern DBs.

What really pushes me towards decimals is the consequence of getting something wrong. Relying on comparing raw ints, with variable exponents baked in, can lead to catastrophic errors with multi-order-of-magnitude gaps if that implicit exponent isn't carried along correctly. It's a massive footgun always waiting to happen. Decimals avoid that whole class of problem. So the goal is to maximise the "safe" decimals as source of truth everywhere you can, and have very well-tested "in and out" paths for any producers or consumers with different representation preferences. To me, that's good system design. And when, not if, you're manually inspecting DB records to track down a bug, having everything normalized in a glanceable, obvious format like decimals will let you recognise errors faster and more intuitively.

This goes extra for crypto, especially stablecoins, where one USD stablecoin might be exp-6 and another might be exp-9. And you're representing them in the same DB! Off by a factor of a thousand if you miss that exponent! Decimalize that immediately says I.

show 2 replies
skipantsyesterday at 4:36 PM

That was the first thing that popped out and made me distrust the whole wiki; there's only One Right Way to store money (as integers[1], as you said) and it should have been explicit about that.

You can also use fixed-point if whatever you're using supports it but it's still technically integers.

lxgryesterday at 2:31 PM

> thank you Rust decimals represented as JSON floats

What do you mean? JSON doesn’t have floats, it has numbers, and how they’re used after being parsed is not part of the spec.

> If I ever see a monetary value stored in something else than integers I'm going to run away screaming

That’s good, then we’ll likely not be working on the same system :) I consider running from “amounts as integer” systems these days (but usually unfortunately can’t). In an idealized codebase that only seasoned financial programmers are allowed to touch, it can go well, but such a system is usually either overly exclusive or risks becoming brittle.

show 3 replies
m00xyesterday at 7:57 PM

You can't do everything you need with an integer. There are values you might want to display or calculate with that are smaller than cents. In some places you'll need things like BigDecimal, which are immune to floating point errors in most cases.

It's also safe to return decimal values for displaying values.

show 1 reply