Tutorial part 4

In part 3 we have learnt about portfolio states and how to use them in scenario analyses. Here we learn how to export them and how to combine several ones.

Example data

We start with a similar portfolio state as in the previous part:

[1]:
import portfolyo as pf
import pandas as pd

index = pd.date_range(
    "2024-09-01", "2024-11-01", freq="15min", inclusive="left", tz="Europe/Berlin"
)
# Creating portfolio line with market prices (here: price-forward curve).
ts_prices = pf.dev.p_marketprices(index, avg=200)
prices = pf.PfLine({"p": ts_prices})


# Creating offtake portfolio line.
ts_offtake = -1 * pf.dev.w_offtake(index, avg=50)
offtake = pf.PfLine({"w": ts_offtake})

# Creating portfolio line with sourced volume.
ts_sourced_power1, ts_sourced_price1 = pf.dev.wp_sourced(
    ts_offtake, "QS", 0.3, p_avg=120
)
sourced_quarters = pf.PfLine({"w": ts_sourced_power1, "p": ts_sourced_price1})
ts_sourced_power2, ts_sourced_price2 = pf.dev.wp_sourced(
    ts_offtake, "MS", 0.2, p_avg=150
)
sourced_months = pf.PfLine({"w": ts_sourced_power2, "p": ts_sourced_price2})
sourced = pf.PfLine(
    {"quarter_products": sourced_quarters, "month_products": sourced_months}
)

# Create the portfolio state.
pfs1 = pf.PfState(offtake, prices, sourced).asfreq("MS")

pfs1.print()
PfState object.
. Start: 2024-09-01 00:00:00+02:00 (incl)    . Timezone    : Europe/Berlin
. End  : 2024-11-01 00:00:00+01:00 (excl)    . Start-of-day: 00:00:00
. Freq : <MonthBegin> (2 datapoints)
                                                w           q           p             r
                                               MW         MWh     Eur/MWh           Eur
──────── offtake
           2024-09-01 00:00:00 +0200        -49.4     -35 593
           2024-10-01 00:00:00 +0200        -54.7     -40 748
────── pnl_cost
          2024-09-01 00:00:00 +0200         49.4      35 593      147.61     5 253 964
          2024-10-01 00:00:00 +0200         54.7      40 748      163.85     6 676 561
 ───── sourced
         2024-09-01 00:00:00 +0200         28.9      20 844      123.03     2 564 413
         2024-10-01 00:00:00 +0200         26.0      19 389      132.10     2 561 236
 ├───── quarter_products
        2024-09-01 00:00:00 +0200         13.8       9 943      103.24     1 026 519
        2024-10-01 00:00:00 +0200         11.1       8 261      118.15       976 055
 └───── month_products
        2024-09-01 00:00:00 +0200         15.1      10 901      141.07     1 537 894
        2024-10-01 00:00:00 +0200         14.9      11 128      142.45     1 585 180
 └────── unsourced
           2024-09-01 00:00:00 +0200         20.5      14 749      182.36     2 689 551
           2024-10-01 00:00:00 +0200         28.7      21 358      192.68     4 115 325

Arithmatic

The final part about portfolio lines is the arithmatic that can be done with them.

Let’s create a second portfolio state:

[2]:
offtake2 = offtake * 2
sourced2 = pf.PfLine(pd.DataFrame({"w": 80, "p": 100}, index))
pfs2 = pf.PfState(offtake2, prices, sourced2).asfreq("MS")
pfs2.print()
PfState object.
. Start: 2024-09-01 00:00:00+02:00 (incl)    . Timezone    : Europe/Berlin
. End  : 2024-11-01 00:00:00+01:00 (excl)    . Start-of-day: 00:00:00
. Freq : <MonthBegin> (2 datapoints)
                                                w           q           p             r
                                               MW         MWh     Eur/MWh           Eur
──────── offtake
           2024-09-01 00:00:00 +0200        -98.9     -71 186
           2024-10-01 00:00:00 +0200       -109.4     -81 495
────── pnl_cost
          2024-09-01 00:00:00 +0200         98.9      71 186      118.77     8 454 414
          2024-10-01 00:00:00 +0200        109.4      81 495      131.98    10 755 857
 ├────── sourced
         2024-09-01 00:00:00 +0200         80.0      57 600      100.00     5 760 000
         2024-10-01 00:00:00 +0200         80.0      59 600      100.00     5 960 000
 └────── unsourced
           2024-09-01 00:00:00 +0200         18.9      13 586      198.33     2 694 414
           2024-10-01 00:00:00 +0200         29.4      21 895      219.04     4 795 857

Note that pfs1 and pfs2 have distinct unsourced prices at this month level, even though they were created using the same market prices on the quarter-hour level.

Addition and subtraction

We can add these two portfolio states:

[3]:
pfs1 + pfs2
c:\users\ruud.wijtvliet\ruud\python\dev\portfolyo\portfolyo\core\pfline\enable_arithmatic.py:82: PfLineFlattenedWarning: When adding a FlatPfLine and NestedPfLine, the NestedPfLine is flattened.
  warnings.warn(
[3]:
PfState object.
. Start: 2024-09-01 00:00:00+02:00 (incl)    . Timezone    : Europe/Berlin
. End  : 2024-11-01 00:00:00+01:00 (excl)    . Start-of-day: 00:00:00
. Freq : <MonthBegin> (2 datapoints)
                                                w           q           p             r
                                               MW         MWh     Eur/MWh           Eur
──────── offtake
           2024-09-01 00:00:00 +0200       -148.3    -106 778
           2024-10-01 00:00:00 +0200       -164.1    -122 243
─●────── pnl_cost
 │         2024-09-01 00:00:00 +0200        148.3     106 778      128.38    13 708 378
 │         2024-10-01 00:00:00 +0200        164.1     122 243      142.60    17 432 418
 ├────── sourced
 │         2024-09-01 00:00:00 +0200        108.9      78 444      106.12     8 324 413
 │         2024-10-01 00:00:00 +0200        106.0      78 989      107.88     8 521 236
 └────── unsourced
           2024-09-01 00:00:00 +0200         39.4      28 334      190.02     5 383 965
           2024-10-01 00:00:00 +0200         58.1      43 254      206.02     8 911 182

Note that the individual components are added together. The volumes (offtake, sourced, unsourced) are summed; the prices (sourced and unsourced) are the energy-weighted averaged. (Or, put differently, the revenues are also summed, and the prices are calculated from the volume-total and renevue-total.)

Note also that the sourced volume of pfs1 has been flattened, i.e., the values of its children are lost. This is because pfs2 does not have any children. This behaviour is described here.

Likewise we can subtract them with pfs1 - pfs2:

[4]:
pfs1 - pfs2
c:\users\ruud.wijtvliet\ruud\python\dev\portfolyo\portfolyo\core\pfline\enable_arithmatic.py:82: PfLineFlattenedWarning: When adding a FlatPfLine and NestedPfLine, the NestedPfLine is flattened.
  warnings.warn(
[4]:
PfState object.
. Start: 2024-09-01 00:00:00+02:00 (incl)    . Timezone    : Europe/Berlin
. End  : 2024-11-01 00:00:00+01:00 (excl)    . Start-of-day: 00:00:00
. Freq : <MonthBegin> (2 datapoints)
                                                w           q           p             r
                                               MW         MWh     Eur/MWh           Eur
──────── offtake
           2024-09-01 00:00:00 +0200         49.4      35 593
           2024-10-01 00:00:00 +0200         54.7      40 748
─●────── pnl_cost
 │         2024-09-01 00:00:00 +0200        -49.4     -35 593       89.92    -3 200 450
 │         2024-10-01 00:00:00 +0200        -54.7     -40 748      100.11    -4 079 296
 ├────── sourced
 │         2024-09-01 00:00:00 +0200        -51.1     -36 756       86.94    -3 195 587
 │         2024-10-01 00:00:00 +0200        -54.0     -40 211       84.52    -3 398 764
 └────── unsourced
           2024-09-01 00:00:00 +0200          1.6       1 163       -4.18        -4 863
           2024-10-01 00:00:00 +0200         -0.7        -537    1 267.81      -680 532

That was it for this tutorial!