Add luxury/vertical signal to auto-tiering

pick_baseline now applies a premium floor: if prospect.vertical matches
rate_card.tiering.premium_verticals (luxury/premium/deluxe/5성/특1급…), the
auto-selected tier is floored to premium_min_tier (default basic) so a premium
single property won't drop to the smb entry tier.

Validated: L'Escape (hotel_luxury, 1 property) smb -> basic (₩10.5M);
non-luxury single -> smb (₩3.0M); SHR chain -> treatment (₩29.5M) unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 01:15:55 +09:00
parent 0496262cd5
commit 95d6fdf499
4 changed files with 36 additions and 9 deletions

View File

@@ -44,17 +44,35 @@ def scope_multiplier(rate, f):
return float(bands[-1][1]), driver, count
def pick_baseline(f, override):
TIER_ORDER = {"smb": 0, "basic": 1, "treatment": 2}
def _higher(a, b):
return a if TIER_ORDER.get(a, 0) >= TIER_ORDER.get(b, 0) else b
def is_premium(f, rate):
vertical = (f.get("prospect", {}).get("vertical") or "").lower()
terms = [t.lower() for t in rate.get("tiering", {}).get("premium_verticals", [])]
return any(t in vertical for t in terms)
def pick_baseline(f, override, rate):
if override:
return override
e = f.get("entity", {})
props = e.get("properties_total", 0) or 0
subs = e.get("subbrands_total", 0) or 0
if props > 5 or subs > 3: # multi-brand / chain
return "treatment"
if props <= 1 and subs == 0: # single-property SMB
return "smb"
return "basic" # small multi-property / mid
tier = "treatment"
elif props <= 1 and subs == 0: # single property
tier = "smb"
else: # small multi-property / mid
tier = "basic"
# premium/luxury floor: don't auto-drop a premium prospect to the entry tier
if is_premium(f, rate):
tier = _higher(tier, rate.get("tiering", {}).get("premium_min_tier", "basic"))
return tier
def assemble(f, rate, sow, baseline, billing):
@@ -176,7 +194,7 @@ def main():
with open(args.sow, encoding="utf-8") as fh:
sow = yaml.safe_load(fh)
baseline = pick_baseline(f, args.baseline)
baseline = pick_baseline(f, args.baseline, rate)
tpl_billing = sow["baselines"][baseline].get("billing_rate")
billing = (args.billing if args.billing is not None
else tpl_billing if tpl_billing is not None else rate["billing_rate"])