Lottery Classes
Pactole exposes lottery-handling classes in pactole.lottery:
BaseLottery: Generic implementation for draw-day handling, combination creation, and history search.EuroMillions: Preconfigured lottery (Tuesday,Friday) usingEuroMillionsCombination.EuroDreams: Preconfigured lottery (Monday,Thursday) usingEuroDreamsCombination.
In day-to-day usage, a lottery object is the main entry point to:
- Plan plays around next draw dates.
- Build valid game-specific combinations.
- Inspect cached historical draws.
- Search past results for winning ranks.
Create lottery instances
Use built-in classes when your game rules match EuroMillions or EuroDreams.
from pactole import EuroDreams, EuroMillions
euromillions = EuroMillions()
eurodreams = EuroDreams()
print(euromillions.draw_days.days)
print(eurodreams.draw_days.days)
Get last and next draw dates
Lottery instances delegate date computations to configured draw days.
This is typically used to decide if a ticket should be registered for the current draw or for the next one.
from datetime import date
from pactole import EuroMillions
lottery = EuroMillions()
today = date(2026, 2, 19)
print(lottery.get_last_draw_date(from_date=today, closest=True))
print(lottery.get_next_draw_date(from_date=today, closest=True))
Create and generate tickets from a lottery
Use the lottery API to keep code generic, even if you later swap provider or game type.
from pactole import EuroMillions
lottery = EuroMillions()
played = lottery.get_combination(numbers=[3, 15, 22, 28, 44], stars=[2, 9])
suggested = lottery.generate(n=3)
spread_suggested = lottery.generate(n=12, partitions=4)
print(played)
for ticket in suggested:
print(ticket)
print(len(spread_suggested))
partitions lets you spread generated tickets across slices of the full combination
rank space. This is useful when generating larger batches.
Work with historical draw records
Lottery classes expose helper methods backed by their configured provider.
force=True refreshes source data before reading.
from pactole import EuroMillions
lottery = EuroMillions()
ticket = lottery.get_combination(numbers=[3, 15, 22, 28, 44], stars=[2, 9])
print(lottery.count())
raw_rows = lottery.dump(force=False)
print(raw_rows[:1])
records = list(lottery.get_records())
matches = list(lottery.find_records(ticket))
print(len(records))
for found in matches[:3]:
print(found.record.draw_date, found.rank)
Understand returned models
get_records() yields DrawRecord instances. A record includes:
period,draw_date,deadline_datecombination(lottery-specific combination object)numbers(component values keyed by component name)winning_ranks(list ofWinningRankdataclasses)
find_records() yields FoundCombination instances. Each one includes:
record(DrawRecord)rank(winning rank for your query)match(CompoundCombinationrepresenting the matched values)
from pactole import EuroMillions
lottery = EuroMillions()
ticket = lottery.get_combination(numbers=[3, 15, 22, 28, 44], stars=[2, 9])
records = list(lottery.get_records())
found = list(lottery.find_records(ticket))
record = records[0]
print(type(record).__name__, record.draw_date, record.combination.to_dict())
if found:
first = found[0]
print(type(first).__name__, first.rank, first.match.to_string())
DrawRecord serialization
Use these helpers depending on target format:
record.to_csv(): flat export withcomponent_indexfields and rank fields (for examplenumbers_1,stars_2,numbers_rank,combination_rank,rank_1_gain).record.to_dict()/record.to_json(): API-friendly dictionary with:combination: serialized combination payloadnumbers: flattened list (combination.values)winning_ranks: list of{rank, winners, gain}
Important validation rule:
DrawRecord.from_dict()andDrawRecord.from_json()validate thatnumbers == combination.valuesand raiseValueErrorif they differ.
FoundCombination serialization
found.to_csv()storesmatchas a string (viato_string()).found.to_dict()/found.to_json()storematchas a dictionary (viadump()).
Build a Pandas DataFrame from raw export
Use dump() when you want tabular analysis (filtering, grouping, charts, exports).
import pandas as pd
from pactole import EuroMillions
lottery = EuroMillions()
raw_rows = lottery.dump(force=False)
df = pd.DataFrame(raw_rows)
df["draw_date"] = pd.to_datetime(df["draw_date"])
print(df.shape)
print(df[["draw_date", "numbers_1", "numbers_2", "stars_1", "stars_2"]].head())
# Example: average rank-1 gain by year
df["year"] = df["draw_date"].dt.year
avg_rank1_gain = df.groupby("year", as_index=False)["rank_1_gain"].mean()
print(avg_rank1_gain.tail())
Search with rank constraints
Use min_rank and max_rank to filter matches by prize category.
- Without
strict, bounds are inclusive. - With
strict=True, bounds are exclusive, so only ranks strictly betweenmin_rankandmax_rankare returned. - If you pass only
min_rank, the upper bound defaults to the combination's maximum winning rank. - If you pass only
max_rank, the lower bound defaults to the combination's minimum winning rank.
from pactole import EuroMillions
lottery = EuroMillions()
ticket = lottery.get_combination(numbers=[3, 15, 22, 28, 44], stars=[2, 9])
ranks_1_to_4 = list(lottery.find_records(ticket, min_rank=1, max_rank=4))
exactly_rank_4 = list(lottery.find_records(ticket, min_rank=4, max_rank=4))
strictly_between_1_and_5 = list(
lottery.find_records(ticket, min_rank=1, max_rank=5, strict=True)
)
print(len(ranks_1_to_4), len(exactly_rank_4), len(strictly_between_1_and_5))
Build your own lottery class
Subclass BaseLottery by passing a configured provider to super().__init__.
from pactole.combinations import EuroMillionsCombination
from pactole.data.providers import FDJProvider
from pactole.lottery import BaseLottery
class CustomLottery(BaseLottery):
def __init__(self) -> None:
super().__init__(
provider=FDJProvider(
"euromillions-my-million",
draw_days=["MONDAY", "THURSDAY"],
combination_factory=EuroMillionsCombination,
cache_name="custom_lottery",
)
)
Create a specific provider class first
For reusable setups, create a provider class dedicated to one source/game configuration, then inject it into your lottery class.
from pactole.combinations import EuroMillionsCombination
from pactole.data.providers import FDJProvider
from pactole.lottery import BaseLottery
class EuroMillionsFDJProvider(FDJProvider):
def __init__(self) -> None:
super().__init__(
resolver="euromillions-my-million",
draw_days=["TUESDAY", "FRIDAY"],
draw_day_refresh_time="22:00",
combination_factory=EuroMillionsCombination,
cache_name="euromillions_fdj_custom",
)
class EuroMillionsCustomLottery(BaseLottery):
def __init__(self) -> None:
super().__init__(provider=EuroMillionsFDJProvider())
Create a fully custom provider from resolver + parser
If your archive source is not FDJ, create a provider by composing your own resolver and parser.
from pactole.combinations import EuroMillionsCombination
from pactole.data import BaseParser, BaseProvider, BaseResolver, DrawRecord
class CustomResolver(BaseResolver):
def _load_cache(self) -> dict[str, str]:
return {
"draws_2026.zip": "https://local.test/lottery/draws_2026.zip",
}
class CustomParser(BaseParser):
def __call__(self, data: dict) -> DrawRecord:
# Parse one source row into a DrawRecord.
# Implement mapping according to your data schema.
...
class CustomProvider(BaseProvider):
def __init__(self) -> None:
super().__init__(
resolver=CustomResolver(),
parser=CustomParser(combination_factory=EuroMillionsCombination),
draw_days=["TUESDAY", "FRIDAY"],
combination_factory=EuroMillionsCombination,
cache_name="custom_provider",
)
Configure built-in lotteries with environment variables
EuroMillions and EuroDreams can be customized without subclassing.
PACTOLE_CACHE_ROOT,FDJ_ARCHIVES_PAGE_URLEUROMILLIONS_PROVIDER_CLASS,EURODREAMS_PROVIDER_CLASSEUROMILLIONS_DRAW_DAYS,EURODREAMS_DRAW_DAYSEUROMILLIONS_DRAW_DAY_REFRESH_TIME,EURODREAMS_DRAW_DAY_REFRESH_TIMEEUROMILLIONS_CACHE_NAME,EURODREAMS_CACHE_NAMEEUROMILLIONS_ARCHIVES_PAGE,EURODREAMS_ARCHIVES_PAGE
For defaults and precedence rules, see Environment variables.
Example: