How the portfolio performed
Compared with the 12 months before. Each figure is explained in plain words underneath.
Every night of the year, by expected demand
Each square is one night across all five hotels. The colour is the expected demand compared with the rooms available, based on bookings already made plus the bookings that usually arrive later. Hover over any night for details.
Selling brief for the next six weeks
The nights that need a decision, why they stand out, and the recommended action. The revenue manager decides; this list makes the decision faster and consistent.
| Night | Why it stands out | Demand level | Booked now | Same time last year | Recommended action |
|---|
Are we ahead of or behind last year?
Share of rooms already booked for each of the next 90 nights, compared with the same point last year.
How far ahead guests book
Share of a night's final bookings already made at each point before arrival.
Demand we turned away
When a hotel is full, the guests who could not book are never recorded, so sold-out nights always look smaller than they really were. This estimate recovers that hidden demand, so next year's rates on those nights can be set higher.
Largest sold-out nights in the last 12 months
| Night | Hotel | Rooms sold | Estimated demand | True demand (known in sample) | Average rate |
|---|
Occupancy by month, year over year
Each line is one twelve-month period, October to September.
Occupancy and rate by night of the week
Last 12 months. Friday and Saturday nights carry the strongest demand and rates.
What each booking channel really earns
The rate a guest pays is not what the hotel keeps. After commissions and booking costs, the same room is worth different amounts depending on where it was sold.
| Channel | Share of room nights | Average rate | Cost to acquire | Rate kept after cost | Acquisition spend |
|---|
The earlier the booking, the more likely it cancels
Share of bookings cancelled, by how far ahead they were made (arrivals in the last 12 months).
What local events are worth
Average rate on event nights compared with similar nights (same month and weekday) without an event.
| Event | Rate on event nights | Similar nights | Rate uplift |
|---|
Hotel by hotel
Last 12 months, with the change in revenue per available room compared with the year before.
| Hotel | Style | Rooms | Occupancy | Average rate | RevPAR | RevPAR change | Room revenue |
|---|
Forecast accuracy check
The forecast was tested on the last 12 months as if they had not happened yet. Lower error is better. "Copy last year" is the common spreadsheet method of using the same night last year.
| Forecast made | Average error: booking-pace forecast | Average error: copy last year |
|---|
For BI reviewers: how this page is built
All figures on this page are produced by a Python and SQL pipeline in this repository and exported as JSON. Nothing is typed in by hand.
- Data: about 640,000 sample bookings and 78,000 denied requests generated by generate.py. Requests are replayed against room inventory in booking order, so sell-outs, denials and cancellations behave as they do in a real reservation system.
- Warehouse: SQLite star schema built by SQL scripts: dimensions for hotel, date, segment and channel; facts at booking, stay-night and denied-night grain; a daily hotel mart. Ten data quality checks must pass before the site data refreshes.
- Pace: on-the-books at any lead time is rebuilt from booking and cancellation dates on the stay-night fact, which is equivalent to keeping a nightly reservation snapshot.
- Forecast: additive pickup by hotel, weekday and month, back-tested on the last 12 months against a same-day-last-year (364-day) baseline.
- Unconstraining: for each sold-out night, the last lead time at which it was under 90% full is found; similar nights that came close to selling out but stayed open supply the expected pickup ratio from that point. The sample's known denials are used only to measure accuracy, never in the estimate.
- Demand levels: expected unconstrained demand divided by capacity: 100% and above is Compression, 85% High, 60% Shoulder, below 60% Need.
- Code: demand.py, site_export.py, build.py.