revmng
Revenue management for Python: the building blocks for selling fixed, perishable capacity well. Single-leg seat protection, overbooking, pricing, network bid prices and the standard performance metrics, computed from the textbook methods and validated against published worked examples.
The core has no third-party dependencies. Every result carries provenance
(library version, input hash, timestamp) and a JSON-safe to_dict().
pip install revmng
Quick start
import revmng
# Two-class seat protection (Littlewood's rule)
revmng.littlewood(fare_high=1000, fare_low=400, mean_high=50, sd_high=18)
# -> protect this many seats for the high fare; sell the low fare below it
# Multi-class nested booking limits (EMSR-b, the industry-standard heuristic)
a = revmng.emsr_b(
[(1000, 30, 12), (700, 40, 15), (400, 60, 20)], # (fare, demand mean, sd)
capacity=120,
)
a.booking_limits_int # nested whole-seat booking limits
print(a.summary())
# Overbooking: balance the cost of bumping against the cost of empty seats
o = revmng.overbooking_limit(100, no_show_rate=0.12,
denied_cost=400, spoilage_cost=180)
o.authorization_limit # how many reservations to accept
# Pricing: the newsvendor stocking quantity and the profit-maximising price
revmng.newsvendor(price=10, cost=4, demand_mean=100, demand_sd=30).optimal_quantity
revmng.optimal_price(revmng.LinearDemand(intercept=100, slope=2), unit_cost=10)
# Score booking limits against perfect-hindsight and no-control benchmarks
revmng.revenue_opportunity([1000, 800, 600, 200], [25, 30, 20, 50], capacity=100,
booking_limits=[100, 70, 45, 32]).rom
# Exact optimal protection levels (and the expected revenue EMSR leaves behind)
opt = revmng.optimal_protection_levels(
[(1000, 30, 12), (700, 40, 15), (400, 60, 20)], capacity=120)
opt.expected_revenue
# Should we take this group? (displacement analysis)
cost = revmng.group_displacement(group_size=30, capacity=100,
demand=[70, 95, 85], value_per_unit=70)
revmng.evaluate_group(group_rate=60, units=90, displacement_cost=cost,
variable_cost=50).accept
# Should we accept this multi-night stay? (length-of-stay bid-price control)
revmng.evaluate_stay({"Mon": 80, "Tue": 120, "Wed": 90},
["Mon", "Tue", "Wed"], total_rate=320).accept
Network bid prices need SciPy and the charts need matplotlib:
pip install "revmng[network]" # bid_prices via the deterministic LP
pip install "revmng[plot]" # booking-limit, overbooking and ROM charts
from revmng import Product
n = revmng.bid_prices(
[Product(fare=200, uses={"AB": 1}, demand=60),
Product(fare=300, uses={"AB": 1, "BC": 1}, demand=40)],
capacities={"AB": 100, "BC": 100},
)
n.bid_prices # marginal value of each resource
n.accept(fare=250, uses={"AB": 1, "BC": 1})
What it covers
| Area | Functions |
|---|---|
| Single-leg capacity control | littlewood, emsr_a, emsr_b |
| Exact optimal control | optimal_protection_levels, policy_revenue |
| Overbooking | overbooking_limit (service level and cost based) |
| Pricing | newsvendor, optimal_price (LinearDemand, ConstantElasticityDemand) |
| Group evaluation | evaluate_group, group_displacement |
| Length-of-stay | evaluate_stay |
| Performance metrics | revpar, adr, occupancy, yield_, load_factor, rasm, casm, spill, spoilage |
| Revenue opportunity | revenue_opportunity, nested_revenue |
| Network (needs SciPy) | bid_prices, Product |
| Charts (needs matplotlib) | booking_limit_chart, emsr_curve, overbooking_cost_curve, price_curve, newsvendor_curve, revenue_opportunity_chart, bid_price_chart |
optimal_protection_levels computes the exact optimal nested protection levels
by dynamic programming (the policy the EMSR heuristics approximate) and reports
the optimal expected revenue; policy_revenue scores any policy against it.
Group and length-of-stay decisions share one idea: accept a request when its
contribution covers the opportunity cost of the capacity it displaces (a
marginal seat value or a sum of nightly bid prices).
Demand for the capacity-control methods is assumed normal and independent, with the classic low-before-high booking assumption. EMSR-a and EMSR-b are heuristics; for two classes both reduce to Littlewood's optimal rule.
What is out of scope
revmng computes decisions from the demand you supply. It does not forecast demand, estimate willingness-to-pay, or unconstrain censored bookings, and it does not connect to any reservation, property-management or distribution system. Those belong upstream; keeping them out is what lets every result here be reproduced and audited.
Validation
The methods are checked against published and hand-derived worked examples (see
tests/validation_cases.json):
| Method | Source | Result |
|---|---|---|
| Littlewood booking limits | Phillips, Pricing and Revenue Optimization (2005), Ex. 9.2 | 25 / 50 / 76 reproduced |
| Revenue opportunity metric | Phillips (2005), Ex. 9.7 | perfect 66000, no-control 46000, realized 59200, ROM 66% |
| EMSR-b (two classes) | Belobaba (1989) | equals Littlewood's optimal rule |
| EMSR-b (three classes) | Belobaba (1989) formula, arithmetic shown | protection 23.71 / 70.83, limits 97 / 50 |
| Optimal control (DP) | Talluri and van Ryzin (2004) | matches Littlewood for two classes; expected revenue at least EMSR-b's |
A clean multi-class EMSR-b worked example with both inputs and outputs is hard to
find in the open literature, so the heuristics are also cross-checked two
independent ways (tests/test_crosscheck.py): their protection levels match a
separate SciPy implementation of the same formulas to 1e-9, and over many random
instances EMSR-b never exceeds revmng's exact dynamic program and stays within
about 0.5% of it, matching Belobaba's reported near-optimality.
| Group displacement | standard displacement analysis, arithmetic shown | 40 displaced room-nights, cost 2800, break-even rate 81.11 |
| Length-of-stay bid price | Talluri and van Ryzin (2004), arithmetic shown | 290 hurdle, accept at rate 320 |
| Newsvendor quantity | critical-fractile rule | reproduced |
| Overbooking (service level) | deterministic no-show formula | reproduced |
Definitions and methods
- K. Littlewood, "Forecasting and control of passenger bookings" (1972).
- P. Belobaba, "Application of a probabilistic decision model to airline seat inventory control," Operations Research (1989). EMSR-a and EMSR-b.
- K. Talluri and G. van Ryzin, The Theory and Practice of Revenue Management (2004).
- R. Phillips, Pricing and Revenue Optimization (2005).
License
MIT. Written and maintained by Atakan Arikan, MSc Student at Tsinghua University and Politecnico di Milano.
Metadata
Release files for revmng 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| revmng-0.2.0.tar.gz | 34.6 kB | With provenance | Details for revmng-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| revmng-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for revmng-0.2.0-py3-none-any.whl |
Total release size: 65.5 kB
Release files / revmng-0.2.0.tar.gz
| Download URL | revmng-0.2.0.tar.gz |
|---|---|
| Size | 34.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c47d946eff1acea4653cba5a903002afaebee5413ba48d9f320476ea6a1e3725
|
|
BLAKE2b-256 checksum How to use checksums |
802a7257f07cdac322416fb52838e195d5a4872c12e47821cedbffccf3466a94
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 16, 2026.
Transparency logRelease files / revmng-0.2.0-py3-none-any.whl
| Download URL | revmng-0.2.0-py3-none-any.whl |
|---|---|
| Size | 30.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6ff1910085e736e23efa7b90e472df007e6ef172b846f5d40b1bb9d12132dd46
|
|
BLAKE2b-256 checksum How to use checksums |
c89070ca7028512295c7bba1090f84820392de8dbc9b78e5944a29e445057c95
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 16, 2026.
Transparency logSecurity for revmng 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for revmng-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.