Watch the video above, then read on for the detail and worked examples you can use straight away in your finance code.
Why string formatting matters in finance work
You build reports, alerts, and data exports every week. They all need readable output: clean columns, consistent decimal places, proper thousands separators. String formatting is how you get there without writing messy code or doing the formatting work by hand.
The format method is the bridge between basic string building (which gets unwieldy fast) and professional output that your colleagues can trust. It keeps your code lean, readable, and easy to maintain when requirements change, which they always do.
What the format method does: a simple comparison
Start with the problem. You have data: a rate of 2.45%, a balance of £1,234,567.89, a counterparty name. You need to put them into a sentence that reads cleanly.
Concatenation gets awkward:
rate = 2.45
balance = 1234567.89
counterparty = "JP Morgan"
# Concatenation: verbose and hard to read
message = "Rate for " + counterparty + " is " + str(rate) + "% on balance of £" + str(balance)
print(message)
The format method does the same job more clearly.
rate = 2.45
balance = 1234567.89
counterparty = "JP Morgan"
# format method: cleaner and more readable
message = "Rate for {} is {}% on balance of £{}".format(counterparty, rate, balance)
print(message)
Both produce the same output. But the format method reads like a template. You can see the shape of the sentence without parsing plus signs and type conversions. And as you will soon see, format does much more: it controls how numbers appear, which matters for finance work.
Positional placeholders: the basics
The curly braces {} in your string are placeholders. When you call format, you pass values in order, and they fill the placeholders left to right.
currency = "GBP"
amount = 500000
tenor = "3M"
message = "Deposit of {} {} at {}".format(amount, currency, tenor)
print(message)
# Output: Deposit of 500000 GBP at 3M
You can also use index numbers if you need to reuse a value or change the order:
currency = "GBP"
amount = 500000
tenor = "3M"
message = "{0} {1} deposit for {2}. Amount: {0} {1}".format(amount, currency, tenor)
print(message)
# Output: 500000 GBP deposit for 3M. Amount: 500000 GBP
The index {0}, {1}, {2} refers to the position of arguments in format. This is useful when you repeat a value or need a different order.
Named placeholders: templates for repeating reports
This is where format becomes powerful for finance work. Instead of positional indices, you use names inside the braces:
ftp_data = {
"desk": "Funding",
"rate": 1.85,
"tenor": "1Y",
"volume": 50000000,
"currency": "GBP"
}
template = "FTP Pricing: {desk} desk, {tenor} {currency} {volume} at {rate}%"
message = template.format(**ftp_data)
print(message)
# Output: FTP Pricing: Funding desk, 1Y GBP 50000000 at 1.85%
The **ftp_data unpacks the dictionary into named arguments that match the placeholder names. Now your template is self documenting. Anyone reading it can see what each placeholder needs. And when your FTP pricing template needs updating (because the format team just changed the spec), you change one line, not fifty.
Here is a real example: a daily liquidity summary that you send to multiple desks:
desks = [
{"name": "Funding", "lcr": 135.2, "nsfr": 118.5, "target_lcr": 100},
{"name": "Markets", "lcr": 142.1, "nsfr": 125.3, "target_lcr": 100},
{"name": "Commercial", "lcr": 128.9, "nsfr": 115.2, "target_lcr": 100},
]
template = "{name}: LCR {lcr}% (target {target_lcr}%), NSFR {nsfr}%"
for desk in desks:
print(template.format(**desk))
# Output:
# Funding: LCR 135.2% (target 100%), NSFR 118.5%
# Markets: LCR 142.1% (target 100%), NSFR 125.3%
# Commercial: LCR 128.9% (target 100%), NSFR 115.2%
A weekly note on treasury, liquidity and practical Python. No spam, unsubscribe any time.
Same template, different data. That is the real value of named placeholders.
Named placeholders also protect you from argument order bugs. If you add a new field to your data dictionary, the template stays correct. With positional placeholders, one reordering breaks your output.
Number formatting: decimals, thousands separators, and percentages
Finance is about numbers, and numbers need consistent presentation. Format has built in formatting options.
The syntax is {placeholder:format_spec}. Here are the most useful ones:
balance = 1234567.891234
rate = 0.0245
utilisation = 0.856
# Fixed decimal places
print("Balance: £{:,.2f}".format(balance))
# Output: Balance: £1,234,567.89
# Percentage (automatically multiplies by 100)
print("Rate: {:.2%}".format(rate))
# Output: Rate: 2.45%
# Thousands separator with decimals
print("Utilisation: {:.1%}".format(utilisation))
# Output: Utilisation: 85.6%
# Integer with comma separator
print("Notional: £{:,}".format(int(balance)))
# Output: Notional: £1,234,567
Named placeholder with format specs:
trade = {
"notional": 5000000,
"spot_rate": 1.08732,
"forward_rate": 1.09145,
}
template = "FX forward: {notional:,} USD at {forward_rate:.5f} (spot {spot_rate:.5f})"
print(template.format(**trade))
# Output: FX forward: 5,000,000 USD at 1.09145 (spot 1.08732)
Format spec codes:
,for thousands separator.Nffor N decimal places (fixed point).N%for N decimal places as a percentage.Nefor scientific notation>N,<N,^Nfor right, left, or centre alignment in N characters
Real finance examples: LCR reporting, FTP pricing, daily liquidity
Here is an LCR summary for a regulatory report header:
lcr_snapshot = {
"date": "2025-01-17",
"lcr_ratio": 142.35,
"stock_hqla": 8950000000,
"net_outflows": 6290000000,
}
template = """
=== LCR Report ===
Date: {date}
LCR Ratio: {lcr_ratio:.2f}%
Stock of HQLA (High Quality Liquid Assets): £{stock_hqla:,.0f}
Net Cash Outflows: £{net_outflows:,.0f}
"""
print(template.format(**lcr_snapshot))
An FTP pricing report for internal distribution:
pricing = [
{"tenor": "1W", "rate": 4.75, "volume": 125000000, "status": "active"},
{"tenor": "1M", "rate": 4.72, "volume": 250000000, "status": "active"},
{"tenor": "3M", "rate": 4.68, "volume": 500000000, "status": "active"},
{"tenor": "1Y", "rate": 4.55, "volume": 300000000, "status": "active"},
]
print("{:<5} {:<8} {:<15} {:<10}".format("Tenor", "Rate", "Volume (£m)", "Status"))
print("-" * 40)
template = "{tenor:<5} {rate:>7.2f}% {volume:>14,.0f} {status:<10}"
for rate_row in pricing:
print(template.format(**rate_row))
# Output:
# Tenor Rate Volume (£m) Status
# ----------------------------------------
# 1W 4.75% 125,000,000 active
# 1M 4.72% 250,000,000 active
# 3M 4.68% 500,000,000 active
# 1Y 4.55% 300,000,000 active
The alignment codes in the template above (<5, >7, >14) control column width and direction. < aligns left, > aligns right, and the number is the column width in characters. This ensures your columns stay neat regardless of data length.
A daily liquidity alert:
alert = {
"desk": "Funding",
"current_lcr": 128.5,
"minimum_lcr": 100.0,
"buffer_pct": 28.5,
"time": "08:15",
}
template = "[{time}] {desk} desk - LCR {current_lcr:.1f}% (minimum {minimum_lcr:.1f}%, buffer {buffer_pct:.1f}%)"
print(template.format(**alert))
# Output: [08:15] Funding desk - LCR 128.5% (minimum 100.0%, buffer 28.5%)
The time field matters here: it tells your trading desk when the snapshot was taken, so they know whether the position has moved since the last check.
When to use format over concatenation
Use format when:
- You have more than two or three values to insert (concatenation gets unreadable fast).
- You need to format numbers: decimals, thousands separators, percentages, or alignment.
- You are building the same message shape repeatedly with different data (use named placeholders and reuse the template).
- Your output spans multiple lines.
- You want your code to be maintainable by other people (or by you in six months).
Concatenation is acceptable for single line logging statements where you are just recording a quick event. For anything else, use format.
Where to go from here
Format is the standard way to build text in Python. Once you are comfortable with it, you will write cleaner, faster code. Your reports will be more consistent and easier to maintain.
Next, explore f strings (formatted string literals, introduced in Python 3.6). They are similar to format but more concise. Compare the same FTP output with an f string:
# format method
template = "FTP: {tenor} at {rate:.2f}%"
message = template.format(tenor="1Y", rate=4.55)
# f string
tenor = "1Y"
rate = 4.55
message = f"FTP: {tenor} at {rate:.2f}%"
F strings are now the standard for new code, but format is still widely used and more portable if you work in older codebases.
In the meantime, practise on your current reports. Pick one template you generate by hand or with concatenation, and rewrite it using format with named placeholders. You will see the difference immediately.
If you are building Python skills for finance, keep practising. These are the patterns you will use every day in a treasury or risk team.

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.
