mirror of
https://github.com/mblanke/Lottery-Tracker.git
synced 2026-03-01 14:10:22 -05:00
Version 1.1
This commit is contained in:
212
config.py
Normal file
212
config.py
Normal file
@@ -0,0 +1,212 @@
|
||||
"""
|
||||
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),
|
||||
)
|
||||
Reference in New Issue
Block a user