When I look at the result of a trading bot, knowing how much it earned is not enough for me. I also want to understand a more down-to-earth thing: at what price did it actually buy and sell? The order asked for one price. Did the exchange give the same price, a better one, or a worse one?
I checked this against backup copies of the databases of two of our live bots. I took the available history from 29 August to 22 October 2026. For this review I kept only limit orders — the ones that state a price boundary we agree to buy or sell at.
The number that came out was reassuring: not a single fill was worse than the order's recorded price. But the check did not end there. In the same history I also found orders that were never filled at all. Price and the fact of a trade turned out to be two different questions.
First, let's agree on what we're counting
The sample has 2,242 limit orders in total. Of these, 2,219 received at least a partial fill, and 23 were cancelled without any fill. That is the first split:
2,242 = 2,219 filled + 23 unfilled.
Now we look only at the first group. We compare the average fill price with each order's recorded price. Buying cheaper means better; selling higher also means better. The result:
- 1,759 orders filled exactly at the recorded price;
- 460 got an average price better than the recorded one;
- 0 got an average price worse than the recorded one.
2,219 = 1,759 at the order price + 460 better + 0 worse.
The two splits are not there for the beauty of the arithmetic. If you place only "460 better" next to "23 not filled," you get a striking picture, but the meaning is lost: the first are counted among orders that were filled, the second are not part of that group at all.
There is one more important detail. "Filled" does not mean "filled in full." Within our 2,219 there are 22 partially filled orders. They received part of the volume, and the rest was not filled. That is not the same case as a cancellation with no fill at all. When comparing prices, I used the average price of the part that was actually filled, rather than treating the whole order as completed.
Entries and exits gave different numbers
I looked separately at how the bots opened and increased positions, and how they exited them. First entries and follow-up add-on buys are combined into one group here. All the numbers below refer only to limit orders that were filled.
| What the bot did | Filled | At recorded price | Better | Worse |
|---|---|---|---|---|
| Entries and add-ons | 1,327 | 1,101 | 226 | 0 |
| Exits | 892 | 658 | 234 | 0 |
| Together | 2,219 | 1,759 | 460 | 0 |
On exits there were 234 better prices, on entries and add-ons 226, even though there are fewer exit orders. This is an observation, not an explanation. From this table I cannot say why it turned out this way: because of market movement, the chosen prices, or other fill conditions. Answering that would mean examining the specific orders and the situation around them.
And 234 against 226 certainly does not mean exits earned more money thanks to their fills. What is counted here is cases, not profit. Orders can differ in volume and in the size of the price improvement.
Why zero worse prices is no miracle
A limit order is fairly literal. For a purchase it says: "no higher than the stated price." For a sale: "no lower." If you compare the fill price with that order's actual limit, excluding fees, a fill worse than the limit does not satisfy its condition.
So our zero should not be presented as a special ability of the bot. It did not talk the exchange into waiving the rules of trading. We verified that the recorded fills in this sample were not worse than the orders' recorded prices.
But a condition on price is not a promise to fill the whole volume. You can get a fill at the limit or better, you can get only part, or you can get nothing. The mere existence of an order does not mean a trade took place.
Our small rehearsal of a mistake
While preparing a short post, I managed to mix two samples. The draft was about limit orders, but the count of better fills came in from a general tally: 516 instead of 460. The number was real, it just answered a different question. The general sample also included market orders; the check caught the mismatch before publication. In short, the post could be shortened — the check on it could not. In the final post the samples are already separated.
What the 23 cancellations mean
In the databases these 23 limit orders have a cancelled status and zero fills. That is all that can be said with confidence about their outcome within this count.
Calling them "23 lost profitable trades" would be an invention. We did not calculate what would have happened under a different decision. A cancelled order is not a loss, but it cannot be counted as filled either. Besides, an order is not the same as a whole position: one position can contain several orders.
I did not establish the cause of each cancellation here. Was there not enough counter-volume, did the price move, did another order appear afterwards, why was this particular one cancelled? The finished measurement has no verified answer for each case. I chose not to pick the most convenient explanation.
For me these 23 matter above all as part of the whole picture. If you show only the prices of fills that happened, you can quietly strike out the orders that never got there. Then everything told would be true, but the telling would stay incomplete.
Which price I compared against
This is the main boundary of the measurement. The comparison price is taken from the order's record in the database. It is not a separate market quote saved at the moment of sending, and not a promise that at that exact price, at that moment, the whole needed volume could have been bought or sold.
For limit orders, this comparison answers a clear question: how does the average fill price relate to the recorded price limit? But it does not measure all the possible costs of real trading.
I did not include market orders in this table. For them, comparing the fill with a recorded reference price does not by itself prove a gain relative to the market available at the time. So you cannot take the resulting zero, stretch it over all order types, and declare: "we have no slippage."
Fees also stayed outside the price comparison: the exact fee of each individual fill cannot be established from this data. A better price does not mean a free trade. And the money difference between two prices cannot simply be added to the bot's profit: that is not a calculation of how the whole trading would have gone under a different fill.
Finally, this is about two databases and the stated period. From this observation no rule follows for all bots, all exchanges, and all future orders. A database stores history, but it does not by itself prove that the trading regime stayed unchanged throughout.
What I took away from the check
I did not get proof of perfect execution, but a more precise description of our sample. Among filled limit orders, no case worse than the recorded price was found. Some got a better price. Cancellations with no fill and partially filled orders remain a separate matter.
Now, when I see a pretty number about fills, I want to ask three questions. Which price was the comparison against? Which orders went into the count? And where did the ones that were never filled go?
The answers do not make the result worse. They let you understand what exactly it shows. A good price matters. It just does not yet mean the order turned into a trade.
This is a review of a specific measurement, not a recommendation to make trades and not a promise of future results. The text and illustrations were prepared with the help of AI and verified against the measurement tables.


Top comments (3)
The recorded-price boundary also raises an amendment question. If a limit moved after submission, comparing every fill against the final order row could hide which limit applied when each fill arrived.
For the next check, I would keep each order revision and join fills to the active revision by exchange sequence, with execution IDs deduplicated. For the 22 partials, the sum of execution quantities should match cumulative filled quantity, while requested quantity minus that sum remains unfilled.
Does your backup keep the revision history, or only the latest order row? That would help separate a price check from an audit of the order lifecycle.
Good question. Our copy has one row per order, with cumulative fills and an average execution price, but no amendment history or individual fills.
One detail worth clarifying: we compared that average with
ft_price, a separate stored reference, not the changingpricefield. In the Freqtrade source we checked, routine updates leave that reference unchanged. Still, that doesn't establish which limit was active for every fill.So this was a comparison of stored order-level values, not a reconstruction of the order lifecycle. For that we'd need individual fills and the effective order amendments. Have you found an exchange that provides the full amendment history afterwards, or do you normally capture it live?
Nice that you kept the 23 cancelled orders in the count instead of dropping them. In my own backtest engine I once had take profits filled at the bar's close, and fixing fill bugs like that moved results by up to about 4%, so I've stopped trusting fill assumptions I haven't checked. Do you compare these live fills against what your backtest assumed for the same orders, or only against the order record?