Most traders can tell you their win rate. Very few can tell you how much they lose, on average, when they lose. That second number decides whether an account grows or bleeds, and you can compute it from a plain export of your trades.
Here is the small script I use to audit a trading log. It needs pandas and a CSV with one row per closed trade.
The data
close_time,symbol,side,pnl,risk
2026-09-01 14:32,XAUUSD,buy,180,100
2026-09-01 16:05,XAUUSD,sell,-100,100
2026-09-02 15:10,US30,buy,-230,100
2026-09-02 15:41,US30,buy,-100,100
2026-09-03 09:20,EURUSD,sell,95,100
2026-09-03 14:50,XAUUSD,buy,-100,100
2026-09-04 15:02,US30,sell,310,100
2026-09-04 15:30,US30,sell,-100,100
2026-09-07 14:45,XAUUSD,buy,-100,
2026-09-07 15:12,XAUUSD,buy,-340,
2026-09-08 10:05,EURUSD,buy,60,100
2026-09-08 15:20,US30,buy,-100,100
pnl is the result of the trade in account currency. risk is what you would have lost if the stop had been hit, also in account currency. Leave it empty when a trade had no stop: that gap is information too.
The script
import pandas as pd
trades = pd.read_csv("trades.csv", parse_dates=["close_time"]).sort_values("close_time")
# 1. Trades with no planned risk (no stop loss) cannot be measured in R
no_stop = trades["risk"].isna()
print(f"Trades without a stop: {no_stop.sum()} of {len(trades)}, "
f"P&L {trades.loc[no_stop, 'pnl'].sum():+.0f}")
# 2. R-multiple: what each trade made or lost, in units of what you planned to risk
measured = trades[~no_stop].copy()
measured["r"] = measured["pnl"] / measured["risk"]
wins = measured[measured["r"] > 0]
losses = measured[measured["r"] <= 0]
print(f"Win rate: {len(wins) / len(measured):.0%}")
print(f"Average win: {wins['r'].mean():+.2f} R")
print(f"Average loss: {losses['r'].mean():+.2f} R")
print(f"Expectancy: {measured['r'].mean():+.2f} R per trade")
# 3. Losses bigger than planned: the stop was moved, widened or ignored
overshoot = losses[losses["r"] < -1]
print(f"Losses beyond -1R: {len(overshoot)}")
# 4. Max drawdown on the equity curve, all trades included
equity = trades["pnl"].cumsum()
drawdown = equity - equity.cummax()
print(f"Max drawdown: {drawdown.min():+.0f}")
# 5. What happens right after a loss
trades["after_loss"] = trades["pnl"].shift(1) < 0
after = trades[trades["after_loss"]]
print(f"Trades taken right after a loss: {len(after)}, P&L {after['pnl'].sum():+.0f}")
What it prints
Trades without a stop: 2 of 12, P&L -440
Win rate: 40%
Average win: +1.61 R
Average loss: -1.22 R
Expectancy: -0.09 R per trade
Losses beyond -1R: 1
Max drawdown: -705
Trades taken right after a loss: 7, P&L -305
How to read it
The win rate is not the problem. 40% sounds bad, but the average win is 1.61R. With losses held at exactly -1R, this log would have an expectancy of about +0.05R per trade: small, but positive.
The losses are the problem. The average loss is -1.22R, not -1R. One trade lost 2.3 times what was planned. That usually means a stop was moved further away "to give the trade room", and that single habit is enough to turn the expectancy negative: -0.09R per trade.
The trades without a stop cost more than anything else. Two trades, -440, more than four planned losses put together. They are invisible in R, which is exactly why they need their own line in the report.
Look at what happens after a loss. Seven of the twelve trades were opened right after a losing one, for a total of -305. If that pattern holds on a bigger sample, the fix is a rule (a pause after a loss), not a new indicator.
Drawdown is measured on everything. The R statistics ignore the trades without a stop, but the equity curve does not. The -705 is what the account actually went through.
Why R and not dollars
R puts every trade on the same scale, whatever the position size or the instrument. A +$300 trade on gold and a +$60 trade on EUR/USD can both be +1R. Once everything is in R, you can compare setups, sessions and weekdays fairly, and you can see when risk discipline slips, because the losses stop being equal to -1.
To go further, group the same calculations by symbol, by hour of the day or by weekday. Three lines of groupby usually say more about a trading week than any chart.
I got tired of doing this by hand every weekend, so I built Tradoshi, a trading journal that computes these numbers on every trade. Its free plan imports your trade history from a file, and the paid plans connect read-only to MetaTrader, cTrader, NinjaTrader or Interactive Brokers. If you would rather keep a CSV and a script, the code above is all you need.
Trading involves a significant risk of capital loss. Nothing in this post is investment advice.













