""" Lemontropia Suite - Market Price Tracker Fetch and track market prices from EntropiaWiki and other sources. """ import json import logging import requests from decimal import Decimal from pathlib import Path from dataclasses import dataclass from typing import Dict, Optional, List from datetime import datetime, timedelta logger = logging.getLogger(__name__) @dataclass class MarketPrice: """Market price data for an item.""" item_name: str markup: Decimal # Percentage (e.g., 120.5 means 120.5%) tt_value: Decimal # Trade Terminal value mu_value: Decimal # Markup value last_updated: datetime source: str # Where the price came from class EntropiaWikiPrices: """ Fetch market prices from EntropiaWiki. Note: This requires scraping or API access. EntropiaWiki may have rate limits. """ BASE_URL = "https://www.entropiawiki.com" def __init__(self, cache_duration: int = 3600): self.cache_duration = cache_duration # Seconds self.price_cache: Dict[str, MarketPrice] = {} self.cache_timestamps: Dict[str, datetime] = {} self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Lemontropia-Suite/1.0 (Personal Use)' }) def _is_cache_valid(self, item_name: str) -> bool: """Check if cached price is still valid.""" if item_name not in self.cache_timestamps: return False age = datetime.now() - self.cache_timestamps[item_name] return age.seconds < self.cache_duration def get_item_price(self, item_name: str) -> Optional[MarketPrice]: """ Get market price for item. Note: This is a placeholder. Actual implementation would need to scrape EntropiaWiki or use their API if available. """ # Check cache first if self._is_cache_valid(item_name): return self.price_cache[item_name] # Placeholder: In reality, this would fetch from wiki # For now, return None to indicate we need real implementation logger.debug(f"Price lookup not implemented for: {item_name}") return None def clear_cache(self): """Clear price cache.""" self.price_cache.clear() self.cache_timestamps.clear() class ManualPriceTracker: """ Manual price tracking for items you frequently trade. """ def __init__(self, data_dir: Optional[Path] = None): self.data_dir = data_dir or Path.home() / ".lemontropia" / "prices" self.data_dir.mkdir(parents=True, exist_ok=True) self.prices: Dict[str, MarketPrice] = {} self._load_prices() def _load_prices(self): """Load saved prices.""" price_file = self.data_dir / "manual_prices.json" if price_file.exists(): try: with open(price_file, 'r') as f: data = json.load(f) for name, price_data in data.items(): self.prices[name] = MarketPrice( item_name=name, markup=Decimal(str(price_data.get('markup', 100))), tt_value=Decimal(str(price_data.get('tt', 0))), mu_value=Decimal(str(price_data.get('mu', 0))), last_updated=datetime.fromisoformat(price_data.get('updated', datetime.now().isoformat())), source='manual' ) except Exception as e: logger.error(f"Failed to load prices: {e}") def _save_prices(self): """Save prices to file.""" price_file = self.data_dir / "manual_prices.json" try: data = {} for name, price in self.prices.items(): data[name] = { 'markup': str(price.markup), 'tt': str(price.tt_value), 'mu': str(price.mu_value), 'updated': price.last_updated.isoformat(), } with open(price_file, 'w') as f: json.dump(data, f, indent=2) except Exception as e: logger.error(f"Failed to save prices: {e}") def set_price(self, item_name: str, markup: Decimal, tt_value: Decimal): """Manually set price for an item.""" mu_value = tt_value * (markup / 100) self.prices[item_name] = MarketPrice( item_name=item_name, markup=markup, tt_value=tt_value, mu_value=mu_value, last_updated=datetime.now(), source='manual' ) self._save_prices() logger.info(f"Set price for {item_name}: {markup}%") def get_price(self, item_name: str) -> Optional[MarketPrice]: """Get price for item.""" return self.prices.get(item_name) def calculate_loot_value(self, loot_items: List[Tuple[str, Decimal, int]]) -> Dict: """ Calculate total value of loot based on tracked prices. Args: loot_items: List of (item_name, tt_value, quantity) Returns: Dict with tt_value, mu_value, markup breakdown """ total_tt = Decimal("0") total_mu = Decimal("0") unknown_items = [] for item_name, tt_value, quantity in loot_items: item_tt = tt_value * quantity total_tt += item_tt price = self.get_price(item_name) if price: item_mu = item_tt * (price.markup / 100) total_mu += item_mu else: # Assume 100% (TT value) if no price total_mu += item_tt unknown_items.append(item_name) return { 'total_tt': total_tt, 'total_mu': total_mu, 'total_markup': (total_mu / total_tt * 100) if total_tt > 0 else Decimal("0"), 'unknown_items': list(set(unknown_items)), } class ProfitCalculator: """ Calculate crafting/hunting profitability with current market prices. """ def __init__(self, price_tracker: ManualPriceTracker): self.price_tracker = price_tracker def calculate_crafting_profit(self, blueprint: str, material_costs: Dict[str, Decimal], output_items: List[Tuple[str, Decimal, int]]) -> Dict: """ Calculate crafting profitability. Args: blueprint: Blueprint name material_costs: Dict of material_name -> cost output_items: List of (item_name, tt_value, quantity) Returns: Profit analysis """ # Calculate input cost input_cost = sum(material_costs.values()) # Calculate output value loot_value = self.price_tracker.calculate_loot_value(output_items) output_mu = loot_value['total_mu'] # Calculate profit profit = output_mu - input_cost profit_margin = (profit / input_cost * 100) if input_cost > 0 else Decimal("0") return { 'input_cost': input_cost, 'output_tt': loot_value['total_tt'], 'output_mu': output_mu, 'profit': profit, 'profit_margin': profit_margin, 'is_profitable': profit > 0, 'unknown_prices': loot_value['unknown_items'], } def calculate_hunting_profit(self, ammo_cost: Decimal, armor_decay: Decimal, healing_cost: Decimal, loot_items: List[Tuple[str, Decimal, int]]) -> Dict: """ Calculate hunting profitability. Args: ammo_cost: Total ammo spent armor_decay: Armor decay cost healing_cost: Healing cost loot_items: List of (item_name, tt_value, quantity) > Returns: Profit analysis """ total_cost = ammo_cost + armor_decay + healing_cost loot_value = self.price_tracker.calculate_loot_value(loot_items) loot_mu = loot_value['total_mu'] profit = loot_mu - total_cost return_pct = (loot_mu / total_cost * 100) if total_cost > 0 else Decimal("0") return { 'total_cost': total_cost, 'ammo_cost': ammo_cost, 'armor_decay': armor_decay, 'healing_cost': healing_cost, 'loot_tt': loot_value['total_tt'], 'loot_mu': loot_mu, 'profit': profit, 'return_pct': return_pct, 'unknown_prices': loot_value['unknown_items'], } # Export main classes __all__ = ['MarketPrice', 'ManualPriceTracker', 'ProfitCalculator', 'EntropiaWikiPrices']