Files
Lottery-Tracker/config.py
2026-02-18 08:24:54 -05:00

213 lines
7.3 KiB
Python

"""
Centralized configuration for the Lottery Investment Calculator.
All magic numbers, URLs, tax rates, and tunable parameters live here.
Values are loaded from environment variables with sensible defaults.
"""
import os
from dataclasses import dataclass, field
# ---------------------------------------------------------------------------
# US State Tax Rates (2024-2025)
# ---------------------------------------------------------------------------
STATE_TAX_RATES: dict[str, dict] = {
"AL": {"name": "Alabama", "rate": 0.05},
"AK": {"name": "Alaska", "rate": 0.0},
"AZ": {"name": "Arizona", "rate": 0.025},
"AR": {"name": "Arkansas", "rate": 0.044},
"CA": {"name": "California", "rate": 0.133},
"CO": {"name": "Colorado", "rate": 0.044},
"CT": {"name": "Connecticut", "rate": 0.0699},
"DE": {"name": "Delaware", "rate": 0.066},
"FL": {"name": "Florida", "rate": 0.0},
"GA": {"name": "Georgia", "rate": 0.055},
"HI": {"name": "Hawaii", "rate": 0.11},
"ID": {"name": "Idaho", "rate": 0.058},
"IL": {"name": "Illinois", "rate": 0.0495},
"IN": {"name": "Indiana", "rate": 0.0305},
"IA": {"name": "Iowa", "rate": 0.06},
"KS": {"name": "Kansas", "rate": 0.057},
"KY": {"name": "Kentucky", "rate": 0.04},
"LA": {"name": "Louisiana", "rate": 0.0425},
"ME": {"name": "Maine", "rate": 0.0715},
"MD": {"name": "Maryland", "rate": 0.0575},
"MA": {"name": "Massachusetts", "rate": 0.09},
"MI": {"name": "Michigan", "rate": 0.0425},
"MN": {"name": "Minnesota", "rate": 0.0985},
"MS": {"name": "Mississippi", "rate": 0.05},
"MO": {"name": "Missouri", "rate": 0.048},
"MT": {"name": "Montana", "rate": 0.059},
"NE": {"name": "Nebraska", "rate": 0.0564},
"NV": {"name": "Nevada", "rate": 0.0},
"NH": {"name": "New Hampshire", "rate": 0.0},
"NJ": {"name": "New Jersey", "rate": 0.1075},
"NM": {"name": "New Mexico", "rate": 0.059},
"NY": {"name": "New York", "rate": 0.109},
"NC": {"name": "North Carolina", "rate": 0.045},
"ND": {"name": "North Dakota", "rate": 0.0195},
"OH": {"name": "Ohio", "rate": 0.0357},
"OK": {"name": "Oklahoma", "rate": 0.0475},
"OR": {"name": "Oregon", "rate": 0.099},
"PA": {"name": "Pennsylvania", "rate": 0.0307},
"RI": {"name": "Rhode Island", "rate": 0.0599},
"SC": {"name": "South Carolina", "rate": 0.064},
"SD": {"name": "South Dakota", "rate": 0.0},
"TN": {"name": "Tennessee", "rate": 0.0},
"TX": {"name": "Texas", "rate": 0.0},
"UT": {"name": "Utah", "rate": 0.0465},
"VT": {"name": "Vermont", "rate": 0.0875},
"VA": {"name": "Virginia", "rate": 0.0575},
"WA": {"name": "Washington", "rate": 0.0},
"WV": {"name": "West Virginia", "rate": 0.055},
"WI": {"name": "Wisconsin", "rate": 0.0765},
"WY": {"name": "Wyoming", "rate": 0.0},
"DC": {"name": "District of Columbia", "rate": 0.105},
}
# ---------------------------------------------------------------------------
# Lottery Odds (1 in N for jackpot)
# ---------------------------------------------------------------------------
LOTTERY_ODDS: dict[str, dict] = {
"powerball": {
"name": "Powerball",
"odds": 292_201_338,
"ticket_cost": 2.0,
"country": "us",
},
"megaMillions": {
"name": "Mega Millions",
"odds": 302_575_350,
"ticket_cost": 2.0,
"country": "us",
},
"lottoMax": {
"name": "Lotto Max",
"odds": 33_294_800,
"ticket_cost": 5.0,
"country": "canadian",
},
"lotto649": {
"name": "Lotto 6/49",
"odds": 13_983_816,
"ticket_cost": 3.0,
"country": "canadian",
},
}
# Annuity constants
ANNUITY_YEARS = 30
ANNUITY_ANNUAL_INCREASE = 0.05 # 5% annual increase (Powerball/MM standard)
def _env_float(key: str, default: float) -> float:
"""Read a float from an environment variable with a fallback."""
raw = os.environ.get(key)
if raw is None:
return default
try:
return float(raw)
except ValueError:
return default
def _env_str(key: str, default: str) -> str:
return os.environ.get(key, default)
def _env_int(key: str, default: int) -> int:
raw = os.environ.get(key)
if raw is None:
return default
try:
return int(raw)
except ValueError:
return default
def _env_bool(key: str, default: bool) -> bool:
raw = os.environ.get(key, "").lower()
if raw in ("1", "true", "yes"):
return True
if raw in ("0", "false", "no"):
return False
return default
@dataclass(frozen=True)
class ScraperURLs:
"""Target URLs for lottery scraping."""
powerball: str = "https://www.lotto.net/powerball"
mega_millions: str = "https://www.lotto.net/mega-millions"
olg: str = "https://www.olg.ca/"
@dataclass(frozen=True)
class TaxConfig:
"""Tax and financial parameters."""
lump_sum_rate: float = 0.52
federal_tax_rate: float = 0.37
default_state_tax_rate: float = 0.055
usd_cad_rate: float = 1.35
investment_income_tax_rate: float = 0.5353
personal_withdrawal_pct: float = 0.10
@dataclass(frozen=True)
class InvestmentDefaults:
"""Default investment calculation parameters."""
invest_percentage: float = 0.90
annual_return: float = 0.045
cycles: int = 8
@dataclass(frozen=True)
class AppConfig:
"""Top-level application configuration."""
# Flask
debug: bool = False
host: str = "0.0.0.0"
port: int = 5000
allowed_origins: str = "*"
# Sub-configs
urls: ScraperURLs = field(default_factory=ScraperURLs)
tax: TaxConfig = field(default_factory=TaxConfig)
investment: InvestmentDefaults = field(default_factory=InvestmentDefaults)
# Scraper cache TTL in seconds (default 6 hours)
cache_ttl: int = 21600
def load_config() -> AppConfig:
"""Build an ``AppConfig`` from environment variables."""
return AppConfig(
debug=_env_bool("FLASK_DEBUG", False),
host=_env_str("FLASK_HOST", "0.0.0.0"),
port=_env_int("FLASK_PORT", 5000),
allowed_origins=_env_str("ALLOWED_ORIGINS", "*"),
urls=ScraperURLs(
powerball=_env_str("SCRAPER_URL_POWERBALL", ScraperURLs.powerball),
mega_millions=_env_str("SCRAPER_URL_MEGA_MILLIONS", ScraperURLs.mega_millions),
olg=_env_str("SCRAPER_URL_OLG", ScraperURLs.olg),
),
tax=TaxConfig(
lump_sum_rate=_env_float("LUMP_SUM_RATE", TaxConfig.lump_sum_rate),
federal_tax_rate=_env_float("FEDERAL_TAX_RATE", TaxConfig.federal_tax_rate),
default_state_tax_rate=_env_float("DEFAULT_STATE_TAX_RATE", TaxConfig.default_state_tax_rate),
usd_cad_rate=_env_float("USD_CAD_RATE", TaxConfig.usd_cad_rate),
investment_income_tax_rate=_env_float("INVESTMENT_INCOME_TAX_RATE", TaxConfig.investment_income_tax_rate),
personal_withdrawal_pct=_env_float("PERSONAL_WITHDRAWAL_PCT", TaxConfig.personal_withdrawal_pct),
),
investment=InvestmentDefaults(
invest_percentage=_env_float("DEFAULT_INVEST_PCT", InvestmentDefaults.invest_percentage),
annual_return=_env_float("DEFAULT_ANNUAL_RETURN", InvestmentDefaults.annual_return),
cycles=_env_int("DEFAULT_CYCLES", InvestmentDefaults.cycles),
),
cache_ttl=_env_int("CACHE_TTL_SECONDS", 21600),
)