updated to matched betting and partial matched betting

This commit is contained in:
Omar Najjar 2024-07-05 14:35:10 +10:00
parent ac646be4e3
commit 140a2de655
2 changed files with 132 additions and 6 deletions

View file

@ -5,14 +5,40 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bets and Odds</title> <title>Bets and Odds</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.notification {
padding: 10px;
margin: 10px 0;
border: 1px solid transparent;
border-radius: 4px;
}
.notification.info {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
.notification.success {
color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6;
}
.notification.error {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
</style>
</head> </head>
<body> <body>
<h1>Betting System</h1> <h1>Betting System</h1>
<div id="notification" class="notification info" style="display: none;"></div>
<form id="betForm"> <form id="betForm">
<label for="userId">User ID:</label> <label for="userId">User ID:</label>
<input type="number" id="userId" name="userId" required> <input type="number" id="userId" name="userId" required>
<label for="team">Team:</label> <label for="team">Team:</label>
<input type="number" id="team" name="team" required> <button type="button" id="teamAButton" data-team="0">Team A</button>
<button type="button" id="teamBButton" data-team="1">Team B</button>
<input type="hidden" id="team" name="team" required>
<label for="cashAmount">Cash Amount:</label> <label for="cashAmount">Cash Amount:</label>
<input type="number" id="cashAmount" name="cashAmount" required> <input type="number" id="cashAmount" name="cashAmount" required>
<button type="submit">Place Bet</button> <button type="submit">Place Bet</button>
@ -34,16 +60,60 @@
<h2>All Bets</h2> <h2>All Bets</h2>
<div id="allBetsContainer"></div> <div id="allBetsContainer"></div>
<p>Fulfilled Bets: <span id="fulfilledCounter">0</span></p>
<p>Waiting to be Fulfilled: <span id="unfulfilledCounter">0</span></p>
<script> <script>
const betForm = document.getElementById('betForm'); const betForm = document.getElementById('betForm');
const oddsContainer = document.getElementById('oddsContainer'); const oddsContainer = document.getElementById('oddsContainer');
const oddsOnOfferContainer = document.getElementById('oddsOnOfferContainer'); const oddsOnOfferContainer = document.getElementById('oddsOnOfferContainer');
const allBetsContainer = document.getElementById('allBetsContainer'); const allBetsContainer = document.getElementById('allBetsContainer');
const notification = document.getElementById('notification');
const teamAButton = document.getElementById('teamAButton');
const teamBButton = document.getElementById('teamBButton');
const teamInput = document.getElementById('team');
const oddsForm = document.getElementById('oddsForm'); const oddsForm = document.getElementById('oddsForm');
const fulfilledCounter = document.getElementById('fulfilledCounter');
const unfulfilledCounter = document.getElementById('unfulfilledCounter');
const ctx = document.getElementById('bettingChart').getContext('2d'); const ctx = document.getElementById('bettingChart').getContext('2d');
let chart; let chart;
const showNotification = (message, type) => {
notification.textContent = message;
notification.className = `notification ${type}`;
notification.style.display = 'block';
};
const hideNotification = () => {
notification.style.display = 'none';
};
const checkBetAvailability = async () => {
const response = await fetch('/bets');
const data = await response.json();
const unmatchedBets = data.unmatchedBets;
const opposingBetAvailable = unmatchedBets.some(bet => bet.team !== parseInt(teamInput.value));
betForm.querySelector('button[type="submit"]').disabled = !opposingBetAvailable;
if (!opposingBetAvailable) {
showNotification('Bets on this team are disabled until there is a matching bet on the other team.', 'info');
} else {
hideNotification();
}
};
teamAButton.addEventListener('click', () => {
teamInput.value = 0;
checkBetAvailability();
});
teamBButton.addEventListener('click', () => {
teamInput.value = 1;
checkBetAvailability();
});
betForm.addEventListener('submit', async (e) => { betForm.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
const userId = document.getElementById('userId').value; const userId = document.getElementById('userId').value;
@ -56,6 +126,16 @@
updateOdds(data.odds); updateOdds(data.odds);
updateChart(data.odds); updateChart(data.odds);
updateAllBets(data.allBets); updateAllBets(data.allBets);
updateCounters(data.allBets);
checkBetAvailability();
if (data.allBets.some(bet => bet.userId === parseInt(userId) && bet.status === 'matched')) {
showNotification('Your bet has been successfully fulfilled!', 'success');
} else if (data.allBets.some(bet => bet.userId === parseInt(userId) && bet.status === 'partially matched')) {
showNotification('Your bet has been partially fulfilled and is waiting to be fully matched.', 'info');
} else {
showNotification('Your bet is waiting to be fulfilled.', 'info');
}
}); });
oddsForm.addEventListener('submit', async (e) => { oddsForm.addEventListener('submit', async (e) => {
@ -116,10 +196,21 @@
allBetsContainer.innerHTML = ''; allBetsContainer.innerHTML = '';
bets.forEach(bet => { bets.forEach(bet => {
const p = document.createElement('p'); const p = document.createElement('p');
p.textContent = `User ${bet.userId} bet ${bet.amount} on Team ${bet.team} at odds ${bet.odds}`; p.textContent = `User ${bet.userId} bet ${bet.amount} on Team ${bet.team} at odds ${bet.odds} (${bet.status})`;
allBetsContainer.appendChild(p); allBetsContainer.appendChild(p);
}); });
} }
function updateCounters(bets) {
const fulfilledBets = bets.filter(bet => bet.status === 'matched').length;
const unfulfilledBets = bets.filter(bet => bet.status === 'unmatched' || bet.status === 'partially matched').length;
fulfilledCounter.textContent = fulfilledBets;
unfulfilledCounter.textContent = unfulfilledBets;
}
teamInput.addEventListener('input', checkBetAvailability);
checkBetAvailability();
</script> </script>
</body> </body>
</html> </html>

View file

@ -5,7 +5,7 @@ const app = express()
const port = 3000 const port = 3000
const fee = 0.05 // 0.01 = 1% const fee = 0.05 // 0.01 = 1%
const initialLiquidity = 1000 // Initial liquidity for the market const initialLiquidity = 0 // Initial liquidity for the market
interface Balances { [key: number]: number } interface Balances { [key: number]: number }
@ -13,12 +13,15 @@ interface Bet {
userId: number userId: number
team: number team: number
amount: number amount: number
remainingAmount: number
odds: number[] odds: number[]
status: 'unmatched' | 'partially matched' | 'matched'
} }
const userBalances: Balances = { 1: 1000, 2: 1000, 3: 1000, 4: 1000 } const userBalances: Balances = { 1: 1000, 2: 1000, 3: 1000, 4: 1000 }
let teamFunds: Balances = { 0: initialLiquidity, 1: initialLiquidity } // Initial funds for each team let teamFunds: Balances = { 0: initialLiquidity, 1: initialLiquidity } // Initial funds for each team
const bets: Bet[] = [] const bets: Bet[] = []
const unmatchedBets: Bet[] = []
function calculateTotalFunds (funds: Balances): number { function calculateTotalFunds (funds: Balances): number {
return Object.values(funds).reduce((acc: number, val: number) => acc + val, 0) return Object.values(funds).reduce((acc: number, val: number) => acc + val, 0)
@ -40,6 +43,32 @@ function calculateCost (funds: Balances): number {
return result return result
} }
function matchBet (bet: Bet): Bet | null {
for (let i = 0; i < unmatchedBets.length; i++) {
const unmatchedBet = unmatchedBets[i]
if (unmatchedBet.team !== bet.team && unmatchedBet.remainingAmount > 0) {
const matchedAmount = Math.min(bet.remainingAmount, unmatchedBet.remainingAmount)
unmatchedBet.remainingAmount -= matchedAmount
bet.remainingAmount -= matchedAmount
if (unmatchedBet.remainingAmount === 0) {
unmatchedBet.status = 'matched'
unmatchedBets.splice(i, 1)
} else {
unmatchedBet.status = 'partially matched'
}
if (bet.remainingAmount === 0) {
bet.status = 'matched'
return unmatchedBet
} else {
bet.status = 'partially matched'
}
}
}
return null
}
function placeBet (userBalances: Balances, funds: Balances, userId: number, team: number, cashAmount: number): Balances { function placeBet (userBalances: Balances, funds: Balances, userId: number, team: number, cashAmount: number): Balances {
const initialCost = calculateCost(funds) const initialCost = calculateCost(funds)
@ -72,10 +101,15 @@ function placeBet (userBalances: Balances, funds: Balances, userId: number, team
userBalances[userId] -= transactionCost userBalances[userId] -= transactionCost
// Record the bet with the current odds
const odds = calculateOdds(newFunds) const odds = calculateOdds(newFunds)
bets.push({ userId, team, amount: cashAmount, odds }) const newBet: Bet = { userId, team, amount: cashAmount, remainingAmount: cashAmount, odds, status: 'unmatched' }
const matchedBet = matchBet(newBet)
if (matchedBet == null) {
unmatchedBets.push(newBet)
}
bets.push(newBet)
return newFunds return newFunds
} }
@ -113,7 +147,8 @@ app.get('/bets', (req, res) => {
odds, odds,
yourBets: bets.filter(bet => bet.userId === userId), yourBets: bets.filter(bet => bet.userId === userId),
allBets: bets, allBets: bets,
totalFunds totalFunds,
unmatchedBets
})) }))
}) })