MR2 ARA Supply Versus Market Rates¶
Run this example in Colab.¶
APIs Used : Tonnage List API, Market Rates API
Description :
In this Notebook the main goal is to construct and display the vessel count per day for MR2 tankers with respect to ARA area.
The script walks though the installation of the signal ocean SDK and import of the required dependencies used for the processing of the data.
Also the parameters
vessel_class_filter, load_port_filter, days_back, laycan_end_in_days, vessel_filter, route_description, start_date, end_date
are initialized, in order to be used to achieve the desired output.
Next the Tonnage List API and the Market Rates API are called to fetch the corresponding data to our query based on the parameters set.
Lastly, we display the chart of the data that we retrieved and present them as time series.
Output : Time-Series Graph displaying the historic supply of MR2 Vessels in ARA region versus the corresponding Market Rates for the past year.
Setup¶
!pip install signal-ocean
Requirement already satisfied: signal-ocean in /usr/local/lib/python3.10/dist-packages (13.0.0) Requirement already satisfied: requests<3,>=2.23.0 in /usr/local/lib/python3.10/dist-packages (from signal-ocean) (2.32.3) Requirement already satisfied: python-dateutil<3,>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from signal-ocean) (2.8.2) Requirement already satisfied: pandas<3,>=2 in /usr/local/lib/python3.10/dist-packages (from signal-ocean) (2.2.2) Requirement already satisfied: numpy>=1.18.5 in /usr/local/lib/python3.10/dist-packages (from signal-ocean) (1.26.4) Requirement already satisfied: strictly-typed-pandas==0.1.4 in /usr/local/lib/python3.10/dist-packages (from signal-ocean) (0.1.4) Requirement already satisfied: typeguard<3.0.0,>=2.13.3 in /usr/local/lib/python3.10/dist-packages (from signal-ocean) (2.13.3) Requirement already satisfied: pandas-stubs in /usr/local/lib/python3.10/dist-packages (from strictly-typed-pandas==0.1.4->signal-ocean) (2.2.2.240909) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas<3,>=2->signal-ocean) (2024.2) Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas<3,>=2->signal-ocean) (2024.2) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil<3,>=2.8.1->signal-ocean) (1.17.0) Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.23.0->signal-ocean) (3.4.0) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.23.0->signal-ocean) (3.10) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.23.0->signal-ocean) (2.2.3) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.23.0->signal-ocean) (2024.8.30) Requirement already satisfied: types-pytz>=2022.1.1 in /usr/local/lib/python3.10/dist-packages (from pandas-stubs->strictly-typed-pandas==0.1.4->signal-ocean) (2024.2.0.20241003)
Import required dependencies:
import pandas as pd
import matplotlib.pyplot as plt
from datetime import date, timedelta
from signal_ocean import Connection
from signal_ocean.market_rates import MarketRatesAPI, CargoId
from signal_ocean.tonnage_list import (
TonnageListAPI,
VesselClassFilter,
PortFilter,
VesselFilter,
PushType,
MarketDeployment,
CommercialStatus,
VesselSubclass,
IndexLevel,
DateRange,
)
Parametrization¶
signal_ocean_api_key = "" # Replace with your subscription key
vessel_class_filter = VesselClassFilter(name_like="MR2") # Focus on the MR2 segment
load_port_filter = PortFilter(name_like="Rotterdam") # As a basis port for the Tonnage List Rotterdam is chosen
days_back = 365 # Last year info
laycan_end_in_days = 7 # Laycan day window
vessel_filter = VesselFilter(
vessel_subclass=VesselSubclass.CLEAN, # Focusing on Clean carrying MR2s i.e. LR2
latest_ais_since=5) # Filtering out vessels missing recent AIS info
'''
market_deployments=[MarketDeployment.RELET, MarketDeployment.SPOT], # Only Relevant for Tankers
push_types=[PushType.PUSHED_POSS, PushType.PUSHED], # Signal Ocean Platform UI filter: Pushed State
commercial_statuses= [CommercialStatus.ON_SUBS] #[CommercialStatus.AVAILABLE, CommercialStatus.ON_SUBS, CommercialStatus.FAILED, CommercialStatus.CANCELLED], # Signal Ocean Platform UI filter: Availability State
operational_statuses=[OperationalStatus.BALLAST_UNFIXED] #, OperationalStatus.BALLAST_FIXED], ## Signal Ocean Platform UI filter: Voyage State
'''
route_description = "MR2 - Cont/USAC" # Route Abbreviation for Market Rates retrieval later
today = date.today()
start_date = today - timedelta(days=days_back) # set start date for the Chart window
end_date = today - timedelta(1) # set end date for the Chart window
Get your personal Signal Ocean API subscription key (acquired here) and use it to create a Connection
:
connection = Connection(signal_ocean_api_key)
Create instances of APIs used throughout this notebook:
tonnage_list_api = TonnageListAPI(connection)
market_rate_api = MarketRatesAPI(connection)
For more information, see the Tonnage List and Market Rates API sections.
Main CodeBlock¶
Fetch the historical tonnage list¶
load_port = tonnage_list_api.get_ports(load_port_filter)[0] # Retrieve load port ID
load_port
Port(id=3689, name='Rotterdam')
vessel_class = tonnage_list_api.get_vessel_classes(vessel_class_filter)[0] # Retrieve vessel class object
load_port = tonnage_list_api.get_ports(load_port_filter)[0] # Retrieve load port object
htl = tonnage_list_api.get_historical_tonnage_list(
loading_port = load_port, # load port of the requested Tonnage List
vessel_class = vessel_class, # vessel class of interest
laycan_end_in_days = laycan_end_in_days, # laycan period
date_range = DateRange(start_date, end_date), # a range for which are the tonnage lists of interest
vessel_filter = vessel_filter, # which vessels should be included in the response
)
htl_for_supply_trend = htl.to_data_frame()
htl_for_supply_trend
name | vessel_class | ice_class | year_built | deadweight | length_overall | breadth_extreme | subclass | market_deployment_point_in_time | push_type_point_in_time | ... | open_prediction_accuracy_point_in_time | open_country_point_in_time | open_narrow_area_point_in_time | open_wide_area_point_in_time | availability_port_type_point_in_time | availability_date_type_point_in_time | fixture_type_point_in_time | current_vessel_sub_type_id_point_in_time | current_vessel_sub_type_point_in_time | willing_to_switch_current_vessel_sub_type_point_in_time | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
date | imo | |||||||||||||||||||||
2024-12-15 12:00:00+00:00 | 9447744 | NH Erle | MR2 | NaN | 2010 | 49999 | 183.30 | 32 | Clean | Spot | Pushed | ... | Narrow Area | Netherlands | Continent | UK Continent | Source | Source | NaN | 2 | Clean | False |
9882396 | Solar Katherine | MR2 | 1A | 2020 | 49990 | 183.00 | 32 | Clean | Relet | Pushed | ... | Narrow Area | Netherlands | Continent | UK Continent | Source | Source | NaN | 2 | Clean | False | |
9718870 | Silver Heba | MR2 | NaN | 2016 | 49897 | 183.06 | 32 | Clean | Relet | Pushed | ... | Narrow Area | Netherlands | Continent | UK Continent | Source | Source | NaN | 2 | Clean | False | |
9308132 | Unite | MR2 | NaN | 2006 | 50322 | 189.02 | 32 | Clean | Spot | Not Pushed | ... | Narrow Area | Belgium | Continent | UK Continent | Source | Source | NaN | 2 | Clean | False | |
9364588 | Jane | MR2 | NaN | 2008 | 51505 | 183.00 | 32 | Clean | Spot | Not Pushed | ... | Narrow Area | Netherlands | Continent | UK Continent | Source | Source | NaN | 2 | Clean | False | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2024-12-16 12:00:00+00:00 | 9789257 | Atlantic Falcon | MR2 | NaN | 2018 | 49951 | 183.06 | 32 | Clean | Spot | Pushed | ... | Port | Poland | Baltic Sea Low | Baltic | Source | Source | NaN | 2 | Clean | False |
9315068 | Star Osprey | MR2 | NaN | 2007 | 51213 | 183.00 | 32 | Clean | Spot | Pushed | ... | Port | Belgium | Continent | UK Continent | Source | Source | NaN | 2 | Clean | False | |
9708760 | Shenandoah Trader | MR2 | NaN | 2015 | 50124 | 183.00 | 32 | Clean | Contract | Not Pushed | ... | Narrow Area | Italy | Central Mediterranean | Mediterranean | Source | Source | NaN | 2 | Clean | False | |
9345659 | Papillon | MR2 | NaN | 2007 | 47302 | 182.50 | 32 | Clean | Spot | Pushed | ... | Narrow Area | Mauritania | Africa Atlantic Coast | West Africa | Source | Source | NaN | 2 | Clean | False | |
9344007 | Hardrada | MR2 | NaN | 2007 | 45983 | 179.88 | 32 | Clean | Spot | Not Pushed | ... | Port | Russian Federation | Baltic Sea Upper | Baltic | Source | Prediction | NaN | 2 | Clean | False |
28237 rows × 29 columns
Get daily market rates for the route of interest¶
We'll need a helper function to find our desired route for the market rates:
def return_selected_route(vessel_class, route_description):
vessel_routes = market_rate_api.get_routes(vessel_class_id=vessel_class.id)
for i, _ in enumerate(vessel_routes):
if vessel_routes[i].description == route_description:
break
return vessel_routes[i]
Which we can use to find the route object returned from the Market Rates API:
market_rate_route = return_selected_route(vessel_class, route_description)
market_rate_route
Route(id='R27', description='MR2 - Cont/USAC', unit='WS', vessel_class_id=88, cargo_id=1, load_port_id=3688, discharge_port_id=3864, load_area_id=24758, discharge_area_id=24747, load_port_2_id=None, discharge_port_2_id=None, load_area_2_id=None, discharge_area_2_id=None, deprecated_to=None, deprecated_since=None)
And use it to query for market rates:
market_rates = market_rate_api.get_market_rates(
start_date = start_date, # Date from which we want the market rates for the specific route
route_id = market_rate_route.id, # The ID of the route of interest
vessel_class_id = vessel_class.id, # The ID of the specific vessel class that we want the rates for
end_date = end_date, # Last day of the time window that we want the rates for
cargo_id = CargoId(market_rate_route.cargo_id) # Cargo type that we want to concentrate to
)
market_rates = pd.DataFrame([vars(vr) for vr in market_rates])
market_rates["rate_date"] = pd.to_datetime(market_rates["rate_date"])
market_rates.set_index("rate_date", inplace=True)
market_rates
route_id | rate_value | unit | vessel_class_id | deprecated_to | |
---|---|---|---|---|---|
rate_date | |||||
2023-12-17 | R27 | 195.0 | WS | 88 | None |
2023-12-18 | R27 | 195.0 | WS | 88 | None |
2023-12-19 | R27 | 180.0 | WS | 88 | None |
2023-12-20 | R27 | 175.0 | WS | 88 | None |
2023-12-21 | R27 | 180.0 | WS | 88 | None |
... | ... | ... | ... | ... | ... |
2024-12-12 | R27 | 145.0 | WS | 88 | None |
2024-12-13 | R27 | 140.0 | WS | 88 | None |
2024-12-14 | R27 | 140.0 | WS | 88 | None |
2024-12-15 | R27 | 140.0 | WS | 88 | None |
2024-12-16 | R27 | 135.0 | WS | 88 | None |
366 rows × 5 columns
Plot the daily supply trend with market rates¶
def plot_drawing():
fig, axs = plt.subplots(figsize=(15, 6))
supply_trend = htl_for_supply_trend.groupby(IndexLevel.DATE, sort=True).size()
supply_trend.index = supply_trend.index.strftime("%d %b %y")
# Left axis (Supply plot)
supply_plot = supply_trend.plot(ax=axs, x="date", color="#3086EF", marker="o")
supply_plot.set_ylabel(
"Vessel count", color=plt.gca().lines[-1].get_color(), fontsize=14
)
supply_plot.set_ylim(ymin=0)
# Right axis (Market rate plot)
market_rates.index = market_rates.index.strftime("%d %b %y")
mr_plot = market_rates.plot(
ax=axs, y="rate_value", secondary_y=True, color="#F06C6E", marker="d"
)
mr_plot.set_ylabel(
"Market Rates (WS)", color=plt.gca().lines[-1].get_color(), fontsize=14
)
axs.set_xlim(0, len(supply_trend) - 1)
axs.set_xlabel("")
axs.get_legend().remove()
Output¶
plot_drawing()