Cargo revenue management

Why selling space in the hold is harder than selling seats, and what a bid price per kilo and per cubic metre does about it.

Passenger RM sells identical seats booked weeks ahead. Cargo RM sells space that runs out by weight or by volume, booked late and often tendered heavier, lighter or not at all. Below, one flight's hold is sold under first come, first served and bid-price control. Companion to Cargo 101, chapters 07 and 08; builds on RM 101, chapter 04.

Aircraft
Demand
Show-up variability

Loading the scenario…

How it works

Background

Passenger RM sells identical seats, booked weeks ahead by people who mostly turn up. Cargo RM sells one space that can run out by weight or by volume, to shipments of every size and density, booked in the last few days by forwarders who often hold space in advance, and who then tender more or less than they booked, or nothing at all. The page sells one flight's hold to four kinds of shipper under two policies: first come, first served, and bid-price control, which values each kilo and each cubic metre with a small linear program and accepts a shipment only if it pays for the space it takes. Both overbook, and both pay for any cargo they have to offload. The network version of bid prices is on the network bid prices page.

The flight and its demand

One flight, either the belly of a long-haul passenger widebody (16,000 kg and 110 m³ available for cargo) or a large main-deck freighter (95,000 kg and 580 m³ usable). Both are assumptions in round numbers: a belly's cargo capacity depends on the passenger load, bags and fuel on the day, and a freighter's usable volume is well below its gross hold volume because pallets are contoured and never packed perfectly. For scale, Boeing lists a maximum structural payload of 107 tonnes for the 777 Freighter.

Booking requests come from four kinds of shipper, each with its own rate, timing, show-up rate and mix of densities (all in cargorm.json):

For one flight, the number of requests from each shipper type is a Poisson draw whose mean is itself random (a gamma factor with a 30% coefficient of variation). Each request gets a weight from a lognormal distribution (capped at 3,000 kg and 12 m³ in the belly, 12,000 kg and 60 m³ on the freighter), a density from one of two or three profiles (dense, standard, light, each with ±20% lognormal noise), a rate with 15% lognormal noise around its type's rate, and a booking time: the share of the 14-day window elapsed is Beta(5, 2) for allotments, Beta(4, 2) for spot, Beta(7, 1) for express and Beta(2, 2) for special cargo. "Balanced" demand asks for about 1.75 times the flight's weight capacity in booked kilos; the other two demand settings shift the mix and scale every density by 0.8 or 1.35.

What a shipment pays

Air cargo is charged on the chargeable weight, the higher of the actual weight and the volume weight. IATA's general rule is to divide the volume in cm³ by 6,000, so one cubic metre counts as 166.67 kg: cw = max(w, 166.67 v), and revenue = rate × cw.

Bid prices from a two-resource linear program

At 14 days out and then about once a day, the bid-price policy solves the deterministic LP for the demand still to come. Each product j is a shipper type in one density profile, with expected remaining requests Dj, average weight wj, average volume vj and expected revenue rj:

maximise Σj rj xj subject to Σj wj xj ≤ Cw, Σj vj xj ≤ Cv, 0 ≤ xj ≤ Dj,

where Cw and Cv are the kilos and cubic metres still free under the overbooking limits, net of the allotment block until it is released. The bid prices πw (US$ per kg) and πv (US$ per m³) are the dual values of the two capacity constraints. Because there are only two of them, the page finds them exactly without a solver: the dual, minimise πwCw + πvCv + Σj Dj (rj − πwwj − πvvj)+ over π ≥ 0, is convex and piecewise linear in two variables, so its minimum lies at one of the points where two of the lines rj = πwwj + πvvj (or the axes) cross, and the page checks them all. The results match an LP solver (HiGHS) to rounding.

A request with booked weight w, volume v and revenue R is accepted if it fits and its expected revenue covers the value of its space: s t̄ R ≥ πww + πvv, where s is its shipper type's show-up rate and t̄ the average tendered share of booked weight. The same rule prices a dense shipment mainly by its kilos and a light one mainly by its cubic metres, so when the hold is short of volume, bulky low-rate cargo is turned away first. Requests within a forwarder's remaining allotment are always accepted: that is the contract.

Show-up, overbooking and offloads

Every accepted booking either shows up or not (with its type's show-up rate, scaled by the variability setting). If it shows, the tendered weight is the booked weight times a uniform factor (70–110% at medium variability, 85–105% at low, 55–115% at high), and the tendered volume is the booked volume times the same factor times a lognormal error with a 5% upward bias, because shippers tend to under-declare volume. Both policies accept up to the capacity times one plus the overbooking level, set separately for weight and volume. At departure, if the tendered cargo is over the aircraft's weight or volume, general cargo (allotment and spot) is offloaded before express and special cargo, starting with the least revenue per share of the aircraft it uses, until the rest fits; anything removed that turns out not to be needed is put back. An offloaded shipment earns nothing on this flight and costs the offload cost per chargeable kg: US$4.00 by default, more than any rate, because the cargo still has to move (handling, storage, trucking or space on a later flight) and the customer was promised this one. Net revenue = revenue flown − offload cost.

The overbooking grid in section 4 is precomputed by scripts/cargorm/precompute.js with the same code as this page, for every scenario, on 1,000 flights per cell for the belly and 300 for the freighter, in 5-point steps from 0 to 60%, and stored in cargorm-overbooking.json. It stores revenue and offloaded kilos separately, so a new offload cost only needs a subtraction. Sections 1 to 3 run live in your browser, and the generator is seeded, so the page opens on the same flights every time.

Sources

Limitations

This is a personal project and isn't affiliated with any airline.