Watch the video above, then read on for the detail and practical patterns you need when working with numbers in your financial Python code.
Why Numbers Matter in Finance Code
You cannot write financial code without understanding how Python handles numbers. A treasury analyst calculating interest accrual, a risk team setting regulatory thresholds, a settlements team reconciling positions: all of them rely on numbers being correct. Python gives you two main numerical types: integers and floats. They look similar, but they behave differently, and choosing the wrong one or mixing them carelessly will lead to silent errors that only show up when the books do not balance.
This is not academic. It is about writing code you can trust.
Integers: Whole Numbers in Python
An integer is a whole number with no decimal part. In Python, integers are exact. There is no ambiguity: 100 is 100, and 1000 is 1000. You create them by typing the number without a decimal point.
cash_balance = 50000
number_of_positions = 42
settlement_days = 2
Python stores these exactly in memory. Arithmetic on integers is also exact. If you add two integers, you get an integer back (unless division is involved; we will come to that).
opening_balance = 1000000
inflow = 500000
closing_balance = opening_balance + inflow
print(closing_balance) # 1500000
Integers scale to any size. If you are working with notional amounts in the millions, or tick volumes in the billions, Python handles them without overflow. This is different from many other languages, and it is a strength.
The main constraint with integers is that they cannot represent fractions. If your calculation produces a fraction, you need a float or you need to stay integer and lose the fractional part.
Floats: Decimals and the Precision Problem
A float represents a decimal number. You create one by including a decimal point.
interest_rate = 0.045
price = 99.75
discount = 0.1
Floats are useful because most financial data is fractional: interest rates, prices, yields, spreads. But floats come with a cost: they are not exact.
Python (like almost all programming languages) stores floats using a binary format called IEEE 754. This format cannot represent all decimal numbers exactly. The result is rounding error that sneaks into your calculations.
The classic example is 0.1 + 0.2:
result = 0.1 + 0.2
print(result) # 0.30000000000000004
This is not a Python bug. It is a consequence of how decimal fractions are encoded in binary. The numbers 0.1 and 0.2 do not have exact binary representations, so Python stores approximations. When you add them, the approximations combine and the rounding error becomes visible.
In an interactive session or notebook, this looks like a minor annoyance. In production code that reconciles millions of pounds across thousands of transactions, it becomes a real liability.
Print the result of 0.1 + 0.2 in your own Python environment and see it for yourself. Understanding this problem viscerally, not just in theory, changes how you think about financial calculations.
Arithmetic Operations and Order of Evaluation
Python evaluates arithmetic operations in a standard order. Multiplication and division happen before addition and subtraction. Operations of the same precedence are evaluated left to right.
result = 100 + 50 * 2
print(result) # 200, not 300. The * happens first.
result = 100 / 5 / 4
print(result) # 5.0. Left to right: 100 / 5 = 20, then 20 / 4 = 5
Use brackets to override precedence when your calculation needs it.
accrued_interest = (principal * rate * days) / 365
This matters more in finance than in other domains. The order of operations affects your answer. Make your intent explicit with brackets, even when they are not strictly necessary. It saves the next person reading your code from guessing.
Division in Python 3 always returns a float, even if both operands are integers and the result is a whole number.
result = 100 / 4
print(result) # 25.0, not 25
print(type(result)) # <class 'float'>
If you need integer division (division that throws away the remainder), use the // operator.
full_days = 67 // 7
print(full_days) # 9
remainder_days = 67 % 7
print(remainder_days) # 4
The modulo operator % gives you the remainder. Both are useful in financial code: calculating day counts, chunking data into batches, or testing divisibility.
Working with Money: The Decimal Approach
If you are handling money in Python, floats are the wrong tool. Binary representation errors are not theoretical; they compound. A reconciliation system processing thousands of transactions will accumulate errors that make your position wrong by a few pence or pounds.
The solution is the Decimal module from the standard library. Decimal works in base 10, as humans write numbers. It stores numbers exactly and performs arithmetic exactly (to a configurable precision).
A weekly note on treasury, liquidity and practical Python. No spam, unsubscribe any time.
from decimal import Decimal
rate = Decimal('0.045')
principal = Decimal('1000000')
days = 92
accrued = principal * rate * days / Decimal('365')
print(accrued) # Exact result, no rounding error
Notice that you pass numbers to Decimal as strings, not as floats. This is important. If you do Decimal(0.1), you still get the float's rounding error. Pass a string, and Decimal converts it correctly.
# Wrong
x = Decimal(0.1)
print(x) # 0.1000000000000000055511151231...
# Right
x = Decimal('0.1')
print(x) # 0.1
Decimal is slower than float because it is more precise. In interactive analysis or one time scripts, it hardly matters. In production code that processes millions of rows, you might need to benchmark. But for most treasury and risk work, the extra microseconds are negligible against the cost of a reconciliation error.
When to use Decimal vs float: Use Decimal for any money amount, rate, or value that appears in a P&L or a balance sheet. Use float only for intermediate calculations where precision to 15 decimal places is acceptable (rare in finance). Use integer for counts: number of deals, day counts, portfolio size.
For treasury workflows, use quantize() to round a Decimal to a specific number of decimal places without converting to float.
from decimal import Decimal, ROUND_HALF_UP
amount = Decimal('1234.567')
# Round to 2 decimal places for GBP
rounded = amount.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
print(rounded) # 1234.57
This is useful when you need to match output to a specific precision (for reporting or regulatory submission) without losing the exactness of your underlying calculation.
Type Conversion and Mixing Numbers
Python allows you to mix integers and floats in a calculation. The result is a float.
notional = 1000000 # integer
rate = 0.05 # float
annual_interest = notional * rate
print(annual_interest) # 50000.0 (float)
print(type(annual_interest)) # <class 'float'>
You can convert types explicitly using int(), float(), and Decimal().
x = 10
y = float(x)
print(y) # 10.0
price = 99.75
whole_price = int(price)
print(whole_price) # 99 (truncated, not rounded)
amount = Decimal('1234.567')
as_float = float(amount)
print(as_float) # 1234.567
Be careful with int(). It truncates towards zero, it does not round. If you need rounding, use the round() function.
price = 99.75
print(int(price)) # 99
print(round(price)) # 100
Converting Decimal to float loses precision: you go from exact base 10 arithmetic back to the binary approximation. Avoid this conversion in financial code. If you have calculated something as Decimal and need to store or display it, keep it as Decimal. Convert to float only if you are passing data to a function that strictly requires it.
# Avoid this in production
exact_amount = Decimal('100.10')
as_float = float(exact_amount) # Precision lost
# Better
exact_amount = Decimal('100.10')
# Keep it as Decimal or export it as a string
In financial code, conversion choices matter. If you are splitting a notional across multiple counterparties and you use int() without thinking, you will lose the fractional pounds. It builds up.
Variables and Reusable Calculations
A variable stores a number so you can use it multiple times and update it without changing your code.
opening_cash = 5000000
daily_outflow = 250000
days_elapsed = 3
closing_cash = opening_cash - (daily_outflow * days_elapsed)
print(closing_cash) # 4250000
This is cleaner and more maintainable than writing the numbers inline. More importantly, if the daily outflow changes, you update one line, not five.
You can reassign a variable. This is useful when you are building a calculation step by step.
balance = 1000000
print(f"Opening: {balance}")
balance = balance + 500000
print(f"After deposit: {balance}")
balance = balance - 250000
print(f"After withdrawal: {balance}")
# Output:
# Opening: 1000000
# After deposit: 1500000
# After withdrawal: 1250000
The f string (formatted string literal) makes it easy to print variables alongside text. This is the standard way to format output in modern Python, and it is worth getting comfortable with it early.
Combine variables and arithmetic to build flexible calculations. This is how you move from a one time script to code that handles different scenarios without modification.
principal = 2000000
annual_rate = 0.04
holding_period_days = 45
interest = principal * annual_rate * holding_period_days / 365
print(f"Interest earned: {interest:.2f}")
# Output: Interest earned: 9863.01
The :.2f format specifier rounds the output to 2 decimal places for display. It does not change the underlying value, just how it is printed. This is useful for reports.
Common Pitfalls and How to Avoid Them
Float precision creeping in where it should not. Every time you use a float in a calculation with money, you introduce rounding error. It is small per transaction but cumulative. Use Decimal for money from the start.
Dividing two integers and forgetting you get a float. If you do 100 / 3, you get 33.33333... (a float). If you meant integer division, use //. Be explicit in your code about which one you want.
Mixing Decimal and float in the same calculation. Python will convert Decimal to float, losing precision. Keep Decimal separate or convert everything to Decimal before you start.
from decimal import Decimal
# Wrong
result = Decimal('100.50') + 0.1
# This converts Decimal to float and you lose precision
# Right
result = Decimal('100.50') + Decimal('0.1')
Rounding errors compounding in loops. If you calculate something in a loop and add the results, rounding errors stack. Use Decimal if you are accumulating financial numbers.
Not testing with real numbers. 0.1 + 0.2 is a toy example. Test your code with actual rates, notionals, and day counts from your business. Run a small reconciliation and check that the pennies balance.
What Comes Next
You now have a foundation in how Python handles numbers. This unlocks comparisons (is this rate above a threshold?), loops (process each transaction), and functions (write reusable calculation logic). The next steps are to apply these patterns to real workflows in your team: liquidity forecasting, interest accrual, regulatory reporting calculations.
You can test every pattern here interactively in a Jupyter notebook and see the results immediately. Our guides on getting started with notebooks and using them for finance work will help you set up a proper environment.
Start with integer and float. Understand them viscerally. Then reach for Decimal when money is on the line. That discipline is what separates scripts that happen to work from code you can put in production.

The Complete Python Course
Welcome to the most practical and beginner friendly Python Bootcamp Course on YouTube.
Take the courseGet the next one in your inbox
A weekly note across Finance & Treasury, Innovation & Automation and Career Development. No spam, unsubscribe any time.
Notes across finance and treasury, innovation and automation, and career development, written by practitioners who do the work.
