SPB Git

spb/wp36_uqo

Public
13.1 KB

# WP36 — Data access and schema audit (Phase 0)

Date of audit: 2026-11-01 (snapshot taken 2026-11-01 ≈ 02:50 America/Toronto, last sync in snapshot 2026-11-01 06:49:42 UTC). Auditor: lead research agent (Claude Code), on behalf of S.-P. Boucher (UQO). Status of the gate: PASSED for H1, H2, H5 and the cross-sectional/dispersion parts of H4; CONDITIONAL for H3 (association only) and H6 (construction + internal validation only; no external forecasting test possible — see §7).

# 1. Source inventory (evidence-backed)

Item Finding Evidence
Platform Auto-Ka (www.auto-ka.com), independent Quebec used-vehicle aggregator built and operated by the author (Groupe KA). M4M64b:~/auto-ka/README.md, mld status (node M4M64b, port 8095, PM2 auto-ka-web + auto-ka-sync).
Authorized access path Owner-approved, read-only copy of the production SQLite database (sqlite3 .backup), taken on the production node, personal-account table dropped before transfer. No scraping of Auto-Ka, no use of the public HTTP API, no bypass of robots.txt (Disallow: /api/). data/raw/autoka-snapshot-20261101.db (1.35 GB, mode 0444), SHA-256 in data/raw/autoka-snapshot-20261101.db.sha256 (verified after transfer).
Documented API FastAPI endpoints /api/vehicles, /api/vehicles/{uid} (includes price history), /api/stats/*, /api/sources, /api/dealers — documented in README but disallowed to crawlers; not used for this paper (the DB copy is strictly richer). README §"API principale".
Upstream data-generation process 143 registered sources (131 dealer websites "direct", 11 portals, 1 marketplace = Kijiji private sellers) crawled by 31 connector modules; each cycle re-reads every source, normalises each listing to one schema, hashes content, logs price changes, and marks a listing inactive after MISS_GRACE = 2 consecutive absences; removals are suspended when a drift alert fires (volume ≤ 25 % of the 5-sync median, or ≥ 80 % null prices). VIN-based cross-source de-duplication elects a canonical listing. autoka/db.py lines 150–310, autoka/dedup.py, data/sources.json.
Crawl cadence Full cycle every ≈ 3.3 h on average (585 cycles between 2026-08-12 12:30 UTC and 2026-11-01 06:49 UTC; min 0.64 h, max 13.4 h; no gap > 24 h for any source). ≈ 7 observations per listing-day. sync_log (83 531 rows).
Model APIs None required. The paper uses no LLM/external model API; no credential verification needed. —

# 2. Schema (tables kept in the research snapshot)

Table Rows Role / key fields
vehicles 273 265 One row per listing uid = source:external_id. Fields: make, model, trim, year, price, price_label, mileage_km, transmission, fuel, drivetrain, body_type, colours, engine, doors, seats, vin, stock_number, dealer_name, city, region, lat/lng, description, features (JSON), details (JSON), images (JSON), carfax_url, content_hash, first_seen, last_seen, updated_at, miss_count, active, kind (auto/moto/scooter), dup_of, dup_sources.
price_log 428 484 (uid, ts, price) — one row at first observation and one row per price change (not per observation). price = NULL = price withdrawn from display.
sync_log 83 531 One row per (source, cycle): found, added, updated, removed, ok, message, stats JSON (null-price rate, null-km rate, missed, alert).
dealers 138 Geocoded registry of direct dealer sources (address, phone, website, city, lat/lng).
recalls 109 186 Transport Canada recalls keyed by make/model/year (public data).
vin_decode 4 655 Decoded VIN attributes (partial coverage).
detail_cache 77 039 Opaque per-connector payload cache; not used.
users — Dropped from the research snapshot (personal accounts).

Timestamps are Unix epoch seconds (UTC). first_seen = first observation; last_seen = last observation; for inactive rows updated_at = time the listing was declared inactive (two consecutive misses after last_seen).

# 3. Temporal coverage and panel structure

Quantity Value
Observation window 2026-08-12 12:32 UTC → 2026-11-01 06:49 UTC (81 days, 585 full crawl cycles)
Listings ever observed 273 265 (auto 271 992; moto 1 218; scooter 55)
Active at snapshot 62 821 (auto 62 052) ; inactive (exited) 210 444
New listings per month (first_seen) Aug 99 859 (incl. 46 k stock on day 1, left-censored) ; Sep 85 452 ; Oct 87 355
Price observations 428 484 rows for 270 909 uids; 61 962 uids with ≥ 2 price points, 26 576 with ≥ 3
Monthly price-log rows Aug 124 379 ; Sep 147 476 ; Oct 155 766
Listing duration (inactive, all sources) p10 0.74 d, p25 0.93 d, p50 2.6 d, p75 13.9 d, p90 35.1 d — the low quantiles are driven by sampled portals (see §5)

A longitudinal listing panel therefore exists, but it is short (11.5 weeks) and starts with a left-censored stock. Listing-level dynamics (H2, H5) are feasible; market-level time series (H6) have at most 81 daily / 11 weekly points.

# 4. Field coverage (null rates, all 273 265 listings)

Field Null/empty Field Null/empty
price 1.7 % make / model 0.0 % / 0.0 %
mileage_km 0.4 % year 0.1 %
trim 5.6 % VIN 33.3 % (AutoTrader 100 %, Kijiji 79 %, Otogo 18 %, direct dealers ≈ 0 %)
body_type 56.3 % fuel 35.1 %
transmission 32.2 % drivetrain 37.7 %
region 9.4 % city 1.7 %
lat/lng 4.8 % dealer_name 0.0 %
stock_number 57.8 % exterior colour / engine 56 % / 57 %

Price integrity: 2 164 listings at price ≤ 1 $ (placeholders), 29 between 1 and 500 $, 1 632 between 500 and 2 000 $, 506 above 250 000 $, 5 above 1 M$ (max 26.5 M$ — data error). price_label empty for 6 739. Mileage ≤ 10 km: 1 588 (new/demo or placeholder); > 500 000 km: 48. Year < 1990: 131; year = 2027: 784 (new-model-year stock). Hedonic-eligible core (auto, canonical, 2005 ≤ year ≤ 2026, 1 000 ≤ km ≤ 400 000, 2 000 ≤ price ≤ 250 000, make & model present): 242 508 of 259 133 canonical auto listings.

# 5. Source-level quality (decisive for the dynamic analyses)

Measured from sync_log and vehicles; "reactivation share" = (sum of removed events − inactive uids) / removed events, i.e. the share of exit events that were later reversed (flicker).

Source (type) Listings Mean found / cycle Exits with duration < 1 d Reactivation share Verdict
otogo (portal, full inventory, VIN ≈ 82 %, exact GPS, 538 dealers) 79 045 24 719 30 % 0.44 Keep for levels and dynamics with flicker-robust exit rule; uid stable per dealer listing (only 1.7 % of VINs have > 1 otogo uid, mostly genuine dealer-to-dealer transfers)
autousagee (AMVOQ portal, full inventory, 391 dealers) 28 858 11 973 5 % 0.04 Keep
direct dealer websites (136 sources, 50 223 listings, 17 801 active) 50 223 — 4.8 % mostly ≤ 0.1 Core dynamic sample (exceptions below)
hgregoire, automobileendirect, megacentre, clubautozone (banners) 11 038 — low 0.10–0.21 Keep
autotrader (portal, sliding window capped at 2 000 of > 4 000) 65 526 1 853 11 % (mean duration 2.2 d) 0.12 Exclude from dynamics (sampled, no VIN); eligible for cross-sectional hedonics only with a flag
cargurus (portal, capped at 500) 37 501 507 78 % 0.17 Exclude from dynamics; cross-section only
kijiji (private sellers, no VIN 79 %, trim missing 55 %) 11 874 3 041 15 % 0.04 Separate "private-seller" segment; excluded from dealer analyses
leprixdugros (banner) 6 124 1 019 11 % 0.94 Connector flicker: exclude from exit/markdown hazards
albioccasion (rate-limited 429) 2 057 853 — 0.52 Exclude from dynamics
baiecomeaugm, septilesgm, stjeromeautodepot, excelmoto (404 / connector failures 214–271 cycles) < 500 each — — 0.85 / 0.39 / — Exclude
classeauto:-98 1 uid — — — Identity collision (36 194 price rows, 48 distinct prices): exclude uid

Other anomalies: 3 451 uids show A→B→A price oscillation (e.g. otogo:1694844 alternating 37 840 / 40 794 $ twice a day for weeks — two feed variants of the same listing); 11 479 of 121 239 price changes occur < 24 h after the previous one. A "sustained price path" rule is therefore required before any markdown analysis (see research/analysis_plan.md).

Among non-portal sources, price changes are 100 532 cuts vs 11 447 rises (mean cut 3.4 %, mean rise 15.0 % — rises are dominated by placeholder/oscillation artefacts).

# 6. Geographic and dealer coverage

Active canonical listings by administrative region (2026-11-01): Montérégie 8 744, Capitale-Nationale 5 442, Laurentides 4 999, Montréal 4 511, Chaudière-Appalaches 3 605, Laval 3 036, Lanaudière 2 965, Mauricie 2 060, Estrie 1 965, Centre-du-Québec 1 652, Bas-Saint-Laurent 1 504, Outaouais 1 342, Abitibi-Témiscamingue 1 265, Saguenay–Lac-Saint-Jean 1 027, Côte-Nord 799, Gaspésie–Îles-de-la-Madeleine 291; missing region 4 754 (to be recovered from lat/lng). 138 geocoded direct dealers; otogo adds 538 dealer names with exact GPS; 16 308 direct-dealer VINs are also observed on otogo (cross-source consistency checks for exits).

Representativeness: the direct-dealer registry over-weights Capitale-Nationale, Bas-Saint-Laurent, Chaudière-Appalaches and Côte-Nord relative to population (registry built by the operator, not sampled). External benchmarks: see reports/external_benchmarks_inventory.md.

# 7. Feasibility matrix (H1–H6)

Hypothesis Feasible? Sample Limits
H1 nonlinear depreciation (hedonic asking prices) Yes 242 k canonical listings; first-observed price per listing (or listing-month panel) Asking prices, not transactions; composition; dealer effects collinear with region for single-site dealers
H2 markdown hazard Yes Dynamic sample (direct dealers + otogo + autousagee + banners), listing-day risk set, flicker-robust price paths Left-censoring of the day-1 stock (46 k); 81-day window → right-censoring heavy for long spells
H3 competitor response Association only Local competitor sets (same make/model/year ± 1, radius 25/50 km) from GPS No exogenous variation; reflection problem; common shocks → report as conditional association with event-study diagnostics
H4 regional segmentation Yes (cross-section + within-spec dispersion) Hedonic residuals by region with model-trim-year cohorts Dealer FE absorb region for single-location dealers → use dealer-group/chain FE variants
H5 listing exit & relative price Yes, exit ≠ sale Same as H2; exit = final absence ≥ 7 d No transaction data; validation limited to cross-source consistency (direct site vs otogo) and relisting detection
H6 AMLI Construction + internal validation only Daily/weekly market-level panel, 81 days No out-of-sample forecasting test of external economic variables is possible with ≤ 3 monthly points; stated as a documented limitation
  1. Immutable raw snapshot: sqlite3 .backup on M4M64b → drop users → VACUUM → SHA-256 → zstd → scp → verify (done 2026-11-01; data/raw/, read-only, never committed).
  2. src/ucml/ingest: read-only DuckDB/Polars extraction to Parquet in data/interim/ (vehicles, price_log, sync_log, dealers), with a manifest (row counts, min/max timestamps, SHA-256 of the source).
  3. All downstream transformations versioned in src/ucml/* with tests; exclusion log in data/processed/exclusions.csv.

# 9. Data governance

  • Public vs restricted fields. Make/model/trim/year/price/mileage/attributes/region/city/dealer name are public asking-price information published by dealers. VIN, stock number, listing URL, images, free-text descriptions and exact coordinates are treated as restricted: kept only in data/raw and data/interim (not committed, not released), used solely for linkage (VIN de-duplication, relisting detection, dealer geography). Released/processed tables carry anonymised dealer IDs (d####) and listing IDs (l#######), region/city and 1 km-rounded coordinates.
  • Personal information. The users table was dropped before the data left the production node. Kijiji private-seller listings may contain phone numbers in free text: descriptions are never exported from data/interim; the private-seller segment is analysed only through structured fields.
  • Retention. Raw snapshot retained for the life of the paper on the author's encrypted laptop and the production node; processed Parquet retained with the repository; nothing is pushed to any third-party service.
  • Attribution. Data source cited as "Auto-Ka (www.auto-ka.com), operator's database snapshot of 2026-11-01"; dealer sites are the ultimate sources (listed in data/sources.json, mirrored in data/processed/sources_registry.csv).
  • Permitted use. Owner-authorised academic research use by the platform operator; no redistribution of listing-level raw data.
  • Access control. Snapshot mode 0444, directory excluded by .gitignore.
Files · f9bf466deda3bcf4729bb4dc6e4078483843c761
Menu

Theme