Table of contents
Sport-Arbitrage
Sport-Arbitrage is a small web calculator for sports-betting arbitrage: you enter the odds two betting sites give for the same match, and it tells you whether betting on opposite outcomes at both sites locks in a profit, and how to split the stake. It is an AlgoÉTS repository with a browser page and a Python notebook that run the same formulas.
1. Overview
index.html is a single form styled with Tailwind CSS 2.2.19 from a CDN. All the logic is in script.js:
hasArbitrageOpportunity(odds)adds the inverses of the odds and reports an opportunity when the sum is below 1.calculateArbitragePercentagereturns(1 − Σ 1/odds) × 100.calculateOptimalStakessplits the total investment in proportion to each inverse odd, so every outcome returns the same amount.calculateTotalReturnandcalculateGuaranteedProfitgive the return per outcome and the smallest return minus the investment.calculateArbitrage()reads the form, tests two cross-site pairings (win at site 1 with loss at site 2, and loss at site 1 with win at site 2) and adds a draw to each pairing in three-outcome mode, then writes one result card per opportunity.
arbitrage.ipynb contains the same functions in Python, with sample odds hard-coded in the first cell.
2. Use cases
Browser calculator that checks two bookmakers' odds for a sports-betting arbitrage and splits the stake.
- Pick 2 or 3 outcomes
- Bettor
The Number of Odds selector in index.html calls toggleOddsFields() in script.js, which shows the two Draw fields only for three outcomes.
- Enter odds from two sites
- Bettor
The form takes Win and Lost odds for Site 1 and Site 2, optional Draw odds, and the total investment.
- Detect an arbitrage
- Bettor
calculateArbitrage() tests fixed cross-site pairings; hasArbitrageOpportunity() reports one when the sum of inverse odds is below 1.
- Split the stake
- Bettor
For each opportunity, script.js prints the arbitrage percentage, optimal stakes, returns per outcome and guaranteed profit.
- Check the math in Python
- Bettor
arbitrage.ipynb implements the same functions in Python with hard-coded sample odds for the two-outcome case.
2.1. Scenario: Test the notebook's sample odds
- Open index.html in a browser and keep Number of Odds at 2.
- Enter Win Site 1 = 1.3, Lost Site 1 = 3.93, Win Site 2 = 1.42 and Lost Site 2 = 2.9.
- Enter a Total Investment of 100 and click Calculate.
- Read the result: only the lost-site1 and win-site2 pair qualifies, with stakes of about 26.54 and 73.46.
- Compare with the guaranteed profit line (about 4.31) and rerun arbitrage.ipynb to check the same numbers.
Files readREADME.mdindex.htmlscript.jsstyle.cssarbitrage.ipynb
3. How the algorithm works
Implied probability. Decimal odds o pay o times the stake when the bet wins, stake included. The probability at which that bet breaks even is 1/o, called the implied probability. At one bookmaker, the implied probabilities of all outcomes add up to more than 1; the excess is the bookmaker’s margin. Two sites with slightly different opinions can still disagree enough that, by taking the best price for each outcome across sites, the sum falls below 1.
The arbitrage condition. For outcomes that cover every possible result, with odds o₁ … oₙ taken from any sites:
S = 1/o₁ + 1/o₂ + … + 1/oₙ
arbitrage exists when S < 1
Stake sizing. To get the same payout whichever outcome happens, each stake must be proportional to its implied probability: stakeᵢ = investment × (1/oᵢ) / S. Every outcome then pays stakeᵢ × oᵢ = investment / S, so the guaranteed profit is investment × (1/S − 1). This is exactly what calculateOptimalStakes, calculateTotalReturn and calculateGuaranteedProfit compute.
Worked example, two outcomes. Site 1 offers 2.10 on a win and 1.80 on a loss; site 2 offers 1.75 on a win and 2.05 on a loss.
| Step | Value |
|---|---|
| Site 1 alone: 1/2.10 + 1/1.80 | 1.0317, a 3.2% margin, no arbitrage |
| Site 2 alone: 1/1.75 + 1/2.05 | 1.0592 |
| Win at site 1 + loss at site 2: 1/2.10 + 1/2.05 = 0.4762 + 0.4878 | 0.9640 < 1, arbitrage |
| Loss at site 1 + win at site 2: 1/1.80 + 1/1.75 | 1.1270, none |
| Stakes for 1,000 $: 1000 × 0.4762 / 0.9640 and 1000 × 0.4878 / 0.9640 | 493.98 $ on the win, 506.02 $ on the loss |
| Payout either way: 493.98 × 2.10 = 506.02 × 2.05 | 1,037.35 $ |
| Guaranteed profit | 37.35 $, or 3.73% of the investment |
The page reports an “arbitrage percentage” of 3.60%, which is (1 − S) × 100. That number is the gap below 1, not the return: the return on the money staked is 1/S − 1, here 3.73%.
Three outcomes. With a draw, the same formulas take three odds. Win at 2.60 on site 1, loss at 3.40 on site 2 and draw at 3.50 on site 1 give S = 0.3846 + 0.2941 + 0.2857 = 0.9644. For 1,000 $, the stakes are 398.79 $, 304.96 $ and 296.25 $, each outcome pays 1,036.86 $, and the profit is 36.86 $.
flowchart TD
A["Read odds: win and loss at site 1 and site 2, draws if 3-way, investment"] --> B{"Two or three outcomes?"}
B -->|two| P1["Pair A: win site 1 + loss site 2"]
B -->|two| P2["Pair B: loss site 1 + win site 2"]
B -->|three| P3["Set A: win site 1 + loss site 2 + draw site 1"]
B -->|three| P4["Set B: loss site 1 + win site 2 + draw site 2"]
P1 & P2 & P3 & P4 --> S["S = sum of 1/odds"]
S --> C{"S < 1?"}
C -->|no| N["Skip this combination"]
C -->|yes| K["stakeᵢ = investment × (1/oᵢ) / S"]
K --> R["payoutᵢ = stakeᵢ × oᵢ = investment / S"]
R --> G["profit = min(payout) − investment"]
G --> O["Result card per combination"]
N --> E{"Any card?"}
O --> E
E -->|no| X["No Arbitrage Opportunities Found"]
4. Getting started
Nothing needs to be built. Clone the repository and open the page in a browser (the Tailwind stylesheet loads from jsDelivr, so the page needs network access to look right):
git clone https://github.com/AlgoETS/Sport-Arbitrage.git
cd Sport-Arbitrage
Then open index.html, choose 2 or 3 odds, fill in the odds and the total investment, and click Calculate. To run the Python version:
jupyter notebook arbitrage.ipynb
5. Repository guide
index.html: the calculator form, instructions and result area.script.js: field toggling, arbitrage test, stake split, returns and profit display.arbitrage.ipynb: Python version of the formulas for the two-outcome case.style.css: basic layout styles (not referenced byindex.html).
6. Features
- switch between two-outcome (win/loss) and three-outcome (win/loss/draw) markets;
- test cross-site pairings for an arbitrage with the inverse-odds sum;
- show the arbitrage percentage, optimal stakes, return per outcome and guaranteed profit;
- run entirely in the browser, with no server or account;
- check the same math in a Jupyter notebook.
7. Limits
- Only two fixed pairings are tested. In three-outcome mode, the draw from site 1 always joins win at site 1 and loss at site 2, and the draw from site 2 joins loss at site 1 and win at site 2; other mixes are never tried.
- The “arbitrage percentage” shown is
(1 − S) × 100, slightly lower than the actual return1/S − 1. - The formulas assume decimal odds, and the form does not validate input: empty fields produce
NaN. - Stakes are not rounded and ignore fees, betting limits and odds changes, so the “guaranteed profit” holds only if every bet is accepted at the entered odds.
style.cssis not linked from the page, and the notebook covers only the two-outcome case.- There are no tests.
8. Sources
9. CI/CD pipeline
No CI/CD pipeline in the repository. The code is built and run by hand, as described above.
Generated from the source at commit dbee188.
10. Deployment diagram
Mermaid Deployment diagram
flowchart LR
repo[("GitHub repository")]
local["Local machine"]
repo -->|"clone and run"| local
The repository has no Dockerfile, Compose file or hosting configuration, so the code runs on the machine that clones it.
11. External dependencies
The repository declares no external packages: it has no manifest such as pyproject.toml, requirements.txt, package.json or pom.xml.
12. Glossary
Repository
The versioned source files, configuration, and history of a project.
README
The repository’s entry document, usually covering its purpose and initial setup.
Dependency
A library, tool, or service that another component needs to build or run.
HTTP
A request-and-response protocol used by browsers and web services. A method, path, headers, and optional body describe a request.
Volume and bind mount
A volume is storage managed by the container platform; a bind mount exposes a chosen host path inside a container.
Published port
A host port mapped to a service’s container port. Publishing a port can expose that service beyond its container network.
CLI
A text interface in which commands and arguments select operations. Exit codes let scripts detect success or failure.
Build
The steps that turn source files and dependencies into a runnable program or distributable artifact.
Mermaid
A text-based diagram language. Nodes and connections describe a model; the diagram itself does not execute the project.
13. References
- AlgoÉTS. (2026, November 5). [Web page] github.com.
- AlgoETS. (2026, November 5). Sport-Arbitrage. [Software repository] github.com.
- AlgoETS. (2026, November 5). README. [Source file or documentation] github.com.
- AlgoETS. (2026, November 5). index.html. [Source file or documentation] github.com.
- AlgoETS. (2026, November 5). script.js. [Source file or documentation] github.com.
- AlgoETS. (2026, November 5). arbitrage.ipynb. [Source file or documentation] github.com.
Sources without a publication date show the date they were last checked, November 5, 2026. File links retain the inspected revision where available.