Code is read far more than it is written
A trading codebase lives for years and is changed by people who did not write it, often under pressure and often by you, six months later. Clean code is code that can be understood quickly and changed safely. It is not about style rules; it is about making a wrong change visible. A function that is badly named, does three things and hides a limit inside a formula is where the expensive bugs live, such as a risk limit nobody remembered was hard-coded.
# The same calculation, written two ways
def f(d, e, m, w):
r = []
if sum([abs(x) for x in d.values()]) / e > m:
r.append("LEVERAGE")
for k in sorted(d):
if abs(d[k]) / sum([abs(x) for x in d.values()]) > w:
r.append("CONCENTRATION:" + k)
return r
book = {"AAPL": 80_000.0, "MSFT": -10_000.0, "NVDA": 10_000.0}
print(f(book, 50_000.0, 1.5, 0.5)) # works, but what is d? e? m? w?Names, small functions, no hidden numbers
Three habits do most of the work. Name things for what they are: gross_exposure instead of f, max_leverage instead of m. Keep functions small and single-purpose: each does one job you can name in a few words, so it can be tested alone and reused, and a bigger function is built by calling them. Do not bury numbers: a limit like 1.5 typed inside a formula is a magic number nobody can find or change safely; pass it in as an argument (or name it as a constant). Computing the gross exposure once in its own function also removes the repeated expression.
# Small functions with names that say what they return; the limits come in as arguments
def gross_exposure(positions):
return sum(abs(value) for value in positions.values())
def leverage(positions, equity):
return gross_exposure(positions) / equity
def risk_flags(positions, equity, max_leverage, max_weight):
flags = []
if leverage(positions, equity) > max_leverage:
flags.append("LEVERAGE")
gross = gross_exposure(positions)
for symbol in sorted(positions):
if abs(positions[symbol]) / gross > max_weight:
flags.append(f"CONCENTRATION:{symbol}")
return flags
book = {"AAPL": 80_000.0, "MSFT": -10_000.0, "NVDA": 10_000.0}
print(risk_flags(book, 50_000.0, 1.5, 0.5)) # ['LEVERAGE', 'CONCENTRATION:AAPL']
print(risk_flags(book, 50_000.0, 3.0, 0.9)) # [] the same code, looser limitsGuard the edges and say why
Handle the bad input first and return or raise at once (a guard clause), so the main path reads straight down without nesting. A leverage of gross / equity with zero equity should be a clear ValueError, not a ZeroDivisionError from deep inside, and an empty book should give 0.0 rather than a crash. Comments should explain why, which the code cannot say (# the exchange rounds to the cent), not repeat what the code already shows. Docstrings state what a function takes and returns. Finally, prefer pure functions, which depend only on their arguments and have no side effects: they are the ones you can test with a handful of inputs.
# A guard clause stops bad input at the door and says exactly what is wrong
def leverage(positions, equity):
if equity <= 0:
raise ValueError("equity must be positive")
return sum(abs(value) for value in positions.values()) / equity
print(leverage({"AAPL": 60_000.0, "MSFT": -30_000.0}, 45_000.0)) # 2.0
print(leverage({}, 45_000.0)) # 0.0 an empty book is fine
try:
leverage({"AAPL": 60_000.0}, 0.0)
except ValueError as error:
print("rejected:", error) # rejected: equity must be positive# A pure function answers the same way every time; hidden state does not
history = []
def running_notional(price, quantity): # remembers earlier calls: the answer depends on the past
history.append(price * quantity)
return sum(history)
def notional(price, quantity): # depends only on its arguments
return price * quantity
print(running_notional(10.0, 5), running_notional(10.0, 5)) # 50.0 100.0 same call, different answers
print(notional(10.0, 5), notional(10.0, 5)) # 50.0 50.0 always the same- Name things for what they are, keep functions small and single-purpose, and build bigger ones by calling them.
- No magic numbers: limits come in as arguments or named constants; compute a repeated value once.
- Guard bad input first with a clear error, comment the why not the what, and prefer pure functions you can test.
Next up: type hints and static checking, letting tools catch mistakes before the code runs.
Lab · what's graded
- Net the book with
net_exposure25 pts - Add up every position with
gross_exposure25 pts - Guard the denominator in
leverage25 pts - Compose the checks in
risk_flags25 pts