This commit is contained in:
Omar Najjar 2024-07-04 21:10:38 +10:00
parent 3c37ce8af5
commit 99ee5182ac
2 changed files with 213 additions and 84 deletions

View file

@ -0,0 +1,125 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bets and Odds</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<h1>Betting System</h1>
<form id="betForm">
<label for="userId">User ID:</label>
<input type="number" id="userId" name="userId" required>
<label for="team">Team:</label>
<input type="number" id="team" name="team" required>
<label for="cashAmount">Cash Amount:</label>
<input type="number" id="cashAmount" name="cashAmount" required>
<button type="submit">Place Bet</button>
</form>
<h2>Current Odds</h2>
<div id="oddsContainer"></div>
<h2>Betting Graph</h2>
<canvas id="bettingChart" width="400" height="200"></canvas>
<h2>Odds on Offer</h2>
<form id="oddsForm">
<label for="offerCashAmount">Cash Amount:</label>
<input type="number" id="offerCashAmount" name="offerCashAmount" required>
<button type="submit">Get Odds on Offer</button>
</form>
<div id="oddsOnOfferContainer"></div>
<h2>All Bets</h2>
<div id="allBetsContainer"></div>
<script>
const betForm = document.getElementById('betForm');
const oddsContainer = document.getElementById('oddsContainer');
const oddsOnOfferContainer = document.getElementById('oddsOnOfferContainer');
const allBetsContainer = document.getElementById('allBetsContainer');
const oddsForm = document.getElementById('oddsForm');
const ctx = document.getElementById('bettingChart').getContext('2d');
let chart;
betForm.addEventListener('submit', async (e) => {
e.preventDefault();
const userId = document.getElementById('userId').value;
const team = document.getElementById('team').value;
const cashAmount = document.getElementById('cashAmount').value;
const response = await fetch(`/bets?userId=${userId}&team=${team}&cashAmount=${cashAmount}`);
const data = await response.json();
updateOdds(data.odds);
updateChart(data.odds);
updateAllBets(data.allBets);
});
oddsForm.addEventListener('submit', async (e) => {
e.preventDefault();
const cashAmount = document.getElementById('offerCashAmount').value;
const response = await fetch(`/odds-on-offer?cashAmount=${cashAmount}`);
const data = await response.json();
updateOddsOnOffer(data.oddsOnOffer);
});
function updateOdds(odds) {
oddsContainer.innerHTML = '';
odds.forEach((odd, index) => {
const p = document.createElement('p');
p.textContent = `Team ${index}: ${odd}`;
oddsContainer.appendChild(p);
});
}
function updateOddsOnOffer(odds) {
oddsOnOfferContainer.innerHTML = '';
odds.forEach((odd, index) => {
const p = document.createElement('p');
p.textContent = `Team ${index}: ${odd}`;
oddsOnOfferContainer.appendChild(p);
});
}
function updateChart(odds) {
if (chart) {
chart.destroy();
}
chart = new Chart(ctx, {
type: 'bar',
data: {
labels: odds.map((_, index) => `Team ${index}`),
datasets: [{
label: 'Odds',
data: odds,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
}
function updateAllBets(bets) {
allBetsContainer.innerHTML = '';
bets.forEach(bet => {
const p = document.createElement('p');
p.textContent = `User ${bet.userId} bet ${bet.amount} on Team ${bet.team} at odds ${bet.odds}`;
allBetsContainer.appendChild(p);
});
}
</script>
</body>
</html>

View file

@ -1,134 +1,138 @@
import express from 'express'
import path from 'path'
const app = express()
const port = 3000
const fee = 0.05 // 0.01 = 1%
const initialLiquidity = 1000 // Initial liquidity for the market
function countLiquidity (sharesHeld: number[]): number {
return sharesHeld.reduce((acc, val) => acc + val, 0)
interface UserBalances { [key: number]: number }
interface TeamFunds { [team: number]: number }
interface Bet {
userId: number
team: number
amount: number
odds: number[]
}
function calculateOdds (sharesHeld: number[]): number[] {
const liquidity = countLiquidity(sharesHeld)
const exps = sharesHeld.map(shares => Math.exp(shares / liquidity))
const userBalances: UserBalances = { 1: 1000, 2: 1000, 3: 1000, 4: 1000 }
let teamFunds: TeamFunds = { 0: initialLiquidity, 1: initialLiquidity } // Initial funds for each team
const bets: Bet[] = []
function calculateTotalFunds (funds: TeamFunds): number {
return Object.values(funds).reduce((acc: number, val: number) => acc + val, 0)
}
function calculateOdds (funds: TeamFunds): number[] {
const totalFunds = calculateTotalFunds(funds)
const exps = Object.values(funds).map(fund => Math.exp(fund / totalFunds))
return exps
}
// function calculateMarginalPrice (team: number, sharesHeld: number[]): number {
// const liquidity = countLiquidity(sharesHeld)
// console.log('Liquidity:', sharesHeld)
// const exps = sharesHeld.map(shares => Math.exp(shares / liquidity))
// console.log('Exps:', exps)
// const sumExp = exps.reduce((acc, val) => acc + val, 0)
// return exps[team] / sumExp
// }
function countBets (userShares: UserShare[]): number[] {
return Object.values(userShares.reduce((acc, curr) => {
const index = Object.keys(acc).length
acc[index] = Object.values(curr).reduce((a: number, b: number) => a + b, 0)
return acc
}, {}))
}
function calculateCost (sharesHeld: number[]): number {
const liquidity = countLiquidity(sharesHeld)
const exps = sharesHeld.map(shares => Math.exp(shares / liquidity))
const sumExp = exps.reduce((acc, val) => acc + val, 0)
function calculateCost (funds: TeamFunds): number {
const totalFunds = calculateTotalFunds(funds)
const exps = Object.values(funds).map(fund => Math.exp(fund / totalFunds))
const sumExp = exps.reduce((acc: number, val: number) => acc + val, 0)
const lnSumExp = Math.log(sumExp)
const result = liquidity * lnSumExp
const result = totalFunds * lnSumExp
if (Number.isNaN(result)) return 0
return result
}
function placeBet (userShares: UserShare[], userId: number, team: number, sharesBought: number): UserShare[] {
const initialCost = calculateCost(countBets(userShares))
function placeBet (userBalances: UserBalances, funds: TeamFunds, userId: number, team: number, cashAmount: number): TeamFunds {
const initialCost = calculateCost(funds)
if (sharesBought === undefined || sharesBought === null || sharesBought <= 0) {
console.log('No shares bought')
return userShares
if (cashAmount === undefined || cashAmount === null || cashAmount <= 0) {
console.log('No cash amount specified')
return funds
}
sharesBought *= 1 - fee
cashAmount *= 1 - fee
if (team === undefined || userShares[team] === undefined) {
if (team === undefined || funds[team] === undefined) {
console.log('Invalid team')
return userShares
return funds
}
const newUserShares = userShares
if (newUserShares[team][userId] === undefined) newUserShares[team][userId] = 0
newUserShares[team][userId] += sharesBought
const transactionCost = calculateCost(countBets(userShares)) - initialCost
if (transactionCost > userBalances[userId] && userBalances[userId] !== undefined && userBalances[userId] !== null) {
if (userBalances[userId] < cashAmount) {
console.log('Not enough balance')
return userShares
return funds
}
const newFunds = { ...funds }
newFunds[team] += cashAmount
const transactionCost = calculateCost(newFunds) - initialCost
if (transactionCost > userBalances[userId]) {
console.log('Not enough balance after transaction')
return funds
}
userBalances[userId] -= transactionCost
if (dollarsBet[userId] === undefined) dollarsBet[userId] = 0
dollarsBet[userId] += transactionCost
console.log('Paid $' + (transactionCost as unknown as string) + ' for ' + (sharesBought as unknown as string) + ' shares on team ' + (team as unknown as string) + ' by user ' + (userId as unknown as string))
// Record the bet with the current odds
const odds = calculateOdds(newFunds)
bets.push({ userId, team, amount: cashAmount, odds })
return newUserShares
return newFunds
}
interface UserShare { [key: number]: number }
function probabilityToDecimalOdds (probability: number): string {
return (1 / probability).toFixed(2)
}
const userBalances: UserShare = { 1: 1000, 2: 1000, 3: 1000, 4: 1000 }
const dollarsBet: UserShare = { 0: 2 }
let userShares: UserShare[] = [{ 0: 1 }, { 0: 1 }]
// app.get('/bets', (_, res) => {
// const bets = countBets(userShares)
// const odds = bets.map((_, i) => {
// return calculateMarginalPrice(i, bets).toFixed(4)
// })
// res.send(JSON.stringify({
// bets,
// odds
// }))
// })
app.use(express.static(path.join(__dirname, 'public')))
app.get('/bets', (req, res) => {
res.header('Content-Type', 'application/json')
const userId: number = parseInt(req.query.userId as unknown as string)
const team: number = parseInt(req.query.team as unknown as string)
const sharesBought: number = parseInt(req.query.sharesBought as unknown as string)
const userId: number = parseInt(req.query.userId as string)
const team: number = parseInt(req.query.team as string)
const cashAmount: number = parseInt(req.query.cashAmount as string)
if (sharesBought === undefined || sharesBought === null || sharesBought <= 0) {
res.send('No shares bought')
if (cashAmount === undefined || cashAmount === null || cashAmount <= 0) {
res.send('No cash amount specified')
return
}
if (team === undefined || userShares[team] === undefined) {
if (team === undefined || teamFunds[team] === undefined) {
res.send('Invalid team')
return
}
userShares = placeBet(userShares, userId, team, sharesBought)
teamFunds = placeBet(userBalances, teamFunds, userId, team, cashAmount)
const bets = countBets(userShares)
const odds: number[] = calculateOdds(bets)
const odds: string[] = Object.keys(teamFunds).map(teamKey => {
const teamIndex = parseInt(teamKey)
return probabilityToDecimalOdds(calculateOdds(teamFunds)[teamIndex])
})
const liquidity = countLiquidity(bets)
const totalFunds = calculateTotalFunds(teamFunds)
res.send(JSON.stringify({
bets,
odds,
yourBets: userShares.map((shares, team) => {
const userBets = shares[userId] ?? 0
return {
shares: userBets,
winPayout: liquidity * Math.floor(100 * (userBets / bets[team])) / 100,
dollarsBet: dollarsBet[userId] ?? 0
}
})
yourBets: bets.filter(bet => bet.userId === userId),
allBets: bets,
totalFunds
}))
})
app.get('/odds-on-offer', (req, res) => {
const cashAmount: number = parseInt(req.query.cashAmount as string)
if (cashAmount === undefined || cashAmount === null || cashAmount <= 0) {
res.send('No cash amount specified')
return
}
const newFunds = { ...teamFunds, 0: teamFunds[0] + cashAmount, 1: teamFunds[1] }
const oddsOnOffer = Object.keys(newFunds).map(teamKey => {
const teamIndex = parseInt(teamKey)
return probabilityToDecimalOdds(calculateOdds(newFunds)[teamIndex])
})
res.send(JSON.stringify({
oddsOnOffer
}))
})