The Freight Report - the shipdata.net daily dry-bulk market report
The Daily News page is shipdata.net's plain-English daily market read. It turns the same live fleet observability that powers the DBPI and the route engine into a written report - a one-line headline, a global supply/demand read, a section per tonnage band with its busiest lanes, and a closing forward-looking Outlook that projects where the market is heading from momentum and leading indicators.
What the page is and how to reach it
Daily News lives at three routes, all public (no login required) and all server-rendered as static HTML so they index cleanly for search engines and load instantly:
/news- the index: a reverse-chronological list of recent reports (newest first, up to the 60 most recent). Rendered bynews_index_html./news/<date>- a single day's report, where<date>is an ISO date like2026-06-02. Rendered bynews_report_html./api/news- a machine-readable feed: the same recent list as JSON, an array of{"date", "title"}objects (also capped at 60, newest first). Useful for scripting, monitoring, or pulling the latest headline into another tool.
You can reach News from two fixed places on every public content page: the News link in the sticky top navigation bar, and the News link in the footer. The nav order is News, Guides, Market, About, Sign in, and a Register button; the footer reads © shipdata.net - dry-bulk fleet intelligence followed by News, Guides, About, Market links.
Why it matters: this is the one place on the site where the raw numbers are interpreted for you in narrative form. The Market page gives you the index and the rates; News tells you what they mean today and what changed since the last report - the kind of morning read a desk would otherwise write by hand. Because every figure is computed from shipdata.net's own positioning data, there is no external rate survey in the loop.
The index page (/news) - element by element
The index opens with an <h1> heading "Market News" and a lede line (styled .lede): "A fresh, plain-English read of the dry-bulk market - published daily." Below it is the report list.
- Report rows (
.newsrow) - one clickable anchor per published report, linking to/news/<date>. Each row is a flex layout (space-between): the report's title as an<h3>on the left and the date in small muted text on the right (the.dclass,white-space:nowrapso the date never wraps). A thin bottom border separates each row. - Hover behaviour - hovering anywhere on a row turns the title
<h3>to the accent colour (.newsrow:hover h3), signalling the whole row is a link. The row inherits text colour otherwise, so it reads like a list item, not a button. - Title vs date fallback - the title shown is the report's own headline. If a report somehow has no stored title, the row falls back to showing the date string in the headline slot (
it["title"] or it["date"]), so a row is never blank. - Ordering and cap - rows are newest-first (the backing query is
ORDER BY report_date DESC LIMIT 60), so the top row is always the latest read. The list caps at 60 entries - roughly two months of daily history. - Empty state - before the first report exists, the entire list is replaced by a single lede line: "No reports published yet - the first daily market read will appear here shortly." Seeing this is normal on a fresh deployment or thin-coverage period; it is not an error. It also appears if the read-only database connection fails (the backing helper returns an empty list rather than erroring).
How to use it: scan the titles down the page to read the market's tone day by day - a run of "firming" headlines followed by "holding" or "softening" is itself a signal. Click any row to open the full report for that date.
A single report (/news/<date>) - page chrome and meta line
Opening a report renders inside the shared content shell (_content_page), with these fixed elements around the body:
- Sticky top bar (
.ctop) - the shipdata.net logo (links home) plus the nav row (News / Guides / Market / About / Sign in / Register). It stays pinned to the top as you scroll the report. - Breadcrumb (
.crumb) - at the very top of the body:News › <date>, where "News" links back to the index. Use it to step back to the list. - Title (
<h1>) - the report's one-line headline, the model's capture of the day's tone and direction (e.g. a tightening or softening call). This is the same text shown on the index row. If absent, it falls back to "Market report <date>". - Meta line (
.meta, small muted text) - directly under the title: the report date, optionally followed by two summary chips separated by middots (·):DBPI <value>(the day's freight-pressure index, rounded to one decimal) andLSR <n>%(the global laden sailing ratio as a whole-number percentage). These two headline scalars are only shown if they were stored for that day - if either is missing, that chip simply does not appear, and if both are missing the meta line is just the date. - Report body - the rendered narrative (covered in detail below).
- "← All reports" link - at the foot of the body (with extra top margin), returning you to the index.
- Footer (
.cfoot) - the shared © line and link row, identical across all content pages.
The shell is theme-aware: it reads your saved dark/light preference from localStorage before paint, sets per-page SEO/Open-Graph meta (title, description, canonical URL /news/<date>, og:type=article), and defers /static/ui.js so site-wide widgets (e.g. the command palette) are available.
Invalid or missing dates: the route only accepts a strict YYYY-MM-DD pattern (the path segment is truncated to 10 characters and matched with re.fullmatch(r"\d{4}-\d{2}-\d{2}")); anything else returns a plain 404 Not found. A validly-formatted date that has no stored report also returns 404. So a 404 here means "no report for that day," not a broken link - there is no report for days the generator did not run, and only dates that appear in the index are guaranteed to resolve.
The report body: headline, global section, band → region breakdown, outlook
The body is authored to a fixed five-part structure, and the renderer (_news_body_html) supports a deliberately tiny markdown dialect: it splits the text on blank lines into blocks; any block whose first line begins with # or ## becomes a section heading (rendered as an <h2>); a ### line becomes a smaller <h3> sub-heading (used for the per-region rows); the leading hashes are stripped; **bold** becomes <strong>, single newlines inside a block become <br>, and everything is HTML-escaped first - so the report text can never inject markup. The structure you will see, top to bottom:
- 1. Headline - the single tone line. The generator splits it off as the page title, so it appears as the
<h1>above the body rather than inside it. - 2.
## Global supply & demand- 2-3 paragraphs covering the DBPI level and its day-over-day move, what the LSR and the laden-vs-ballast tonnage split say about how much open (empty, ballasting) tonnage is in the water, and the overall trajectory. This is the macro read: tightening, loosening, or holding. - 3. A
## <Band>section for every tonnage band - the band-by-band breakdown is generated deterministically from the data (not written by the model), so it always walks the full size ladder in DWT order - every band the taxonomy runs (the coaster bands, Handysize, Handymax/Supramax, Panamax, Capesize and the finer cuts), with none ever dropped. Each band header shows its bottom-up day-rate - the same figure the Market page publishes - plus the voyages and tonne-miles tracked, and aMomentum:line (vs balanced, day / week % change, from the FFA Reports engine). A band still accruing enough tracked activity to assess is shown as building rather than omitted. - 4. A
<Region>sub-section for each major load region in the band - the heart of the report. Under every band, the busiest load regions each get a focused paragraph on three things: (a) supply/demand balance - open tonnage, inbound ballast vs laden arrivals, ballast-share and the demand-pull rel (is the region short of ships and supportive, or long and soft?); (b) where cargo is concentrating - the cargo-density load hubs ranked by Port Activity Index, and the 7-day ballast-pull trend as a leading signal (empty ships steam toward a load zone before rates there rise, so a rising ballast-pull flags the ports most likely to firm next); and (c) the busiest lanes out of that region and what is driving them. A band with no active region yet says so in one line. - 5.
## Outlook (forward view)- the predictive section (covered in full in the next section): a genuinely forward-looking read of where the market is heading over the coming days and weeks, with a directional call for the overall market and the bands showing the strongest signals.
First-report caveat (built into the prompt): the generator instructs the model to only describe a day-over-day change where an actual "d/d" figure is present in the data. On the very first report (no previous snapshot to diff against), there are no d/d figures, so the report describes current levels only and makes no claim about "yesterday." That keeps the earliest reports honest rather than inventing a trend.
How to read it as a charterer/broker/owner: start with the global section to set your bias, then jump to the band you trade. Within the band, read it region by region - the per-region paragraphs are your tactical read - a lane firming on "ballast pull building" tells you owners are repositioning empty toward that load area (tightening supply there), while "congestion" firming tells you tonnage is stuck (tightening for a different reason). The stated reason matters because it tells you whether the move is likely to persist (structural flow) or unwind (a queue clearing).
The Outlook (forward view) - the predictive section
The report now closes with a dedicated ## Outlook (forward view) section: a forward read of where the market is likely heading over the coming days and weeks. It is not a restatement of today's levels - it is a directional projection, and it is grounded only in the forward-leaning signals the engine actually has.
What feeds the Outlook:
- FFA Reports momentum - each band's day-over-day and week-over-week % change plus its vs-balanced positioning (rich or cheap relative to its break-even × neutral anchor), taken from the same numbers shown on the FFA Reports page. Acceleration or fade in these is the primary momentum input.
- The 7-day ballast-pull trend per lane - the key leading indicator. Empty ships ballast toward a load region before rates there catch up, so a building ballast pull flags a firming lane ahead of the spot move.
- World port/sea ratio - how much tonnage is tied up in port/anchorage versus sailing; above its norm it is supportive (effective supply shrinks).
- LSR direction and the day-over-day deltas - the macro tightening/loosening trajectory.
What it outputs: a clear directional call - firming, softening, or range-bound - for the overall market and for the bands with the strongest signals, each with its reason (e.g. "Panamax likely to firm: South Brazil → North China ballast pull building while the band already trades above balanced").
How honest it is: the prompt forces the model to state this is a model-and-momentum scenario read, not a guarantee, and to keep it as market commentary - it does not give investment or trading advice, does not tell anyone to buy/sell/charter/fix, and does not state price targets as certainties. On the very first report (no prior snapshot) it makes no day-over-day claims at all.
How it sharpens over time: today the forward signals are still thin, so the Outlook is broad. As lane coverage fills (more ships and voyages tracked) and as the day-over-day snapshot history accrues, the momentum and ballast-pull inputs get denser and the projection becomes more specific - eventually lane-by-lane. If a true forward FFA curve is later loaded, the Outlook can anchor to it (contango / backwardation = the market's own priced expectation) on top of these internal signals.
How a report is produced from our own fleet data
The data is collected by _collect_report_data (which runs against fresh live data - in practice on the production server, so the report's numbers match what you see on the Market and lane tools, not a stale local copy) and handed to generate_daily_report. The report-grade language model (run on our own infrastructure) writes only the global supply/demand narrative and the forward Outlook; the band-by-band breakdown is generated deterministically and spliced in before the Outlook, so every tonnage band always appears in full regardless of what the model chose to write. Generation requires the LLM host, so production simply serves whatever was stored; it does not regenerate on request. The build sequence:
- Global block - pulled from
freight_pressure_index(): the DBPI (printed with its stated neutral of 1000), the global LSR (laden sailing ratio = laden tonnage / total at-sea tonnage), and the at-sea tonnage split in millions of dwt (global_laden_sea_dwtvsglobal_ballast_sea_dwt, e.g. "320M dwt laden vs 180M dwt ballast"). LSR is the engine's core scarcity signal: a higher share of laden tonnage means fewer empty ships chasing cargo. If the DBPI itself is unavailable the generator aborts rather than publishing a hollow report. - Per-band block - for every band (the full ladder, in size order) the report prints the bottom-up composite $/day - the same demand-pull rate the Market page shows, so the two surfaces agree and each band header reconciles with the busiest lanes listed beneath it. The DBPI scarcity level is used only as a fallback when bottom-up priced no lanes for a band; a band with neither shows as building. Each header also carries the band's total voyages and tonne-miles tracked. Every rate the report quotes is computed by shipdata.net from positioning, not assessed by a third party.
- Per-lane block - from
bottomup_market()'snamed_lanes(already sorted by tonne-miles). Each lane carries its tonne-miles, voyage count, indicative rate, and a 7-day ballast-demand trend (bt), printed as "ballast-pull +N% 7d" when present. Lanes are filtered to those with real voyage support and capped at the busiest per band - so the report grows in depth as coverage fills, and never pads thin lanes. - FFA-Reports momentum block - for each band the generator also pulls the FFA Reports figures via
freight_report(): the spot level, the vs-balanced %, and the day (d1) and week (d7) % changes, printed as the band'smomentum:line. These are the forward-leaning numbers the Outlook leans on. - Global drivers & port/sea ratio - the world
port/sea ratioand the FFA Reports driver sentences (tightest / softest band, world tone) are added to the global block as additional forward context. - Day-over-day diffing - before writing, the generator loads the most recent previous report's stored
metricsJSON and computes deltas for DBPI, LSR (in percentage points), each band's rate, and each lane's tonne-miles. Small moves print as "~flat d/d." Today's metrics are then stored alongside the report (stamped with the rate basis) so tomorrow can diff against them in turn. A band's day-over-day rate move is only shown when the previous report priced it on the same basis, so a one-off change in methodology never prints as a bogus overnight jump. This is why the narrative can say "firmed/softened/held" with a number behind it.
Why this matters: the report is a written projection of the exact same engine outputs you can inspect on the Market page and the lane/route tools. If a lane paragraph says Panamax firmed on ballast pull into a load region, that is grounded in the tonne-mile ranking and the 7-day ballast trend the engine measured - you can cross-check it, and act on it, rather than taking an opaque assessment on faith.
Reading the headline scalars: DBPI and LSR
The two chips on the meta line are the report's executive summary in two numbers. Learn to read them at a glance:
DBPI <value>- the Dry-Bulk Pressure Index, shown rounded to one decimal. 1000 is neutral (the report says so explicitly in its data block). Above 1000 means freight pressure is building (tighter market, rates biased up); below 1000 means it is easing. Read the chip for level, then read the global section for the day-over-day move - a high DBPI that just softened tells a different story than a high DBPI still climbing.LSR <n>%- the global laden sailing ratio as a whole-number percent (the stored fraction times 100). It answers "what share of the fleet at sea is loaded versus ballasting empty?" A higher LSR means fewer empty ships hunting cargo, i.e. tighter supply and upward rate pressure; a falling LSR means open tonnage is building. This is the scarcity gauge underneath the DBPI.
How to use them together: the chips give you the day's stance in one line before you read a word of prose. If DBPI is above 1000 and LSR is high and rising, the market is tight and tightening - expect the band sections to read "firming." If DBPI is near neutral and LSR is slipping, expect a "holding" or "softening" tone with open tonnage being the named culprit. Because both scalars are stored per report, you can also page back through the index and watch them drift over weeks - the cheapest possible trend read.
Practical workflow and reading tips
A concise way to work the page day to day:
- Bookmark
/news, not a dated URL. The index always surfaces the latest report at the top, and dated URLs only resolve for days that actually have a report. - Read top-down on a report: headline → meta chips (DBPI, LSR) → Global supply & demand → your band → that band's busiest lanes → Outlook. Each layer narrows from macro stance to the lane you can actually fix cargo on.
- Trust the stated reason on a lane. "Ballast pull building" (owners repositioning empty toward a load area) implies a more durable tightening than "congestion" (tonnage temporarily stuck, which unwinds when the queue clears). Same firming, different shelf life.
- Use the d/d language as a filter. Anything tagged "~flat d/d" is noise; the report only commits to firmed/softened where a real delta exists. On a first-ever report there will be no day-over-day claims at all - that is by design, not a gap.
- Cross-check against the live tools. Every figure traces back to the DBPI, the band rates, and the lane tonne-mile ranking you can open on the Market and route pages. The News report is the narrative layer on top of that engine, not a separate data source.
- Watch the headline cadence over the index. A string of "firming" titles giving way to "holding" then "softening" is a turn you can see at the list level before reading any single report in full.
One operational note: a 404 on a /news/<date> link almost always means no report was generated for that day (the generator did not run, or coverage was too thin), not a broken site. Step back via the breadcrumb or the footer News link and pick a date that appears in the index.