vortexa-claude-skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/VERSION +1 -0
- package/bin/.gitkeep +0 -0
- package/bin/setup.js +302 -0
- package/commands/vortexa/_check-setup.md +9 -0
- package/commands/vortexa/_skill-template.md +100 -0
- package/commands/vortexa/breakdown.md +294 -0
- package/commands/vortexa/cargo-flows.md +247 -0
- package/commands/vortexa/compare.md +315 -0
- package/commands/vortexa/custom.md +214 -0
- package/commands/vortexa/explain.md +124 -0
- package/commands/vortexa/init.md +133 -0
- package/commands/vortexa/oow.md +189 -0
- package/commands/vortexa/seasonal.md +185 -0
- package/commands/vortexa/voyages.md +285 -0
- package/context/.gitkeep +0 -0
- package/context/cargo-movements.md +738 -0
- package/context/date-units.md +188 -0
- package/context/endpoint-template.md +176 -0
- package/context/entity-resolution.md +217 -0
- package/context/guardrails.md +161 -0
- package/context/reference-endpoints.md +651 -0
- package/context/voyages.md +636 -0
- package/lib/__init__.py +4 -0
- package/lib/aliases.json +52 -0
- package/lib/api.py +20 -0
- package/lib/entities.py +254 -0
- package/lib/inventory.py +140 -0
- package/lib/movements.py +242 -0
- package/lib/requirements.txt +6 -0
- package/lib/seasonal.py +200 -0
- package/lib/timeseries.py +271 -0
- package/lib/utils.py +120 -0
- package/lib/vessels.py +192 -0
- package/lib/visualization.py +164 -0
- package/lib/voyages.py +236 -0
- package/package.json +28 -0
- package/templates/.env.template +3 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
# Voyages & VoyagesSearchEnriched
|
|
2
|
+
|
|
3
|
+
> Endpoint documentation for querying vessel voyages, laden/ballast periods, events, and location enrichment.
|
|
4
|
+
> For date/unit rules, see date-units.md (pre-loaded). For entity resolution, see entity-resolution.md (pre-loaded).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to Use Voyages vs CargoMovements
|
|
9
|
+
|
|
10
|
+
This is the most important decision. Getting it wrong means querying the wrong endpoint entirely.
|
|
11
|
+
|
|
12
|
+
| User Wants | Use This | Why |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Product-centric flow volumes (exports, imports, trade flows) | CargoMovements / CargoTimeSeries | CM tracks cargo quantity from origin to destination |
|
|
15
|
+
| Vessel-centric voyage tracking (where did this ship go?) | VoyagesSearchEnriched | Voyages tracks the vessel journey with events |
|
|
16
|
+
| OOW volumes broken down by region | CM for volumes + Voyages for location | CM-Authoritative pattern (see below) |
|
|
17
|
+
| Individual vessel journey details | VoyagesSearchEnriched | Full voyage with events, ports, durations |
|
|
18
|
+
| Congestion / waiting time analysis | Congestion breakdown or VoyagesSearchEnriched | Location + waiting time data |
|
|
19
|
+
| Aggregate vessel counts or DWT over time | VoyagesTimeseries | Time-series vessel-perspective metrics |
|
|
20
|
+
| Tonne-miles, voyage counts with corporate splits | VoyagesTimeseriesV2 | V2 has richer corporate breakdowns |
|
|
21
|
+
| Fleet utilisation, ballast positioning | VoyagesTimeseries or VoyagesSearchEnriched | Voyage status + vessel class filters |
|
|
22
|
+
| "How much crude flowed from A to B?" | CargoMovements / CargoTimeSeries | NEVER use Voyages for volume flow questions |
|
|
23
|
+
| "How many tankers arrived at port X?" | VoyagesTimeseries with `arrivals` | Vessel count, not cargo volume |
|
|
24
|
+
|
|
25
|
+
**Signal words for Voyages:** "vessels", "ships", "voyages", "fleet", "tonne-miles", "DWT", "congestion", "ballast", "laden", "voyage events", "vessel journey", "speed", "waiting time"
|
|
26
|
+
|
|
27
|
+
**Signal words for CargoMovements:** "exports", "imports", "barrels", "tonnes", "cargo", "trade flow", "product flow", "volumes shipped", "floating storage volumes"
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## CRITICAL RULES
|
|
32
|
+
|
|
33
|
+
**NEVER** mix V1 underscore naming with V2 hyphen naming:
|
|
34
|
+
- V1: `origin_country`, `vessel_count`, `tonne_miles`
|
|
35
|
+
- V2: `origin-country`, `voyage-count`, `tonne-miles-sum`
|
|
36
|
+
- V2 metric and breakdown are constructor args; V1 they are search params
|
|
37
|
+
|
|
38
|
+
**NEVER** forget the `exclude_overlapping_entries` behavior difference:
|
|
39
|
+
- Timeseries: defaults to `true` (de-duplicates overlapping voyage periods)
|
|
40
|
+
- Search: defaults to `false` (may count overlapping days)
|
|
41
|
+
|
|
42
|
+
**NEVER** assume ballast voyage origin = vessel's starting port:
|
|
43
|
+
- For ballast voyages, `origins` = last discharge port (not where the ballast voyage started)
|
|
44
|
+
- For ballast voyages, `destinations` = next load port (not where the ballast voyage ends)
|
|
45
|
+
- This is the opposite of laden voyage logic and a common source of confusion
|
|
46
|
+
|
|
47
|
+
**ALWAYS** specify `voyage_status` filter -- default returns all statuses including outdated ones.
|
|
48
|
+
|
|
49
|
+
**ALWAYS** set `size` higher than 1 for search endpoints -- the default is `1`, which returns only a single record.
|
|
50
|
+
|
|
51
|
+
**ALWAYS** set `time_min` and `time_max` explicitly -- never rely on defaults.
|
|
52
|
+
|
|
53
|
+
**ALWAYS** match `voyage_date_range_activity` to user intent:
|
|
54
|
+
|
|
55
|
+
| User Says | `voyage_date_range_activity` | Meaning |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| "active vessels", "vessels during this period" | `active` | Voyage overlapped the time window at any point |
|
|
58
|
+
| "arrivals", "vessels that arrived" | `arrivals` | Voyage ended (final discharge) within the time window |
|
|
59
|
+
| "departures", "vessels that departed" | `departures` | Voyage started (first load) within the time window |
|
|
60
|
+
| "transited waypoint" | `waypoint` | Had a waypoint event within the time window |
|
|
61
|
+
|
|
62
|
+
See also: guardrails.md for cross-cutting rules on entity resolution, date ranges, and confirmation steps.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Endpoint Selection
|
|
67
|
+
|
|
68
|
+
| Endpoint | Purpose | Returns | When to Use |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| VoyagesSearchEnriched | Individual voyage details | List of voyage objects with events | "show voyages", "vessel journey", "list ships" |
|
|
71
|
+
| VoyagesTimeseries (V1) | Aggregate vessel metrics over time | Time series with breakdowns | "how many vessels", "DWT trend", "fleet utilisation" |
|
|
72
|
+
| VoyagesTimeseriesV2 | Voyage-level metric aggregations | Time series with V2 splits | "tonne-miles by class", "voyage count by corporate" |
|
|
73
|
+
| Congestion breakdown | Congestion counts by location | Port/terminal congestion data | "port congestion", "waiting times" |
|
|
74
|
+
| Top hits | Voyage counts by property | Ranked list | "busiest ports", "top destinations" |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## What is a Voyage
|
|
79
|
+
|
|
80
|
+
A voyage is a **continuous laden or ballast period** for a single vessel. Each voyage contains multiple **Voyage Events** describing activity during that period.
|
|
81
|
+
|
|
82
|
+
**Key rules:**
|
|
83
|
+
- A laden voyage starts at first load, ends at full discharge (partial discharges do not end a voyage)
|
|
84
|
+
- A ballast voyage starts after full discharge, ends before next load
|
|
85
|
+
- Multiple loads/discharges within one continuous period = still one voyage
|
|
86
|
+
- STS transfers, congestion/waiting, and floating storage are recorded as events within the voyage, not as separate voyages
|
|
87
|
+
- Congestion events include duration and location, extractable from voyage events
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Status Filters
|
|
92
|
+
|
|
93
|
+
Each status filter has a corresponding `*_excluded` variant for exclusion.
|
|
94
|
+
|
|
95
|
+
### Voyage Status
|
|
96
|
+
|
|
97
|
+
| Value | Meaning | When to Use |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| `laden` | Vessel is carrying cargo | "loaded ships", "carrying cargo" |
|
|
100
|
+
| `ballast` | Vessel is empty / repositioning | "empty vessels", "ballast fleet" |
|
|
101
|
+
|
|
102
|
+
### Movement Status
|
|
103
|
+
|
|
104
|
+
| Value | Meaning | When to Use |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `moving` | Vessel underway | "in transit", "moving vessels" |
|
|
107
|
+
| `stationary` | Vessel stopped | "anchored", "stopped" |
|
|
108
|
+
| `waiting` | Vessel queuing | "waiting vessels", "queuing" |
|
|
109
|
+
| `congestion` | Vessel in congestion zone | "congested" |
|
|
110
|
+
| `slow` | Vessel slow steaming | "slow steaming" |
|
|
111
|
+
|
|
112
|
+
### Cargo Status
|
|
113
|
+
|
|
114
|
+
| Value | Meaning | When to Use |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `in-transit` | Cargo underway | "in transit cargo" |
|
|
117
|
+
| `loading` | Cargo being loaded | "at load port" |
|
|
118
|
+
| `discharging` | Cargo being discharged | "at discharge port" |
|
|
119
|
+
| `floating-storage` | Cargo in floating storage | "stored at sea" |
|
|
120
|
+
|
|
121
|
+
### Location Status
|
|
122
|
+
|
|
123
|
+
| Value | Meaning | When to Use |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `berth` | At berth / docked | "docked" |
|
|
126
|
+
| `anchorage-zone` | At anchorage | "anchored" |
|
|
127
|
+
| `on-the-sea` | At sea | "on the water" |
|
|
128
|
+
| `dry-dock` | In dry dock | "in dry dock" |
|
|
129
|
+
|
|
130
|
+
### Commitment Status
|
|
131
|
+
|
|
132
|
+
| Value | Meaning | When to Use |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| `committed` | Vessel booked / fixed | "committed", "fixed" |
|
|
135
|
+
| `uncommitted` | Vessel not committed | "open", "available" |
|
|
136
|
+
| `open` | Vessel open for charter | "open" |
|
|
137
|
+
| `unknown` | Status unknown | -- |
|
|
138
|
+
|
|
139
|
+
### Vessel Risk Level
|
|
140
|
+
|
|
141
|
+
| Value | When to Use |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `low` | "clean vessels", "low risk" |
|
|
144
|
+
| `medium` | "medium risk" |
|
|
145
|
+
| `high` | "high risk", "risky vessels" |
|
|
146
|
+
| `sanctioned` | "sanctioned fleet" |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Geography Filters
|
|
151
|
+
|
|
152
|
+
All geography filters require Vortexa hex hash IDs (see entity-resolution.md). Each has a `*_excluded` variant.
|
|
153
|
+
|
|
154
|
+
| Parameter | Filters On | Use When User Says |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `origins` | Cargo origin(s); for ballast = last discharge port | "departing from", "loaded in" |
|
|
157
|
+
| `destinations` | Cargo destination(s); for ballast = next load port | "heading to", "discharging at" |
|
|
158
|
+
| `locations` | Any visited location during the voyage | "passing through", "currently at" |
|
|
159
|
+
| `voyage_origins` | First port visit of the voyage | "voyage started from" |
|
|
160
|
+
| `voyage_destinations` | Last port visit of the voyage | "voyage ended at" |
|
|
161
|
+
| `voyage_routes` | First origin OR final destination matches | "route involves" |
|
|
162
|
+
| `congestion_target_location` | Location where congestion occurred | "congested at" |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## API Reference: VoyagesSearchEnriched
|
|
167
|
+
|
|
168
|
+
**Endpoint:** `POST /v5/voyages/search-enriched`
|
|
169
|
+
**SDK:** `VoyagesSearchEnriched().search(...)`
|
|
170
|
+
**Purpose:** Retrieve individual voyage records with full event, vessel, product, and corporate detail.
|
|
171
|
+
|
|
172
|
+
### Endpoint-Specific Parameters
|
|
173
|
+
|
|
174
|
+
| Parameter | Type | Default | Description |
|
|
175
|
+
|---|---|---|---|
|
|
176
|
+
| `size` | integer | `1` | Records per page (0-500). **Default is 1 -- always set higher.** |
|
|
177
|
+
| `unit` | enum | `b` | Quantity unit: `b` (barrels), `t` (tonnes), `cbm` (cubic metres) |
|
|
178
|
+
| `order` | enum | `vessel_name` | Sort by: `vessel_name`, `dwt`, `vessel_class`, `start_date`, `end_date`, `latest_product`, `tonne_miles`, `distance`, `duration`, `quantity`, `quantity_tonnes`, `quantity_barrels`, `quantity_cubic_metres` |
|
|
179
|
+
| `order_direction` | enum | `desc` | `asc` or `desc` |
|
|
180
|
+
| `search_after` | object | none | Pagination cursor from previous response's `next_request` |
|
|
181
|
+
| `_response` | enum | `json` | `json` or `csv` |
|
|
182
|
+
|
|
183
|
+
### SDK-Specific Parameters
|
|
184
|
+
|
|
185
|
+
| Parameter | Description |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `columns` | `None` for `.to_list()`, `'all'` or list of column names for `.to_df()` |
|
|
188
|
+
| `origin_behaviour` | Which departure mode `voyage_date_range_activity` counts: `first_load` or `any_load` |
|
|
189
|
+
| `destination_behaviour` | Which arrival mode counts: `last_discharge` or `any_discharge` |
|
|
190
|
+
| `flags` | SDK uses `flags` instead of REST `vessel_flags` |
|
|
191
|
+
| `ice_class` | SDK uses `ice_class` instead of REST `vessel_ice_class` |
|
|
192
|
+
| `vessel_propulsion` | Filter by propulsion type IDs |
|
|
193
|
+
|
|
194
|
+
### DataFrame Columns
|
|
195
|
+
|
|
196
|
+
Available columns for `.to_df()` and CSV output:
|
|
197
|
+
|
|
198
|
+
**Vessel:** `vessel_name`, `imo`, `dwt`, `capacity`, `vessel_class`, `build_year`, `flag`, `risk_rating`, `scrubber`, `coating`
|
|
199
|
+
|
|
200
|
+
**Voyage:** `voyage_status`, `cargo_status`, `start_date`, `end_date`, `duration`, `distance`, `tonne_miles`, `quantity`, `voyage_id`, `previous_voyage_id`, `next_voyage_id`
|
|
201
|
+
|
|
202
|
+
**Origin (cargo origin):** `origin`, `origin_terminal`, `origin_port`, `origin_country`, `origin_shipping_region`, `origin_basin`, `origin_wider_shipping_region`, `origin_country_zone`, `origin_alternative_region`, `origin_state_or_province`
|
|
203
|
+
|
|
204
|
+
**Destination (cargo destination):** `destination`, `destination_terminal`, `destination_port`, `destination_country`, `destination_shipping_region`, `destination_basin`, `destination_wider_shipping_region`, `destination_country_zone`, `destination_alternative_region`, `destination_state_or_province`
|
|
205
|
+
|
|
206
|
+
**First/Final (voyage-level):** `first_origin`, `first_origin_terminal`, `first_origin_port`, `first_origin_country`, `first_origin_shipping_region` (and same pattern with `final_destination_*`)
|
|
207
|
+
|
|
208
|
+
**Commercial:** `charterer`, `effective_controller`, `time_charterer`
|
|
209
|
+
|
|
210
|
+
**Product:** `latest_product`, `latest_product_group`, `latest_product_category`, `latest_product_grade`
|
|
211
|
+
|
|
212
|
+
### CSV Output
|
|
213
|
+
|
|
214
|
+
Request CSV by setting `_response='csv'` or header `accept: text/csv`, with `csv_columns='all'` or a list of specific columns.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## API Reference: VoyagesTimeseries (V1)
|
|
219
|
+
|
|
220
|
+
**Endpoint:** `POST /v5/voyages/timeseries`
|
|
221
|
+
**SDK:** `VoyagesTimeseries().search(...)`
|
|
222
|
+
**Purpose:** Time-series vessel data. Aggregate metrics like vessel count, cargo quantity, tonne-miles, DWT, speed, wait times with breakdowns.
|
|
223
|
+
|
|
224
|
+
### Endpoint-Specific Parameters
|
|
225
|
+
|
|
226
|
+
| Parameter | Type | Default | Description |
|
|
227
|
+
|---|---|---|---|
|
|
228
|
+
| `breakdown_frequency` | enum | `day` | `day`, `week`, `doe_week`, `month`, `quarter`, `year` |
|
|
229
|
+
| `breakdown_property` | enum | `vessel_count` | What to measure: `vessel_count`, `utilisation`, `cargo_quantity`, `avg_wait_time`, `dwt`, `cubic_capacity`, `tonne_miles`, `avg_distance`, `avg_speed` |
|
|
230
|
+
| `breakdown_split_property` | enum | `none` | How to split. See split values below. |
|
|
231
|
+
| `breakdown_unit_operator` | enum | `sum` | `sum` or `avg` |
|
|
232
|
+
| `breakdown_size` | integer | `100` | Max breakdown categories (0-10000). Overflow grouped under "Other". |
|
|
233
|
+
| `exclude_overlapping_entries` | boolean | `true` | De-duplicate overlapping voyage periods. **Defaults true for timeseries.** |
|
|
234
|
+
|
|
235
|
+
### Split Property Values (V1)
|
|
236
|
+
|
|
237
|
+
**Vessel:** `vessel_status`, `vessel_class_group`, `vessel_class_coarse`, `vessel_class_granular`, `vessel_flag`, `commitment_status`, `fixture_status`, `cargo_status`, `location_status`, `movement_status`, `presets`
|
|
238
|
+
|
|
239
|
+
**Corporate:** `effective_controller`, `charterer`
|
|
240
|
+
|
|
241
|
+
**Origin geography:** `origin_region`, `origin_shipping_region`, `origin_shipping_region_v2`, `origin_wider_shipping_region`, `origin_basin`, `origin_trading_region`, `origin_trading_sub_region`, `origin_trading_block`, `origin_country`, `origin_country_zone`, `origin_port`, `origin_port_or_sts_zone`, `origin_terminal`, `origin_alternative_region`, `origin_state_or_province`
|
|
242
|
+
|
|
243
|
+
**Destination geography:** `destination_region`, `destination_shipping_region`, `destination_shipping_region_v2`, `destination_wider_shipping_region`, `destination_basin`, `destination_trading_region`, `destination_trading_sub_region`, `destination_trading_block`, `destination_country`, `destination_country_zone`, `destination_port`, `destination_port_or_sts_zone`, `destination_terminal`, `destination_alternative_region`, `destination_state_or_province`
|
|
244
|
+
|
|
245
|
+
**Location (current):** `location_port`, `location_country`, `location_shipping_region`, `location_shipping_region_v2`, `location_wider_shipping_region`, `location_basin`, `location_country_zone`
|
|
246
|
+
|
|
247
|
+
**Congestion location:** `congestion_location_port`, `congestion_location_country`, `congestion_location_shipping_region`, `congestion_location_shipping_region_v2`, `congestion_location_basin`, `congestion_location_wider_shipping_region`, `congestion_location_region`, `congestion_location_trading_region`, `congestion_location_trading_sub_region`, `congestion_location_trading_block`, `congestion_location_alternative_region`, `congestion_location_country_zone`, `congestion_location_state_or_province`
|
|
248
|
+
|
|
249
|
+
**Product:** `product_group`, `product_group_product`, `product_category`, `product_grade`, `latest_product_group`, `latest_product_group_product`, `latest_product_category`, `latest_product_grade`
|
|
250
|
+
|
|
251
|
+
### Response Format
|
|
252
|
+
|
|
253
|
+
Each row contains `key` (timestamp), `value` (aggregate), `count`, and a `breakdown` array of `{id, label, value, count}` pairs for each split category.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## API Reference: VoyagesTimeseriesV2
|
|
258
|
+
|
|
259
|
+
**Endpoint:** `POST /v5/voyages/timeseries/{metric}/{breakdown_property}`
|
|
260
|
+
**SDK:** `VoyagesTimeseriesV2(metric, breakdown_property).search(...)`
|
|
261
|
+
**Purpose:** Voyage-level metric aggregations with metric and breakdown set at instantiation (SDK) or in URL path (REST).
|
|
262
|
+
|
|
263
|
+
### Constructor / Path Parameters
|
|
264
|
+
|
|
265
|
+
| Parameter | Values |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `metric` | `voyage-count`, `voyage-count-per-day`, `tonne-miles-sum`, `tonne-miles-sum-per-day`, `distance-avg`, `distance-voyage-avg`, `duration-avg` |
|
|
268
|
+
| `breakdown_property` | `status`, `vessel-class-group`, `vessel-class-coarse`, `vessel-class-granular`, `origin-region`, `origin-shipping-region`, `origin-wider-shipping-region`, `origin-trading-region`, `origin-basin`, `origin-country`, `origin-port`, `origin-terminal`, `destination-region`, `destination-shipping-region`, `destination-wider-shipping-region`, `destination-trading-region`, `destination-basin`, `destination-country`, `destination-port`, `destination-terminal`, `location-waypoint`, `product-group`, `product-group-product`, `product-category`, `product-grade`, `corporate-entity-cargo-trader`, `corporate-entity-charterer`, `corporate-entity-effective-controller`, `corporate-entity-vessel-owner`, `corporate-entity-time-charterer` |
|
|
269
|
+
|
|
270
|
+
### Search Parameters
|
|
271
|
+
|
|
272
|
+
| Parameter | Type | Default | Description |
|
|
273
|
+
|---|---|---|---|
|
|
274
|
+
| `breakdown_frequency` | enum | `day` | `day`, `week`, `doe_week`, `month`, `quarter`, `year` |
|
|
275
|
+
| `breakdown_size` | integer | `100` | Max breakdown categories |
|
|
276
|
+
| `exclude_overlapping_entries` | boolean | `true` | De-duplicate overlapping voyages |
|
|
277
|
+
|
|
278
|
+
All shared filters apply.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## API Reference: Top Hits
|
|
283
|
+
|
|
284
|
+
**Endpoint:** `POST /v5/voyages/top-hits`
|
|
285
|
+
**Purpose:** Count of voyages aggregated by a chosen property. Use for "busiest port", "top destination" rankings.
|
|
286
|
+
|
|
287
|
+
| Parameter | Type | Description |
|
|
288
|
+
|---|---|---|
|
|
289
|
+
| `breakdown_size` | integer | Number of top results |
|
|
290
|
+
| `breakdown_property` | enum | Fixed: `vessel_count` |
|
|
291
|
+
| `breakdown_split_property` | enum | Same options as V1 timeseries split |
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## API Reference: Congestion Breakdown
|
|
296
|
+
|
|
297
|
+
**Endpoint:** `POST /v5/voyages/congestion-breakdown`
|
|
298
|
+
**Purpose:** Count of congested voyages by location. Use for port congestion analysis.
|
|
299
|
+
|
|
300
|
+
| Parameter | Type | Description |
|
|
301
|
+
|---|---|---|
|
|
302
|
+
| `breakdown_size` | integer | Number of results |
|
|
303
|
+
| `breakdown_property` | enum | `port`, `terminal`, or `shipping_region` |
|
|
304
|
+
| `order` | enum | `location`, `avg_wait`, `dwt`, `capacity`, `count` |
|
|
305
|
+
| `order_direction` | enum | `asc` or `desc` |
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## V1 vs V2 Timeseries: When to Use Which
|
|
310
|
+
|
|
311
|
+
| Aspect | V1 (`VoyagesTimeseries`) | V2 (`VoyagesTimeseriesV2`) |
|
|
312
|
+
|---|---|---|
|
|
313
|
+
| Architecture | Single endpoint, property + split params | Metric and breakdown in constructor/URL |
|
|
314
|
+
| Naming convention | Underscores (`origin_country`) | Hyphens (`origin-country`) |
|
|
315
|
+
| Metrics | `vessel_count`, `cargo_quantity`, `avg_wait_time`, `dwt`, `cubic_capacity`, `tonne_miles`, `avg_distance`, `avg_speed`, `utilisation` | `voyage-count`, `voyage-count-per-day`, `tonne-miles-sum`, `tonne-miles-sum-per-day`, `distance-avg`, `distance-voyage-avg`, `duration-avg` |
|
|
316
|
+
| Corporate splits | `charterer`, `effective_controller` only | Full corporate: `corporate-entity-cargo-trader`, `-charterer`, `-effective-controller`, `-vessel-owner`, `-time-charterer` |
|
|
317
|
+
| Vessel owner/TC filters | Not in V1 SDK `search()` | `vessel_owners`, `time_charterer` available |
|
|
318
|
+
| Use case | General vessel analytics, fleet metrics (DWT, speed, wait time, utilisation) | Voyage-level metrics, corporate breakdowns, per-day rates |
|
|
319
|
+
|
|
320
|
+
**Rule of thumb:** Use V1 for vessel-perspective metrics (DWT, speed, wait time, utilisation). Use V2 for voyage-perspective metrics (voyage counts, tonne-miles sums, duration averages) especially when you need corporate entity breakdowns or per-day rates.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Shared Filter Reference
|
|
325
|
+
|
|
326
|
+
### Time & ID Filters
|
|
327
|
+
|
|
328
|
+
| Parameter | Type | Description |
|
|
329
|
+
|---|---|---|
|
|
330
|
+
| `time_min` | ISO 8601 | Start of time range. **Always specify explicitly.** |
|
|
331
|
+
| `time_max` | ISO 8601 | End of time range. **Always specify explicitly.** |
|
|
332
|
+
| `voyage_id` | string[] | Filter by specific voyage IDs |
|
|
333
|
+
| `cargo_movement_id` | string[] | Filter by related cargo movement IDs |
|
|
334
|
+
|
|
335
|
+
### Product Filters
|
|
336
|
+
|
|
337
|
+
| Parameter | Description |
|
|
338
|
+
|---|---|
|
|
339
|
+
| `products` | Product IDs in the voyage's cargo |
|
|
340
|
+
| `latest_products` | Product IDs of the latest cargo (current voyage if laden, previous if ballast) |
|
|
341
|
+
|
|
342
|
+
### Corporate Filters
|
|
343
|
+
|
|
344
|
+
| Parameter | Description |
|
|
345
|
+
|---|---|
|
|
346
|
+
| `charterers` | Charterer corporation IDs |
|
|
347
|
+
| `effective_controllers` | Effective controller corporation IDs |
|
|
348
|
+
| `vessel_owners` | Vessel owner corporation IDs (REST) |
|
|
349
|
+
| `time_charterer` | Time charterer corporation IDs (REST) |
|
|
350
|
+
|
|
351
|
+
### Vessel Filters
|
|
352
|
+
|
|
353
|
+
| Parameter | Type | Description |
|
|
354
|
+
|---|---|---|
|
|
355
|
+
| `vessels` | string[] | Vessel IDs OR vessel class strings (e.g., `oil_vlcc`, `lng_tfde_dfde`) |
|
|
356
|
+
| `vessel_flags` | string[] | Flag country IDs (REST) / `flags` (SDK) |
|
|
357
|
+
| `vessel_ice_class` | string[] | Ice class IDs (REST) / `ice_class` (SDK) |
|
|
358
|
+
| `vessel_propulsion` | string[] | Propulsion type IDs |
|
|
359
|
+
| `vessel_age_min` / `max` | number | Age in years (0-100) |
|
|
360
|
+
| `vessel_dwt_min` / `max` | number | Deadweight tonnage (0-550,000) |
|
|
361
|
+
| `vessel_cbm_min` / `max` | number | Cubic capacity (0-300,000) |
|
|
362
|
+
| `vessel_wait_time_min` / `max` | number | Days until vessel available |
|
|
363
|
+
| `vessel_scrubbers` | enum | `disabled` (default), `inc` (only with), `exc` (only without) |
|
|
364
|
+
| `vessel_risk_level` | enum[] | `low`, `medium`, `high`, `sanctioned` |
|
|
365
|
+
| `vessel_tags` | Tag[] | Time-bound vessel tag objects |
|
|
366
|
+
| `average_speed_min` / `max` | number | Avg speed in knots (0-100) |
|
|
367
|
+
|
|
368
|
+
### Toggle Filters
|
|
369
|
+
|
|
370
|
+
| Parameter | Values | Description |
|
|
371
|
+
|---|---|---|
|
|
372
|
+
| `has_ship_to_ship` | `disabled`, `inc`, `exc` | Filter by STS occurrence |
|
|
373
|
+
| `has_charterer` | `disabled`, `inc`, `exc` | Filter by charterer presence |
|
|
374
|
+
|
|
375
|
+
### Behaviour Filters
|
|
376
|
+
|
|
377
|
+
| Parameter | Values | Description |
|
|
378
|
+
|---|---|---|
|
|
379
|
+
| `voyage_date_range_activity` | `active`, `arrivals`, `departures`, `waypoint` | How time range interacts with voyages |
|
|
380
|
+
| `origin_behaviour` | `first_load`, `any_load` | Which load counts for departure activity (SDK) |
|
|
381
|
+
| `destination_behaviour` | `last_discharge`, `any_discharge` | Which discharge counts for arrival activity (SDK) |
|
|
382
|
+
| `intra_movements` | `all`, `exclude_intra_country`, `exclude_intra_geography` | Exclude domestic/intra-region voyages |
|
|
383
|
+
| `exclude_overlapping_entries` | boolean | De-duplicate overlapping voyages. **Timeseries: true; Search: false.** |
|
|
384
|
+
| `event_types` | `vessel`, `cargo`, `status` | Filter which event types are returned in voyage records |
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Worked Examples
|
|
389
|
+
|
|
390
|
+
### Example 1: Laden VLCC voyages from MEG to Asia
|
|
391
|
+
|
|
392
|
+
**User:** "Show me all laden VLCC voyages from the Middle East to Asia this quarter"
|
|
393
|
+
|
|
394
|
+
**Analysis:**
|
|
395
|
+
- "laden" -> `voyage_status='laden'`
|
|
396
|
+
- "VLCC" -> `vessels=['oil_vlcc']`
|
|
397
|
+
- "from the Middle East" -> resolve MEG geography ID -> `origins`
|
|
398
|
+
- "to Asia" -> resolve Asia geography ID -> `destinations`
|
|
399
|
+
- "this quarter" -> `time_min`/`time_max` for current quarter (see date-units.md)
|
|
400
|
+
- Individual records -> VoyagesSearchEnriched
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
from vortexasdk import VoyagesSearchEnriched, Geographies
|
|
404
|
+
from datetime import datetime
|
|
405
|
+
|
|
406
|
+
meg = [g.id for g in Geographies().search("Middle East", filter_layer="region").to_list()]
|
|
407
|
+
asia = [g.id for g in Geographies().search("Asia", filter_layer="region").to_list()]
|
|
408
|
+
|
|
409
|
+
df = VoyagesSearchEnriched().search(
|
|
410
|
+
time_min=datetime(2026, 1, 1),
|
|
411
|
+
time_max=datetime(2026, 3, 31),
|
|
412
|
+
voyage_status="laden",
|
|
413
|
+
vessels=["oil_vlcc"],
|
|
414
|
+
origins=meg,
|
|
415
|
+
destinations=asia,
|
|
416
|
+
columns="all",
|
|
417
|
+
).to_df()
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Example 2: Monthly vessel count by destination country
|
|
421
|
+
|
|
422
|
+
**User:** "How many tankers arrived at European ports monthly in 2025, split by country?"
|
|
423
|
+
|
|
424
|
+
**Analysis:**
|
|
425
|
+
- "how many tankers" -> VoyagesTimeseries, `breakdown_property='vessel_count'`
|
|
426
|
+
- "arrived" -> `voyage_date_range_activity='arrivals'`
|
|
427
|
+
- "European ports" -> resolve Europe region ID -> `destinations`
|
|
428
|
+
- "monthly" -> `breakdown_frequency='month'`
|
|
429
|
+
- "split by country" -> `breakdown_split_property='destination_country'`
|
|
430
|
+
|
|
431
|
+
```python
|
|
432
|
+
from vortexasdk import VoyagesTimeseries, Geographies
|
|
433
|
+
from datetime import datetime
|
|
434
|
+
|
|
435
|
+
europe = [g.id for g in Geographies().search("Europe", filter_layer="region").to_list()]
|
|
436
|
+
|
|
437
|
+
df = VoyagesTimeseries().search(
|
|
438
|
+
time_min=datetime(2025, 1, 1),
|
|
439
|
+
time_max=datetime(2025, 12, 31),
|
|
440
|
+
destinations=europe,
|
|
441
|
+
voyage_date_range_activity="arrivals",
|
|
442
|
+
breakdown_frequency="month",
|
|
443
|
+
breakdown_property="vessel_count",
|
|
444
|
+
breakdown_split_property="destination_country",
|
|
445
|
+
).to_df()
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Example 3: Tonne-miles by vessel class (V2)
|
|
449
|
+
|
|
450
|
+
**User:** "Show weekly tonne-miles for crude voyages in 2025, split by vessel class"
|
|
451
|
+
|
|
452
|
+
**Analysis:**
|
|
453
|
+
- "tonne-miles" -> metric `tonne-miles-sum` (V2 hyphen naming)
|
|
454
|
+
- "split by vessel class" -> `breakdown_property='vessel-class-granular'` (V2 hyphen naming)
|
|
455
|
+
- "crude" -> resolve Crude product ID -> `products`
|
|
456
|
+
- "weekly" -> `breakdown_frequency='week'`
|
|
457
|
+
- V2 for metric+split architecture
|
|
458
|
+
|
|
459
|
+
```python
|
|
460
|
+
from vortexasdk import VoyagesTimeseriesV2, Products
|
|
461
|
+
from datetime import datetime
|
|
462
|
+
|
|
463
|
+
crude = [p.id for p in Products().search("crude").to_list() if p.name == "Crude & Condensates"]
|
|
464
|
+
|
|
465
|
+
df = VoyagesTimeseriesV2(
|
|
466
|
+
metric="tonne-miles-sum",
|
|
467
|
+
breakdown_property="vessel-class-granular",
|
|
468
|
+
).search(
|
|
469
|
+
time_min=datetime(2025, 1, 1),
|
|
470
|
+
time_max=datetime(2025, 12, 31),
|
|
471
|
+
products=crude,
|
|
472
|
+
voyage_status="laden",
|
|
473
|
+
breakdown_frequency="week",
|
|
474
|
+
).to_df()
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Example 4: Port congestion analysis
|
|
478
|
+
|
|
479
|
+
**User:** "Which ports have the worst congestion for LNG carriers right now?"
|
|
480
|
+
|
|
481
|
+
**Analysis:**
|
|
482
|
+
- "congestion" -> Congestion breakdown endpoint
|
|
483
|
+
- "worst" -> order by `avg_wait` descending
|
|
484
|
+
- "LNG carriers" -> `vessels=['lng']`
|
|
485
|
+
- "right now" -> last 7 days
|
|
486
|
+
|
|
487
|
+
```python
|
|
488
|
+
import requests
|
|
489
|
+
from datetime import datetime, timedelta
|
|
490
|
+
|
|
491
|
+
headers = {"x-api-key": VORTEXA_API_KEY}
|
|
492
|
+
payload = {
|
|
493
|
+
"time_min": (datetime.now() - timedelta(days=7)).isoformat() + "Z",
|
|
494
|
+
"time_max": datetime.now().isoformat() + "Z",
|
|
495
|
+
"vessels": ["lng"],
|
|
496
|
+
"breakdown_property": "port",
|
|
497
|
+
"order": "avg_wait",
|
|
498
|
+
"order_direction": "desc",
|
|
499
|
+
"breakdown_size": 20,
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
response = requests.post(
|
|
503
|
+
"https://api.vortexa.com/v5/voyages/congestion-breakdown",
|
|
504
|
+
headers=headers,
|
|
505
|
+
json=payload,
|
|
506
|
+
)
|
|
507
|
+
data = response.json()
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Example 5: Ballast fleet positioning (supply indicator)
|
|
511
|
+
|
|
512
|
+
**User:** "How many ballast VLCCs are heading to the MEG this month?"
|
|
513
|
+
|
|
514
|
+
**Analysis:**
|
|
515
|
+
- "ballast" -> `voyage_status='ballast'`
|
|
516
|
+
- "VLCCs" -> `vessels=['oil_vlcc']`
|
|
517
|
+
- "heading to the MEG" -> resolve MEG ID -> `destinations` (for ballast, this means next load port)
|
|
518
|
+
- "this month" -> current month date range
|
|
519
|
+
- "how many" but wants vessel list -> VoyagesSearchEnriched for detail
|
|
520
|
+
|
|
521
|
+
```python
|
|
522
|
+
from vortexasdk import VoyagesSearchEnriched, Geographies
|
|
523
|
+
from datetime import datetime
|
|
524
|
+
|
|
525
|
+
meg = [g.id for g in Geographies().search("Middle East", filter_layer="region").to_list()]
|
|
526
|
+
|
|
527
|
+
df = VoyagesSearchEnriched().search(
|
|
528
|
+
time_min=datetime(2026, 2, 1),
|
|
529
|
+
time_max=datetime(2026, 2, 28),
|
|
530
|
+
voyage_status="ballast",
|
|
531
|
+
vessels=["oil_vlcc"],
|
|
532
|
+
destinations=meg,
|
|
533
|
+
voyage_date_range_activity="active",
|
|
534
|
+
columns=["vessel_name", "imo", "dwt", "origin_country", "destination_country",
|
|
535
|
+
"start_date", "distance", "duration"],
|
|
536
|
+
).to_df()
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Example 6: Fleet DWT utilisation over time with split
|
|
540
|
+
|
|
541
|
+
**User:** "Show me total laden DWT by shipping region for Suezmaxes, monthly"
|
|
542
|
+
|
|
543
|
+
**Analysis:**
|
|
544
|
+
- "DWT" -> `breakdown_property='dwt'` (V1 metric)
|
|
545
|
+
- "by shipping region" -> `breakdown_split_property='destination_shipping_region_v2'` (V1 underscore naming)
|
|
546
|
+
- "Suezmaxes" -> `vessels=['oil_suezmax']`
|
|
547
|
+
- "monthly" -> `breakdown_frequency='month'`
|
|
548
|
+
- V1 for DWT metric
|
|
549
|
+
|
|
550
|
+
```python
|
|
551
|
+
from vortexasdk import VoyagesTimeseries
|
|
552
|
+
from datetime import datetime
|
|
553
|
+
|
|
554
|
+
df = VoyagesTimeseries().search(
|
|
555
|
+
time_min=datetime(2025, 1, 1),
|
|
556
|
+
time_max=datetime(2025, 12, 31),
|
|
557
|
+
voyage_status="laden",
|
|
558
|
+
vessels=["oil_suezmax"],
|
|
559
|
+
breakdown_frequency="month",
|
|
560
|
+
breakdown_property="dwt",
|
|
561
|
+
breakdown_split_property="destination_shipping_region_v2",
|
|
562
|
+
).to_df()
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## CM-Authoritative OOW Pattern (Preview)
|
|
568
|
+
|
|
569
|
+
For accurate oil-on-water (OOW) volumes broken down by region, **CargoMovements provides authoritative volumes** while **VoyagesSearchEnriched provides location enrichment**.
|
|
570
|
+
|
|
571
|
+
**Why not just use Voyages for OOW?** Voyages tracks the vessel, not the cargo. A single vessel may carry multiple parcels, and cargo quantities are authoritative only in CargoMovements (0% variance from CargoTimeSeries).
|
|
572
|
+
|
|
573
|
+
**High-level pattern:**
|
|
574
|
+
1. Query CargoMovements with `cargo_on_water_state` for authoritative OOW periods and quantities
|
|
575
|
+
2. Query VoyagesSearchEnriched for vessel location data (shipping_region from events)
|
|
576
|
+
3. Join on `cargo_movement_id` (16-char truncated) to enrich CM cargoes with voyage location
|
|
577
|
+
4. Query location at end of day for the latest region assignment
|
|
578
|
+
5. Full-day counting: if OOW period overlaps a day at all, count full quantity
|
|
579
|
+
|
|
580
|
+
**When a user asks "OOW by region":** Use this CM-Authoritative pattern. Do NOT use VoyagesTimeseries alone -- it will give vessel counts or DWT, not cargo volumes.
|
|
581
|
+
|
|
582
|
+
Full implementation details will be in the cargo-movements.md context document and the OOW skill.
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## Defaults and Common Pitfalls
|
|
587
|
+
|
|
588
|
+
| Parameter | Default | Pitfall | Fix |
|
|
589
|
+
|---|---|---|---|
|
|
590
|
+
| `time_min/max` | Current timestamp | Silently returns only current data | Always specify explicitly |
|
|
591
|
+
| `size` (search) | `1` | Returns only 1 record | Set to 500 for batch retrieval |
|
|
592
|
+
| `voyage_date_range_activity` | `active` | Returns all voyages touching the window, may overcount | Use `arrivals`/`departures` for event-anchored counting |
|
|
593
|
+
| `exclude_overlapping_entries` | `true` (TS), `false` (search) | Double-counting in search, dedup in timeseries | Set explicitly based on intent |
|
|
594
|
+
| `breakdown_frequency` | `day` | Too granular for long ranges | Use `month` for >3 months |
|
|
595
|
+
| `breakdown_property` (V1) | `vessel_count` | Wrong metric silently | Override for DWT, tonne-miles, speed |
|
|
596
|
+
| `breakdown_split_property` | `none` | No breakdown returned | Set to split by geography, vessel class, etc. |
|
|
597
|
+
| `breakdown_size` | `100` | Categories beyond 100 grouped as "Other" | Increase for many-category splits |
|
|
598
|
+
| `unit` | `b` (barrels) | Wrong for LNG/LPG | Use `t` for LNG/LPG, `cbm` for LNG volume |
|
|
599
|
+
| `vessel_scrubbers` | `disabled` | No filter applied | Use `inc`/`exc` when scrubber status matters |
|
|
600
|
+
| `intra_movements` | `all` | Includes domestic voyages | Exclude for international-only analysis |
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## Error Recovery
|
|
605
|
+
|
|
606
|
+
**Empty results?** Check in this order:
|
|
607
|
+
1. Are entity IDs correct? Re-run geography/product search to verify (see entity-resolution.md)
|
|
608
|
+
2. Is the time range reasonable? Voyages need non-trivial windows
|
|
609
|
+
3. Is `voyage_date_range_activity` appropriate? `active` is broadest; `arrivals`/`departures` are narrower
|
|
610
|
+
4. For search: is `size` still set to 1 (default)?
|
|
611
|
+
5. Are status filters too restrictive? Remove filters one at a time
|
|
612
|
+
6. Is `intra_movements` excluding data? Try `all` first
|
|
613
|
+
|
|
614
|
+
**Unexpected counts / double-counting?**
|
|
615
|
+
1. Check `exclude_overlapping_entries` -- `false` can double-count days with overlapping voyages
|
|
616
|
+
2. Verify `voyage_date_range_activity` matches intent -- `active` counts every voyage that touched the window, not just those that started/ended in it
|
|
617
|
+
3. For ballast voyages, `origins` means the last discharge port and `destinations` means the next load port -- this is inverted from laden logic
|
|
618
|
+
|
|
619
|
+
**V1 vs V2 confusion?**
|
|
620
|
+
- V2 uses hyphens (`tonne-miles-sum`, `origin-country`); V1 uses underscores (`tonne_miles`, `origin_country`)
|
|
621
|
+
- V2 metric + breakdown are constructor args; V1 they are search params
|
|
622
|
+
- Mixing naming conventions will silently fail or return errors
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
## Validation Rules
|
|
627
|
+
|
|
628
|
+
1. `time_min` must be before `time_max`
|
|
629
|
+
2. `size` for search defaults to 1 -- set to 500 for batch retrieval
|
|
630
|
+
3. All ID filters require valid Vortexa hex hash IDs (see entity-resolution.md)
|
|
631
|
+
4. `vessel_age_min/max`: 0-100 years
|
|
632
|
+
5. `vessel_dwt_min/max`: 0-550,000 tonnes
|
|
633
|
+
6. `vessel_cbm_min/max`: 0-300,000 cubic metres
|
|
634
|
+
7. `average_speed_min/max`: 0-100 knots
|
|
635
|
+
8. For pagination, use `search_after` from previous response's `next_request`
|
|
636
|
+
9. `breakdown_size` max is 10,000
|
package/lib/__init__.py
ADDED