mirror of
https://github.com/mblanke/Lottery-Tracker.git
synced 2026-03-01 14:10:22 -05:00
213 lines
7.3 KiB
Python
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),
|
|
)
|