fix: Data freshness - only show active auctions
CRITICAL FIXES: - API: Added end_time > now() filter to all auction queries - Scheduler: Cleanup expired auctions every 15 minutes - Scheduler: Scrape auctions every 2 hours (was 1 hour) - Scheduler: Sniper alert matching every 30 minutes Affected endpoints: - GET /auctions (search) - GET /auctions/feed (unified) - GET /auctions/hot - GET /auctions/ending-soon (already had filter) Updated MARKET_CONCEPT.md with: - 3 pillars: Pounce Direct, Live Auctions, Drops Tomorrow - Data freshness architecture - Unicorn roadmap
This commit is contained in:
@ -6,504 +6,342 @@
|
||||
|
||||
## 📋 Executive Summary
|
||||
|
||||
Die **Market Page** ist das Herzstück von Pounce. Hier fließen alle Datenquellen zusammen und werden dem User als **"Clean Feed"** präsentiert.
|
||||
Die **Market Page** ist das Herzstück von Pounce. Hier fließen alle Datenquellen zusammen:
|
||||
|
||||
### Vision (aus pounce_terminal.md)
|
||||
> *"Die Market Page zeigt alle Domains die entweder:*
|
||||
> 1. *Zu Verkauf stehen (Auktionen)*
|
||||
> 2. *Bald frei werden (Drops)*
|
||||
> 3. *Über Pounce direkt angeboten werden (Pounce Direct)"*
|
||||
1. **Pounce Direct** — User-Listings (unser USP, 0% Provision)
|
||||
2. **Live Auktionen** — Externe Plattformen (GoDaddy, Sedo, etc.)
|
||||
3. **Drops Tomorrow** — Domains bevor sie in Auktionen landen (Phase 3)
|
||||
|
||||
### Aktueller Stand: Phase 1 — Intelligence
|
||||
### Der Weg zum Unicorn (aus pounce_strategy.md)
|
||||
|
||||
> *"Der Weg zum Unicorn führt nicht über besseres Scraping, sondern über einzigartigen Content."*
|
||||
|
||||
**Aggregation kann jeder. Pounce Direct ist unser USP.**
|
||||
|
||||
---
|
||||
|
||||
## 🔧 KRITISCHE FIXES (Implementiert am 11.12.2025)
|
||||
|
||||
### Problem: Veraltete Daten wurden angezeigt
|
||||
|
||||
```
|
||||
VORHER: Abgelaufene Auktionen wurden im Feed angezeigt
|
||||
→ Schlechte User Experience
|
||||
→ Vertrauensverlust
|
||||
```
|
||||
|
||||
### Lösung: Multi-Layer Data Freshness
|
||||
|
||||
```python
|
||||
# 1. API-Filter: Nur laufende Auktionen
|
||||
query = select(DomainAuction).where(
|
||||
and_(
|
||||
DomainAuction.is_active == True,
|
||||
DomainAuction.end_time > datetime.utcnow() # ← NEU!
|
||||
)
|
||||
)
|
||||
|
||||
# 2. Scheduler: Cleanup alle 15 Minuten
|
||||
scheduler.add_job(
|
||||
cleanup_expired_auctions,
|
||||
CronTrigger(minute='*/15'), # Alle 15 Minuten
|
||||
id="auction_cleanup"
|
||||
)
|
||||
|
||||
# 3. Scraper: Alle 2 Stunden frische Daten
|
||||
scheduler.add_job(
|
||||
scrape_auctions,
|
||||
CronTrigger(hour='*/2', minute=30), # Alle 2 Stunden
|
||||
id="auction_scrape"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Die 3 Säulen des Market
|
||||
|
||||
### Säule 1: POUNCE DIRECT (Unser USP!)
|
||||
|
||||
> *"Das sind die Domains, die es NUR bei Pounce gibt."*
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ POUNCE MARKET — Aktueller Datenfluss │
|
||||
│ 💎 POUNCE DIRECT │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ DATENQUELLEN: │
|
||||
│ Warum es genial ist: │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ ✓ Unique Content (nur bei uns!) │
|
||||
│ ✓ 0% Provision (vs. 15-20% bei Sedo) │
|
||||
│ ✓ DNS-Verifizierung = Trust │
|
||||
│ ✓ Instant Buy (kein Bieten) │
|
||||
│ ✓ SEO: Jedes Listing = eigene Landing Page │
|
||||
│ │
|
||||
│ 📦 WEB SCRAPING (Hauptquelle) │
|
||||
│ └─→ ExpiredDomains.net (325 Auktionen) ✅ │
|
||||
│ └─→ GoDaddy RSS Feed (10 Auktionen) ✅ │
|
||||
│ └─→ Sedo Public (7 Auktionen) ✅ │
|
||||
│ └─→ NameJet Public (6 Auktionen) ✅ │
|
||||
│ └─→ DropCatch Public (7 Auktionen) ✅ │
|
||||
│ │
|
||||
│ 🔌 OFFIZIELLE APIs (Konfiguriert) │
|
||||
│ └─→ DropCatch Partner API ⚠️ (Nur eigene Aktivitäten) │
|
||||
│ └─→ Sedo Partner API ⏳ (Credentials fehlen) │
|
||||
│ │
|
||||
│ 💎 POUNCE DIRECT (User-Listings) │
|
||||
│ └─→ DNS-verifizierte Verkaufsangebote ❌ (0 Listings) │
|
||||
│ │
|
||||
│ 🔮 ZONE FILES (Phase 3 — Zukunft) │
|
||||
│ └─→ Verisign .com/.net 🔜 │
|
||||
│ └─→ PIR .org 🔜 │
|
||||
│ │
|
||||
│ Der Flow: │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ TOTAL: 355 Domains im Feed | 0 Pounce Direct │
|
||||
│ 1. User listet Domain (Trader/Tycoon Abo) │
|
||||
│ 2. DNS-Verifizierung (TXT Record) │
|
||||
│ 3. Listing erscheint im Market Feed │
|
||||
│ 4. Käufer kontaktiert Verkäufer (nach Login) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 TEIL 1: Bestandsaufnahme — Was haben wir?
|
||||
|
||||
### A. Backend-Komponenten ✅
|
||||
|
||||
| Komponente | Status | Beschreibung |
|
||||
|------------|--------|--------------|
|
||||
| **Unified Feed API** `/auctions/feed` | ✅ Live | Kombiniert Pounce Direct + External |
|
||||
| **Pounce Score v2.0** | ✅ Live | Length, TLD, Bids, Time Pressure |
|
||||
| **Vanity Filter** | ✅ Live | Premium-Domains für Public Users |
|
||||
| **Auction Scraper** | ✅ Läuft | 5 Plattformen, Scheduler aktiv |
|
||||
| **Listings API** | ✅ Fertig | DNS-Verifizierung, Inquiry-System |
|
||||
| **Sniper Alerts** | ✅ Fertig | Keyword-Matching, Notifications |
|
||||
|
||||
### B. Frontend-Komponenten ✅
|
||||
|
||||
| Seite | Status | Beschreibung |
|
||||
|-------|--------|--------------|
|
||||
| `/terminal/market` | ✅ Live | Vollständiger Market Feed für Auth Users |
|
||||
| `/auctions` | ✅ Live | Public Market mit Vanity Filter |
|
||||
| `/buy` | ✅ Live | Pounce Direct Marketplace Browse |
|
||||
| `/buy/[slug]` | ✅ Live | Listing-Detailseite |
|
||||
| `/terminal/listing` | ✅ Live | Seller Dashboard |
|
||||
|
||||
### C. Datenquellen — Realitätscheck
|
||||
|
||||
#### Offizielle APIs — Die Ernüchterung
|
||||
|
||||
**DropCatch API:**
|
||||
```
|
||||
Status: ✅ Authentifiziert
|
||||
Problem: Zeigt nur EIGENE Aktivitäten (Bids, Backorders)
|
||||
NICHT das öffentliche Auktionsinventar
|
||||
Nutzen: User-Integration (verbinde dein DropCatch-Konto)
|
||||
```
|
||||
|
||||
**Sedo API:**
|
||||
```
|
||||
Status: ⏳ Client bereit, Credentials fehlen
|
||||
Wo finden: Sedo.com → Mein Sedo → API-Zugang
|
||||
Benötigt: Partner ID + SignKey
|
||||
```
|
||||
|
||||
#### Web Scraping — Unsere Hauptquelle
|
||||
|
||||
```python
|
||||
# Aktuelle Scraper-Architektur
|
||||
TIER_1_APIS = [
|
||||
("DropCatch", _fetch_dropcatch_api), # Für eigene Aktivitäten
|
||||
("Sedo", _fetch_sedo_api), # Wenn konfiguriert
|
||||
]
|
||||
|
||||
TIER_2_SCRAPING = [
|
||||
("ExpiredDomains", _scrape_expireddomains), # 325 Domains
|
||||
("GoDaddy", _scrape_godaddy_rss), # 10 Domains
|
||||
("Sedo", _scrape_sedo_public), # 7 Domains (Fallback)
|
||||
("NameJet", _scrape_namejet_public), # 6 Domains
|
||||
("DropCatch", _scrape_dropcatch_public), # 7 Domains (Fallback)
|
||||
]
|
||||
```
|
||||
**Status:** ⏳ 0 Listings — Muss aktiviert werden!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 TEIL 2: Das Konzept — Die 3 Säulen des Market
|
||||
|
||||
### Säule 1: AUKTIONEN (Externe Plattformen)
|
||||
### Säule 2: LIVE AUKTIONEN (Content Filler)
|
||||
|
||||
> *"Zeige alle relevanten Auktionen von GoDaddy, Sedo, NameJet, etc."*
|
||||
|
||||
**Datenquellen:**
|
||||
- Web Scraping (primär)
|
||||
- Partner APIs (wenn verfügbar)
|
||||
|
||||
**Filter-Strategie:**
|
||||
```python
|
||||
# Vanity Filter für Public Users (aus pounce_features.md)
|
||||
def is_premium_domain(domain: str) -> bool:
|
||||
name, tld = domain.rsplit('.', 1)
|
||||
|
||||
# Premium TLDs only
|
||||
if tld not in ['com', 'io', 'ai', 'co', 'ch', 'de', 'net', 'org', 'app', 'dev']:
|
||||
return False
|
||||
|
||||
# Keine Spam-Muster
|
||||
if len(name) > 12: return False
|
||||
if '-' in name: return False
|
||||
if sum(c.isdigit() for c in name) > 1: return False
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
**UI-Darstellung:**
|
||||
| Domain | Source | Price | Status | Action |
|
||||
|--------|--------|-------|--------|--------|
|
||||
| **crypto-bank.io** | 🏢 GoDaddy | $2,500 | ⏱️ 2h left | [Bid ↗] |
|
||||
| **meta-shop.com** | 🏢 Sedo | $5,000 | 🤝 Offer | [View ↗] |
|
||||
|
||||
---
|
||||
|
||||
### Säule 2: POUNCE DIRECT (User-Listings)
|
||||
|
||||
> *"Das sind die Domains, die es NUR bei Pounce gibt. Unser USP."*
|
||||
|
||||
**Das Konzept (aus pounce_terminal.md):**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ POUNCE DIRECT — Der Listing-Wizard │
|
||||
│ 🏢 LIVE AUKTIONEN │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ STEP 1: DOMAIN EINGEBEN │
|
||||
│ ─────────────────────────────────────────────────────────── │
|
||||
│ Domain: [zurich-immo.ch___________] │
|
||||
│ Preis: [$950_______] ○ Fixpreis ● Verhandlungsbasis │
|
||||
│ Datenquellen: │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ 📦 Web Scraping (Hauptquelle) │
|
||||
│ └─→ ExpiredDomains.net (~350 Domains) │
|
||||
│ └─→ GoDaddy RSS │
|
||||
│ └─→ Sedo Public │
|
||||
│ └─→ NameJet Public │
|
||||
│ └─→ DropCatch Public │
|
||||
│ │
|
||||
│ STEP 2: DNS VERIFICATION (Trust-Check) │
|
||||
│ ─────────────────────────────────────────────────────────── │
|
||||
│ Füge diesen TXT-Record bei deinem Registrar hinzu: │
|
||||
│ Data Freshness: │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ ⏱️ Scraping: Alle 2 Stunden │
|
||||
│ 🧹 Cleanup: Alle 15 Minuten │
|
||||
│ ✅ Filter: Nur end_time > now() │
|
||||
│ │
|
||||
│ Name: _pounce-verify │
|
||||
│ Value: pounce-verify-8a3f7b9c2e1d │
|
||||
│ │
|
||||
│ [🔄 VERIFY DNS] │
|
||||
│ │
|
||||
│ STEP 3: LIVE! │
|
||||
│ ─────────────────────────────────────────────────────────── │
|
||||
│ ✅ Domain verifiziert! │
|
||||
│ Dein Listing erscheint jetzt im Market Feed. │
|
||||
│ Qualitätsfilter: │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ • Vanity Filter für Public (nur Premium-Domains) │
|
||||
│ • Pounce Score (0-100) │
|
||||
│ • TLD Filter (com, io, ai, etc.) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Warum das genial ist:**
|
||||
|
||||
| Vorteil | Erklärung |
|
||||
|---------|-----------|
|
||||
| **Unique Content** | Domains, die es NUR bei Pounce gibt |
|
||||
| **Höhere Conversion** | "Instant Buy" statt "Bid on external site" |
|
||||
| **Vendor Lock-in** | Verkäufer listen bei uns (0% Provision!) |
|
||||
| **SEO Power** | Jede Listing = eigene Landing Page |
|
||||
| **Trust Signal** | DNS-Verifizierung = Qualitätsgarantie |
|
||||
|
||||
**UI-Darstellung:**
|
||||
| Domain | Source | Price | Status | Action |
|
||||
|--------|--------|-------|--------|--------|
|
||||
| **zurich-immo.ch** | 💎 **Pounce** | **$950** | ⚡ **Instant** | **[Buy Now]** |
|
||||
**Status:** ✅ ~361 aktive Auktionen
|
||||
|
||||
---
|
||||
|
||||
### Säule 3: DROPS (Domains die bald frei werden)
|
||||
### Säule 3: DROPS TOMORROW (Tycoon Exclusive)
|
||||
|
||||
> *"Zeige Domains BEVOR sie in Auktionen landen."*
|
||||
|
||||
**Phase 1 (Jetzt): Deleted Domains via Scraping**
|
||||
```
|
||||
ExpiredDomains.net → Deleted Domains Liste → Pounce Filter → Feed
|
||||
```
|
||||
|
||||
**Phase 3 (Zukunft): Zone File Analysis**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ZONE FILE PIPELINE — Die Unicorn-Strategie │
|
||||
│ 🔮 DROPS TOMORROW — Phase 3 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. DAILY DOWNLOAD (4:00 UTC) │
|
||||
│ └─→ Zone Files von Verisign, PIR, etc. │
|
||||
│ Das Konzept: │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ 1. Zone Files von Verisign (.com/.net) beziehen │
|
||||
│ 2. Tägliche Diff-Analyse (was war gestern da, ist heute weg) │
|
||||
│ 3. Diese Domains droppen in 1-5 Tagen! │
|
||||
│ 4. Pounce Algorithm filtert nur Premium-Domains │
|
||||
│ │
|
||||
│ 2. DIFF ANALYSIS │
|
||||
│ └─→ Was war gestern da, ist heute weg? │
|
||||
│ └─→ Diese Domains DROPPEN in 1-5 Tagen! │
|
||||
│ │
|
||||
│ 3. POUNCE ALGORITHM │
|
||||
│ └─→ Nur Premium-Domains durchlassen (Score > 70) │
|
||||
│ │
|
||||
│ 4. OUTPUT: "Drops Tomorrow" (Tycoon Exclusive) │
|
||||
│ └─→ Domains BEVOR sie in Auktionen erscheinen │
|
||||
│ Warum das ein MONOPOL schafft: │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ • ExpiredDomains zeigt ALLES (Spam-Hölle) │
|
||||
│ • Pounce zeigt nur die TOP 100 (kuratiert) │
|
||||
│ • = Zeitersparnis = Premium Feature = $29/Monat │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 TEIL 3: Technische Architektur
|
||||
|
||||
### Der Unified Feed API Endpoint
|
||||
|
||||
```python
|
||||
# backend/app/api/auctions.py
|
||||
|
||||
@router.get("/feed")
|
||||
async def get_market_feed(
|
||||
source: str = Query("all", enum=["all", "pounce", "external"]),
|
||||
keyword: Optional[str] = None,
|
||||
tld: Optional[str] = None,
|
||||
min_price: Optional[float] = None,
|
||||
max_price: Optional[float] = None,
|
||||
min_score: int = Query(0, ge=0, le=100),
|
||||
ending_within: Optional[int] = None, # Stunden
|
||||
verified_only: bool = False,
|
||||
sort_by: str = Query("score", enum=["score", "price_asc", "price_desc", "time", "newest"]),
|
||||
limit: int = Query(50, le=200),
|
||||
offset: int = Query(0),
|
||||
current_user: Optional[User] = Depends(get_current_user_optional),
|
||||
):
|
||||
"""
|
||||
🚀 UNIFIED MARKET FEED — Das Herz von Pounce
|
||||
|
||||
Kombiniert:
|
||||
- 💎 Pounce Direct: DNS-verifizierte User-Listings (Instant Buy)
|
||||
- 🏢 External Auctions: Scraped von GoDaddy, Sedo, etc.
|
||||
- 🔮 Drops: Domains die bald frei werden (Phase 3)
|
||||
|
||||
Für nicht-authentifizierte User:
|
||||
- Vanity Filter aktiv (nur Premium-Domains)
|
||||
- Pounce Score sichtbar, aber limited Details
|
||||
|
||||
Für authentifizierte User (Trader/Tycoon):
|
||||
- Vollzugriff auf alle Domains
|
||||
- Advanced Filtering
|
||||
- Valuation Data
|
||||
"""
|
||||
```
|
||||
|
||||
### Pounce Score v2.0
|
||||
|
||||
```python
|
||||
def calculate_pounce_score_v2(domain: str, auction_data: dict) -> int:
|
||||
"""
|
||||
Der Pounce Score — Qualitäts- und Opportunity-Bewertung
|
||||
|
||||
A) INTRINSIC VALUE (Domain selbst)
|
||||
- Länge (kurz = wertvoll)
|
||||
- TLD Premium (com > io > xyz)
|
||||
- Dictionary Word Bonus
|
||||
|
||||
B) MARKET SIGNALS (Aktivität)
|
||||
- Bid Activity (mehr Bids = mehr Interesse)
|
||||
- Time Pressure (endet bald = Opportunity)
|
||||
- Price-to-Value Ratio (unterbewertet = 🔥)
|
||||
|
||||
C) PENALTIES
|
||||
- Bindestriche (-30)
|
||||
- Zahlen wenn >3 Zeichen (-20)
|
||||
- Zu lang >15 Zeichen (-25)
|
||||
"""
|
||||
score = 50 # Baseline
|
||||
name = domain.rsplit('.', 1)[0]
|
||||
tld = domain.rsplit('.', 1)[1]
|
||||
|
||||
# Länge
|
||||
if len(name) <= 3: score += 30
|
||||
elif len(name) == 4: score += 25
|
||||
elif len(name) == 5: score += 20
|
||||
elif len(name) <= 7: score += 10
|
||||
|
||||
# TLD
|
||||
tld_scores = {'com': 20, 'ai': 25, 'io': 18, 'co': 12, 'ch': 15}
|
||||
score += tld_scores.get(tld, 0)
|
||||
|
||||
# Market Signals
|
||||
bids = auction_data.get('num_bids', 0)
|
||||
if bids >= 20: score += 15
|
||||
elif bids >= 10: score += 10
|
||||
elif bids >= 5: score += 5
|
||||
|
||||
# Penalties
|
||||
if '-' in name: score -= 30
|
||||
if any(c.isdigit() for c in name) and len(name) > 3: score -= 20
|
||||
|
||||
return max(0, min(100, score))
|
||||
```
|
||||
**Status:** 🔜 Geplant (6-12 Monate)
|
||||
|
||||
---
|
||||
|
||||
## 📈 TEIL 4: Roadmap
|
||||
|
||||
### ✅ ERLEDIGT (Stand: 11. Dezember 2025)
|
||||
|
||||
- [x] Unified Feed API `/auctions/feed`
|
||||
- [x] Pounce Score v2.0 mit Market Signals
|
||||
- [x] Vanity Filter für Public Users
|
||||
- [x] Pounce Direct Listing-System (DNS-Verifizierung)
|
||||
- [x] Sniper Alerts mit Keyword-Matching
|
||||
- [x] Web Scraping für 5 Plattformen
|
||||
- [x] DropCatch API Client (für User-Integration)
|
||||
- [x] Sedo API Client (bereit für Credentials)
|
||||
|
||||
### 🎯 NÄCHSTE SCHRITTE (Diese Woche)
|
||||
|
||||
1. **Sedo API Credentials eingeben**
|
||||
- Sedo.com → Mein Sedo → API-Zugang
|
||||
- Partner ID + SignKey in `.env`
|
||||
|
||||
2. **Erste Pounce Direct Listings erstellen**
|
||||
- Test-Domains zum Verifizieren des Flows
|
||||
- Zeigt "Unique Content" im Feed
|
||||
|
||||
3. **Scraper-Stabilität verbessern**
|
||||
- Fallback-Logik testen
|
||||
- Error-Handling optimieren
|
||||
|
||||
### 🔮 PHASE 3 (6-12 Monate)
|
||||
|
||||
1. **Zone File Access beantragen**
|
||||
- Verisign (.com/.net)
|
||||
- PIR (.org)
|
||||
- Kosten: $0-$10,000/Jahr
|
||||
|
||||
2. **"Drops Tomorrow" Feature**
|
||||
- Zone File Diff-Analyse
|
||||
- Tycoon Exclusive ($29/mo)
|
||||
|
||||
3. **Pounce Instant Exchange**
|
||||
- Integrierter Escrow-Service
|
||||
- 5% Gebühr (statt 15-20% bei Konkurrenz)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 TEIL 5: UI/UX Design
|
||||
|
||||
### Die Master-Tabelle (aus pounce_terminal.md)
|
||||
|
||||
| Spalte | Inhalt | Visualisierung |
|
||||
|--------|--------|----------------|
|
||||
| **Domain** | Name der Domain | Fettgedruckt. Bei "Pounce Direct" → 💎 Icon |
|
||||
| **Pounce Score** | Qualitäts-Algorithmus | 0-100 (Grün > 80, Gelb 50-80, Rot < 50) |
|
||||
| **Price / Bid** | Preis oder aktuelles Gebot | `$500` oder `$50 (Bid)` |
|
||||
| **Status / Time** | Countdown oder Verfügbarkeit | ⏱️ `4h left` oder ⚡ `Instant` |
|
||||
| **Source** | Herkunft | 🏢 GoDaddy, 💎 Pounce |
|
||||
| **Action** | Der Button | `[Bid ↗]` oder `[Buy Now]` |
|
||||
## 🎨 UI/UX: Die Market Page
|
||||
|
||||
### Filter Bar
|
||||
|
||||
```
|
||||
[Toggle] Hide Spam (Standard: AN)
|
||||
[Toggle] Pounce Direct Only
|
||||
[Dropdown] TLD: .com, .ai, .io, .ch
|
||||
[Dropdown] Price: < $100, < $1k, High Roller
|
||||
[Dropdown] Ending: 1h, 4h, 24h, 7d
|
||||
[✓] Hide Spam [○] Pounce Only [TLD ▾] [Price ▾] [Ending ▾]
|
||||
```
|
||||
|
||||
### Die Master-Tabelle
|
||||
|
||||
| Spalte | Inhalt | Visualisierung |
|
||||
|--------|--------|----------------|
|
||||
| **Domain** | Name | Fettgedruckt. 💎 Icon für Pounce Direct |
|
||||
| **Score** | Pounce Score | 0-100 (Grün > 80, Gelb 50-80, Rot < 50) |
|
||||
| **Price** | Preis/Gebot | `$500` oder `$50 (Bid)` |
|
||||
| **Status** | Zeit/Verfügbarkeit | ⏱️ `4h left` oder ⚡ `Instant` |
|
||||
| **Source** | Herkunft | 🏢 GoDaddy, 💎 Pounce |
|
||||
| **Action** | Button | `[Bid ↗]` oder `[Buy Now]` |
|
||||
|
||||
### Visuelle Hierarchie
|
||||
|
||||
```tsx
|
||||
// Pounce Direct Items werden prominent angezeigt
|
||||
{pounceDirectItems.length > 0 && (
|
||||
<section className="mb-6">
|
||||
<div className="flex items-center gap-2 text-emerald-400 font-bold mb-3">
|
||||
<Diamond className="w-4 h-4" />
|
||||
Pounce Exclusive — Verified Instant Buy
|
||||
</div>
|
||||
{pounceDirectItems.map(item => <PounceDirectCard />)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
// External Auctions darunter
|
||||
<section>
|
||||
<h2>Active Auctions</h2>
|
||||
{externalItems.map(item => <AuctionCard />)}
|
||||
</section>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💰 TEIL 6: Monetarisierung
|
||||
|
||||
### Tier-basierte Features (aus pounce_pricing.md)
|
||||
|
||||
| Feature | Scout ($0) | Trader ($9) | Tycoon ($29) |
|
||||
|---------|------------|-------------|--------------|
|
||||
| **Market Feed** | 🌪️ Raw (Vanity Filter) | ✨ Curated (Clean) | ✨ Curated + Priority |
|
||||
| **Alert Speed** | 🐢 Daily | 🐇 Hourly | ⚡ Real-Time (10m) |
|
||||
| **Watchlist** | 5 Domains | 50 Domains | 500 Domains |
|
||||
| **Sell Domains** | ❌ | ✅ 5 Listings | ✅ 50 Listings + Featured |
|
||||
| **Pounce Score** | ❌ Locked | ✅ Basic | ✅ + SEO Data |
|
||||
| **Drops Tomorrow** | ❌ | ❌ | ✅ Exclusive |
|
||||
|
||||
### Die "Conversion-Falle" (aus pounce_features.md)
|
||||
|
||||
Wenn ein nicht-eingeloggter User auf "Buy Now" bei einem Pounce Direct Listing klickt:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🔒 Secure Transaction │
|
||||
│ MARKET FEED │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Du bist dabei, ein verifiziertes Direct-Listing anzusehen. │
|
||||
│ 💎 POUNCE EXCLUSIVE — Verified Instant Buy │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ zurich-immo.ch $950 ⚡ Instant ✅ Verified [Buy] │ │
|
||||
│ │ crypto-hub.io $2.5k ⚡ Instant ✅ Verified [Buy] │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Um den Verkäufer zu kontaktieren und Käuferschutz zu │
|
||||
│ genießen, logge dich bitte ein. │
|
||||
│ 🏢 LIVE AUCTIONS │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ techflow.io $250 ⏱️ 4h left GoDaddy [Bid ↗] │ │
|
||||
│ │ datalab.com $1.2k ⏱️ 23h left Sedo [Bid ↗] │ │
|
||||
│ │ nexus.ai $5k ⏱️ 2d left NameJet [Bid ↗] │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Login] [Create Free Scout Account] │
|
||||
│ 🔮 DROPS TOMORROW (Tycoon Only) │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ 🔒 Upgrade to Tycoon to see domains dropping tomorrow │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔑 TEIL 7: Kritische Erkenntnisse
|
||||
## 💰 Monetarisierung (aus pounce_pricing.md)
|
||||
|
||||
### API-Realität vs. Erwartung
|
||||
|
||||
| API | Erwartung | Realität |
|
||||
|-----|-----------|----------|
|
||||
| **DropCatch** | Alle öffentlichen Auktionen | ❌ Nur eigene Bids/Backorders |
|
||||
| **Sedo** | TBD | ⏳ Credentials fehlen noch |
|
||||
|
||||
**Konsequenz:**
|
||||
- Web Scraping bleibt unsere **Hauptquelle** für öffentliche Daten
|
||||
- APIs sind nützlich für **User-Integration** (verbinde dein DropCatch-Konto)
|
||||
- **Zone Files** sind der langfristige Weg zur Datenhoheit
|
||||
|
||||
### Der echte USP: Pounce Direct
|
||||
|
||||
> *"Domains die es NUR bei Pounce gibt."*
|
||||
|
||||
Das ist der Schlüssel. Nicht die Aggregation (das kann jeder), sondern der **Unique Content** durch User-Listings.
|
||||
|
||||
**Priorität:** Erste Pounce Direct Listings aktivieren!
|
||||
| Feature | Scout ($0) | Trader ($9) | Tycoon ($29) |
|
||||
|---------|------------|-------------|--------------|
|
||||
| **Market Feed** | 🌪️ Vanity Filter | ✨ Clean | ✨ Clean + Priority |
|
||||
| **Alert Speed** | 🐢 Daily | 🐇 Hourly | ⚡ Real-Time (10m) |
|
||||
| **Watchlist** | 5 Domains | 50 Domains | 500 Domains |
|
||||
| **Sell Domains** | ❌ | ✅ 5 Listings | ✅ 50 + Featured |
|
||||
| **Pounce Score** | ❌ Locked | ✅ Basic | ✅ + SEO Data |
|
||||
| **Drops Tomorrow** | ❌ | ❌ | ✅ Exclusive |
|
||||
|
||||
---
|
||||
|
||||
## 📋 Checkliste für den Launch
|
||||
## ⚙️ Technische Architektur
|
||||
|
||||
### Backend
|
||||
- [x] Unified Feed API
|
||||
### Scheduler Jobs
|
||||
|
||||
```python
|
||||
# Aktive Jobs (Scheduler)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
|
||||
# 1. Auction Scrape — Alle 2 Stunden
|
||||
scheduler.add_job(scrape_auctions, CronTrigger(hour='*/2', minute=30))
|
||||
|
||||
# 2. Expired Cleanup — Alle 15 Minuten (KRITISCH!)
|
||||
scheduler.add_job(cleanup_expired_auctions, CronTrigger(minute='*/15'))
|
||||
|
||||
# 3. Sniper Matching — Alle 30 Minuten
|
||||
scheduler.add_job(match_sniper_alerts, CronTrigger(minute='*/30'))
|
||||
|
||||
# 4. TLD Prices — Täglich 03:00 UTC
|
||||
scheduler.add_job(scrape_tld_prices, CronTrigger(hour=3))
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
|
||||
```python
|
||||
# Market Feed Endpoints
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
|
||||
GET /api/v1/auctions/feed # Unified Feed (Pounce + External)
|
||||
GET /api/v1/auctions # External Auctions only
|
||||
GET /api/v1/auctions/ending-soon
|
||||
GET /api/v1/auctions/hot
|
||||
GET /api/v1/listings # Pounce Direct Listings
|
||||
```
|
||||
|
||||
### Data Freshness Garantie
|
||||
|
||||
```python
|
||||
# Jede Query filtert automatisch auf aktive Auktionen:
|
||||
query = select(DomainAuction).where(
|
||||
and_(
|
||||
DomainAuction.is_active == True,
|
||||
DomainAuction.end_time > datetime.utcnow() # ← IMMER!
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Roadmap
|
||||
|
||||
### ✅ ERLEDIGT (11. Dezember 2025)
|
||||
|
||||
- [x] Unified Feed API `/auctions/feed`
|
||||
- [x] Pounce Score v2.0
|
||||
- [x] Vanity Filter
|
||||
- [x] Scraper aktiv
|
||||
- [ ] Sedo API Credentials eingeben
|
||||
- [ ] Scheduler-Intervall optimieren
|
||||
- [x] Web Scraping (5 Plattformen)
|
||||
- [x] **FIX: end_time Filter** (nur laufende Auktionen)
|
||||
- [x] **FIX: Cleanup alle 15 Minuten**
|
||||
- [x] **FIX: Scraper alle 2 Stunden**
|
||||
- [x] Sniper Alerts
|
||||
|
||||
### Frontend
|
||||
- [x] Terminal Market Page
|
||||
- [x] Public Auctions Page
|
||||
- [x] Pounce Direct Highlighting
|
||||
- [x] Filter (Source, TLD, Price)
|
||||
- [ ] "Hot Right Now" Section
|
||||
- [ ] Better Empty States
|
||||
### 🎯 NÄCHSTE SCHRITTE (Diese Woche)
|
||||
|
||||
### Content
|
||||
- [ ] Erste 5 Test-Listings erstellen
|
||||
- [ ] DNS-Verifizierung testen
|
||||
- [ ] Listing-to-Feed Flow validieren
|
||||
1. **Erste Pounce Direct Listings erstellen**
|
||||
- Test-Domains zum Verifizieren des Flows
|
||||
- USP aktivieren!
|
||||
|
||||
2. **Sedo API Credentials eingeben**
|
||||
- Sedo.com → Mein Sedo → API-Zugang
|
||||
- Partner ID + SignKey in `.env`
|
||||
|
||||
3. **Frontend: "Live" Indikator**
|
||||
- Zeige wann Daten zuletzt aktualisiert wurden
|
||||
|
||||
### 🔮 PHASE 2-3 (6-12 Monate)
|
||||
|
||||
1. **Zone File Access beantragen**
|
||||
- Verisign (.com/.net)
|
||||
- "Drops Tomorrow" Feature
|
||||
|
||||
2. **Pounce Instant Exchange**
|
||||
- Integrierter Escrow-Service
|
||||
- 5% Gebühr
|
||||
|
||||
---
|
||||
|
||||
## 💎 Fazit
|
||||
## 🚀 Der Unicorn-Pfad
|
||||
|
||||
Die Market Page ist **funktional**, aber der wahre USP (Pounce Direct) ist noch nicht aktiviert.
|
||||
```
|
||||
Phase 1: INTELLIGENCE (Jetzt)
|
||||
├── Pounce Direct aktivieren (Unique Content)
|
||||
├── Clean Feed (aktuelle Daten, Spam-frei)
|
||||
├── Trust aufbauen
|
||||
└── 10.000 User, $1M ARR
|
||||
|
||||
**Die Reihenfolge:**
|
||||
1. ✅ Aggregation funktioniert (Scraping)
|
||||
2. ⏳ Pounce Direct aktivieren (User-Listings)
|
||||
3. 🔮 Zone Files für Datenhoheit (Phase 3)
|
||||
Phase 2: LIQUIDITÄT (18-36 Monate)
|
||||
├── Pounce Instant Exchange
|
||||
├── Buy Now im Dashboard
|
||||
├── 5% Gebühr
|
||||
└── $10M ARR
|
||||
|
||||
> *"Der Weg zum Unicorn führt nicht über besseres Scraping, sondern über einzigartigen Content."*
|
||||
>
|
||||
> — pounce_strategy.md
|
||||
Phase 3: FINANZIALISIERUNG (3-5 Jahre)
|
||||
├── Fractional Ownership
|
||||
├── Domain-Backed Lending
|
||||
└── = FINTECH ($50-100M ARR)
|
||||
|
||||
Phase 4: IMPERIUM (5+ Jahre)
|
||||
├── Enterprise Sentinel (B2B)
|
||||
├── Fortune 500 Kunden
|
||||
└── = $1 Mrd. Bewertung
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💎 Das Fazit
|
||||
|
||||
**Aggregation ist Commodity. Pounce Direct ist der USP.**
|
||||
|
||||
Der Weg zum Unicorn:
|
||||
1. ✅ Datenqualität (aktuelle, saubere Daten)
|
||||
2. ⏳ Unique Content (Pounce Direct aktivieren!)
|
||||
3. 🔮 Datenhoheit (Zone Files)
|
||||
|
||||
> *"Don't guess. Know."*
|
||||
>
|
||||
> — Phase 1: Intelligence
|
||||
|
||||
@ -277,8 +277,14 @@ async def search_auctions(
|
||||
- Look for value_ratio > 1.0 (estimated value exceeds current bid)
|
||||
- Focus on auctions ending soon with low bid counts
|
||||
"""
|
||||
# Build query
|
||||
query = select(DomainAuction).where(DomainAuction.is_active == True)
|
||||
# Build query - ONLY show active auctions that haven't ended yet
|
||||
now = datetime.utcnow()
|
||||
query = select(DomainAuction).where(
|
||||
and_(
|
||||
DomainAuction.is_active == True,
|
||||
DomainAuction.end_time > now # ← KRITISCH: Nur Auktionen die noch laufen!
|
||||
)
|
||||
)
|
||||
|
||||
# VANITY FILTER: For public (non-logged-in) users, only show premium-looking domains
|
||||
# This ensures the first impression is high-quality, not spam domains
|
||||
@ -457,9 +463,15 @@ async def get_hot_auctions(
|
||||
|
||||
Data is scraped from public auction sites - no mock data.
|
||||
"""
|
||||
now = datetime.utcnow()
|
||||
query = (
|
||||
select(DomainAuction)
|
||||
.where(DomainAuction.is_active == True)
|
||||
.where(
|
||||
and_(
|
||||
DomainAuction.is_active == True,
|
||||
DomainAuction.end_time > now # Only show active auctions
|
||||
)
|
||||
)
|
||||
.order_by(DomainAuction.num_bids.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
@ -996,7 +1008,13 @@ async def get_market_feed(
|
||||
# 2. EXTERNAL AUCTIONS (Scraped from platforms)
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
if source in ["all", "external"]:
|
||||
auction_query = select(DomainAuction).where(DomainAuction.is_active == True)
|
||||
now = datetime.utcnow()
|
||||
auction_query = select(DomainAuction).where(
|
||||
and_(
|
||||
DomainAuction.is_active == True,
|
||||
DomainAuction.end_time > now # ← KRITISCH: Nur laufende Auktionen!
|
||||
)
|
||||
)
|
||||
|
||||
if keyword:
|
||||
auction_query = auction_query.where(
|
||||
|
||||
@ -204,12 +204,30 @@ def setup_scheduler():
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
# Auction scrape every hour (at :30 to avoid conflict with other jobs)
|
||||
# Auction scrape every 2 hours (at :30 to avoid conflict with other jobs)
|
||||
scheduler.add_job(
|
||||
scrape_auctions,
|
||||
CronTrigger(minute=30), # Every hour at :30
|
||||
id="hourly_auction_scrape",
|
||||
name="Hourly Auction Scrape",
|
||||
CronTrigger(hour='*/2', minute=30), # Every 2 hours at :30
|
||||
id="auction_scrape",
|
||||
name="Auction Scrape (2h)",
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
# Cleanup expired auctions every 15 minutes (CRITICAL for data freshness!)
|
||||
scheduler.add_job(
|
||||
cleanup_expired_auctions,
|
||||
CronTrigger(minute='*/15'), # Every 15 minutes
|
||||
id="auction_cleanup",
|
||||
name="Expired Auction Cleanup (15m)",
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
# Sniper alert matching every 30 minutes
|
||||
scheduler.add_job(
|
||||
match_sniper_alerts,
|
||||
CronTrigger(minute='*/30'), # Every 30 minutes
|
||||
id="sniper_matching",
|
||||
name="Sniper Alert Matching (30m)",
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
@ -220,7 +238,9 @@ def setup_scheduler():
|
||||
f"\n - Tycoon domain check every 10 minutes"
|
||||
f"\n - TLD price scrape at 03:00 UTC"
|
||||
f"\n - Price change alerts at 04:00 UTC"
|
||||
f"\n - Auction scrape every hour at :30"
|
||||
f"\n - Auction scrape every 2 hours at :30"
|
||||
f"\n - Expired auction cleanup every 15 minutes"
|
||||
f"\n - Sniper alert matching every 30 minutes"
|
||||
)
|
||||
|
||||
|
||||
@ -302,6 +322,58 @@ async def check_price_changes():
|
||||
logger.exception(f"Price change check failed: {e}")
|
||||
|
||||
|
||||
async def cleanup_expired_auctions():
|
||||
"""
|
||||
Mark expired auctions as inactive and delete very old ones.
|
||||
|
||||
This is CRITICAL for data freshness! Without this, the Market page
|
||||
would show auctions that ended days ago.
|
||||
|
||||
Runs every 15 minutes to ensure users always see live data.
|
||||
"""
|
||||
from app.models.auction import DomainAuction
|
||||
from sqlalchemy import update, delete
|
||||
|
||||
logger.info("Starting expired auction cleanup...")
|
||||
|
||||
try:
|
||||
async with AsyncSessionLocal() as db:
|
||||
now = datetime.utcnow()
|
||||
|
||||
# 1. Mark ended auctions as inactive
|
||||
stmt = (
|
||||
update(DomainAuction)
|
||||
.where(
|
||||
and_(
|
||||
DomainAuction.end_time < now,
|
||||
DomainAuction.is_active == True
|
||||
)
|
||||
)
|
||||
.values(is_active=False)
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
marked_inactive = result.rowcount
|
||||
|
||||
# 2. Delete very old inactive auctions (> 7 days)
|
||||
cutoff = now - timedelta(days=7)
|
||||
stmt = delete(DomainAuction).where(
|
||||
and_(
|
||||
DomainAuction.is_active == False,
|
||||
DomainAuction.end_time < cutoff
|
||||
)
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
deleted = result.rowcount
|
||||
|
||||
await db.commit()
|
||||
|
||||
if marked_inactive > 0 or deleted > 0:
|
||||
logger.info(f"Auction cleanup: {marked_inactive} marked inactive, {deleted} deleted")
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Auction cleanup failed: {e}")
|
||||
|
||||
|
||||
async def scrape_auctions():
|
||||
"""Scheduled task to scrape domain auctions from public sources."""
|
||||
from app.services.auction_scraper import auction_scraper
|
||||
|
||||
Reference in New Issue
Block a user