1. Overview
Live: https://bidefy.vercel.app. API: https://bidefy.iba-jobs.workers.dev/api/v1/health.
Bidefy is tender intelligence for Bangladesh's public procurement portal, e-GP. It indexes every public tender notice and contract award, resolves the messy names of bidders and procuring entities into stable identities, predicts the likely award value of a live tender, flags unusual award patterns with the numbers behind them, and sends web push alerts when a tender matching a subscriber's filters appears.
The gap it fills
Existing services sell daily notice alerts filtered by category, district and organisation, at roughly 1,050 taka a month. None of them use the 877,000 public contract awards on the same portal. So none can answer the questions a bidder actually has: who wins this kind of tender at this entity, at what value, and how often. Bidefy is that layer. It does not compete on alerts.
What is deliberately not built
- No WhatsApp or Telegram in version one. Web push only, because it costs nothing and needs no Meta account.
- No payments. The Pro tier shows a request-access form until there is a merchant account.
- No claim of fraud. Flags describe patterns with counts and intervals, never a verdict.
- No mirroring of raw notices. The product publishes derived intelligence.
Cost
Production cost is zero. GitHub Actions crawls and trains, Cloudflare Workers and D1 serve the site and send push, GitHub Pages hosts this document and the command centre.
Source facts
| Fact |
Value |
| Tender notices indexed |
about 625,800 |
| Contract awards indexed |
about 877,200 |
| Request rate |
one per second, one session, checkpointed |
| Data not published by the portal |
losing bids, bidder counts, official cost estimates |
The tender security amount, which each procuring entity sets as a share of its estimate, is the cost proxy for prediction.
2. Market and competition
Who pays for tender information today
Bangladesh's e-GP portal publishes every public tender notice and contract award without a login. Around it sits a small industry of alert services that re-sell the notices.
| Service |
What it sells |
Price |
| BDTender |
Daily notices by category, district and organisation over email and WhatsApp, plus e-GP training |
1,050 taka a month, 8,190 a year, 9,999 premium |
| AllTender |
Notices from newspapers, websites and e-GP |
Subscription |
| Global aggregators |
Bangladesh notices bundled into a world feed |
Foreign-currency subscriptions |
None of them use the contract award data. None can say who wins at a given entity, at what value, or how often. That is the layer Bidefy sells.
The buyers
- Contractors and suppliers bidding on multi-crore tenders. One better-priced bid pays for years of subscription. They already pay the alert services.
- Companies selling to government, including large groups with tender desks.
- Banks and lenders that finance contractors against awarded contracts and need a verified award history.
- Journalists and researchers, who use the free tier and spread the product.
Why not compete on alerts
The alert market has an anchor at about 1,050 taka a month and a lifetime-licence mentality. Bidefy gives the alert layer away and prices the intelligence layer against the value of one contract, not against a competitor's feature table.
What the portal does not publish
Losing bids, bidder counts and official cost estimates are not public. Bidefy predicts award value bands from the security amount, the entity's history and the category, and reports an interval with a deferral rate rather than a point.
3. The system
One pipeline, nightly
- Crawl. One request per second against the portal's two public list endpoints, 200 rows a page, checkpointed after every flush, resumed across nights until the index is complete, then a daily delta that stops at the first page with nothing new.
- Compact. Small Parquet parts merge into one file once twenty accumulate, so the repository stays a few hundred files rather than thousands.
- Resolve. Bidder names are normalised (prefixes such as M/S dropped, punctuation stripped, Bangla combining marks kept), blocked on their first long token, and compared with character n-gram TF-IDF. Pairs at or above 0.92 merge, pairs from 0.80 to 0.92 wait in a review file for a human decision, and every entity gets a stable id from its canonical name.
- Fetch details. Every open tender's detail page is fetched, one a second. It carries the portal's own category tags and the refundable tender security, which are the two most valuable fields on the whole portal and appear nowhere in the list pages.
- Classify and price. The category is read from those tags where present, and predicted only where it is not. The award band is built from the security where the notice publishes one, and from the entity's history otherwise. Both may decline.
- Load. The last twelve months and every live tender go to Cloudflare D1 in statements under 100 KB, budgeted in row writes rather than rows, because every index entry counts as one.
- Serve. A Worker answers the API from D1. The site on Vercel renders on the server from that API with short revalidation, so pages are fast and shareable.
- Alert. Every hour the Worker takes tenders newer than its watermark, matches them against each subscription's filters, sends web push through the Web Crypto push protocol, records what it sent so nothing repeats, and deletes endpoints that answer 404 or 410.
The deferral principle
A classifier that must answer every time is wrong more often than one allowed to say "not sure". Bidefy publishes accuracy only for the predictions it acts on, and publishes the deferral rate next to it, because a high accuracy with a hidden deferral rate is meaningless. The award-value model follows the same rule: a band with its measured coverage, never a single number, and a decline when the band would be too wide to act on.
How well the models work
Every current figure lives on its own page, What Bidefy gets right, and how often, which is rewritten by each training run so it cannot drift from the model it describes. The short version:
- An award band built from the tender security published in the notice lands within about 8 percent of the real award value. A band built from the entity's history alone is far looser.
- A tender's category is read from the portal's own tags for every live tender, so on the live site it is not a prediction at all. The classifier only covers the archive.
- Both models may decline, and the deferral rate is always printed beside the accuracy.
- The history band is two quantile models with a conformal pad, not one estimate stretched by a difficulty score. That change alone raised coverage and cut the share of tenders declined, without moving any threshold. What was tried, and what the measurements said has the before and after.
Two lessons worth keeping
A model must not be scored on a rule you wrote. The category model once trained partly on labels produced by a keyword rule inside Bidefy, and was then scored on a test set containing those same labels. It looked accurate. Measured against the portal's own tags it was not, and feeding it twenty thousand keyword labels drove real accuracy from 89.8 percent to 55.4 percent. The labels were removed.
Read the answer before predicting it. The portal publishes category tags on each tender's detail page, and a tender security that is a fixed share of the buyer's own cost estimate. Fetching those pages turned the category from a prediction into a lookup, and cut the award band from roughly eleven times wide to about one and a half. Most of the accuracy came from collecting better evidence, not from a better model.
Budgets that shape the design
| Budget |
Value |
Consequence |
| Portal politeness |
1 request per second, one session |
Full index takes an hour from Dhaka, several nights from a US runner |
| D1 writes |
100,000 a day, and every index entry counts as a write |
Budget kept in writes, not rows: a tender row costs five. Awards never go in as rows; bidder and entity profiles carry JSON aggregates. 80,000 writes a day across both runs, tracked in the watermark |
| D1 statement size |
about 100 KB |
Byte-aware batching, roughly 150 rows per statement |
| GitHub Actions |
6 hours per job |
300-minute crawl budget, checkpoint and resume |
| Push per subscription |
20 per hourly run |
A noisy filter cannot flood a phone |
What is not built, on purpose
No WhatsApp, no payments, no fraud verdicts, no mirror of raw notices, no losing-bid data because the portal does not publish it.
4. Five buyer profiles
These profiles are computed from public award data, not interviews. Each is a real bidder selected by a fixed rule; the rule is stated so the selection can be reproduced.
Known limit: bidders are resolved by name, so a common trade name can combine several firms. The generalist rule therefore requires a footprint of three districts or fewer. Award values above 500 crore were keyed in taka on the portal and are converted to crore; no genuine award in the index is that large.
The specialist: Md. Zinnat Hossain Biswas
- Rule: most awards at a single procuring entity (Faridpur Road Division, 115 awards).
- Awards indexed: 418, total value 120.89 crore
- Entities: 44, districts: 40
- Main categories: roads_bridges, buildings_civil
What Bidefy shows them: Every new tender from that entity within the hour, the entity's award history, and the value band before they price.
The generalist: A.K. International
- Rule: awards under 5 or more ministries, most awards among them.
- Awards indexed: 72, total value 4.98 crore
- Entities: 28, districts: 1
- Main categories: buildings_civil, electrical_power
What Bidefy shows them: Category filters across ministries and a single feed instead of five portals' worth of notices.
The infrastructure contractor: M. M. Builders & Engineers Ltd.
- Rule: highest total award value in the last twelve months (1208.37 crore across 8 awards).
- Awards indexed: 12, total value 1525.04 crore
- Entities: 8, districts: 7
- Main categories: roads_bridges, it_equipment
What Bidefy shows them: Who else wins at the entities they target, the concentration flags, and bands on multi-crore tenders where one mispriced bid costs more than a year of Pro.
The newcomer: Bangladesh Milk Producers' Co-operative Union Limited
- Rule: first indexed award within the last 90 days, most awards since (72).
- Awards indexed: 72, total value 4.02 crore
- Entities: 72, districts: 64
- Main categories: food_catering, it_equipment
What Bidefy shows them: Which entities award to newcomers, and the typical value at their size, so they bid where they can win.
The regional bidder: M/S G.M. INTERNATIONAL
- Rule: highest share of awards in one district (Dhaka, 100% of 97).
- Awards indexed: 97, total value 26.34 crore
- Entities: 6, districts: 1
- Main categories: electrical_power, buildings_civil
What Bidefy shows them: District-filtered alerts and the entities in their district ranked by volume.
Pricing signal: the first three profiles bid on tenders where a one percent pricing error exceeds 2,500 taka. The last two are free-tier users until they grow.
5. A primer on Bangladesh public procurement
Written for the owner before an interview, and for anyone reading Bidefy who has never bid on a tender. Everything here is what the site assumes you know.
What a tender is
A tender is how a public body buys without picking a favourite. The buying office, called the procuring entity, publishes what it needs, when bids close, and the rules. Firms submit sealed, priced offers. A committee opens them after closing, checks each bid is complete and eligible, and for most goods and works awards the contract to the lowest responsive price. The buyer then publishes a Notification of Award naming the winner and the value, and signs the contract.
In Bangladesh this runs on one portal, e-GP, owned by the Bangladesh Public Procurement Authority under the Public Procurement Act 2006 and Rules 2008. Around 90 percent of public purchasing by volume goes through it. Every notice, correction and award is public without a login. That is the fact Bidefy is built on.
How to read a tender notice
| Field on the notice |
What it means |
What Bidefy does with it |
| Tender id |
e-GP's number for the notice |
The key for everything |
| Reference number |
The buyer's own file number |
Shown for matching paperwork |
| Procuring entity |
The office buying and signing |
Profile page with award history and concentration |
| Ministry, division, organisation |
The layers above the entity |
Filters and the award model |
| Nature |
Goods, Works or Services |
Shown on cards |
| Method |
How bids are invited (see below) |
Tooltip, model feature |
| Type |
NCT for national, ICT for international bidders |
Tooltip |
| Publishing and closing |
The bidding window |
Days-left badge, alerts |
| Tender security |
Refundable deposit, a fixed share of the buyer's secret cost estimate |
Proxy for tender size in the award model |
| Document price |
Fee for the full document |
Shown on the detail page |
| Category tags |
The portal's own classification, only on the detail page |
Training labels for Bidefy's category classifier |
The procurement methods
- OTM, Open Tendering. Anyone eligible may bid. The default for anything sizeable. Most of what Bidefy indexes.
- LTM, Limited Tendering. Only enlisted firms are invited. Smaller or specialised buys.
- RFQ, Request for Quotation. Quick quotes for low-value items, short deadlines, thin paperwork.
- DPM, Direct Procurement. One supplier, no competition, allowed in defined cases such as emergencies or proprietary parts.
- OSTETM and TSTM. Two-envelope and two-stage variants for complex procurements where the technical offer is judged before the price is seen.
What the statuses mean
Live means bids are being accepted. Being processed means closed and under evaluation. Contract Awarded means a winner has signed. Cancelled and Rejected end the process; Re-Tendered means the buyer will try again, usually because too few valid bids arrived. Corrigendum means a published correction, often a new closing date; Bidefy shows that note separately from the status.
Money
Values are stated in crore. One crore is ten million taka, one lakh is a hundred thousand, so one crore is a hundred lakh. Bidefy shows amounts under one crore in lakh. The median award in the index is about eleven lakh; one in ten is above 1.2 crore. On the portal some values are keyed in taka by mistake; Bidefy converts anything above 500 crore, because no genuine award in the index is that large.
What the buyer never tells you
The buyer's cost estimate, the losing bids and the number of bidders are not published. That is why Bidefy predicts a band rather than reporting one, and why the tender security matters: it is the only public number tied to the estimate.
The five questions a bidder asks
- Is there a tender I should see today? Alerts by keyword, ministry, category, status.
- What has this entity paid for this kind of work? The band, and the entity's recent awards.
- Who usually wins here? The entity's top bidders and the share the leader holds.
- Is this entity worth my time? Concentration flags: if one firm has won 80 percent of the last twelve months, the odds are stated, not hidden.
- Who am I up against? Bidder profiles with award history, entities, districts and categories.
What Bidefy refuses to claim
A pattern is not a verdict. A concentrated entity may simply have one competent local supplier. Bidefy publishes shares and counts and lets the reader decide. The award model publishes its error and its deferral rate together, and declines when the band would be too wide to act on. The category classifier does the same.
Two sentences for an interview
Bidefy reads the public procurement portal every night, resolves 28,000 bidders from 210,000 awards, predicts what an entity will pay with a calibrated band, and alerts a bidder within the hour. It is free at the alert layer and priced at the intelligence layer, and it declines to answer when it cannot answer honestly.
6. What Bidefy gets right, and how often
Every figure here is measured on data the model was not trained on, and this page is written by the training run itself, so it cannot drift from the model it describes.
Two rules govern everything below. A model may decline, and when it declines that is reported next to its accuracy, because an accuracy figure without its deferral rate is not a measurement. And a prediction is only scored against evidence the model could not have seen: the portal's own records, never a rule Bidefy wrote.
Award value
The history route is trained on 546,033 awards, calibrated on 327,620 out-of-sample residuals, and scored on the 136,579 most recent awards, everything signed on or after 2025-06-24. Every award it is scored on is later in time than every award used to fit or calibrate it, so this is a forecast, not a fit.
A tender notice publishes a refundable tender security. Buyers set it as a fixed share of a cost estimate they do not publish, and awards land near that estimate, so where a security exists it pins the value far more tightly than history can.
The two routes are measured on two different windows, and are never averaged into one headline. The archive of awards reaches back years, but detail pages have only been fetched for roughly the last year, so every published security on record is recent. Split the whole archive by date and all of them land after the cut, leaving the security multiplier nothing to learn from. So that route is given its own split, at eighty percent of the securities by date, fitted on the earlier ones and scored on the later ones. Both windows are strictly forward-looking.
|
From the tender security |
From entity history |
| Awards scored |
423 |
105,039 |
| Fitted on |
1,673 earlier securities |
546,033 earlier awards |
| Scored on awards signed from |
2026-08-04 |
2025-06-24 |
| Median error of the central estimate |
8.7 percent |
37.9 percent |
| Share of awards inside the band |
79.7 percent |
78.6 percent |
| Typical band, high over low |
1.42x |
5.09x |
Of the 2,096 awards in the archive whose notice published a security, that is every one the route could be scored on without fitting and testing on the same rows.
What a bidder actually meets
Of the tenders open right now, 80.0 percent publish a security and take the precise route; the rest fall to history. The archive is a poor guide to that split, because its detail pages were mostly never fetched, so a security looks absent there when it was only uncollected. Weighting the two measured routes by the split the site actually serves:
| Route |
Share of open tenders |
Median error |
Typical band |
| From the tender security |
80.0 percent |
8.7 percent |
1.42x |
| From entity history |
20.0 percent |
37.9 percent |
5.09x |
Four tenders in five get the precise answer. That is a property of what the portal publishes, not of the model, and it is the single most valuable thing found in this project. Nothing here is an average of the two rows: each is measured on its own held-out window and reported as itself.
Taking the test window exactly as crawled, with whatever mix of routes it happens to contain, the median error is 37.0 percent and the typical band is 5.02x wide. For scale, the spread between the 10th and 90th percentile of all awards is 56x, which is the band someone would quote knowing nothing at all. Simply guessing the median award for every tender gives a median error of 79.5 percent.
The history route is not one number
Open tendering is the hardest method to price and the one the history route is mostly asked about, because a large open tender is exactly the kind that publishes no security. Quoting a single history figure would hide that, so here is each method on its own.
| Method |
Tenders answered |
Median error |
Inside the band |
Typical band |
Declined |
| LTM |
53,453 |
32.2 percent |
81.2 percent |
4.26x |
6.3 percent |
| OTM |
40,133 |
45.3 percent |
75.4 percent |
6.19x |
31.4 percent |
| RFQU |
6,132 |
43.0 percent |
78.9 percent |
6.26x |
30.1 percent |
| RFQ |
4,299 |
42.9 percent |
77.3 percent |
5.3x |
45.9 percent |
| DPM |
446 |
69.5 percent |
71.5 percent |
7.16x |
28.9 percent |
| RFQL |
403 |
46.0 percent |
86.4 percent |
6.55x |
53.1 percent |
| OSTETM |
129 |
57.0 percent |
67.4 percent |
9.31x |
76.3 percent |
Bidefy declines when a band would be too wide to act on. Where that line is drawn is a product decision, not a statistical one, so here is the whole trade:
| Widest band shown |
Tenders declined |
| 4x |
73.6 percent |
| 6x |
48.8 percent |
| 8x |
32.0 percent |
| 12x |
15.8 percent |
| 20x |
5.1 percent |
Category
A tender's category is read from the portal's own tags wherever Bidefy has fetched that tender's detail page. The model below exists only to cover tenders whose detail page has not been fetched, mostly older archived ones.
Of the 3,405 tenders open right now, 85.2 percent take their category straight from the portal. For those the category is not a prediction at all, and nothing is declined.
Trained and scored on 6,640 tenders across 15 categories, cross-validated on portal category tags only, entity priors from the training fold.
| Measure |
Value |
| Accuracy on the predictions it commits to |
93.0 percent |
| Share of tenders it declines |
44.7 percent |
| Accuracy if forced to answer every time |
75.9 percent |
| Macro F1 across categories |
0.639 |
| Deferral needed to reach 93 percent |
44.7 percent |
| Deferral needed to reach 95 percent |
52.2 percent |
This model is not deterministic. Run the same cross-validation again, on the same data with the same seed, and the share it declines moves by up to 0.5 percent and its macro F1 by 0.016. That is the floor below which a change to this model cannot be distinguished from chance, and it is published here because a figure quoted without it invites reading an improvement into noise. The numbers above pool 3 runs, which is why they are steadier than any one of them.
What would move these numbers
The award band is limited by what a notice says. The title carries the item but rarely the quantity, and the quantity lives in a tender document behind a fee. Fetching the detail page of every live tender is what unlocks the security route, and that is now part of the nightly run. The category model is limited by labelled examples, and every detail page fetched adds one.
An earlier version of the category model also trained on labels produced by a keyword rule Bidefy wrote. Removing them was worth doing: adding twenty thousand such rows had driven accuracy against the portal's real tags from 89.8 percent down to 55.4 percent, while making the published figure look better, because the model was partly being scored on the rule it had been taught to copy.
7. What was tried, and what the measurements said
A model is only trustworthy if the things that did not work are written down beside the things
that did. Every row below is an experiment run on the real data, with the evaluation population
held fixed so the comparison is about the change and nothing else.
Two rules were applied throughout. No result counts if it came from moving a confidence
threshold or a band-width cutoff, because that trades one published number for another without
the model getting better. And no result counts if the change altered which tenders were being
scored, because a model can always look better by being asked easier questions.
Award value
The award model answers with a band, and reports what share of real awards landed inside it.
Every comparison in this section was run on the same 210,544-award extract, so the arms differ only
by the change under test. The crawl has since more than tripled that archive, to 682,612 awards, and
the figures published on the accuracy page come from the full set. A number here will therefore not
match one there; what carries across is the direction and size of each change, not its level.
| Change |
Median error |
Inside the band |
Band width |
Declined |
Kept |
| Band scaled by a predicted difficulty |
33.9 percent |
77.1 percent |
4.60x |
27.9 percent |
replaced |
| Band calibrated per method and per size |
33.7 percent |
77.0 percent |
4.70x |
28.3 percent |
no |
| Band calibrated per size alone |
34.7 percent |
77.1 percent |
4.95x |
25.2 percent |
no |
| Two quantile models, conformalised |
34.8 percent |
79.6 percent |
4.89x |
21.4 percent |
yes |
Splitting the calibration by size did nothing. The idea was that a small purchase and a large
one are not equally predictable, so one shared band serves neither. Measured, it moved coverage by
a tenth of a point and cost width. Rejected.
Replacing the difficulty scale with two quantile models worked. The old band took one central
prediction and stretched it by a predicted difficulty, which assumes every tender's error has the
same shape, only wider or narrower. Real awards are not like that: a tender can have a firm floor
and a long tail above it. Fitting one model to the low edge and one to the high edge, then padding
both by a conformal margin, lets the two edges move independently.
It answered 31,891 of the test tenders instead of 29,107, and covered more of them. The median
error rose from 33.9 to 34.8 percent, and that rise is composition, not decay: the central estimate
comes from the same model in both, so the only reason the error moved is that 2,784 tenders it used
to decline are now answered, and those are the hard ones.
Giving the model the buyer's own past price level
The model already had the buyer as a category, so in principle it could learn what each one pays.
In practice it has to learn that level by level from whatever rows it happens to see, and most
buyers are rare. Handing it the running average of what this buyer has paid before, and what it has
paid before under this same procurement method, is the same evidence in a shape it can use. Only
awards signed earlier count, so no tender is ever informed by its own outcome.
|
Without |
With |
| Tenders answered by the history route |
31,891 |
32,136 |
| Median error |
37.0 percent |
36.6 percent |
| Inside the band |
79.4 percent |
79.4 percent |
| Typical band |
5.06x |
4.92x |
| Declined |
21.4 percent |
20.8 percent |
Every figure moves the right way at once, which is what separates a real effect from noise: a
coincidence moves some up and some down. It answers more tenders, with narrower bands, at the same
coverage. The drop in declines is about three times its own sampling error. Kept.
The tender document price is not a second security
A notice publishes two prices the buyer chose: the refundable security, and the fee to download the
tender document. The security is a fixed share of the buyer's cost estimate, which is why it pins the
award so tightly. The document fee looked like it might be a second such signal, and it is published
more often than the security is.
It is not. Across 2,418 awards that published one, the ratio of award to document fee runs from about
570 at the tenth percentile to 7,230 at the ninetieth, a spread of 12.7 times. The security's ratio
spans 1.5 times over the same range. Pricing an award straight off the document fee gives a median
error of 51 percent, worse than the history route it would have replaced. The fee is set on coarse
value slabs, so it says which bracket a tender is in and almost nothing more. Rejected.
Category
First, how much does this model move when nothing changes?
Everything below rests on this number, and it should have been measured first. Running the same
cross-validation three times, on the same data, with the same seed, gives these deferral rates:
| Run |
Declines |
Macro F1 |
| 1 |
44.41 percent |
0.658 |
| 2 |
45.06 percent |
0.649 |
| 3 |
46.11 percent |
0.642 |
A spread of 1.7 points of deferral and 1.6 points of macro F1, from nothing at all. The training is
not bit-reproducible, and the deferral threshold is chosen at a percentile of a confidence
distribution, so a difference in the last decimal place of a probability moves which tenders fall
either side of the line.
Pinning every maths library to a single thread was the obvious suspect, and it is only half the
story: single-threaded, the deferral rate repeats exactly, but macro F1 still moves 1.3 points
between two identical runs. Correctness is stable while the identity of the wrong answer is not, so
predictions on near-ties still shift, and macro F1 reads those shifts. Single-threading would cost
several times the training time and still leave macro F1 unstable, so the fix is to pool runs rather
than chase bit-reproducibility.
That is the floor. Any change to this model worth less than about two points is indistinguishable
from chance on a single run, and several comparisons made earlier in this project were inside it and
should not have been called results. The model now pools three runs for every published figure and
prints the spread beside them.
Training only on the decisive labels makes it much worse
The portal's tags are noisy, so the obvious move is to train only on labels that won by a clear
margin of keywords and keep scoring on all of them. Discarding the 23 percent of labels decided by a
single keyword against a close second:
|
Every label |
Decisive labels only |
| Declines at 93 percent accuracy |
44.4 percent |
53.3 percent |
| Macro F1 |
0.648 |
0.577 |
| Accuracy if forced to answer |
76.0 percent |
71.9 percent |
Nine points of deferral and seven of macro F1, far outside the noise floor. The labels that look
weakest still carry more signal than the rows are worth losing. Rejected, and worth remembering: the
first version of this experiment had a guard that silently refused to apply the filter at all, so it
produced three sets of numbers that differed only by the noise above and looked like a mild
improvement. The guard was found only because the arms should have been bit-identical and were not.
Does the buyer's own history still help? No longer measurably
A hospital does not buy bridges, so who is buying is evidence about what is being bought, and this
model multiplies its text probabilities by the buyer's own category history. That was worth several
points of deferral when the label set was smaller. With 6,640 labels, and each measurement pooling
three cross-validations:
|
Without the buyer's history |
With it |
| Declines at 93 percent accuracy |
45.1 percent |
44.8 percent |
| Macro F1 |
0.643 |
0.646 |
| Accuracy if forced to answer |
75.8 percent |
75.6 percent |
Three tenths of a point, against a run-to-run spread of eight tenths to nearly two points. The
honest reading is that the feature no longer earns its place on the evidence available, and that an
earlier version of this comparison, which put its value at nine tenths of a point, was reading noise.
It is kept because it costs nothing and was genuinely useful at a smaller label set, not because
this measurement supports it. Worth retesting when the label set doubles.
What limits this model is the label, not the model
The portal's category tags are the only honest ground truth available, and they are themselves
noisy. A tender carries a median of 27 tags, and where a tender's own description also resolves to a
category, it agrees with the tag-derived label only 50.3 percent of the time across 3,626 tenders.
That is the thing to fix. It does not make the reported accuracy wrong, since it is measured against
those tags either way, but it does mean a large share of the apparent mistakes are cases where the
tags and the tender's own words disagree, and no amount of modelling resolves that from the inside.
What the larger archive did to the headline
Retraining on 682,612 awards rather than 210,544 moved the security route's coverage from 83.9
percent to 79.7 percent, and left its median error and band width where they were, at 8.7 percent and
1.42 times. The coverage did not fall because the model got worse. It fell because the route is now
scored under a stricter rule.
Every published security in the archive is recent, since detail pages have only been fetched for
about the last year. Split the whole archive by date and all of them land after the cut, so the
multiplier has nothing to learn from and the route switches itself off entirely, which is what the
first full-archive run did: zero securities scored. The route now gets its own forward split, fitted
on 1,673 earlier securities and scored on the 423 signed later. Coverage of 79.7 percent against a
target of 80 is a band doing exactly what it promises. The earlier 83.9 percent was a band quietly
wider than it needed to be.
What an archived detail page is actually worth
The security route is so much better than the history route that the obvious move is to fetch detail
pages for tenders that have already been awarded, not just for open ones, so the model has more of
them to learn from. Only 5,794 of the 125,136 awards signed since September 2025 had been fetched.
The first 603 pages of that crawl say what the rest will cost. The portal no longer serves every
archived page in full:
| Out of 100 archived award pages fetched |
|
| Come back as a stub, with nothing on them |
41 |
| Come back complete |
59 |
| Carry category tags |
59 |
| Publish a security |
38 |
So roughly five requests buy three usable pages. An earlier estimate put the security rate at 85
percent, taken from open tenders, and open tenders are not a fair guide: they are current, and the
portal serves them whole. Against archived awards the rate is 38 per hundred fetched.
The lever is still much the largest available. Fetching the remaining 119,342 awards in the window
would add roughly 45,000 securities against the 2,096 on record, and roughly 70,000 category labels
against 6,640. It is a twentyfold increase in the evidence behind the precise route and a tenfold
increase in the category training set, and it needs no modelling at all. At one request a second it
is about 33 hours of crawling, which is why it now has an hour of every nightly run.
The two rejected ideas worth remembering
Keyword labels. Adding twenty thousand labels from a keyword rule raised the published accuracy
and destroyed the real one, from 89.8 percent to 55.4 percent against the portal's own tags, because
the model was partly being scored on the rule it had been taught to copy.
Richer text features and finer security multipliers. Both were tried, neither moved a number
outside its noise, and both were reverted rather than kept for the sake of having changed something.