Rules Reference
Read from live code constants · Year base: 2026Vehicle-type matrix
Applied in auction_service.upsert_listing. Any lot failing these rules is dropped before a Vehicle row is created.
| Code | Category | Max age | Earliest year accepted | Statutory rule | Extra |
|---|---|---|---|---|---|
| C | Cars | 20 yrs | 2007+ | allowed only if ≤ 5 years old | Electric cars (EV/BEV/PHEV): only if < 5 years old |
| M | Motorcycles | 5 yrs | 2022+ | excluded entirely | |
| H | Heavy (trucks, buses, earthmoving, plant, machinery) | 15 yrs | 2012+ | allowed only if ≤ 5 years old | |
| R | Caravans / RVs / Motorhomes | 10 yrs | 2017+ | excluded entirely | |
| L | Trailers | 10 yrs | 2017+ | excluded entirely | |
| O | Boats / Watercraft | 10 yrs | 2017+ | excluded entirely |
How lots get classified
Keyword match (first match wins) from vehicle_service._VEHICLE_TYPE_MAP.
Anything not matching defaults to C (Car).
M — Motorcycles
H — Heavy (trucks, buses, earthmoving, plant, machinery)
R — Caravans / RVs / Motorhomes
O — Boats / Watercraft
L — Trailers
Auction source capture rules
Pickles
- Scrapes both salvage and non-salvage feeds daily
- Timed sales: monitor starts 10 min before first lot close
- Live (Simulcast): WebSocket capture via pickles_watcher.py
- Fees: salvage / non-salvage / industrial tables per lot
Manheim
- Live fetch on session page view — lots upserted to DB with refs
- All 3 path_roots supported (each uses a different HTML selector)
- No native bidding API — data comes from HTML cards + refreshmaxbid
- Fees: salvage / non-salvage / industrial tables
IAAI
- Authenticated via Incapsula cookies + session_fetcher.py
- Live monitor uses WebSocket
- Fees: salvage only (AU ops)
Grays
- Two scrapers: GraysScraper (salvage) + GraysNonSalvageScraper (NS auto)
- Cross-source dedup at upsert: same physical lot can appear in both grays + grays_ns; only one row kept
- Bid-history API is public; returns one record per unique bidder (their highest bid)
- GGG (Going-Going-Gone) anti-snipe: every bid in last 10 min extends close by 10 min
- Bid monitor polls every 60s; 30-min grace post-close to catch GGG triggers
- Post-close finalizer marks status=sold/passed and sets sold_price within ~5 min of GGG ending
- Fees: variable buyer's premium + flat $49.50 admin (Industrial admin: $5,000+ only)
Market price sources
| Source | Role | Used for |
|---|---|---|
| Carsales (dealer) | Tier 1 ceiling | Upper-bound reference for vehicles year ≥ current_year − 2 only |
| Carsales (private) | Tier 2 sell price | Primary exit-price signal for profit calcs |
| Facebook Marketplace | Tier 2 | Supplements Carsales private |
| Non-salvage auction results | Tier 2 | Clean-title hammer prices private-market reference |
| Salvage auction hammer (Pickles/Manheim/IAAI) | Tier 3 | Raw salvage comps |
Seller-type classification
- Anything under 500 km forced to 'dealer' regardless of Carsales adtype (demonstrator / near-new)
- Carsales 'dealer' listings: used as Tier 1 ceiling for year ≥ current_year − 2
- Carsales 'private' listings: Tier 2 — the realistic sell price
- Unknown seller_type not used for scoring (avoids mislabelled demo stock)
Clean-title premium
WOVR'd vehicles sell ~7.5% below no-WOVR counterparts of same year/make/model/km — discount applied when comparing salvage hammer to retail market
WOVR / title tiers (resale ordering)
| Tier | Description |
|---|---|
| No WOVR (clean title) | Best — full resale value, re-registerable anywhere |
| Inspection-passed repairable | Passed roadworthy / structural inspection, buyer-aware |
| Repairable write-off | Repair + inspect + re-register for road use |
| Statutory write-off | No road use in most AU states — parts / scrap / export only |
Damage rules
- Fire / burn / severe fire damage — excluded across ALL sources and types
- Flood / water damage — KEPT (harvestable for parts, per owner rule)
- Hail, impact, collision, mechanical — all kept
Non-vehicle keyword blocklist · 240 terms
From auction_service.NON_VEHICLE_KEYWORDS. Any lot whose title or body contains one of these substrings (case-insensitive) is dropped at upsert AND during Manheim's live fetch.
Buyer-fee schedules (GST-inclusive)
Defined in app/config.py. Categories auto-detected by body/title keywords in detect_fee_category(). Fees apply per lot on top of the hammer.
| < $100 | $33 flat |
| $100 – $500 | $130 flat |
| $500 – $1,000 | $235 flat |
| ≥ $1,000 | 15% of hammer + $140 |
| < $2,000 | $525 flat |
| ≥ $2,000 | 1.65% of hammer + $650 |
| ≤ $5,000 | 22.5% of hammer |
| $5,000 – $40,000 | 16.5% of hammer |
| $40,000 – $55,000 | 12.5% of hammer |
| > $55,000 | 9.9% of hammer |
| ≤ $200 | $121 flat |
| $200 – $500 | $148 flat |
| $500 – $1,000 | $210 flat |
| > $1,000 | 15% of hammer + $88 |
| ≤ $2,000 | $500 flat |
| > $2,000 | 1.5% of hammer + $650 |
| ≤ $5,000 | 22.5% of hammer (min $75) |
| $5,000 – $35,000 | 16.5% of hammer |
| $35,000 – $50,000 | 12.5% of hammer |
| > $50,000 | 9.9% of hammer |
| < $100 | $33 flat |
| $100 – $500 | $125 flat |
| $500 – $1,000 | $230 flat |
| ≥ $1,000 | 14.95% of hammer + $135 |
| $0 – $2,000 | $495 flat |
| $2,001 – $5,000 | $650 flat |
| $5,001 – $10,000 | $710 flat |
| $10,001 – $30,000 | 7% of hammer |
| $30,001 – $40,000 | 6% of hammer |
| > $40,000 | 5% of hammer |
| Auto / Non-salvage / Boats / Caravans | $49.50 inc GST per lot |
| Industrial ($5,000+) | $45 ex GST = $49.50 inc GST |
| Industrial (< $5,000) | no admin fee |
Industrial category auto-trigger
Industrial fee table auto-applies when body/title contains any of:
tractor, excavator, boom lift, wheel loader, forklift, bobcat, grader, bulldozer, skid steer, telehandler, scissor lift, crane, dozer, cherry picker, backhoe, earthmoving, heavy equipment, plant equipment
Manheim payment-processing surcharge
Manheim applies a payment-processing surcharge on the Invoice Portal (cash/cheque not accepted):
• Domestic card — 0.85% + $0.30 · International card — 3.45% + $0.30
• PayPal (domestic) — 0.95% + $0.30 · PayPal (international) — 3.55% + $0.30
Deal Finder scoring
Every active listing gets a 0–100 score via _build_deal_scores(). Statutory write-offs go down the parts track; everything else goes down the resale track. Total = sum of the rows below.
Resale score (non-statutory) · 100 pts
| Component | Max |
|---|---|
|
Damage quality
Multiplier from damage-weight map × 25
|
up to 25 pts |
|
WOVR + PPSR status
No WOVR: 35 · Inspection-passed: 29 · Repairable: 22. Stolen −10, encumbered −5.
|
up to 35 pts |
|
Discount vs market
= 1 − (hammer ÷ effective market). ≥70% gap: 30 · 50–70%: 22–30 · 30–50%: 12–22 · <30%: scaled by discount. Effective market = Tier-2 private × clean-title multiplier (7.5% off if WOVR'd).
|
up to 30 pts |
|
Odometer
ratio factor 1.15 10 pts · 1.0 5 pts · 0.5 0 pts
|
up to 10 pts |
|
Peer scarcity
1 listing: 5 · ≤3: 3 · ≤8: 1 · >8: 0
|
up to 5 pts |
Parts score (statutory) · 100 pts
| Component | Max |
|---|---|
|
Parts damage area
Inverted damage map — rear damage = pristine front parts
|
up to 35 pts |
|
Odometer / condition
Same factor formula, scaled to 25
|
up to 25 pts |
|
Vehicle value proxy
≥$40k: 30 · $25-40k: 22 + scale · $10-25k: 10 + scale · <$10k: low
|
up to 30 pts |
|
Peer scarcity
1 listing: 10 · ≤3: 6 · ≤8: 3 · >8: 0
|
up to 10 pts |
Damage-weight multipliers
First matching keyword wins. Used for both resale (higher = better rebuild) and parts (higher = better parts pool).
Recommended bid formula
| Track | Formula |
|---|---|
| Resale | hammer × 0.85 × damage_weight × odometer_factor (rounded to $100) |
| Parts | avg_hammer × 0.12 × parts_damage × odometer_factor (rounded to $100) |
- Only computed when ≥ 3 real hammer sales exist for that make/model/year
- Tier-2 private-used median is the exit-price reference (not dealer retail)
- Dealer retail is only consulted when the vehicle year ≥ current_year − 2, marked down × 0.75 to estimate private
Decision gating (BID / WATCH / SKIP)
| Decision | Condition |
|---|---|
| bid | score ≥ threshold AND headroom above min — see bid policy below |
| watch | score above watch-floor but doesn't meet bid headroom OR <3 real hammer sales |
| skip | score below watch-floor, OR stolen (profile-gated), OR statutory + resale purpose |
| stat skip exception | Statutory in parts mode is allowed; only statutory + resale purpose is hard-blocked |
Bid decision policy
Active profile: default · version 1 · edits in policies/bid_rules.yaml
| Rule block | Values |
|---|---|
| bid |
min_score = 70 min_headroom_pct = 15 min_headroom_abs = 750
|
| watch |
min_score = 45
|
| hard_skip |
max_hammer_count_required_for_bid = 3 skip_if_stolen = False skip_if_statutory_and_purpose_resale = True
|