Market Overview
Real-time analytics engine — updated daily at 6:00 AM ET
Best EV Game
calculating...
Best Score
weighted rank
Top Prizes Left
games w/ jackpot intact
Avg Portfolio ROI
across all scratch-offs
FL Jackpot Pool
scratch-offs only
Powerball
$325M
Wed & Sat draw
Expected Value Ranking (EV/$ticket)
Top Prize % Remaining by Game
Scratch-Off Analyzer
📍 Retailer Location Intelligence
PRO
How This Works: The FL Lottery publishes where winning tickets are sold — retailer name, city, ZIP, game, and prize amount. We track every claim and cross-reference it against your location.

Why It Matters: Each scratch-off game is printed as a finite run. When a top prize is claimed at a store near you, that specific game has fewer remaining winners in your area's ticket distribution chain. Other games at the same store are unaffected — a Publix that paid out 500X THE CASH is still clean for GOLD RUSH LEGACY.

What To Do: Enter your ZIP code below. The system calculates a Location-Adjusted Score per game (EdgeScore × Freshness × AreaPenalty × RiskMultiplier) so you know exactly which games to buy where — and when to drive to a different region for a specific game.
PLAY = no nearby wins for this game, fresh in your area MAYBE = mid-tier wins nearby, top prizes still available SKIP = top prizes already claimed in your area for this game
Draw Game Analysis
Loading draw history...
Capital Asset Modeling
Correlation · Cointegration · Drawdown · Sharpe · Kelly · Bell Curves
Game ROI Correlation Matrix (top 8 games)
Measures EV return correlation between games by prize tier overlap. Higher = similar risk/reward profile. Use for portfolio diversification.
Price vs EV Scatter — All Games
Each dot = one game. Dots above the diagonal line = positive EV relative to price.
Cross-Game Frequency Correlation — Number Overlap Analysis
Measures how number frequency distributions correlate across games. High correlation = similar hot/cold patterns. Based on actual draw history.
Econometric Test Suite — All Draw Games
ADF = Augmented Dickey-Fuller (stationarity) · χ² = Chi-Squared (uniformity/randomness) · E-G = Engle-Granger (pairwise cointegration)
H₀ for ADF: series has unit root (non-stationary). H₀ for χ²: numbers are uniformly distributed.
Loading data... Select Scratch-Offs or Draw Games above.
Engle-Granger Pairwise Cointegration Matrix
Two-step Engle-Granger: OLS regression → ADF on residuals. Green = evidence of cointegration (p < 0.05). Tests whether game frequency patterns share long-run equilibrium.
Number Frequency Cross-Correlation Heatmap
Pearson correlation of number frequency vectors between game pairs. High values indicate similar hot/cold number distributions across games.
Lottery Sharpe Ratio (Top 10 Games)
Sharpe = (EV − RiskFree) ÷ StdDev(prize distribution). Higher = better risk-adjusted return. Risk-free set to $0 (no-play baseline).
Maximum Drawdown Simulation (200 ticket sessions)
Simulated cumulative P&L over 200 ticket purchases. Drawdown = peak-to-trough decline. Helps model bankroll volatility risk.
Risk-Adjusted Metrics Table
Prize Distribution Bell Curve — Select Game
Expected Outcome Distribution — 50 Ticket Simulation
Kelly Criterion Bet Sizing
Kelly Formula: f* = (bp − q) / b
where b = net odds, p = win prob, q = 1−p
Note: Kelly fractions are typically very small in lottery — this tool uses fractional Kelly (0.1×) for realistic bankroll sizing.
Kelly Bankroll Growth Simulation
EV vs ROI Bubble Chart
Bubble size = ticket price. X = expected value. Y = ROI%. Upper-right = best plays.
Prize Tier Breakdown — Donut Charts
EV CONTRIBUTION %
PRIZES REMAINING %
Unified Probability-Weighted Point System (0–100)
Every game, retailer, and strategy is ranked by a single composite score built from statistically-weighted sub-components. The score drives ALL recommendations across the platform — scratch-off rankings, draw game picks, budget allocations, retailer PLAY/SKIP signals, and strategy tier selections. Hover any score ring for the full breakdown.
Score Distribution — All Games
Component Contribution Breakdown
Master Probability Rankings — Full Table
CAPM Adaptation for Lottery Analytics
In traditional finance: R = Rf + β(Rm − Rf) + α. Here we adapt CAPM to model each scratch-off game as an "asset" where β (Beta) = sensitivity of a game's EV to the overall FL lottery market EV, α (Alpha) = game-specific excess return above what market exposure predicts, and Systematic vs Idiosyncratic risk = how much variance is market-driven vs game-specific. Games with high α and low β are the true edge plays — high return, low correlation to market downturns.
CAPM α (Alpha) — Excess Return vs Market
α > 0 = game outperforms market-predicted EV. Sort by α to find best plays regardless of market conditions.
CAPM β (Beta) — Market Sensitivity
β < 1 = defensive (less volatile than market). β > 1 = aggressive. Jackpot games have higher β.
Security Market Line — α vs β Scatter (Efficient Frontier)
Points above the SML line = underpriced (buy signal). Below = overpriced relative to their risk. X-axis = β (systematic risk). Y-axis = actual EV return. SML = predicted fair return for given β.
CAPM Risk Decomposition Table — Systematic vs Idiosyncratic
R² = systematic risk share. (1−R²) = idiosyncratic (game-specific) risk. High R², low α = market-driven game. Low R², high α = independent edge play.
Multi-State Expansion Architecture — Florida is the template. Each state is added as a new STATE_CONFIG entry with its scraper URL pattern, game ID range, API type, and prize structure. The same CAPM, backtester, hot/cold, and retailer intelligence engines run for every state automatically.
State Comparison — Best EV Game per State
Budget Optimizer
Kelly-weighted recommendations by budget allocation
Configure Session
QUICK SELECT
Budget Allocation Pie
Area Intelligence — Where to Play
Loading area recommendations...
Hot / Cold Number Engine
Full historical frequency analysis · Hot = above avg · Cold = below avg · Due = longest gap
Number Frequency Heatmap — Last 500 Draws
Frequency Bar Chart with Probability %
🔥 HOT NUMBERS
❄ COLD NUMBERS
⚡ DUE NUMBERS
Note: "Due" is a statistical observation, not a predictor. Each draw is independent.
Number Pair Co-occurrence Frequency
Rolling Window Analysis (last 20/50/100 draws)
AI Number Suggestion Engine
Moderate: balanced approach across all signal types. Conservative optimizes for 2-3 number matches (frequent small wins). Aggressive targets full-match jackpots.
Loading suggestions...
🔒
PRO FEATURE
Upgrade to PRO for AI suggestions
AI Backtester — Historical Performance
ELITE
Loading backtester...
🔒
ELITE FEATURE
Upgrade to ELITE for AI backtesting
Strategy Center
AI-powered ticket stacking, combined scratch + draw plans, and advanced play strategies
Today's Optimal Ticket Stack
Based on current EV rankings, prize freshness, historical patterns, and Kelly-weighted sizing
Loading ticket stack...
Combined Scratch-Off + Draw Game Plan
60% Scratch / 40% Draw
Loading combined plan...
Timing & Freshness Signals
Loading timing signals...
Advanced Play Strategies
ELITE
Loading advanced strategies...
🔒
ELITE FEATURE
Upgrade to ELITE for advanced strategies
🎭 Contrarian Play Strategy — Collision Minimization Engine
QUANT
Mathematical premise: You cannot change the odds of winning, but you can avoid splitting the jackpot. Humans cluster their picks around dates (1–31), multiples of 5/7, and visual patterns on playslips. The Contrarian engine generates tickets that statistically minimize overlap with other players' selections, maximizing your payout when you do win — the true mathematical edge available to lottery players.
Birthday Trap — Numbers 1-31 represent only 44% of the pool but appear in ~70% of human picks
Multiples Trap — Multiples of 5 (5,10,15...) selected 2× more than random
Decade Clump — Players visually cluster ≥3 numbers in same 10-range on slip
Sequential Runs — Patterns like 7,8,9 or 10,20,30 are human favorites
We target — High numbers (32+), non-multiples, spread distribution
Click regenerate to generate tickets...
⚡ Dynamic EV Trigger — When to Play Engine
QUANT
The premise: Most lottery tickets have negative expected value (EV ~$0.40 on a $2 ticket). But as jackpots roll over, the EV improves. The EV Trigger calculates the exact jackpot threshold where cash value × (1 − 0.37 federal tax) ÷ ticket odds > ticket cost — the mathematical "BUY" signal.

FL advantage: No state income tax — your net payout is higher than most states.
🎯 Anti-Split Jackpot Strategy — Number Popularity Map
QUANT
The math: If you win a jackpot shared with 3 other players, you receive 25% of the pot. Studies show jackpot splits are 40-60% more common when players use "popular" numbers (birthdays, lucky 7, etc.). This tool maps number popularity so you can deliberately choose unpopular combinations — same odds of winning, significantly higher expected payout when you do win.
🔴 = overplayed (avoid) · 🟡 = neutral · 🟢 = underplayed (target)
Source: Survey data on human number selection bias + FL historical patterns
Target number range: 32+ (above birthday bias zone)
Avoid: 1-31, multiples of 5/7, sequential runs
Sweet spot: 38-51, 57-63, 67-69 for Powerball
Loading anti-split picks...
Calculating...
Scraper + API Integration
Production-ready scrapers for Florida Lottery data feeds
// LottoEdge FL — Node.js Scraper (Puppeteer + Cheerio)
// Scrapes: floridalottery.com/games/scratch-offs + draw results
// Run daily via cron: 0 6 * * * node scraper.js

const puppeteer = require('puppeteer');
const cheerio = require('cheerio');
const axios = require('axios');
const fs = require('fs');
const path = require('path');

// ── CONFIG ──────────────────────────────────────────────
const CONFIG = {
  baseUrl: 'https://floridalottery.com',
  scratchUrl: 'https://floridalottery.com/games/scratch-offs',
  drawUrl: 'https://floridalottery.com/games/draw-games',
  gameIds: [1555,1562,1590,1594,1597,1604,1606,
            1608,1618,1619,1620,1621,1622,1623,1627],
  outputDir: './data',
  delay: 1500 // ms between requests (be respectful!)
};

// ── SCRAPE SINGLE SCRATCH GAME ──────────────────────────
async function scrapeScratchGame(gameId) {
  const url = `${CONFIG.baseUrl}/games/scratch-offs/view?id=${gameId}`;
  const res = await axios.get(url, {
    headers: { 'User-Agent': 'Mozilla/5.0 (compatible; LottoEdgeBot/1.0)' }
  });
  const $ = cheerio.load(res.data);
  const prizes = [];

  // Parse prize table
  $('.odds-prizes-table tr, table tr').each((i, row) => {
    const cells = $(row).find('td');
    if (cells.length >= 3) {
      const prizeText = $(cells[0]).text().replace(/[$,]/g,'').trim();
      const oddsText = $(cells[1]).text().match(/[\d,]+$/)?.[0] || '0';
      const remText = $(cells[2]).text().trim();
      const [rem, total] = remText.split(' of ').map(x => parseInt(x.replace(/,/g,'')));
      if (isNaN(rem)) return;
      prizes.push({ prize: +prizeText, odds: +oddsText.replace(/,/g,''), rem, total });
    }
  });

  const topPrize = $('[data-top-prize], .top-prize').first().text().replace(/[$,]/g,'').trim();
  const topRemaining = $('.top-prizes-remaining').first().text().trim();
  const overallOdds = $('.overall-odds').first().text().replace('1:','').trim();

  return { gameId, topPrize: +topPrize || 0, topRemaining, overallOdds, prizes,
    scrapedAt: new Date().toISOString() };
}

// ── SCRAPE ALL GAMES ─────────────────────────────────────
async function scrapeAll() {
  console.log('[LottoEdge] Starting scrape...', new Date().toISOString());
  const results = {};

  for (const id of CONFIG.gameIds) {
    try {
      results[id] = await scrapeScratchGame(id);
      console.log(`  ✓ Game #${id} — ${results[id].prizes.length} prize tiers`);
    } catch (e) {
      console.error(`  ✗ Game #${id}:`, e.message);
    }
    await new Promise(r => setTimeout(r, CONFIG.delay));
  }

  const outPath = path.join(CONFIG.outputDir, `scratch_${Date.now()}.json`);
  fs.writeFileSync(outPath, JSON.stringify(results, null, 2));
  console.log(`[LottoEdge] Saved to ${outPath}`);
  return results;
}

// ── SCRAPE DRAW RESULTS ──────────────────────────────────
async function scrapeDrawResults() {
  const browser = await puppeteer.launch({ headless: 'new' });
  const page = await browser.newPage();
  await page.goto('https://floridalottery.com/games/draw-games/winning-numbers');
  await page.waitForSelector('.winning-numbers', { timeout: 10000 });

  const drawData = await page.evaluate(() => {
    const results = [];
    document.querySelectorAll('.draw-result-row').forEach(row => {
      const game = row.querySelector('.game-name')?.textContent;
      const date = row.querySelector('.draw-date')?.textContent;
      const nums = [...row.querySelectorAll('.ball')].map(b => +b.textContent);
      results.push({ game, date, numbers: nums });
    });
    return results;
  });

  await browser.close();
  return drawData;
}

scrapeAll().then(data => console.log('Complete', Object.keys(data).length, 'games'));
# LottoEdge FL — Python Scraper
# pip install requests beautifulsoup4 selenium pandas schedule

import requests, json, time, schedule, logging
from datetime import datetime
from bs4 import BeautifulSoup
import pandas as pd

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')

BASE_URL = "https://floridalottery.com"
GAME_IDS = [1555,1562,1590,1594,1597,1604,1606,
            1608,1618,1619,1620,1621,1622,1623,1627]
HEADERS = {"User-Agent": "LottoEdge/1.0 (educational research)"}

def scrape_game(game_id: int) -> dict:
    url = f"{BASE_URL}/games/scratch-offs/view?id={game_id}"
    r = requests.get(url, headers=HEADERS, timeout=10)
    r.raise_for_status()
    soup = BeautifulSoup(r.text, "html.parser")

    prizes = []
    for row in soup.select("table tr"):
        cells = row.find_all("td")
        if len(cells) < 3: continue
        prize_str = cells[0].get_text().replace("$","").replace(",","").strip()
        odds_str  = cells[1].get_text().replace(",","").strip()
        rem_str   = cells[2].get_text().strip()
        try:
            rem, total = [int(x.replace(",","")) for x in rem_str.split(" of ")]
            odds_val = int(odds_str.split("-in-")[-1])
            prizes.append({"prize": int(prize_str), "odds": odds_val, "rem": rem, "total": total})
        except: continue

    return {"game_id": game_id, "prizes": prizes,
            "scraped_at": datetime.utcnow().isoformat()}

def calc_ev(game: dict) -> float:
    return sum(p["prize"] * (1/p["odds"]) * (p["rem"]/p["total"]) for p in game["prizes"])

def daily_scrape():
    logging.info("Starting daily FL Lottery scrape...")
    results = {}
    for gid in GAME_IDS:
        try:
            results[gid] = scrape_game(gid)
            ev = calc_ev(results[gid])
            logging.info(f"  Game #{gid}: EV=${ev:.2f}, {len(results[gid]['prizes'])} tiers")
            time.sleep(1.5)
        except Exception as e:
            logging.error(f"  Error game #{gid}: {e}")

    fname = f"data/scratch_{int(time.time())}.json"
    with open(fname, "w") as f:
        json.dump(results, f, indent=2)
    logging.info(f"Saved {len(results)} games to {fname}")

# Schedule daily at 6 AM ET
schedule.every().day.at("06:00").do(daily_scrape)
if __name__ == "__main__":
    daily_scrape()  # run once immediately
    while True: schedule.run_pending(); time.sleep(60)
API Endpoints
MethodEndpointDescription
GET /api/gamesAll active scratch games
GET /api/games/:idGame detail + prize structure
GET /api/ev/:idExpected value calculation
GET /api/rankingsRanked game list by score
GET /api/draw/:gameDraw game history + hot/cold
GET /api/budget/:amountOptimized play plan
POST /api/simulateMonte Carlo simulation
POST /api/alertsSet prize-remaining alert
// Express.js API server — app.js
const express = require('express');
const { scrapeAll, calcEV } = require('./scraper');
const app = express();
app.use(express.json());

// Auth middleware
const auth = (req, res, next) => {
  const key = req.headers['x-api-key'];
  if (!key || !validKeys.has(key))
    return res.status(401).json({ error: 'Invalid API key' });
  next();
};

app.get('/api/games', auth, async (req, res) => {
  const data = loadLatestData();
  res.json(Object.values(data).map(g => ({
    id: g.gameId, ev: calcEV(g).toFixed(2), ...g
  })));
});

app.get('/api/budget/:amount', auth, (req, res) => {
  const b = +req.params.amount;
  res.json(optimizeBudget(b, loadLatestData()));
});

app.post('/api/simulate', auth, (req, res) => {
  const { gameId, tickets, sims } = req.body;
  res.json(monteCarlo(gameId, tickets, sims || 10000));
});

app.listen(3000, () =>
  console.log('LottoEdge API running on :3000'));
# crontab -e  — add this line for daily 6 AM ET scrape:
# 0 11 * * * cd /app && node scraper.js >> logs/scrape.log 2>&1

# GitHub Actions workflow (.github/workflows/daily-scrape.yml):
---
name: Daily FL Lottery Scrape
on:
  schedule:
    - cron: '0 11 * * *'  # 6 AM ET = 11 UTC
  workflow_dispatch:       # allow manual trigger

jobs:
  scrape:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: node scraper.js
        env:
          DB_URL: ${{ secrets.DATABASE_URL }}
          WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK }}
      - uses: actions/upload-artifact@v4
        with:
          name: scrape-data
          path: data/*.json
          retention-days: 30

# Docker Compose for full stack:
---
version: '3.8'
services:
  api:
    build: .
    ports: ["3000:3000"]
    environment:
      - DB_URL=postgresql://postgres:pass@db:5432/lottoedge
  db:
    image: postgres:16
    volumes: [pgdata:/var/lib/postgresql/data]
  scheduler:
    build: .
    command: node scheduler.js
    depends_on: [db]
volumes:
  pgdata:
// Alert system — sends Discord/Slack/Email when key events occur
const alerts = {
  // Trigger: top prize count drops (someone won!)
  topPrizeClaimed: async (gameId, prizeTier, newCount) => {
    const msg = {
      embeds: [{
        title: `🎰 TOP PRIZE CLAIMED — Game #${gameId}`,
        description: `$${prizeTier.toLocaleString()} prize won! **${newCount} remaining**`,
        color: 0xFF4D6D,
        timestamp: new Date()
      }]
    };
    await axios.post(process.env.DISCORD_WEBHOOK, msg);
  },

  // Trigger: EV crosses threshold (game becomes better value)
  evThreshold: async (gameId, ev, price) => {
    if (ev / price > 0.85) { // 85%+ return threshold
      await axios.post(process.env.SLACK_WEBHOOK, {
        text: `⚡ HIGH EV ALERT: Game #${gameId} at $${ev.toFixed(2)} EV on $${price} ticket`
      });
    }
  },

  // Daily digest email via SendGrid
  dailyDigest: async (topPlays) => {
    await sgMail.send({
      to: process.env.ALERT_EMAIL,
      from: '[email protected]',
      subject: `LottoEdge Daily Brief — ${new Date().toDateString()}`,
      html: buildEmailHTML(topPlays)
    });
  }
};

// Check for changes after each scrape
async function detectChanges(prev, curr) {
  for (const [id, game] of Object.entries(curr)) {
    const p = prev[id];
    if (!p) continue;
    // Top prize claimed?
    if (p.topPrizeCount > game.topPrizeCount)
      await alerts.topPrizeClaimed(id, game.topPrize, game.topPrizeCount);
    // EV improvement?
    await alerts.evThreshold(id, calcEV(game), game.price);
  }
}
Export Center
Download all analytics data as CSV or Excel for offline analysis, Excel modeling, or Python/Pandas import
📊 Scratch-Off Full Analysis
Exports all 15 scratch-off games with: EV, ROI, Sharpe, Kelly fraction, Edge Score, prize structure per tier, top-prize freshness, drawdown estimate, and composite rankings.
🎰 Draw Games Analytics
Exports all draw games with: jackpot amount, ticket cost, EV, expected ROI, hot/cold/due numbers, draw history count, backtester summary (if run), and strategy recommendations.
🤖 Backtester Results
Exports the full backtester history table: draw dates, actual numbers, AI picks per strategy, hit counts, prize amounts, and cumulative ROI. Requires backtest to be run first.
📦 Full Portfolio Report
Master export: scratch-offs + draw games + strategy recommendations + statistical tests + Kelly sizes in one file. Best for Excel modeling or sending to analysts.
🎯 Strategy Recommendations
Exports today's optimal play plan, budget allocations, AI number picks, EV trigger signals, contrarian picks, and anti-split recommendations.
🔥 Hot/Cold Number History
Exports number frequency data per draw game: frequency counts, probability %, chi² deviation, hot/cold classification, gap analysis, and signal composite scores.
Export Preview — Scratch-Off Data
Game IDNamePriceEVROI %Edge ScoreSharpeKelly %Top PrizeTop RemTop TotTop %OddsLaunchedRec
Preview shows first 10 rows of scratch-off export. Actual exports include all rows and additional columns.
Export Notes: CSV files open in Excel, Google Sheets, or any data tool. Excel (.xlsx) exports use multi-sheet format — one sheet per data category. All monetary values are in USD. ROI and probability values are expressed as percentages (%). Data reflects current static + live Supabase values. Re-export after 6 AM ET daily scrape for freshest data.
Choose Your Edge
Professional-grade lottery intelligence for serious players
All subscriptions include 7-day free trial · Cancel anytime · 18+ only · Gambling involves risk · Play responsibly