diff --git a/Server/src/public/index.html b/Server/src/public/index.html new file mode 100644 index 0000000..f61f400 --- /dev/null +++ b/Server/src/public/index.html @@ -0,0 +1,125 @@ + + + + + + Bets and Odds + + + +

Betting System

+
+ + + + + + + +
+ +

Current Odds

+
+ +

Betting Graph

+ + +

Odds on Offer

+
+ + + +
+
+ +

All Bets

+
+ + + + diff --git a/Server/src/server.ts b/Server/src/server.ts index 9ec4556..cee5fa7 100644 --- a/Server/src/server.ts +++ b/Server/src/server.ts @@ -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 })) })