Stage 2 of 4, lesson 12 of 31. Improver
Measure drawdown and losing runs
Track a running balance through a list of trades and find the worst fall from a high and the longest losing run. It introduces keeping state inside a loop.
Risk warning: CFDs and spread bets are complex instruments and come with a high risk of losing money rapidly due to leverage. You can lose money on these products, and each provider publishes its own loss figure under FCA rules. You should consider whether you understand how they work and whether you can afford to take the high risk of losing your money. Automated and API trading can amplify losses through bugs, connectivity failures and runaway orders.
You need: The trade journal lesson. No broker account.
A final total hides how you got there. Two records with the same total can feel completely different to live through: one climbs steadily, the other falls a long way first.
Drawdown is the measure of that fall: the largest drop from a high point to a later low. The script walks through the trades in order, remembers the highest balance seen so far, and checks at every step how far below it the balance now sits.
This is the pattern called keeping state: a few variables that carry what you have learned so far from one pass of the loop to the next. Almost every measurement over a series of trades or prices is built this way.
Files for this lesson
- sample-trades.csv, sample data with invented values
The code
# Measure the worst losing run, not just the final total.
# Uses sample-trades.csv from the trade journal lesson. The trades are invented.
import csv
profits = []
with open("sample-trades.csv", newline="") as f:
for row in csv.DictReader(f):
entry, exit_price, size = float(row["entry"]), float(row["exit"]), float(row["size"])
points = exit_price - entry if row["direction"] == "BUY" else entry - exit_price
profits.append(points * size)
balance = 0.0
peak = 0.0
worst_drawdown = 0.0
longest_losing_run = 0
current_run = 0
for profit in profits:
balance += profit
peak = max(peak, balance) # the highest the balance has been so far
worst_drawdown = max(worst_drawdown, peak - balance) # how far below that peak we are now
current_run = current_run + 1 if profit < 0 else 0
longest_losing_run = max(longest_losing_run, current_run)
print("Final total:", round(balance, 2))
print("Highest point reached:", round(peak, 2))
print("Worst drop from a high:", round(worst_drawdown, 2))
print("Longest run of losing trades:", longest_losing_run)What you should see
Final total: 9.8 Highest point reached: 19.0 Worst drop from a high: 18.4 Longest run of losing trades: 3
Run with Python 3.12 on Windows against the sample files on this page. No network and no account.
Check yourself
Answer each question from the code above before you open it. The answers were written from the script on this page and what it printed.
The final total is 9.8 but the worst drop is 18.4. How can the drop be larger than the total?
The drop is measured from a high to a later low, not from the start. The balance reached 19.0 at its highest, and at some point it sat 18.4 below a high it had already made.
What sets current_run back to zero?
Any trade that is not a loss. The line adds one when profit is below zero and otherwise sets the run to 0, so a win or a flat trade ends the run.
Does the order of the trades in the file matter to this script?
Yes. The total would be the same in any order, but the peak, the drawdown and the losing run all depend on which trade came after which.
Exercise
Track the longest run of winning trades as well, with two new variables updated inside the loop, and print it as the last line.
What the changed script prints
Final total: 9.8 Highest point reached: 19.0 Worst drop from a high: 18.4 Longest run of losing trades: 3 Longest run of winning trades: 3
Captured from a run of the solution on . Run with Python 3.12 on Windows against the sample files on this page. No network and no account.
One solution
# Exercise solution: the lesson script with the one change the exercise asks for.
# Measure the worst losing run, not just the final total.
# Uses sample-trades.csv from the trade journal lesson. The trades are invented.
import csv
profits = []
with open("sample-trades.csv", newline="") as f:
for row in csv.DictReader(f):
entry, exit_price, size = float(row["entry"]), float(row["exit"]), float(row["size"])
points = exit_price - entry if row["direction"] == "BUY" else entry - exit_price
profits.append(points * size)
balance = 0.0
peak = 0.0
worst_drawdown = 0.0
longest_losing_run = 0
current_run = 0
win_run = 0
longest_winning_run = 0
for profit in profits:
balance += profit
peak = max(peak, balance) # the highest the balance has been so far
worst_drawdown = max(worst_drawdown, peak - balance) # how far below that peak we are now
current_run = current_run + 1 if profit < 0 else 0
longest_losing_run = max(longest_losing_run, current_run)
win_run = win_run + 1 if profit > 0 else 0
longest_winning_run = max(longest_winning_run, win_run)
print("Final total:", round(balance, 2))
print("Highest point reached:", round(peak, 2))
print("Worst drop from a high:", round(worst_drawdown, 2))
print("Longest run of losing trades:", longest_losing_run)
print("Longest run of winning trades:", longest_winning_run)Your progress
Your marks are kept in this browser only, in its local storage. They are not sent anywhere, there is no account, and they will not follow you to another browser or device. To remove them, use Clear my progress on the learning track page, or clear this site's data in your browser's settings. Marking needs JavaScript. With scripts off the track reads the same, without the ticks.
This browser refused to store anything for this page, so marks cannot be kept here.
Where this sits in the path
Stage 2 of 4: Working with data. Read prices and trades from files, get dates and money right, and turn a list of trades into a journal.
- Prices from a fileDone
- Dates and time zonesDone
- Read and write JSONDone
- Money with DecimalDone
- Build a trade journalDone
- Measure drawdownDoneYou are here
Repositories to read next
- microsoft/qlib49,155 stars, read . Qlib is an AI-oriented Quant investment platform that aims to use AI tech to empower Quant Research, from exploring ideas to implementing productions. Qlib supports diverse ML modeling paradigms, including supervised learning, market dynamics modeling, and RL, and is now equipped with https://github.com/microsoft/RD-Agent to automate R&D process.
- nautechsystems/nautilus_trader29,633 stars, read . Production-grade Rust-native trading engine with deterministic event-driven architecture
- QuantConnect/Lean21,864 stars, read . Lean Algorithmic Trading Engine by QuantConnect (Python, C#)
- AI4Finance-Foundation/FinRL16,555 stars, read . FinRLĀ®: Financial Reinforcement Learning.
- goldmansachs/gs-quant13,061 stars, read . Python toolkit for quantitative finance
- polakowo/vectorbt9,277 stars, read . The backtesting engine that gives you an unfair advantage. Run thousands of trading ideas before others finish one.