# 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)
```