# Scoring Engine ```mermaid flowchart TB subgraph Input[Scoring Input] DOMAIN["domain, sld, tld
renewalCost (optional)"] end subgraph Signals[4 Signal Computation] direction TB IS[Intrinsic Signal
length, pronounceability
hyphens/numbers penalty
TLD bonus] CS[Commercial Signal
keyword volume × CPC
via KeywordProvider] MS[Market Signal
comparable NameBio sales
via CompsProvider] ES[Expiry Signal
domain age, backlinks
Wayback history] end subgraph Weighting[Dynamic Weight Resolution] AVAIL[SignalAvailability
which signals have data?] WEIGHTS["resolveEffectiveWeights()
redistribute weights
from missing → available signals"] THOLDS["computeEffectiveThresholds()
adjust recommend/confidence
gates based on coverage"] end subgraph Core[Score Computation] WSUM["weightedScore =
Σ(signal_score × effective_weight)"] CONF["confidence =
f(coverage, intrinsic_quality)"] EV["expectedValue =
weightedScore × baseMarketValue ×
(1 + medianSalePrice / baseMV × 0.5)"] BUYMAX["suggestedBuyMax =
min(EV × buyMaxRatio - holdingCost,
absoluteCap)"] LISTPRICE["suggestedListPrice =
expectedValue × listPriceMultiplier"] end subgraph Verdict[Buy / Pass Decision] RECOMMEND["recommended =
confidence ≥ confidenceThreshold
AND weightedScore ≥ recommendThreshold"] end subgraph Output[ScoreResult] OR["expected_value, confidence
suggested_buy_max, suggested_list_price
bidRange: {conservative, aggressive}
breakdown: {intrinsic, commercial, market, expiry}
effectiveWeights, signalStatus"] end Input --> IS Input --> CS Input --> MS Input --> ES IS --> AVAIL CS --> AVAIL MS --> AVAIL ES --> AVAIL AVAIL --> WEIGHTS AVAIL --> THOLDS IS --> WSUM CS --> WSUM MS --> WSUM ES --> WSUM WEIGHTS --> WSUM THOLDS --> RECOMMEND IS --> CONF AVAIL --> CONF WSUM --> EV EV --> BUYMAX EV --> LISTPRICE EV --> RECOMMEND CONF --> RECOMMEND CONF --> BUYMAX BUYMAX --> OR LISTPRICE --> OR EV --> OR CONF --> OR WSUM --> OR RECOMMEND --> OR style Input fill:#1a1a2e,stroke:#16213e,color:#eee style Signals fill:#16213e,stroke:#0f3460,color:#eee style Weighting fill:#0f3460,stroke:#533483,color:#eee style Core fill:#16213e,stroke:#533483,color:#eee style Verdict fill:#2d1b69,stroke:#533483,color:#eee style Output fill:#1a1a2e,stroke:#16213e,color:#eee ``` ## Signal Details | Signal | Computation | Data Source | Conservative Tuning | |--------|-------------|-------------|-------------------| | **Intrinsic** | Domain length score + pronounceability + hyphen/numbers penalty + TLD bonus | Local heuristics | TLD bonus multiplies (0.4-1.0); hyphen and digit penalties floor the score | | **Commercial** | Search volume × CPC (normalised) | `KeywordProvider` | Falls back to 0 (no boost) if provider unavailable | | **Market** | Comparable sales median + recency-weighted average | `CompsProvider` | Falls back to 0; strong signal when available but capped | | **Expiry** | Age decile + backlinks log-scaled + Wayback age | Closeout CSV import | Only fires for expired/closeout domains | ## Weight Redistribution When one or more signals lack data, their weight is redistributed proportionally among the available signals. The intrinsic signal always retains at least its base weight (no redistribution below the intrinsic floor). This ensures the engine never produces a score from zero data. ## Confidence Formula ``` coverage = intrinsic_weight + (commercial_available ? commercial_weight : 0) + (market_available ? market_weight : 0) + (expiry_available ? expiry_weight : 0) signal_confidence = (coverage - intrinsic_min) / (1 - intrinsic_min) × (confidence_cap - confidence_base) × (1 - intrinsic_quality_influence) quality_boost = intrinsic_score × intrinsic_quality_influence × (confidence_cap - confidence_base) confidence = min(confidence_cap, confidence_base + signal_confidence + quality_boost) ```