Files
wifi-densepose/vendor/ruvector/examples/neural-trader/specialized/sports-betting.js

336 lines
13 KiB
JavaScript

/**
* Sports Betting with Neural Trader
*
* Demonstrates using @neural-trader/sports-betting for:
* - Arbitrage detection across sportsbooks
* - Kelly Criterion position sizing
* - Expected Value (EV) calculations
* - Odds comparison and analysis
* - Bankroll management
*/
// Sports betting configuration
const bettingConfig = {
// Bankroll settings
initialBankroll: 10000,
maxBetPercent: 0.05, // 5% max per bet (conservative Kelly)
minEdge: 0.02, // 2% minimum edge to bet
fractionKelly: 0.25, // Quarter Kelly for safety
// Sportsbooks
sportsbooks: ['DraftKings', 'FanDuel', 'BetMGM', 'Caesars', 'PointsBet'],
// Sports to analyze
sports: ['NFL', 'NBA', 'MLB', 'NHL', 'Soccer', 'UFC']
};
// Sample odds data (American format)
const sampleOdds = {
'NFL_Week17_Chiefs_Raiders': {
event: 'Kansas City Chiefs vs Las Vegas Raiders',
sport: 'NFL',
date: '2024-12-29',
time: '16:25 ET',
odds: {
'DraftKings': { moneyline: { home: -280, away: +230 }, spread: { home: -6.5, homeOdds: -110, away: +6.5, awayOdds: -110 }, total: { over: 44.5, overOdds: -110, under: 44.5, underOdds: -110 } },
'FanDuel': { moneyline: { home: -285, away: +235 }, spread: { home: -6.5, homeOdds: -112, away: +6.5, awayOdds: -108 }, total: { over: 44.5, overOdds: -108, under: 44.5, underOdds: -112 } },
'BetMGM': { moneyline: { home: -275, away: +225 }, spread: { home: -6.5, homeOdds: -108, away: +6.5, awayOdds: -112 }, total: { over: 45.0, overOdds: -110, under: 45.0, underOdds: -110 } },
'Caesars': { moneyline: { home: -290, away: +240 }, spread: { home: -7.0, homeOdds: -110, away: +7.0, awayOdds: -110 }, total: { over: 44.5, overOdds: -105, under: 44.5, underOdds: -115 } },
'PointsBet': { moneyline: { home: -270, away: +220 }, spread: { home: -6.5, homeOdds: -115, away: +6.5, awayOdds: -105 }, total: { over: 44.5, overOdds: -112, under: 44.5, underOdds: -108 } }
},
trueProbability: { home: 0.72, away: 0.28 } // Model estimate
},
'NBA_Lakers_Warriors': {
event: 'Los Angeles Lakers vs Golden State Warriors',
sport: 'NBA',
date: '2024-12-30',
time: '19:30 ET',
odds: {
'DraftKings': { moneyline: { home: +145, away: -170 }, spread: { home: +4.5, homeOdds: -110, away: -4.5, awayOdds: -110 }, total: { over: 225.5, overOdds: -110, under: 225.5, underOdds: -110 } },
'FanDuel': { moneyline: { home: +150, away: -175 }, spread: { home: +4.5, homeOdds: -108, away: -4.5, awayOdds: -112 }, total: { over: 226.0, overOdds: -110, under: 226.0, underOdds: -110 } },
'BetMGM': { moneyline: { home: +140, away: -165 }, spread: { home: +4.0, homeOdds: -110, away: -4.0, awayOdds: -110 }, total: { over: 225.5, overOdds: -108, under: 225.5, underOdds: -112 } },
'Caesars': { moneyline: { home: +155, away: -180 }, spread: { home: +5.0, homeOdds: -110, away: -5.0, awayOdds: -110 }, total: { over: 225.0, overOdds: -115, under: 225.0, underOdds: -105 } },
'PointsBet': { moneyline: { home: +160, away: -185 }, spread: { home: +5.0, homeOdds: -105, away: -5.0, awayOdds: -115 }, total: { over: 226.5, overOdds: -110, under: 226.5, underOdds: -110 } }
},
trueProbability: { home: 0.42, away: 0.58 }
}
};
async function main() {
console.log('='.repeat(70));
console.log('Sports Betting Analysis - Neural Trader');
console.log('='.repeat(70));
console.log();
// 1. Display configuration
console.log('1. Betting Configuration:');
console.log('-'.repeat(70));
console.log(` Initial Bankroll: $${bettingConfig.initialBankroll.toLocaleString()}`);
console.log(` Max Bet Size: ${bettingConfig.maxBetPercent * 100}% ($${bettingConfig.initialBankroll * bettingConfig.maxBetPercent})`);
console.log(` Kelly Fraction: ${bettingConfig.fractionKelly * 100}%`);
console.log(` Minimum Edge: ${bettingConfig.minEdge * 100}%`);
console.log(` Sportsbooks: ${bettingConfig.sportsbooks.join(', ')}`);
console.log();
// 2. Analyze each event
for (const [eventId, eventData] of Object.entries(sampleOdds)) {
console.log(`2. Event Analysis: ${eventData.event}`);
console.log('-'.repeat(70));
console.log(` Sport: ${eventData.sport} | Date: ${eventData.date} ${eventData.time}`);
console.log();
// Display odds comparison
console.log(' Moneyline Odds Comparison:');
console.log(' Sportsbook | Home | Away | Home Prob | Away Prob | Vig');
console.log(' ' + '-'.repeat(60));
for (const [book, odds] of Object.entries(eventData.odds)) {
const homeProb = americanToImpliedProb(odds.moneyline.home);
const awayProb = americanToImpliedProb(odds.moneyline.away);
const vig = (homeProb + awayProb - 1) * 100;
console.log(` ${book.padEnd(13)} | ${formatOdds(odds.moneyline.home).padStart(9)} | ${formatOdds(odds.moneyline.away).padStart(9)} | ${(homeProb * 100).toFixed(1).padStart(8)}% | ${(awayProb * 100).toFixed(1).padStart(8)}% | ${vig.toFixed(1)}%`);
}
console.log();
// Find best odds
const bestHomeOdds = findBestOdds(eventData.odds, 'moneyline', 'home');
const bestAwayOdds = findBestOdds(eventData.odds, 'moneyline', 'away');
console.log(` Best Home Odds: ${formatOdds(bestHomeOdds.odds)} at ${bestHomeOdds.book}`);
console.log(` Best Away Odds: ${formatOdds(bestAwayOdds.odds)} at ${bestAwayOdds.book}`);
console.log();
// Check for arbitrage
console.log(' Arbitrage Analysis:');
const arbResult = checkArbitrage(eventData.odds);
if (arbResult.hasArbitrage) {
console.log(` 🎯 ARBITRAGE OPPORTUNITY FOUND!`);
console.log(` Guaranteed profit: ${(arbResult.profit * 100).toFixed(2)}%`);
console.log(` Bet ${arbResult.homeBook} Home: $${arbResult.homeBet.toFixed(2)}`);
console.log(` Bet ${arbResult.awayBook} Away: $${arbResult.awayBet.toFixed(2)}`);
} else {
console.log(` No pure arbitrage available (combined implied: ${(arbResult.combinedImplied * 100).toFixed(1)}%)`);
}
console.log();
// EV calculations
console.log(' Expected Value Analysis (using model probabilities):');
console.log(` Model: Home ${(eventData.trueProbability.home * 100).toFixed(0)}% | Away ${(eventData.trueProbability.away * 100).toFixed(0)}%`);
console.log();
console.log(' Bet | Book | Odds | EV | Kelly | Recommended');
console.log(' ' + '-'.repeat(65));
const evAnalysis = calculateEVForAllBets(eventData);
evAnalysis.forEach(bet => {
const evStr = bet.ev >= 0 ? `+${(bet.ev * 100).toFixed(2)}%` : `${(bet.ev * 100).toFixed(2)}%`;
const kellyStr = bet.kelly > 0 ? `${(bet.kelly * 100).toFixed(2)}%` : '-';
const recBet = bet.recommendedBet > 0 ? `$${bet.recommendedBet.toFixed(0)}` : 'PASS';
console.log(` ${bet.type.padEnd(16)} | ${bet.book.padEnd(13)} | ${formatOdds(bet.odds).padStart(9)} | ${evStr.padStart(8)} | ${kellyStr.padStart(7)} | ${recBet.padStart(11)}`);
});
console.log();
// Top recommended bets
const topBets = evAnalysis.filter(b => b.recommendedBet > 0).sort((a, b) => b.ev - a.ev);
if (topBets.length > 0) {
console.log(` 📊 Top Recommended Bet:`);
const best = topBets[0];
console.log(` ${best.type} at ${best.book}`);
console.log(` Odds: ${formatOdds(best.odds)} | EV: +${(best.ev * 100).toFixed(2)}% | Bet Size: $${best.recommendedBet.toFixed(0)}`);
}
console.log();
}
// 3. Bankroll simulation
console.log('3. Bankroll Growth Simulation:');
console.log('-'.repeat(70));
const simulation = simulateBankrollGrowth(1000, 0.03, 0.55, bettingConfig);
console.log(` Starting Bankroll: $${bettingConfig.initialBankroll.toLocaleString()}`);
console.log(` Bets Placed: ${simulation.totalBets}`);
console.log(` Win Rate: ${(simulation.winRate * 100).toFixed(1)}%`);
console.log(` Final Bankroll: $${simulation.finalBankroll.toLocaleString()}`);
console.log(` ROI: ${((simulation.finalBankroll / bettingConfig.initialBankroll - 1) * 100).toFixed(1)}%`);
console.log(` Max Drawdown: ${(simulation.maxDrawdown * 100).toFixed(1)}%`);
console.log();
// 4. Syndicate management (advanced)
console.log('4. Syndicate Management:');
console.log('-'.repeat(70));
console.log(' Account Diversification Strategy:');
console.log(' - Spread bets across multiple sportsbooks');
console.log(' - Maximum 20% of action per book');
console.log(' - Rotate accounts to avoid limits');
console.log(' - Track CLV (Closing Line Value) per book');
console.log();
console.log('='.repeat(70));
console.log('Sports betting analysis completed!');
console.log('='.repeat(70));
}
// Convert American odds to implied probability
function americanToImpliedProb(odds) {
if (odds > 0) {
return 100 / (odds + 100);
} else {
return Math.abs(odds) / (Math.abs(odds) + 100);
}
}
// Convert implied probability to American odds
function probToAmerican(prob) {
if (prob >= 0.5) {
return Math.round(-100 * prob / (1 - prob));
} else {
return Math.round(100 * (1 - prob) / prob);
}
}
// Format American odds
function formatOdds(odds) {
return odds > 0 ? `+${odds}` : `${odds}`;
}
// Find best odds across sportsbooks
function findBestOdds(odds, market, side) {
let best = { odds: -Infinity, book: '' };
for (const [book, bookOdds] of Object.entries(odds)) {
const odd = bookOdds[market][side];
if (odd > best.odds) {
best = { odds: odd, book };
}
}
return best;
}
// Check for arbitrage opportunity
function checkArbitrage(odds) {
const bestHome = findBestOdds(odds, 'moneyline', 'home');
const bestAway = findBestOdds(odds, 'moneyline', 'away');
const homeProb = americanToImpliedProb(bestHome.odds);
const awayProb = americanToImpliedProb(bestAway.odds);
const combinedImplied = homeProb + awayProb;
if (combinedImplied < 1) {
// Arbitrage exists!
const profit = 1 / combinedImplied - 1;
const totalStake = 1000;
const homeBet = totalStake * (homeProb / combinedImplied);
const awayBet = totalStake * (awayProb / combinedImplied);
return {
hasArbitrage: true,
profit,
combinedImplied,
homeBook: bestHome.book,
awayBook: bestAway.book,
homeBet,
awayBet
};
}
return { hasArbitrage: false, combinedImplied };
}
// Calculate EV for all betting options
function calculateEVForAllBets(eventData) {
const results = [];
const bankroll = bettingConfig.initialBankroll;
for (const [book, odds] of Object.entries(eventData.odds)) {
// Home moneyline
const homeOdds = odds.moneyline.home;
const homeEV = calculateEV(eventData.trueProbability.home, homeOdds);
const homeKelly = calculateKelly(eventData.trueProbability.home, homeOdds);
const homeRec = homeEV >= bettingConfig.minEdge
? Math.min(homeKelly * bettingConfig.fractionKelly, bettingConfig.maxBetPercent) * bankroll
: 0;
results.push({
type: 'Home Moneyline',
book,
odds: homeOdds,
ev: homeEV,
kelly: homeKelly,
recommendedBet: homeRec
});
// Away moneyline
const awayOdds = odds.moneyline.away;
const awayEV = calculateEV(eventData.trueProbability.away, awayOdds);
const awayKelly = calculateKelly(eventData.trueProbability.away, awayOdds);
const awayRec = awayEV >= bettingConfig.minEdge
? Math.min(awayKelly * bettingConfig.fractionKelly, bettingConfig.maxBetPercent) * bankroll
: 0;
results.push({
type: 'Away Moneyline',
book,
odds: awayOdds,
ev: awayEV,
kelly: awayKelly,
recommendedBet: awayRec
});
}
return results.sort((a, b) => b.ev - a.ev);
}
// Calculate Expected Value
function calculateEV(trueProb, americanOdds) {
const impliedProb = americanToImpliedProb(americanOdds);
const decimalOdds = americanOdds > 0 ? (americanOdds / 100) + 1 : (100 / Math.abs(americanOdds)) + 1;
return (trueProb * decimalOdds) - 1;
}
// Calculate Kelly Criterion
function calculateKelly(trueProb, americanOdds) {
const decimalOdds = americanOdds > 0 ? (americanOdds / 100) + 1 : (100 / Math.abs(americanOdds)) + 1;
const b = decimalOdds - 1;
const p = trueProb;
const q = 1 - p;
const kelly = (b * p - q) / b;
return Math.max(0, kelly);
}
// Simulate bankroll growth
function simulateBankrollGrowth(numBets, avgEdge, winRate, config) {
let bankroll = config.initialBankroll;
let peak = bankroll;
let maxDrawdown = 0;
let wins = 0;
for (let i = 0; i < numBets; i++) {
const betSize = bankroll * config.maxBetPercent * config.fractionKelly;
const isWin = Math.random() < winRate;
if (isWin) {
bankroll += betSize * (1 + avgEdge);
wins++;
} else {
bankroll -= betSize;
}
peak = Math.max(peak, bankroll);
maxDrawdown = Math.max(maxDrawdown, (peak - bankroll) / peak);
}
return {
totalBets: numBets,
winRate: wins / numBets,
finalBankroll: Math.round(bankroll),
maxDrawdown
};
}
// Run the example
main().catch(console.error);