hola
This commit is contained in:
parent
3c37ce8af5
commit
99ee5182ac
2 changed files with 213 additions and 84 deletions
125
Server/src/public/index.html
Normal file
125
Server/src/public/index.html
Normal 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>
|
||||
|
|
@ -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
|
||||
}))
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue