Skip to main content

Python implementation of Shin's method for calculating implied probabilities from bookmaker odds

Project description

shin

A Python implementation of Shin's method [1, 2] for calculating implied probabilities from bookmaker odds.

Probabilities calculated in this way have been shown to be more accurate than those obtained by the standard approach of dividing the inverse odds by the booksum [3].

Installation

Requires Python 3.9 or above.

pip install shin

Usage

import shin

shin.calculate_implied_probabilities([2.6, 2.4, 4.3])
[0.37299406033208965, 0.4047794109200184, 0.2222265287474275]

Shin's method assumes there is some unknown proportion of bettors that are insiders, z, and this proportion along with the implied probabilities can be estimated using an iterative procedure described in [4].

Diagnostic information from the iterative procedure can be obtained by setting the full_output argument to True:

import shin

shin.calculate_implied_probabilities([2.6, 2.4, 4.3], full_output=True)
{'implied_probabilities': [0.37299406033208965,
  0.4047794109200184,
  0.2222265287474275],
 'iterations': 425,
 'delta': 9.667822098435863e-13,
 'z': 0.01694251276407055}

The returned dict contains the following fields:

  • implied_probablities
  • iterations - compare this value to the max_iterations argument (default = 1000) to check for failed convergence
  • delta - the final change in z for the final iteration. Compare with the convergence_threshold argument (default = 1e-12) to assess convergence
  • z - the estimated proportion of theoretical betting volume coming from insider traders

When there are only two outcomes, z can be calculated analytically [3]. In this case, the iterations and delta fields of the returned dict are 0 to reflect this:

import shin

shin.calculate_implied_probabilities([1.5, 2.74], full_output=True)
{'implied_probabilities': [0.6508515815085157, 0.3491484184914841],
 'iterations': 0,
 'delta': 0,
 'z': 0.03172728540646625}

Note that with two outcomes, Shin's method is equivalent to the Additive Method of [5].

What's New in Version 0.1.0?

The latest version introduces some substantial changes and breaking API changes.

Default Return Value Behaviour

Previously shin.calculate_implied_probabilities would return a dict that contained convergence details of the iterative fitting procedure along with the implied probabilities:

import shin

shin.calculate_implied_probabilities([2.6, 2.4, 4.3])
{'implied_probabilities': [0.37299406033208965,
  0.4047794109200184,
  0.2222265287474275],
 'iterations': 425,
 'delta': 9.667822098435863e-13,
 'z': 0.01694251276407055}

The default behaviour now is for the function to only return the implied probabilities:

import shin

shin.calculate_implied_probabilities([2.6, 2.4, 4.3])
[0.37299406033208965, 0.4047794109200184, 0.2222265287474275]

The full output can still be had by setting the full_output argument to True:

import shin

shin.calculate_implied_probabilities([2.6, 2.4, 4.3], full_output=True)
{'implied_probabilities': [0.37299406033208965,
  0.4047794109200184,
  0.2222265287474275],
 'iterations': 425,
 'delta': 9.667822098435863e-13,
 'z': 0.01694251276407055}

Passing Mappings

A common scenario is to have a mapping between some selection identifiers and their odds. You can now pass such mappings to shin.calculate_implied_probabilities and have a new dict mapping between the selection identifiers and their probabilities returned:

import shin

shin.calculate_implied_probabilities({"HOME": 2.6, "AWAY": 2.4, "DRAW": 4.3})
{'HOME': 0.37299406033208965,
 'AWAY': 0.4047794109200184,
 'DRAW': 0.2222265287474275}

This also works when asking for the full output to be returned:

import shin

shin.calculate_implied_probabilities({"HOME": 2.6, "AWAY": 2.4, "DRAW": 4.3}, full_output=True)
{'implied_probabilities': {'HOME': 0.37299406033208965,
  'AWAY': 0.4047794109200184,
  'DRAW': 0.2222265287474275},
 'iterations': 426,
 'delta': 9.667822098435863e-13,
 'z': 0.01694251276407055}

Controlling the Optimiser

Starting in version 0.1.0, the iterative procedure is implemented in Rust which provides a considerable performance boost. If you would like to use the old Python based optimiser use the force_python_optimiser argument:

import timeit
timeit.timeit(
    "shin.calculate_implied_probabilities([2.6, 2.4, 4.3], force_python_optimiser=True)",
    setup="import shin",
    number=10000
)
3.9101167659973726
import timeit
timeit.timeit(
    "shin.calculate_implied_probabilities([2.6, 2.4, 4.3])",
    setup="import shin",
    number=10000
)
0.14442387002054602

References

[1] H. S. Shin, “Prices of State Contingent Claims with Insider traders, and the Favorite-Longshot Bias”. The Economic Journal, 1992, 102, pp. 426-435.

[2] H. S. Shin, “Measuring the Incidence of Insider Trading in a Market for State-Contingent Claims”. The Economic Journal, 1993, 103(420), pp. 1141-1153.

[3] E. Štrumbelj, "On determining probability forecasts from betting odds". International Journal of Forecasting, 2014, Volume 30, Issue 4, pp. 934-943.

[4] B. Jullien and B. Salanié, "Measuring the Incidence of Insider Trading: A Comment on Shin". The Economic Journal, 1994, 104(427), pp. 1418–1419

[5] S. Clarke, S. Kovalchik, M. Ingram, "Adjusting bookmaker’s odds to allow for overround". American Journal of Sports Science, 2017, Volume 5, Issue 6, pp. 45-49.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

shin-0.1.0.tar.gz (8.6 kB view hashes)

Uploaded Source

Built Distributions

shin-0.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view hashes)

Uploaded PyPy manylinux: glibc 2.17+ x86-64

shin-0.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view hashes)

Uploaded PyPy manylinux: glibc 2.17+ x86-64

shin-0.1.0-cp312-none-win_amd64.whl (110.4 kB view hashes)

Uploaded CPython 3.12 Windows x86-64

shin-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view hashes)

Uploaded CPython 3.12 manylinux: glibc 2.17+ x86-64

shin-0.1.0-cp312-cp312-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl (473.6 kB view hashes)

Uploaded CPython 3.12 macOS 10.9+ universal2 (ARM64, x86-64) macOS 10.9+ x86-64 macOS 11.0+ ARM64

shin-0.1.0-cp311-none-win_amd64.whl (110.8 kB view hashes)

Uploaded CPython 3.11 Windows x86-64

shin-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view hashes)

Uploaded CPython 3.11 manylinux: glibc 2.17+ x86-64

shin-0.1.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl (473.6 kB view hashes)

Uploaded CPython 3.11 macOS 10.9+ universal2 (ARM64, x86-64) macOS 10.9+ x86-64 macOS 11.0+ ARM64

shin-0.1.0-cp310-none-win_amd64.whl (110.8 kB view hashes)

Uploaded CPython 3.10 Windows x86-64

shin-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view hashes)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

shin-0.1.0-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl (473.6 kB view hashes)

Uploaded CPython 3.10 macOS 10.9+ universal2 (ARM64, x86-64) macOS 10.9+ x86-64 macOS 11.0+ ARM64

shin-0.1.0-cp39-none-win_amd64.whl (110.8 kB view hashes)

Uploaded CPython 3.9 Windows x86-64

shin-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view hashes)

Uploaded CPython 3.9 manylinux: glibc 2.17+ x86-64

shin-0.1.0-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl (473.7 kB view hashes)

Uploaded CPython 3.9 macOS 10.9+ universal2 (ARM64, x86-64) macOS 10.9+ x86-64 macOS 11.0+ ARM64

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page