🔴 ABSORBIDO por
reusalia-backend(2026-08-17) — decisión del dueño (§7.9 del plan del Panel de Socios)Lo que este repo hacía en el PC de Guillem ya corre en el servidor (
reusalia-backend), con los datos en la BD y la operativa en la pestaña Pujas defrontend-socios(socios-reusalia.pages.dev/pujas). Piezas y a dónde fueron:
Aquí (tracker) Ahora (backend) data/manifests/*.csv(182)bstock_manifest_item(los 182 importados; el monitor carga los nuevos)insights.deep_analyze(TVs, cajas, regalados, departamentos)scripts/services/bstock/analisis_lote.py→bstock_lot_analisis(job trasbstock_monitor)scripts/build_recovery.py→data/recovery.jsonscripts/services/bstock/recovery.py→bstock_recovery_departamento(job semanal, sin TVs)calculator.py(9 %)scripts/services/bstock/calculator.py+ espejo JSfrontend-socios/pujas-calc.js; el % es del dueño (config_empresa['bstock_pct_objetivo'], 10 %)pipeline.py(escalera 30/15/10/5, WhatsApp)job puja_recordatorio(T-30, WhatsApp + email, una vez)digest(PDF 09/12/21)correo pujas_del_diade las 09:00 con PDF adjunto (pujas_pdf.py), uno al díawatch(lotes nuevos)bstock_monitorcada 6 h + sondeo de ids nuevos (Buy Now aparte)⛔ Las tareas programadas de Windows de este repo (
Bstock Liquidation Tracker,Bstock Manifest Watch,Bstock Digest 09/12/21) se DESACTIVAN: dos sistemas mandando avisos = avisos duplicados y dos verdades. Este repo queda como motor de referencia (tests, análisis a mano concli lot); sucalculator.pyes un ESPEJO del del backend (país + N palés, testtest_espejo_backend_n_pallets_y_pais). Si vas a cambiar una regla de valoración, cámbiala en el backend y trae aquí la copia.
A self-contained pipeline that monitors Amazon EU liquidation auctions on B-Stock, downloads the lot manifests, runs a profitability analysis and alerts you by email and/or WhatsApp when an auction matches your buying criteria.
Built from a real problem: B-Stock liquidation truckloads close fast, the headline bid hides the true landed cost (transport, VAT, marketplace fee, the Spanish "recargo de equivalencia"), and there's no way to get notified when a genuinely profitable lot appears. This tool scrapes the auctions, computes the maximum bid you can afford for a target margin, and alerts you.
Standalone showcase project. It uses SQLite and has no dependencies beyond the public B-Stock site and (optionally) an SMTP account. It does not place bids — it monitors and advises.
B-Stock listing page (per country)
│ requests + BeautifulSoup
▼
┌──────────────────────┐
│ Parser │ ── auction id, title, retail, pieces, lot type,
│ │ current bid, end time
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Bid calculator │ ── reverse-solves the landed-cost model to give the
│ │ max bid for a target % of retail
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Manifest analyzer │ ── (optional) downloads the lot CSV, aggregates by
│ │ category / condition, finds top-value items
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Rule engine │ ── min retail, max landed %, country, piece count
└──────────┬───────────┘
│ key auction?
▼
┌──────────────────────┐
│ SQLite + Email alert │ ── upsert + bid-history snapshots, one email per lot
└──────────────────────┘
The core of the project. Given a lot's retail value and a target landed cost (as a % of retail), it reverse-solves the cost model to tell you the maximum bid you can place:
total_cost = bid + transport + VAT + bstock_fee + RE
VAT = (transport + bid) × 21%
bstock_fee = bid × 5% (buyer premium; was 4% before 2026)
RE = total_cost × 5.2% (recargo de equivalencia)
Solving for bid:
max_bid = (total_cost − transport×1.21 − 0.052×total_cost) / (1 + 0.05 + 0.21)
Transport is a flat rate per lot type. A part load of 1-3 pallets is billed as a 4-pallet load — you pay for the truck slot, not per pallet.
$ python -m liquidation_tracker.cli bid --retail 16670 --type "Small Truckload" --pct 0.25
Lot type : Small Truckload
Retail value : EUR 16,670.00
Target landed : 25% of retail
----------------------------------------
Max bid : EUR 2,741.38
Transport : EUR 433.11
VAT (21%) : EUR 666.64
B-Stock fee : EUR 109.66
RE (5.2%) : EUR 216.71
Total landed : EUR 4,167.50
Bid % retail : 16.4%Transport is a flat rate per lot type (Truckload, Small Truckload,
4 Pallets DE/PL/IT, …) and is fully configurable.
git clone https://github.com/AspiranteD/liquidation-auction-tracker.git
cd liquidation-auction-tracker
pip install -r requirements.txt
cp .env.example .env # optional: configure email alerts and thresholdsTry it offline (no network) with the bundled sample manifest:
python examples/demo.py# Compute the max bid for a lot
python -m liquidation_tracker.cli bid --retail 16670 --type "Small Truckload" --pct 0.12
# List active auctions for a country, with suggested bids (live)
python -m liquidation_tracker.cli list --country ES
# Analyze a manifest CSV (quick aggregate stats)
python -m liquidation_tracker.cli analyze data/sample_manifest.csv
# Deep-analyze a manifest: TVs (loss), mispriced "giveaways", box/pallet density
python -m liquidation_tracker.cli inspect data/manifests/lot.csv
# Download + deep-analyze the manifests of every active auction (markdown reports)
python -m liquidation_tracker.cli manifests --country ES
# Full pipeline: scrape -> evaluate -> store in SQLite -> alert key auctions
python -m liquidation_tracker.cli monitor --country ES
# Detect new auctions, build their PDF report, send a WhatsApp summary
python -m liquidation_tracker.cli watch
# One combined PDF of every active lot, emailed (SMTP) with the PDF attached
python -m liquidation_tracker.cli digestinspect / manifests go beyond aggregate stats (module insights.py):
- Units and retail value per department, category and subcategory.
- TVs: panels in liquidation lots arrive broken, so their declared retail is treated as a loss and subtracted from the lot's effective retail.
- Giveaways: premium products (iPhones, MacBooks, lenses, consoles...)
declared at absurd prices because they were misclassified. Accessory and
compatibility mentions ("case for iPhone 16") are excluded; findings come
in two tiers (sure / doubtful) with a direct Amazon link to verify, plus an
optional
--verifylive price check. - Box/pallet density: Amazon fills containers to the top — a box with 2 declared items (or with far less declared value than its siblings) means undeclared content. Flagged against the lot's own median.
Reports land in data/reports/ as markdown, one per lot plus a summary.
Two channels, independently switchable in .env:
- Email: set the SMTP variables and
EMAIL_ALERTS_ENABLED=true. - WhatsApp (via the free CallMeBot
API): add the CallMeBot number on WhatsApp, send it
I allow callmebot to send me messages, copy the apikey it replies with intoCALLMEBOT_APIKEY, setCALLMEBOT_PHONEto your number in international format andWHATSAPP_ALERTS_ENABLED=true.
Alerts are a reminder ladder tied to the auction close, evaluated with the bid as it stands at each run (so run the monitor every minute near close time):
- One WhatsApp/email per stage as the close approaches — default
REMINDER_STAGES=30,15,10,5(minutes to close) — while the lot still qualifies. An auction first seen mid-ladder starts at the tightest applicable stage. - Voice-call escalation: at
CALL_AT_MINUTES(5) or less, an additional phone-style call through the free CallMeBot Telegram call API (a TTS voice reads the alert), once per auction. Setup: send/startto@CallMeBot_txtboton Telegram and setCALLMEBOT_TELEGRAM_USER.
An auction qualifies when it passes every rule:
| Rule | Env var | Default |
|---|---|---|
| Country in monitor list | MONITOR_COUNTRIES |
ES |
| Lot family monitored, retail ≥ per-type minimum | ALERT_MIN_RETAIL_4_PALLETS / _SMALL_TRUCKLOAD / _TRUCKLOAD |
20000 / 50000 / 100000 |
| Current bid still lands ≤ ceiling (of retail) | ALERT_MAX_TOTAL_PCT, or ALERT_ELECTRONICS_MAX_TOTAL_PCT when the title matches ELECTRONICS_KEYWORDS |
0.12 / 0.15 |
| Pieces ≥ threshold | ALERT_MIN_PIECES |
0 |
The ceilings apply to the total landed cost (bid + transport + VAT + fee + RE) — a 12% total ceiling puts the bid itself around 5-10% of retail. The suggested max bid in each alert is computed against the applicable ceiling. Each reminder stage fires at most once per auction.
SQLite (data/auctions.db):
auction— latest state per auction plus the computed suggested bid.bid_snapshot— append-only log of the current bid each time the auction is seen, so you can chart how bids evolve toward close.
B-Stock sits behind Cloudflare. A plain requests session with a browser
User-Agent works from most residential IPs (and is what this project uses). If
you hit a challenge page, the network layer (client.py) is isolated behind
three methods (list_auctions, fetch_lot_id, download_manifest) so it can
be swapped for a Playwright-backed client without touching the rest of the
pipeline.
liquidation_tracker/
├── client.py # B-Stock network layer (requests session)
├── parser.py # HTML -> Auction models (unit-testable)
├── calculator.py # the bid calculator (landed-cost model)
├── analyzer.py # manifest CSV -> aggregate stats
├── insights.py # deep manifest analysis (TVs, giveaways, box density)
├── alerts.py # rule engine: is this auction key?
├── notifier.py # email (SMTP) + WhatsApp (CallMeBot) alerts
├── storage.py # SQLite persistence + bid history
├── config.py # env-driven settings
├── pipeline.py # orchestration
└── cli.py # command-line interface
tests/ # pytest (calculator invariants, analyzer)
data/ # sample manifest (anonymized)
examples/demo.py # offline end-to-end demo
pytest -qMIT