Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/open-contracting/cardinal-rs/llms.txt

Use this file to discover all available pages before exploring further.

The percentage difference between the winning bid and the second-lowest valid bid is a low outlier.

Methodology

For each contracting process, the difference is calculated as:
(secondLowestValidBidAmount - winningBidAmount) / winningBidAmount
A contracting process is flagged if the difference is less than or equal to the lower fence of:
Q1 - 1.5(IQR)
Where:
  • Q1 is the first quartile (25th percentile) of all differences
  • IQR is the interquartile range (Q3 - Q1)
  • This is a standard statistical method for detecting outliers
Both the winner and second-lowest bidder are also flagged.
Example:In 25% (Q₁) of contracting processes in Atlantis, the second-lowest valid bid is at most 5% greater than the winning bid. In 75% (Q₃) of contracting processes, it is at most 7% greater. This yields a lower fence of 2% (0.02).In a contracting process, CollusionCorp won with a bid of 100,000,andProcureManipulatelostwiththesecondlowestbidof100,000, and ProcureManipulate lost with the second-lowest bid of 101,000. The difference is 1% (0.01). This is less than the lower fence of 2%, so the process is flagged.
Why is this a red flag?A colluding bidder can mimic competition by submitting a bid that is similar in price (but different in quality, for example) from its colluding partner. This creates the appearance of competition while ensuring the predetermined winner receives the contract.

Output

The indicator returns different values depending on the entity: For contracting processes: The difference value as a decimal (e.g., 0.01 for 1%) For tenderers: Always 0.0 (to indicate they are flagged) If the --map command-line flag is set, the Maps key contains:
  • ocid_tenderer_r024: The flagged tenderers for each flagged OCID

Configuration

All configuration is optional.
threshold
float
Override the calculated lower fence with a fixed threshold. When set, the statistical calculation is bypassed.
[R024]
threshold = 0.05
This would flag any contracting process where the difference is ≤ 5%.
See also: no_price_comparison_procurement_methods and price_comparison_procurement_methods in global configuration for excluding certain procurement types from price-based indicators.

Exclusions

A contracting process is excluded if:
  • An award’s status is pending or invalid
  • The winning bid is not the lowest bid (this indicator requires price-only award criteria)
  • There are multiple active awards (multiple winners) - see issue #14
  • A bid is submitted by multiple tenderers (joint ventures) - see issue #17
  • An award is made to multiple suppliers - see issue #17
Want to eliminate an exclusion? Please contribute to the linked GitHub issues.

Assumptions

This indicator assumes that the tenderer of the winning bid didn’t submit another valid bid.

Example Output

Input (OCDS JSONL with bids):
{"ocid": "ocds-213czf-1", "awards": [{"status": "active", "suppliers": [{"id": "W"}], "value": {"amount": 100000}}], "bids": {"details": [{"tenderers": [{"id": "W"}], "value": {"amount": 100000}, "status": "valid"}, {"tenderers": [{"id": "L"}], "value": {"amount": 101000}, "status": "valid"}]}}
Command:
ocdscardinal indicators --settings settings.ini --no-meta data.jsonl
Output:
{"OCID":{"ocds-213czf-1":{"R024":0.01}},"Tenderer":{"W":{"R024":0.0},"L":{"R024":0.0}}}

Implementation Details

From src/indicators/r024.rs:19-48: The indicator:
  1. During processing: Calculates the ratio for each contracting process and stores in second_lowest_bid_ratios
  2. During finalization:
    • Computes quartiles (Q1, Q3) and IQR from all ratios
    • Calculates lower fence using Tukey’s method
    • Flags processes where ratio ≤ lower fence
    • Also flags both the winner and second-lowest bidder
  3. Optimization: Skips flagging if Q1 ≤ 0 (would flag 75% of processes)
This is a relative indicator that requires sufficient data across multiple contracting processes to establish meaningful statistical thresholds. With small datasets, the lower fence may be unreliable.

Build docs developers (and LLMs) love