Fintech Engineering Handbook

krever1 pts0 comments

Fintech Engineering Handbook

Welcome to the Fintech Engineering Handbook. This resource aims to describe the most important patterns used in software engineering, where money is the primary focus of the system. It can be read in full to get a comprehensive understanding or in parts when dealing with a particular problem.

For whom?

People joining fintech. To get familiar with the domain and the patterns that make money systems trustworthy.

People already in fintech. As a reference to reach for when facing a particular problem, and a shared vocabulary to point colleagues at.

People outside fintech. To understand how building for money differs from what they’re used to, and why.

It’s meant as a living document and contributions are welcomed.

Principles

Everything you will read below is a way to adhere to the three principles:

No invented data. Money can’t be created out of nowhere, hence we can’t tolerate duplicates or arbitrary balance updates.

No lost data. Everything that happens to money has to be tracked and persisted: no precision losses, at-least-once deliveries, event-sourcing, audit trails, immutability.

No trust. Neither towards external providers, internal components nor the world. Failing on broken assumptions, verifying webhooks, verifying data across different sources.

Representing money

Before you can move or record money, you have to represent it. These are the decisions about how a monetary value is modeled, stored, computed and converted. Getting them wrong means every layer above inherits the error.

Precision handling

Money representation is one of the most fundamental decisions in financial systems. There are four primary ways to do it:

Floating-point. Built-in float or double types. This can create unpredictable precision losses and is almost never a good idea. But it’s the fastest and most memory efficient, and requires no additional libraries or data structures.

Arbitrary precision. Types like Java’s BigDecimal let you control the precision of a computation precisely. The code is predictable and we get to decide where and how rounding happens. It fits intermediate work like FX or pricing math, where many operations chain together.

Minor-units precision. For most fiat currencies it’s ok to keep only a fixed precision, the same that is used in the connected central banking system. The number of digits is described by ISO 4217 (don’t assume it’s always 2, it’s not!). In practice this means storing the amount as an integer in its smallest unit - €12.34 becomes 1234. Crypto uses the same integer-smallest-unit idea (satoshis for BTC, wei for ETH), but with two twists: the precision is per-asset and defined by the token itself (e.g. an ERC-20’s decimals), often 18 digits, and the resulting magnitudes overflow 64-bit integers, so you need arbitrary-width integers to hold them.

Rational numbers. When no precision loss is acceptable. This is the most powerful approach but comes with its own caveats. First, it’s slower than the alternatives. Second, it cannot be converted to other formats without losing precision. Third, it usually requires a custom datatype or a library.

Selecting one or the other depends on the class of the system and its responsibilities. There is no rule of thumb here, other than not using floating points. These representations are not mutually exclusive either - how you store an amount and how you compute with it are separate decisions, and a system often combines them, e.g. integer storage with BigDecimal for intermediate computation.

The same care applies when an amount is being serialized. A bare JSON number is an IEEE-754 double in most parsers, so serializing money as a number reintroduces the floating-point problem at the edge, no matter how carefully you represent it internally. Send money either as a string ("12.34") or as an integer in its smallest unit.

Principles touched:

No lost data. The wrong representation silently drops precision that can never be recovered.

Rounding strategies

Rounding is inevitable. It should be done explicitly: any division, currency conversion, fee, interest or rate application, or move between precisions might require rounding.

It’s a business decision. Different rounding strategies have different implications. Sometimes you have to be conservative (e.g. not to spend what you don’t have) and round down; sometimes you care about statistical effects and use half-even. Deciding who gets the fraction might have legal/tax implications.

Round as seldom as possible. The longer you keep full precision, the more options you have to make the right decision in the right context. Rounding should usually happen on boundaries, e.g. before numbers are persisted or shown to the user.

Rounding breaks sums. If a number is split into parts and rounding is applied, the sum of the parts might no longer equal the original number. Depending on the context, this might require explicit handling - e.g. an explicit rounding...

precision money rounding fintech data number

Related Articles