""" Dynamic Pricing Engine with Demand Analytics Advanced pricing algorithms for ticketing platform """ import json import hashlib import datetime import math import random from typing import Dict, List, Optional, Any, Union, Tuple from dataclasses import dataclass, asdict from collections import defaultdict, deque @dataclass class PricingFactors: demand_multiplier: float time_multiplier: float inventory_multiplier: float competitor_multiplier: float weather_multiplier: float event_type_multiplier: float @dataclass class PriceCalculation: original_price: float calculated_price: float factors: PricingFactors confidence_score: float recommendation: str class DynamicPricingEngine: """Advanced dynamic pricing with real-time analytics""" def __init__(self): self.historical_data = defaultdict(list) self.real_time_demand = defaultdict(deque) self.competitor_prices = {} self.weather_data = {} self.event_categories = { 'concert': {'base_multiplier': 1.2, 'volatility': 0.3}, 'sports': {'base_multiplier': 1.1, 'volatility': 0.2}, 'theater': {'base_multiplier': 1.15, 'volatility': 0.15}, 'conference': {'base_multiplier': 1.05, 'volatility': 0.1}, 'festival': {'base_multiplier': 1.3, 'volatility': 0.4} } self.pricing_history = [] def calculate_optimal_price(self, request: Dict[str, Any]) -> PriceCalculation: """Calculate optimal price using multiple factors""" event_id = request['event_id'] base_price = request['base_price'] event_type = request.get('event_type', 'concert') event_date = request.get('event_date') current_inventory = request.get('inventory_available', 100) total_inventory = request.get('total_inventory', 1000) venue_capacity = request.get('venue_capacity', total_inventory) # Calculate individual factors demand_factor = self._calculate_demand_factor(event_id, current_inventory, total_inventory) time_factor = self._calculate_time_factor(event_date) inventory_factor = self._calculate_inventory_factor(current_inventory, total_inventory) competitor_factor = self._calculate_competitor_factor(event_id, event_type) weather_factor = self._calculate_weather_factor(event_id, event_date) event_type_factor = self._calculate_event_type_factor(event_type) # Create pricing factors object factors = PricingFactors( demand_multiplier=demand_factor, time_multiplier=time_factor, inventory_multiplier=inventory_factor, competitor_multiplier=competitor_factor, weather_multiplier=weather_factor, event_type_multiplier=event_type_factor ) # Calculate final price price_multiplier = ( demand_factor * time_factor * inventory_factor * competitor_factor * weather_factor * event_type_factor ) calculated_price = base_price * price_multiplier # Apply price bounds calculated_price = self._apply_price_bounds(base_price, calculated_price, event_type) # Calculate confidence score confidence_score = self._calculate_confidence_score(factors, event_id) # Generate recommendation recommendation = self._generate_recommendation(calculated_price, base_price, confidence_score) calculation = PriceCalculation( original_price=base_price, calculated_price=round(calculated_price, 2), factors=factors, confidence_score=confidence_score, recommendation=recommendation ) # Store calculation for learning self._store_calculation(event_id, calculation) return calculation def _calculate_demand_factor(self, event_id: str, current_inventory: int, total_inventory: int) -> float: """Calculate demand-based multiplier""" # Real-time demand tracking demand_window = 24 # hours current_time = datetime.datetime.now() # Get recent demand data recent_demand = 0 demand_data = self.real_time_demand[event_id] while demand_data and (current_time - demand_data[0]['timestamp']).total_seconds() > demand_window * 3600: demand_data.popleft() for data in demand_data: recent_demand += data['requests'] # Calculate demand velocity demand_velocity = recent_demand / max(1, len(demand_data)) # Inventory pressure inventory_pressure = 1 - (current_inventory / max(1, total_inventory)) # Historical demand patterns historical_avg = self._get_historical_demand_average(event_id) # Combine factors demand_score = (demand_velocity * 0.4) + (inventory_pressure * 0.4) + (historical_avg * 0.2) # Convert to multiplier return 1.0 + (demand_score * 0.5) def _calculate_time_factor(self, event_date: Optional[str]) -> float: """Calculate time-based pricing factor""" if not event_date: return 1.0 try: event_dt = datetime.datetime.fromisoformat(event_date.replace('Z', '+00:00')) current_dt = datetime.datetime.now() # Days until event days_until = (event_dt - current_dt).days # Different time windows have different multipliers if days_until > 90: return 0.95 # Early bird discount elif days_until > 60: return 1.0 # Standard pricing elif days_until > 30: return 1.05 # Building demand elif days_until > 14: return 1.15 # Peak interest elif days_until > 7: return 1.25 # High demand period elif days_until > 3: return 1.4 # Last minute premium elif days_until > 1: return 1.6 # Urgency pricing else: return 1.8 # Event day pricing except Exception: return 1.0 def _calculate_inventory_factor(self, current_inventory: int, total_inventory: int) -> float: """Calculate inventory-based pricing factor""" inventory_ratio = current_inventory / max(1, total_inventory) if inventory_ratio > 0.8: return 0.95 # Plenty available elif inventory_ratio > 0.6: return 1.0 # Good availability elif inventory_ratio > 0.4: return 1.1 # Limited availability elif inventory_ratio > 0.2: return 1.25 # Scarce elif inventory_ratio > 0.1: return 1.5 # Very limited else: return 2.0 # Almost sold out def _calculate_competitor_factor(self, event_id: str, event_type: str) -> float: """Calculate competitor-based pricing factor""" if event_id not in self.competitor_prices: return 1.0 competitor_avg = sum(self.competitor_prices[event_id]) / max(1, len(self.competitor_prices[event_id])) # If competitors are cheaper, we might need to adjust if competitor_avg < 0.9: return 0.95 # Competitive pricing elif competitor_avg > 1.1: return 1.05 # Premium pricing opportunity else: return 1.0 # Market aligned def _calculate_weather_factor(self, event_id: str, event_date: Optional[str]) -> float: """Calculate weather-based pricing factor for outdoor events""" # For indoor events, weather doesn't matter if not event_date or not self._is_outdoor_event(event_id): return 1.0 # Simulate weather impact (would integrate with real weather API) weather_score = 0.8 # Placeholder for weather forecast if weather_score > 0.8: return 1.1 # Great weather, premium pricing elif weather_score > 0.6: return 1.0 # Normal weather elif weather_score > 0.4: return 0.95 # Poor weather, slight discount else: return 0.9 # Bad weather, discount needed def _calculate_event_type_factor(self, event_type: str) -> float: """Calculate event type-based pricing factor""" event_config = self.event_categories.get(event_type, {'base_multiplier': 1.0, 'volatility': 0.2}) # Add some randomness for volatility volatility_adjustment = 1.0 + (random.random() - 0.5) * event_config['volatility'] return event_config['base_multiplier'] * volatility_adjustment def _apply_price_bounds(self, base_price: float, calculated_price: float, event_type: str) -> float: """Apply reasonable bounds to prevent extreme pricing""" # Minimum and maximum multipliers min_multiplier = 0.5 max_multiplier = 3.0 # Event-specific bounds if event_type == 'festival': max_multiplier = 4.0 elif event_type == 'conference': max_multiplier = 2.0 bounded_price = max(base_price * min_multiplier, min(calculated_price, base_price * max_multiplier)) return bounded_price def _calculate_confidence_score(self, factors: PricingFactors, event_id: str) -> float: """Calculate confidence score for pricing prediction""" # Data availability score data_score = min(1.0, len(self.historical_data[event_id]) / 100) # Factor variance score (lower variance = higher confidence) factor_values = [ factors.demand_multiplier, factors.time_multiplier, factors.inventory_multiplier, factors.competitor_multiplier, factors.weather_multiplier, factors.event_type_multiplier ] variance = sum((x - sum(factor_values)/len(factor_values))**2 for x in factor_values) / len(factor_values) variance_score = max(0.0, 1.0 - variance) # Combined confidence confidence = (data_score * 0.6) + (variance_score * 0.4) return round(confidence, 3) def _generate_recommendation(self, calculated_price: float, base_price: float, confidence: float) -> str: """Generate pricing recommendation""" price_change = ((calculated_price - base_price) / base_price) * 100 if confidence < 0.5: return "LIMITED_DATA - Use caution with pricing" elif abs(price_change) < 5: return "MAINTAIN_PRICE - Current pricing is optimal" elif price_change > 20: return "SIGNIFICANT_INCREASE - High demand detected" elif price_change > 10: return "MODERATE_INCREASE - Growing demand" elif price_change < -10: return "MODERATE_DECREASE - Boost sales needed" else: return "MINOR_ADJUSTMENT - Small price optimization" def _store_calculation(self, event_id: str, calculation: PriceCalculation): """Store calculation for machine learning""" self.pricing_history.append({ 'event_id': event_id, 'timestamp': datetime.datetime.now().isoformat(), 'original_price': calculation.original_price, 'calculated_price': calculation.calculated_price, 'factors': asdict(calculation.factors), 'confidence': calculation.confidence_score }) def _get_historical_demand_average(self, event_id: str) -> float: """Get historical demand average for event type""" if not self.historical_data[event_id]: return 0.5 # Default moderate demand recent_data = self.historical_data[event_id][-50:] # Last 50 data points return sum(data['demand'] for data in recent_data) / len(recent_data) def _is_outdoor_event(self, event_id: str) -> bool: """Check if event is outdoors""" # Simplified logic - would check event details return 'outdoor' in event_id.lower() or 'festival' in event_id.lower() def record_demand(self, event_id: str, requests: int): """Record real-time demand data""" self.real_time_demand[event_id].append({ 'timestamp': datetime.datetime.now(), 'requests': requests }) def update_competitor_prices(self, event_id: str, prices: List[float]): """Update competitor pricing data""" self.competitor_prices[event_id] = prices def get_pricing_analytics(self, event_id: str, days: int = 30) -> Dict[str, Any]: """Get comprehensive pricing analytics""" cutoff_date = datetime.datetime.now() - datetime.timedelta(days=days) relevant_calculations = [ calc for calc in self.pricing_history if calc['event_id'] == event_id and datetime.datetime.fromisoformat(calc['timestamp']) > cutoff_date ] if not relevant_calculations: return {'error': 'No data available for specified period'} prices = [calc['calculated_price'] for calc in relevant_calculations] confidences = [calc['confidence'] for calc in relevant_calculations] return { 'event_id': event_id, 'period_days': days, 'total_calculations': len(relevant_calculations), 'price_stats': { 'min_price': min(prices), 'max_price': max(prices), 'avg_price': sum(prices) / len(prices), 'price_volatility': (max(prices) - min(prices)) / (sum(prices) / len(prices)) }, 'confidence_stats': { 'avg_confidence': sum(confidences) / len(confidences), 'min_confidence': min(confidences), 'max_confidence': max(confidences) }, 'trend': self._calculate_price_trend(prices) } def _calculate_price_trend(self, prices: List[float]) -> str: """Calculate price trend over time""" if len(prices) < 2: return 'INSUFFICIENT_DATA' recent_prices = prices[-10:] if len(prices) > 10 else prices older_prices = prices[:-10] if len(prices) > 10 else prices[:len(prices)//2] recent_avg = sum(recent_prices) / len(recent_prices) older_avg = sum(older_prices) / len(older_prices) change_percent = ((recent_avg - older_avg) / older_avg) * 100 if change_percent > 5: return 'INCREASING' elif change_percent < -5: return 'DECREASING' else: return 'STABLE' # Global pricing engine instance pricing_engine = DynamicPricingEngine() def test_pricing_engine(): """Test dynamic pricing engine""" print("Testing Dynamic Pricing Engine...") # Test price calculation pricing_request = { 'event_id': 'OUTDOOR_FESTIVAL_2024', 'base_price': 100.00, 'event_type': 'festival', 'event_date': '2024-07-15T18:00:00Z', 'inventory_available': 150, 'total_inventory': 1000, 'venue_capacity': 1000 } result = pricing_engine.calculate_optimal_price(pricing_request) print(f"Original Price: ${result.original_price}") print(f"Calculated Price: ${result.calculated_price}") print(f"Price Change: {((result.calculated_price - result.original_price) / result.original_price) * 100:.1f}%") print(f"Confidence: {result.confidence_score}") print(f"Recommendation: {result.recommendation}") # Test demand recording pricing_engine.record_demand('OUTDOOR_FESTIVAL_2024', 25) pricing_engine.record_demand('OUTDOOR_FESTIVAL_2024', 30) # Test competitor prices pricing_engine.update_competitor_prices('OUTDOOR_FESTIVAL_2024', [95.0, 105.0, 110.0]) # Get analytics analytics = pricing_engine.get_pricing_analytics('OUTDOOR_FESTIVAL_2024', 7) print(f"Analytics: {analytics}") return True if __name__ == "__main__": test_pricing_engine()