Methodology
How the public
numbers are made
This page is the recipe. Every performance figure on TradeClaw comes out of the steps below, in order, from the same code that runs in production. Read it and you can rebuild the public track record from the raw trade data yourself.
Nothing here is rounded in our favor. Where a number has an unfavorable direction, that direction is shown too. No live results appear on this page: it explains the machine, and the track record shows what the machine produced.
01What an R-multiple is
An R-multiple states a trade result as a multiple of the risk that trade took, not as a raw percentage move. Risk is the distance from the entry price to the stop-loss: the amount the trade was set up to lose if it went wrong. One unit of that risk is 1R.
R = pnl% ÷ risk% risk% = |entry − stop| ÷ entry × 100
Worked example, plain arithmetic. Entry at 100, stop at 98: the risk is 2%. If price reaches 104, a 4% move, the result is 4 ÷ 2 = +2R. A clean stop-out lands near −1R.
R is used instead of raw percent because it makes trades with different stop distances comparable. A wide-stop trade and a tight-stop trade each risk the same 1R, so their outcomes can be averaged together honestly. A high win rate at small R can still lose money, and a low win rate at large R can still make it. R keeps that visible.
02Which trades count
There are two populations, and they are not the same. Keeping them separate is what stops the win rate and the equity curve from telling different stories.
Counted resolved is the win-rate population. A trade counts only if it has a real 24-hour outcome, meaning price actually reached the take-profit or the stop-loss. Excluded from this set are simulated rows, gate-blocked rows (the engine emitted a signal but its full-risk gate refused entry, for example because the spread was too wide or a news lockout was active), and auto-expired rows (the 24-hour window elapsed without hitting either target). A single predicate, isCountedResolved, enforces this everywhere, so the win rate, the equity curve, and the counts all use one definition.
Sized is the stricter equity population. A trade enters the curve only if it has a recorded stop-loss. Without a stop there is no defined risk, and without defined risk there is no position size, so the trade cannot be sized onto the curve. Older rows missing a stop still count toward the win rate, but not toward the equity curve or the R-statistics. The sized set is the subset that carries a stop.
One caveat the counts do not flatter away: the engine fires across many symbols and timeframes at once, and the counted set is the full stream. A real person filtering for only the strongest setups would execute a small fraction of these trades. The raw count describes the firehose, not a tradable account.
03What every trade is charged
Every sized trade is charged its real execution cost before it touches the curve. The charge is not a flat blended guess. It is set per asset class from the same cost model the engine records at signal time. Round-trip means one full trade: you pay the cost entering and pay it again exiting, so the charge is 2 times the sum of fee and slippage per side.
| Asset class | Matched symbols | Fee / side | Slippage / side | Round-trip charged | Funding / 8h (not charged) |
|---|---|---|---|---|---|
| Crypto perps | BTC, ETH, SOL, BNB, XRP, ADA, DOGE, DOT, LINK, AVAX, or any pair ending in USDT | 0.05% | 0.15% | 0.40% | 0.01% |
| Metals | XAU, XAG (gold and silver) | 0.00% | 0.05% | 0.10% | 0.00% |
| FX | Every other pair, the major and minor currencies | 0.00% | 0.02% | 0.04% | 0.00% |
Crypto costs the most to trade, roughly ten times the FX charge, because taker fees and slippage on perps are wider. That gap is a large part of the finding: the assets that move the most also cost the most to churn.
The model also defines perp funding, 0.01% of notional per 8 hours held, shown in the last column. It is not added to the per-trade charge, because when a signal fires the engine does not yet know how long the position will be held. Leaving funding out makes the recorded cost conservative: holding a crypto position through funding windows costs more than the curve deducts, not less. Each trade carries its own recorded cost. Only rows written before that field existed fall back to the model cost for their asset class.
04How the equity curve compounds
The curve starts from a fixed 10,000 and risks a fixed 1% of current equity on every trade. This is textbook fixed-fractional sizing. It replaced an earlier method that staked the whole bankroll per signal and produced unrunnable spikes followed by drawdowns no real account would survive.
At 1% risk, one trade moves equity by about its R-multiple times 1%, minus that trade cost in R. A +2R winner adds roughly 2%; a −1R loser removes roughly 1%, with cost on top of both.
equity change per trade ≈ ( capped R − cost R ) × 1% cost R = cost% ÷ risk%
For the money path only, each trade R is capped at 8R, the parameter named HARD_R_CAP. That cap sits just above the 99th percentile of the live absolute-R distribution, so it clips only the most extreme roughly 1% of trades: single-trade outliers that no one scales out of cleanly. It is deliberately not tighter. A 3R cap would remove the thin right tail the engine edge depends on and would report the result as more negative than it really was. The cap touches only the equity path. The R-statistics, average winning R and expectancy, always use the raw uncapped R, so the cap never flatters the engine quality.
The curve also reports its worst peak-to-trough drop, the maximum drawdown, because a positive endpoint can hide a deep hole in the middle of the run. The path is reported, not just the destination.
05Net expectancy and break-even win rate
Expectancy is the average result of one trade, measured in R. Gross expectancy weighs the average winning R and the average losing R by how often each happens. It measures engine quality before costs.
gross R = winRate × avgRWin + lossRate × avgRLoss net R = gross R − avgCostR
Net expectancy subtracts the real average cost in R. Net is the number that actually compounds the curve, and it can be negative even when gross is positive. That gap is the entire finding: the engine clears a small gross edge, the cost of trading is larger than that edge, and the curve falls even though the gross figure still looks fine.
The break-even win rate is the win rate that would make expectancy exactly zero, given the observed average win and loss sizes.
break-even win rate = −avgRLoss ÷ ( avgRWin − avgRLoss )
If the actual win rate sits above this line, the system has positive expectancy, even if that win rate is below 50%. If it sits below the line, it does not, even if it wins more often than it loses. The track record always shows the actual win rate next to this break-even line, so above or below is explicit rather than implied.
06Where the data lives
A finding is only as good as the data under it, so all of it is public and machine-readable. Every figure above can be recomputed from these endpoints and files.
- /api/research/cost-field
The raw per-trade dataset. One entry per sized trade: timestamp, gross R, cost R, and asset class. Net R per trade is gross R minus cost R, with nothing aggregated away.
- /api/signals/equity
The compounded equity curve and every summary figure described on this page: win rate, gross and net expectancy, break-even win rate, average cost, drawdown.
- /track-record
The rendered results, shown with sample size and date range next to every number.
Source code for each step
- Equity route: sizing, cost deduction, expectancyapps/web/app/api/signals/equity/route.ts
- Signal history: which trades countapps/web/lib/signal-history.ts
- Cost model: the per-asset constantspackages/strategies/src/backtest-options.ts
- Cost-field route: the per-trade datasetapps/web/app/api/research/cost-field/route.ts
Every public number here obeys a written honesty contract: a provenance label, the sample size and window it covers, the cost disclosed next to the figure, and the win rate shown against its break-even line. You can read the contract in full on GitHub.