""" Trademark Valuation Computation Logic Heuristic-based approach for estimating trademark value based on multiple factors """ import re from typing import Dict, Any from datetime import datetime import math import random # ========================================= # Constants # ========================================= REGISTERED = {700, 701, 702, 703, 704, 705, 706, 707, 708, 739, 717, 800} ITU = {688, 718, 719, 720, 721, 722, 724, 725, 730, 731, 732, 733, 734} POU = { 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 632, 638, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660 } STATUS_BOOST = {801, 802, 803, 804, 773, 774, 775, 777, 778, 779, 780, 781, 782, 790} CLASS_CFG = { 5: (50e6, 500e6, 0.08, 0.15, 1), 9: (50e6, 500e6, 0.10, 0.18, 1), 42: (40e6, 400e6, 0.10, 0.18, 1), 36: (40e6, 400e6, 0.05, 0.12, 1), 45: (35e6, 350e6, 0.08, 0.15, 1), 3: (20e6, 200e6, 0.15, 0.25, 2), 10: (25e6, 250e6, 0.08, 0.15, 2), 44: (25e6, 250e6, 0.08, 0.15, 2), 35: (20e6, 200e6, 0.10, 0.18, 2), 38: (25e6, 250e6, 0.08, 0.15, 2), 41: (20e6, 200e6, 0.12, 0.20, 2), 25: (15e6, 150e6, 0.15, 0.25, 3), 29: (10e6, 100e6, 0.12, 0.22, 3), 30: (10e6, 100e6, 0.12, 0.22, 3), 32: (15e6, 150e6, 0.15, 0.25, 3), 33: (20e6, 200e6, 0.18, 0.28, 3), 34: (25e6, 250e6, 0.15, 0.25, 3), 43: (10e6, 100e6, 0.15, 0.25, 3), 28: (10e6, 100e6, 0.12, 0.20, 3), 14: (15e6, 150e6, 0.12, 0.20, 3), 18: (15e6, 150e6, 0.18, 0.28, 3), 7: (8e6, 80e6, 0.05, 0.12, 4), 11: (8e6, 80e6, 0.08, 0.15, 4), 12: (10e6, 100e6, 0.08, 0.15, 4), 37: (5e6, 50e6, 0.05, 0.10, 4), 39: (8e6, 80e6, 0.08, 0.15, 4), 20: (5e6, 50e6, 0.10, 0.18, 4), 21: (5e6, 50e6, 0.10, 0.18, 4), 1: (3e6, 40e6, 0.03, 0.08, 5), 2: (3e6, 40e6, 0.05, 0.10, 5), 4: (3e6, 40e6, 0.03, 0.08, 5), 6: (2e6, 30e6, 0.02, 0.06, 5), 8: (3e6, 40e6, 0.05, 0.12, 5), 13: (5e6, 50e6, 0.08, 0.15, 5), 15: (3e6, 30e6, 0.10, 0.18, 5), 16: (2e6, 25e6, 0.05, 0.10, 5), 17: (2e6, 25e6, 0.03, 0.08, 5), 19: (3e6, 35e6, 0.03, 0.08, 5), 22: (1.5e6, 20e6, 0.03, 0.08, 5), 23: (1.5e6, 20e6, 0.03, 0.08, 5), 24: (2e6, 25e6, 0.05, 0.10, 5), 26: (1e6, 15e6, 0.05, 0.10, 5), 27: (2e6, 25e6, 0.05, 0.10, 5), 31: (3e6, 35e6, 0.03, 0.08, 5), 40: (2e6, 25e6, 0.03, 0.08, 5), } DEFAULT_CFG = (1e6, 10e6, 0.02, 0.05, 6) GENERICS = { 'THE', 'BEST', 'QUALITY', 'SUPER', 'PREMIUM', 'SERVICES', 'SOLUTIONS', 'GROUP', 'GLOBAL', 'INTERNATIONAL', 'ENTERPRISE', 'CONSULTING', 'DIGITAL', 'TECH', 'CLOUD', 'SMART', 'PRO', 'PLUS', 'MAX', 'ULTRA', 'ELITE', 'PRIME', 'EXPRESS', 'DIRECT', 'ONLINE', 'NETWORK', 'SYSTEMS' } INDUSTRY_KW = { 5: ['pharm', 'med', 'health', 'cure', 'heal', 'vita', 'bio', 'thera', 'care'], 9: ['tech', 'soft', 'data', 'cyber', 'digit', 'smart', 'logic', 'byte', 'net', 'web', 'app', 'cloud', 'code'], 35: ['biz', 'corp', 'market', 'brand', 'consult', 'strat', 'manage'], 36: ['fin', 'bank', 'fund', 'invest', 'capital', 'wealth', 'pay', 'credit'], 42: ['tech', 'soft', 'dev', 'code', 'cyber', 'cloud', 'data', 'lab'], 45: ['law', 'legal', 'just', 'counsel', 'attorney', 'protect', 'secure'], } PARENT_COS = { 'johnson & johnson': ['kenvue', 'janssen', 'neutrogena', 'tylenol', 'band-aid', 'listerine'], 'procter & gamble': ['tide', 'pampers', 'gillette', 'oral-b', 'crest', 'pantene', 'olay', 'old spice'], 'unilever': ['dove', 'axe', 'lipton', 'knorr', 'hellmanns', 'ben & jerrys', 'vaseline'], 'nestle': ['nescafe', 'nespresso', 'kitkat', 'gerber', 'purina', 'perrier', 'haagen-dazs'], 'pepsico': ['frito-lay', 'doritos', 'lays', 'cheetos', 'quaker', 'gatorade', 'tropicana'], 'coca-cola': ['sprite', 'fanta', 'minute maid', 'powerade', 'dasani', 'smartwater'], 'meta': ['facebook', 'instagram', 'whatsapp', 'oculus', 'threads'], 'alphabet': ['google', 'youtube', 'android', 'chrome', 'gmail', 'waymo', 'nest'], 'amazon': ['alexa', 'kindle', 'prime', 'aws', 'whole foods', 'twitch', 'audible', 'ring'], 'apple': ['iphone', 'ipad', 'macbook', 'airpods', 'apple watch', 'apple music', 'siri'], 'disney': ['marvel', 'pixar', 'lucasfilm', 'star wars', 'espn', 'abc', 'hulu'], } PREMIUM_DESIGN = {'02.01', '02.03', '03.01', '03.03', '03.05', '04.01', '01.01', '01.03', '24.01', '26.03', '26.05'} STANDARD_DESIGN = {'05.01', '05.03', '06.01', '07.01', '08.01', '19.01'} ENTITY_HINTS = [ 'INC', 'LLC', 'CORP', 'CORPORATION', 'LTD', 'LIMITED', 'GMBH', 'PLC', 'COMPANY', 'CO', 'CO ', 'HOLDINGS', 'GROUP', 'LP', 'LLP', 'PC', 'SA', 'S A', 'SAS', 'BV', 'AB', 'AG', 'NV', 'PTY' ] def evaluate_trademark( mark: str, party_name: str, primary_code: int, status_code: int, goods_and_service: str = "", registration_date_epoch: int = None, market_cap: float = 0, portfolio_size: int = 1, design_codes: str = "", mark_drawing_code: int = 4, multi_class_count: int = 1, intl_reg_count: int = 0, competitor_count: int = 0, office_action_count: int = 0, actual_renewal_count: int = 0, late_renewal_count: int = 0, survived_trial: bool = False, section15_filed: bool = False, filing_66a: bool = False, is_supplemental: bool = False, clamp_total_mult: bool = True, total_mult_min: float = 0.20, total_mult_max: float = 6.00, ) -> Dict[str, Any]: """ Evaluate a single trademark and return full breakdown. """ result: Dict[str, Any] = {} now = int(datetime.now().timestamp() * 1000) # Class config cfg = CLASS_CFG.get(primary_code, DEFAULT_CFG) base_min, base_max, pct_min, pct_max, tier = cfg brand_pct = (pct_min + pct_max) / 2 result['class_tier'] = tier result['class_mult'] = {1: 1.25, 2: 1.15, 3: 1.10}.get(tier, 1.0) # Status flags is_registered = status_code in REGISTERED is_itu = status_code in ITU is_pou = status_code in POU has_boost = status_code in STATUS_BOOST # Base value calculation - PRIMARY DRIVER OF VALUATION portfolio = max(1, min(portfolio_size, 1000)) # Portfolio-based factor: Larger portfolios indicate established businesses # Log scale prevents extreme multipliers while rewarding size if portfolio >= 500: portfolio_factor = 3.5 + math.log10(portfolio / 100) * 0.5 elif portfolio >= 100: portfolio_factor = 2.5 + (portfolio - 100) / 200 elif portfolio >= 50: portfolio_factor = 2.0 + (portfolio - 50) / 100 elif portfolio >= 20: portfolio_factor = 1.5 + (portfolio - 20) / 60 elif portfolio >= 10: portfolio_factor = 1.2 + (portfolio - 10) / 30 elif portfolio >= 5: portfolio_factor = 1.0 + (portfolio - 5) / 20 else: portfolio_factor = 0.7 + (portfolio * 0.06) # Class-based positioning adjustment # Higher tier classes get more aggressive base values if tier == 1: # Premium tech/pharma/finance class_base_factor = 1.8 elif tier == 2: # High-value services class_base_factor = 1.4 elif tier == 3: # Consumer goods class_base_factor = 1.2 elif tier == 4: # Industrial/Manufacturing class_base_factor = 1.0 else: # Misc/Low-value class_base_factor = 0.7 if is_itu: # Intent-to-use: speculative value only base = 2000 if market_cap > 0 else 800 result['base_value'] = base elif is_pou: # Proof of use: some value but limited base = 5000 * class_base_factor result['base_value'] = base elif market_cap > 0: # Known company: use market cap proportional valuation base = (market_cap * brand_pct) / portfolio result['base_value'] = max(base, base_min * 0.1) elif is_registered: # Registered mark: main valuation path # Combine class value with portfolio strength base = ((base_min * 0.3) + (base_max * 0.15)) * class_base_factor * portfolio_factor # Add randomness to prevent clustering (±15%) variance = random.uniform(0.85, 1.15) base = base * variance result['base_value'] = max(base, base_min * 0.05) else: # Abandoned/dead marks base = base_min * 0.02 result['base_value'] = max(base, 1000) # Temporal multiplier - AGE PREMIUM # Older marks = more established brand recognition and goodwill if registration_date_epoch and registration_date_epoch > 0: years = (now - registration_date_epoch) / 31557600000 years = max(0.0, years) else: years = 0.0 if years <= 0: # No registration date: penalty result['temporal_mult'] = 0.6 elif years < 1: # Very new: still establishing result['temporal_mult'] = 0.7 + 0.4 * years elif years < 3: # Young but registered result['temporal_mult'] = 1.1 + 0.2 * (years - 1.0) elif years < 7: # Established presence result['temporal_mult'] = 1.5 + 0.15 * (years - 3.0) elif years < 15: # Strong history result['temporal_mult'] = 2.1 + 0.12 * (years - 7.0) elif years < 30: # Legacy brand result['temporal_mult'] = 3.06 + 0.08 * (years - 15.0) else: # Historic mark (rare premium) result['temporal_mult'] = 4.26 + 0.04 * min(50.0, years - 30.0) # Renewal multiplier result['renewal_mult'] = 1.0 + actual_renewal_count * 0.03 - late_renewal_count * 0.07 result['renewal_mult'] = max(0.70, min(1.30, result['renewal_mult'])) # Description multiplier desc_words = len(goods_and_service.split()) if goods_and_service else 0 result['desc_mult'] = min(1.10, 1.0 + 0.02 * math.sqrt(desc_words / 50.0)) if desc_words > 0 else 1.0 # Multi-class registration - SCOPE OF PROTECTION # More classes = broader market coverage = higher value mc = min(10, max(1, multi_class_count)) if mc >= 8: result['multi_class_mult'] = 1.85 elif mc >= 6: result['multi_class_mult'] = 1.65 elif mc >= 4: result['multi_class_mult'] = 1.42 elif mc >= 3: result['multi_class_mult'] = 1.28 elif mc >= 2: result['multi_class_mult'] = 1.15 else: result['multi_class_mult'] = 1.0 # Geographic reach - INTERNATIONAL PRESENCE # International registrations show serious commercial intent geo = min(15, max(1, intl_reg_count + 1)) if geo >= 10: result['geo_mult'] = 2.5 elif geo >= 6: result['geo_mult'] = 2.0 elif geo >= 4: result['geo_mult'] = 1.6 elif geo >= 3: result['geo_mult'] = 1.35 elif geo >= 2: result['geo_mult'] = 1.18 else: result['geo_mult'] = 1.0 # Competitor proximity - MARKET POSITION # Fewer similar marks = stronger distinctiveness and market power if competitor_count <= 0: result['proximity_mult'] = 1.60 # Unique in class result['market_dominance'] = 'DOMINANT' elif competitor_count < 3: result['proximity_mult'] = 1.45 # Near monopoly result['market_dominance'] = 'STRONG' elif competitor_count < 10: result['proximity_mult'] = 1.25 # Leading position result['market_dominance'] = 'STRONG' elif competitor_count < 30: result['proximity_mult'] = 1.05 # Competitive space result['market_dominance'] = 'MODERATE' elif competitor_count < 75: result['proximity_mult'] = 0.80 # Crowded field result['market_dominance'] = 'WEAK' elif competitor_count < 150: result['proximity_mult'] = 0.60 # Saturated result['market_dominance'] = 'CROWDED' else: result['proximity_mult'] = 0.40 # Commodity space result['market_dominance'] = 'SATURATED' # Section 15 result['section15_mult'] = 1.30 if section15_filed else 1.0 # Individual vs company party_upper = (party_name or '').upper() party_clean = party_upper.replace('.', ' ').replace(',', ' ') is_individual = not any(h in party_clean for h in ENTITY_HINTS) if is_individual and market_cap == 0 and portfolio_size <= 3: result['individual_mult'] = 0.75 elif is_individual: result['individual_mult'] = 0.90 else: result['individual_mult'] = 1.0 # Uniqueness heuristic mark_upper = (mark or '').upper() mark_len = len(mark_upper) has_digit = bool(re.search(r'\d', mark_upper)) vowels = sum(1 for c in mark_upper if c in 'AEIOU') vowel_ratio = vowels / max(1, mark_len) if mark_len <= 3: result['uniqueness_mult'] = 1.05 elif has_digit: result['uniqueness_mult'] = 1.08 elif vowel_ratio < 0.2 or vowel_ratio > 0.6: result['uniqueness_mult'] = 1.10 else: result['uniqueness_mult'] = 1.0 # Industry relevance mark_lower = mark_upper.lower() kws = INDUSTRY_KW.get(primary_code, []) matches = sum(1 for kw in kws if kw in mark_lower) if matches >= 2: result['industry_relevance_mult'] = 1.15 elif matches == 1: result['industry_relevance_mult'] = 1.08 else: result['industry_relevance_mult'] = 1.0 # Generic / descriptive checks is_generic = mark_upper in GENERICS mark_in_gs = False if mark and goods_and_service and len(mark) >= 4: pattern = r'\b' + re.escape(mark_lower) + r'\b' mark_in_gs = bool(re.search(pattern, goods_and_service.lower())) if is_generic and mark_in_gs: result['generic_mult'] = 0.10 elif is_generic: result['generic_mult'] = 0.30 elif mark_in_gs: result['generic_mult'] = 0.60 else: result['generic_mult'] = 1.0 # Parent vs subsidiary party_lower = (party_name or '').lower() is_parent_mark = False for parent, subs in PARENT_COS.items(): parent_words = parent.replace('&', '').split() if all(w in party_lower for w in parent_words): if all(w in mark_lower for w in parent_words): is_parent_mark = True break result['parent_mult'] = 1.15 if is_parent_mark else 1.0 # Office actions oa = min(6, max(0, office_action_count)) result['oa_mult'] = max(0.70, 1.0 - 0.05 * oa) # Portfolio multiplier - CORPORATE STRENGTH INDICATOR # Large portfolios = institutional backing and resources for enforcement if portfolio_size >= 500: result['portfolio_mult'] = 1.65 # Major corporation elif portfolio_size >= 250: result['portfolio_mult'] = 1.50 # Large enterprise elif portfolio_size >= 100: result['portfolio_mult'] = 1.35 # Established company elif portfolio_size >= 50: result['portfolio_mult'] = 1.22 # Mid-size business elif portfolio_size >= 25: result['portfolio_mult'] = 1.14 # Growing portfolio elif portfolio_size >= 10: result['portfolio_mult'] = 1.08 # Active filer elif portfolio_size >= 5: result['portfolio_mult'] = 1.02 # Small business else: result['portfolio_mult'] = 0.90 # Individual/startup # Logo value has_design = mark_drawing_code in [2, 3, 5, 6] codes = set(design_codes.split(';')) if design_codes else set() if not has_design: result['logo_value'] = 0 elif codes & PREMIUM_DESIGN: result['logo_value'] = 75000 + min(25000, len(codes) * 5000) elif codes & STANDARD_DESIGN: result['logo_value'] = 35000 + min(25000, len(codes) * 5000) else: result['logo_value'] = 10000 + min(25000, len(codes) * 5000) # Flat bonuses / penalties if filing_66a: result['filing_66a_bonus'] = {1: 20000, 2: 20000, 3: 12500, 4: 12500}.get(tier, 5000) else: result['filing_66a_bonus'] = 0 result['trial_bonus'] = 50000 if survived_trial else 0 result['status_bonus'] = 10000 if has_boost else 0 result['supplemental_penalty'] = -25000 if is_supplemental else 0 # Final calculation mult_keys = [ 'temporal_mult', 'renewal_mult', 'class_mult', 'desc_mult', 'multi_class_mult', 'geo_mult', 'proximity_mult', 'section15_mult', 'individual_mult', 'uniqueness_mult', 'industry_relevance_mult', 'generic_mult', 'parent_mult', 'oa_mult', 'portfolio_mult' ] mult_product = 1.0 for k in mult_keys: mult_product *= float(result[k]) if clamp_total_mult: mult_product = max(total_mult_min, min(total_mult_max, mult_product)) computed = ( result['base_value'] * mult_product + result['logo_value'] + result['filing_66a_bonus'] + result['trial_bonus'] + result['status_bonus'] + result['supplemental_penalty'] ) result['computed_value'] = max(500, min(50e9, computed)) # CONFIDENCE SCORE - ESTIMATION RELIABILITY # Measures how confident we are in the valuation (data quality + factors) conf = 30 # Baseline: minimal data scenario # === STRONG POSITIVE INDICATORS === # Public company data (highest confidence) if market_cap > 0: conf += 28 # Core registration strength if is_registered: conf += 18 if section15_filed: # Incontestable status conf += 10 if registration_date_epoch: conf += 8 # Portfolio sophistication (signals professional management) if portfolio_size >= 200: conf += 15 elif portfolio_size >= 100: conf += 12 elif portfolio_size >= 50: conf += 9 elif portfolio_size >= 20: conf += 6 elif portfolio_size >= 10: conf += 4 # Market coverage breadth if multi_class_count >= 6: conf += 12 elif multi_class_count >= 4: conf += 8 elif multi_class_count >= 2: conf += 4 # International validation if intl_reg_count >= 5: conf += 14 elif intl_reg_count >= 3: conf += 10 elif intl_reg_count >= 1: conf += 5 # Goods/services specificity if desc_words > 60: conf += 9 elif desc_words > 30: conf += 6 elif desc_words > 15: conf += 3 # Proven longevity if actual_renewal_count >= 4: conf += 12 elif actual_renewal_count >= 2: conf += 8 elif actual_renewal_count >= 1: conf += 4 # Temporal confidence if years >= 25: conf += 12 elif years >= 15: conf += 9 elif years >= 10: conf += 6 elif years >= 5: conf += 3 # Recognized brand indicators if is_parent_mark: conf += 18 if result['industry_relevance_mult'] >= 1.12: conf += 7 elif result['industry_relevance_mult'] > 1: conf += 3 # Legal strength if survived_trial: conf += 10 # Market position clarity if competitor_count == 0: conf += 10 elif competitor_count <= 5: conf += 6 elif competitor_count <= 20: conf += 2 # === NEGATIVE INDICATORS (REDUCE CONFIDENCE) === # Individual ownership (less data, more variability) if is_individual and market_cap == 0: if portfolio_size <= 2: conf -= 20 elif portfolio_size <= 5: conf -= 12 elif portfolio_size <= 10: conf -= 6 # Weakness indicators if is_generic: conf -= 30 # Highly uncertain value if mark_in_gs: conf -= 18 # Descriptive = lower distinctiveness if is_itu: conf -= 30 # Not yet in commerce if not party_name: conf -= 25 # Missing critical data if is_supplemental: conf -= 22 # Weaker protection if late_renewal_count >= 3: conf -= 15 elif late_renewal_count > 0: conf -= 8 * late_renewal_count # Prosecution issues if office_action_count >= 5: conf -= 15 elif office_action_count >= 3: conf -= 10 elif office_action_count > 0: conf -= 5 # Market saturation uncertainty if competitor_count >= 200: conf -= 20 elif competitor_count >= 100: conf -= 12 elif competitor_count >= 50: conf -= 6 # Immaturity penalty if years < 0.5: conf -= 15 elif years < 2: conf -= 8 # Finalize confidence conf = max(5, min(100, conf)) result['confidence_score'] = conf # Classification thresholds if conf >= 78: result['confidence_level'] = 'HIGH' elif conf >= 58: result['confidence_level'] = 'MEDIUM' elif conf >= 35: result['confidence_level'] = 'LOW' else: result['confidence_level'] = 'VERY_LOW' return result