""" Formatting Utilities. Provides consistent formatting for currency, percentages, and numbers. """ from typing import Union def format_currency(amount: float, symbol: str = "€", decimals: int = 2) -> str: """ Format amount as currency with thousands separator. Args: amount: Amount to format symbol: Currency symbol (default: €) decimals: Number of decimal places (default: 2) Returns: Formatted string (e.g., "1,234.56 €") Examples: >>> format_currency(1234.56) '1,234.56 €' >>> format_currency(180000) '180,000.00 €' >>> format_currency(-523.45) '-523.45 €' """ formatted = f"{amount:,.{decimals}f}" return f"{formatted} {symbol}" def format_percentage( value: float, decimals: int = 1, show_sign: bool = True, symbol: str = "%" ) -> str: """ Format value as percentage. Args: value: Percentage value decimals: Number of decimal places (default: 1) show_sign: Show + sign for positive values (default: True) symbol: Percentage symbol (default: %) Returns: Formatted string (e.g., "+12.5%", "-3.2%") Examples: >>> format_percentage(12.5) '+12.5%' >>> format_percentage(-3.2) '-3.2%' >>> format_percentage(7.9, decimals=2) '+7.90%' >>> format_percentage(5.0, show_sign=False) '5.0%' """ if show_sign and value > 0: sign = "+" elif show_sign and value < 0: sign = "" # Negative sign is included in number else: sign = "" formatted = f"{value:.{decimals}f}" return f"{sign}{formatted}{symbol}" def parse_currency(text: str) -> float: """ Parse currency string to float. Args: text: Currency string (e.g., "1,234.56 €" or "1234.56") Returns: Float value Examples: >>> parse_currency("1,234.56 €") 1234.56 >>> parse_currency("1234.56") 1234.56 >>> parse_currency("-523.45 €") -523.45 """ # Remove currency symbols and thousands separators cleaned = text.replace(",", "").replace("€", "").replace("$", "").strip() return float(cleaned) def format_number(value: Union[int, float], decimals: int = 2) -> str: """ Format number with thousands separator. Args: value: Number to format decimals: Number of decimal places (default: 2) Returns: Formatted string Examples: >>> format_number(1234.5678) '1,234.57' >>> format_number(1000000) '1,000,000.00' """ return f"{value:,.{decimals}f}" def format_shares(quantity: float, decimals: int = 0) -> str: """ Format share quantity. Args: quantity: Number of shares decimals: Number of decimal places (default: 0 for whole shares) Returns: Formatted string Examples: >>> format_shares(176) '176' >>> format_shares(1200.5, decimals=2) '1,200.50' """ if decimals == 0 and quantity == int(quantity): return f"{int(quantity):,}" else: return f"{quantity:,.{decimals}f}" def format_compact_currency(amount: float, symbol: str = "€") -> str: """ Format large amounts in compact form (K, M, B). Args: amount: Amount to format symbol: Currency symbol Returns: Compact formatted string Examples: >>> format_compact_currency(1500) '1.5K €' >>> format_compact_currency(180000) '180.0K €' >>> format_compact_currency(1500000) '1.5M €' """ abs_amount = abs(amount) sign = "-" if amount < 0 else "" if abs_amount >= 1_000_000_000: return f"{sign}{abs_amount / 1_000_000_000:.1f}B {symbol}" elif abs_amount >= 1_000_000: return f"{sign}{abs_amount / 1_000_000:.1f}M {symbol}" elif abs_amount >= 1_000: return f"{sign}{abs_amount / 1_000:.1f}K {symbol}" else: return f"{sign}{abs_amount:.2f} {symbol}" def color_for_pnl(pnl: float) -> str: """ Get color code for P&L value. Args: pnl: P&L amount or percentage Returns: Color hex code (green for positive, red for negative, gray for zero) Examples: >>> color_for_pnl(123.45) '#28a745' >>> color_for_pnl(-50.00) '#dc3545' >>> color_for_pnl(0.0) '#6c757d' """ if pnl > 0: return '#28a745' # Bootstrap green elif pnl < 0: return '#dc3545' # Bootstrap red else: return '#6c757d' # Bootstrap gray def format_date(date_str: str, input_format: str = "%Y-%m-%d", output_format: str = "%d/%m/%Y") -> str: """ Format date string. Args: date_str: Input date string input_format: Input date format (default: ISO format) output_format: Output date format (default: DD/MM/YYYY) Returns: Formatted date string Examples: >>> format_date("2025-12-15") '15/12/2025' >>> format_date("2025-12-15", output_format="%d %b %Y") '15 Dec 2025' """ from datetime import datetime date_obj = datetime.strptime(date_str, input_format) return date_obj.strftime(output_format) def truncate_text(text: str, max_length: int = 30, suffix: str = "...") -> str: """ Truncate text to max length. Args: text: Text to truncate max_length: Maximum length suffix: Suffix for truncated text Returns: Truncated text Examples: >>> truncate_text("This is a very long text", 15) 'This is a ve...' >>> truncate_text("Short", 15) 'Short' """ if len(text) <= max_length: return text return text[:max_length - len(suffix)] + suffix