Short-run marginal costs (SRMC)

This page shows how to do short-run marginal cost (SRMC) calculations on OHLC data. All examples below expects you to have an initialized instance of the client called eq.

Operations described here are available under eq.srmc.*.

Requirements: Use these operations for the following curves:

  • Either gas or coal curves with curve_type = OHLC

Important

We recommend reading the section on OHLC data before continuing. These operations run SRMC calculations on OHLC data, so it would help if you were familiar with Energy Quantified’s take on closing prices.

On calculations

Energy Quantified supports SRMC calculations for gas and coal. We calculate short-run marginal costs on-the-fly from closing prices:

  • You can override all factors in our SRMC calculation, such as efficiency, carbon emission factor and conversion rates (except for currency conversion).

  • Results from SRMC calculations are in €/MWh. Currency conversion is using reference rates from the European Central Bank (ECB).

  • We use the trading date for the contract when deciding the currency conversion rate to use.

  • Some countries, such as the UK, has an additional tax (18 GBP/t as of this writing). It is supported in our implementation and is converted to EUR/t and added to the EUA price when provided.

  • Gas contracts are traded with the assumption of effectiveness in higher-heating value, but the power market uses lower-heating value. We support this conversion.

Note: We use settlement prices in the OHLC calculations. When returning OHLC data for SRMC, only the settlement field is set.

More details, such as the formulas used, are available in the SRMC article on Energy Quantified’s Knowledge base (login required).

Load SRMC

Method references: eq.srmc.load_front() and eq.srmc.load_delivery()

Calculating historical short-run marginal costs is very similar to loading historical OHLC data.

However, because SRMC calculations require you to either specify a front contract or a specific contract, we have split load() into two methods. The difference between load_front() and load_delivery() is that the first requires a front parameter and the second requires a delivery parameter.

For a continuous front contract

To calculate SRMC for the coal API2 front contract, specify the curve, begin, end, period and front:

>>> from datetime import date
>>> from energyquantified.metadata import ContractPeriod
>>> srmc_coal = eq.srmc.load_front(
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    begin=date(2020, 12, 1),   # or begin='2020-01-01'
>>>    end=date(2020, 12, 30),    # or end='2020-01-06'
>>>    period=ContractPeriod.MONTH,
>>>    front=1,
>>> )

The response is an SRMC object. It has a curve, a contract (specifying that it is a front-month contract), an SRMCOptions object with all factors used in the calculation, and a list of OHLC objects:

>>> srmc_coal
<SRMC:
 curve=Futures Coal API-2 USD/t ICE OHLC,
 contract=<ContinuousContract: period=MONTH, front=1, field=>,
 options=<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.42, carbon_emissions=0.34056, carbon_tax_area=None>,
 ohlc=<OHLCList: items=[<OHLC>, <OHLC>, ...]>
>

For a specific contract

Similar to front contracts, you can get the SRMC for a specific contract by only changing the function to load_delivery() and swap out the front parameter with a delivery date:

>>> from datetime import date
>>> from energyquantified.metadata import ContractPeriod
>>> srmc_coal = eq.srmc.load_delivery(  # load_delivery()
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    begin=date(2020, 12, 1),
>>>    end=date(2020, 12, 30),
>>>    period=ContractPeriod.MONTH,
>>>    delivery=date(2021, 3, 1),  # March 2021
>>> )

The result is also very similar, except that the contract is now a SpecificContract while previously it was a ContinuousContract:

>>> srmc_coal
<SRMC:
 curve=Futures Coal API-2 USD/t ICE OHLC,
 contract=<SpecificContract: period=MONTH, delivery=2021-03-01, field=>,
 options=<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.42, carbon_emissions=0.34056, carbon_tax_area=None>,
 ohlc=<OHLCList: items=[<OHLC>, <OHLC>, ...]>
>

You can extract any of these attributes:

>>> srmc_coal.curve
<Curve: "Futures Coal API-2 USD/t ICE OHLC", curve_type=OHLC, subscription=FREEMIUM>
>>> srmc_coal.contract
<SpecificContract: period=MONTH, delivery=2021-03-01, field=>
>>> srmc_coal.options
<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.42, carbon_emissions=0.34056, carbon_tax_area=None>
>>> srmc_coal.ohlc
[<OHLC: <Product: traded=2020-12-01, period=MONTH, front=3, delivery=2021-03-01>, open=, high=, low=, close=, settlement=40.96, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-02, period=MONTH, front=3, delivery=2021-03-01>, open=, high=, low=, close=, settlement=41.63, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-03, period=MONTH, front=3, delivery=2021-03-01>, open=, high=, low=, close=, settlement=41.06, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-04, period=MONTH, front=3, delivery=2021-03-01>, open=, high=, low=, close=, settlement=42.34, volume=, open_interest=>,
 ...

And, of course, you can convert the OHLC data to a pandas.DataFrame like this:

>>> srmc_coal.ohlc.to_dataframe()
        traded period  front    delivery  open  high   low close  settlement volume open_interest
0   2020-12-01  month      3  2021-03-01  None  None  None  None       40.96   None          None
1   2020-12-02  month      3  2021-03-01  None  None  None  None       41.63   None          None
2   2020-12-03  month      3  2021-03-01  None  None  None  None       41.06   None          None
3   2020-12-04  month      3  2021-03-01  None  None  None  None       42.34   None          None
...

Load as a time series

Method references: eq.srmc.load_front_as_timeseries() and eq.srmc.load_delivery_as_timeseries()

This method works like eq.srmc.load_front() and eq.srmc.load_delivery() (see previous section) except that it returns a daily time series instead of OHLC data.

For a continuous front contract

To calculate SRMC for the coal API2 front contract, specify the curve, begin, end, period and front:

>>> from datetime import date
>>> from energyquantified.metadata import ContractPeriod
>>> srmc_coal = eq.srmc.load_front_as_timeseries(
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    begin=date(2020, 12, 1),   # or begin='2020-01-01'
>>>    end=date(2020, 12, 30),    # or end='2020-01-06'
>>>    period=ContractPeriod.MONTH,
>>>    front=1,
>>> )

The response is an SRMC object. It has a curve, a contract (specifying that it is a front-month contract), an SRMCOptions object with all factors used in the calculation, and a timeseries:

>>> srmc_coal
<SRMC:
 curve=Futures Coal API-2 USD/t ICE OHLC,
 contract=<ContinuousContract: period=MONTH, front=1, field=SETTLEMENT>,
 options=<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.42, carbon_emissions=0.34056, carbon_tax_area=None>,
 timeseries=<Timeseries: resolution=<Resolution: frequency=P1D, timezone=CET>, curve="None", begin="2020-12-01 00:00:00+01:00", end="2020-12-16 00:00:00+01:00">
>

You can extract the timeseries easily:

>>> timeseries = srmc_coal.timeseries
>>> for (date, value) in timeseries:
>>>    print(date, value)
2020-12-01 00:00:00+01:00 41.06
2020-12-02 00:00:00+01:00 41.68
2020-12-03 00:00:00+01:00 41.09
2020-12-04 00:00:00+01:00 42.41
2020-12-05 00:00:00+01:00 None
2020-12-06 00:00:00+01:00 None
2020-12-07 00:00:00+01:00 41.92
2020-12-08 00:00:00+01:00 41.95
...

Notice that there are empty values during the weekend (5 December and 6 December). That is because there are no trades during these days.

For a specific contract

To calculate SRMC for a specific coal API2 contract, such as the May 2021-contract, specify the curve, begin, end, period and delivery date:

>>> from datetime import date
>>> from energyquantified.metadata import ContractPeriod
>>> srmc_coal = eq.srmc.load_delivery_as_timeseries(
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    begin=date(2020, 12, 1),
>>>    end=date(2020, 12, 30),
>>>    period=ContractPeriod.MONTH,
>>>    delivery=date(2021, 3, 1),  # March 2021
>>> )

The response is an SRMC object. It has a curve, a contract (specifying that it is the March 2021-contract), an SRMCOptions object with all factors used in the calculation, and a timeseries:

>>> srmc_coal
<SRMC:
 curve=Futures Coal API-2 USD/t ICE OHLC,
 contract=<SpecificContract: period=MONTH, delivery=2021-03-01, field=SETTLEMENT>,
 options=<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.42, carbon_emissions=0.34056, carbon_tax_area=None>,
 timeseries=<Timeseries: resolution=<Resolution: frequency=P1D, timezone=CET>, curve="None", begin="2020-12-01 00:00:00+01:00", end="2020-12-16 00:00:00+01:00">
>

You can convert the time series to a pandas.DataFrame easily:

>>> srmc_coal.timeseries.to_dataframe()
                          Futures Coal API-2 USD/t ICE OHLC
                                month 2021-03-01 settlement

date
2020-12-01 00:00:00+01:00                             40.96
2020-12-02 00:00:00+01:00                             41.63
2020-12-03 00:00:00+01:00                             41.06
2020-12-04 00:00:00+01:00                             42.34
2020-12-05 00:00:00+01:00                               NaN
2020-12-06 00:00:00+01:00                               NaN
2020-12-07 00:00:00+01:00                             41.83
2020-12-08 00:00:00+01:00                             41.90
...

Load for a trading day

Method reference: eq.srmc.latest()

This method loads all contracts for a trading day (the latest trading day by default) and calculates the SRMC for each of them. You may also specify an optional date parameter to load data for the latest trading day up to and including the given date:

>>> from datetime import date
>>> srmc_coal = eq.srmc.latest(
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    date=date(2020, 12, 14)  # Optionally set a date
>>> )

The response will contain a list of all OHLC objects from the latest available trading day. The response will be almost the same as for load_front() and load_delivery(), except that we don’t have a contract set:

>>> srmc_coal
<SRMC:
 curve=Futures Coal API-2 USD/t ICE OHLC,
 options=<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.42, carbon_emissions=0.34056, carbon_tax_area=None>,
 ohlc=<OHLCList: items=[<OHLC>, <OHLC>, <OHLC>, ...]>
>
>>> srmc.coal.ohlc
[<OHLC: <Product: traded=2020-12-14, period=MONTH, front=1, delivery=2021-01-01>, open=, high=, low=, close=, settlement=44.15, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=MONTH, front=2, delivery=2021-02-01>, open=, high=, low=, close=, settlement=44.07, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=MONTH, front=3, delivery=2021-03-01>, open=, high=, low=, close=, settlement=43.94, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=QUARTER, front=1, delivery=2021-01-01>, open=, high=, low=, close=, settlement=44.05, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=QUARTER, front=2, delivery=2021-04-01>, open=, high=, low=, close=, settlement=43.79, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=QUARTER, front=3, delivery=2021-07-01>, open=, high=, low=, close=, settlement=43.68, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=YEAR, front=1, delivery=2021-01-01>, open=, high=, low=, close=, settlement=43.78, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=YEAR, front=2, delivery=2022-01-01>, open=, high=, low=, close=, settlement=44.04, volume=, open_interest=>,
 <OHLC: <Product: traded=2020-12-14, period=YEAR, front=3, delivery=2023-01-01>, open=, high=, low=, close=, settlement=44.43, volume=, open_interest=>,
 ...

Load as a forward curve

Method reference: eq.srmc.latest_as_periods()

Loads all contracts for a trading day (the latest trading day by default), sorts them and merges them into a single period-based series (like a forward curve):

>>> from datetime import date
>>> srmc_coal = eq.srmc.latest_as_periods(
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    date=date(2020, 12, 14)  # Optionally set a date
>>> )

Use the time_zone parameter to convert the data to the given timezone:

>>> from energyquantified.time import UTC
>>>
>>> srmc_coal = eq.srmc.latest_as_periods(
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    time_zone=UTC
>>> )

The response is an SRMC object with a period-based series set:

>>> srmc_coal
<SRMC:
 curve=Futures Coal API-2 USD/t ICE OHLC,
 options=<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.42, carbon_emissions=0.34056, carbon_tax_area=None>,
 periodseries=<Periodseries: resolution=<Resolution: frequency=NONE, timezone=CET>, curve="Futures Coal API-2 USD/t ICE OHLC", begin="2021-01-01 00:00:00+01:00", end="2027-01-01 00:00:00+01:00">
>

You can convert the period-based series to a time series or to a pandas.DataFrame in your preferred resolution:

>>> # Convert to a daily time series
>>> from energyquantified.time import Frequency
>>> srmc_coal.periodseries.to_timeseries(frequency=Frequency.P1D)
<Timeseries: resolution=<Resolution: frequency=P1D, timezone=CET>, curve="Futures Coal API-2 USD/t ICE OHLC", begin="2021-01-01 00:00:00+01:00", end="2027-01-01 00:00:00+01:00">
>>> # Convert to a pandas.DataFrame in daily resolution
>>> srmc_coal.periodseries.to_dataframe(frequency=Frequency.P1D)
                          Futures Coal API-2 USD/t ICE OHLC


date
2021-01-01 00:00:00+01:00                             44.15
2021-01-02 00:00:00+01:00                             44.15
2021-01-03 00:00:00+01:00                             44.15
2021-01-04 00:00:00+01:00                             44.15
2021-01-05 00:00:00+01:00                             44.15
...                                                     ...
2026-12-27 00:00:00+01:00                             47.16
2026-12-28 00:00:00+01:00                             47.16
2026-12-29 00:00:00+01:00                             47.16
2026-12-30 00:00:00+01:00                             47.16
2026-12-31 00:00:00+01:00                             47.16

[2191 rows x 1 columns]

Override SRMC factors

All method described above has options you may set to override any the following factors used in the SRMC calculation:

  • efficiency: The efficiency of the fuel

  • carbon_emissions: The carbon emission factor

  • gas_therm_to_mwh: Conversion factor from pence/therm to GBP/MWh

  • api2_tonne_to_mwh: Conversion from coal API2 tonnes to megawatt-hours

  • carbon_tax_area: Set an Area to apply local tax rules. Typically used for Great Britain’s carbon tax.

Just set the factor you would like to change when using the SRMC functions:

>>> from datetime import date
>>> from energyquantified.metadata import ContractPeriod
>>> srmc_coal = eq.srmc.load_front(
>>>    'Futures Coal API-2 USD/t ICE OHLC',
>>>    begin=date(2020, 12, 1),
>>>    end=date(2020, 12, 30),
>>>    period=ContractPeriod.MONTH,
>>>    front=1,
>>>    efficiency=0.2,  # Not very efficient, eh?
>>>    carbon_emissions=0.35  # A little more than the default carbon emission
>>> )

The overridden factors are now visible in the response’s SRMCOptions:

>>> srmc_coal
<SRMC:
 curve=...,
 contract=...,
 options=<SRMCOptions: COAL, api2_tonne_to_mwh=6.978, efficiency=0.2, carbon_emissions=0.35, carbon_tax_area=None>,
 ohlc=...
>