Metadata-Version: 2.4
Name: enappsys
Version: 0.2.1
Summary: The EnAppSys Python client provides a light-weight client that allows for simple access to EnAppSys' API services.
Author-email: Silvan Murre <silvan.murre@montel.energy>, Andras Rozs <andras.rozs@montel.energy>
License-Expression: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests
Provides-Extra: async
Requires-Dist: aiohttp; extra == "async"
Provides-Extra: pandas
Requires-Dist: pandas; extra == "pandas"
Requires-Dist: tzdata; extra == "pandas"
Provides-Extra: dev
Requires-Dist: enappsys[async]; extra == "dev"
Requires-Dist: enappsys[pandas]; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: pytest-benchmark; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Dynamic: license-file

<picture align="center">
  <a href="https://app.enappsys.com/">
    <img alt="EnAppSys" src="docs/assets/enappsys.svg">
  </a>
</picture>

# EnAppSys Python Client

The Python library for the [EnAppSys](https://app.enappsys.com) platform provides a light-weight, typed Python client to interact with EnAppSys' API services. Additionally, there is an asynchronous client for non-blocking operations.

## Project Status

This project is in active development (0.x release series).
While the core API structure is expected to remain stable,
minor breaking changes may occur between releases.

## Installation

Supports Python 3.10+

```bash
pip install enappsys[pandas,async]
```

The extras are optional:

- `pandas` required for converting API responses to DataFrames, e.g. via `client_response.to_df()`
- `async` required for using the `EnAppSysAsync` asynchronous client.

If you only need the synchronous client and raw responses, install without extras:

```bash
pip install enappsys
```

### Configuring credentials

Your EnAppSys username and secret are required to make API requests. You can obtain these as follows:

1. Go to any download page on EnAppSys and click **Copy API URL**.
2. In the copied URL:

    - The value after `user=` is your **username**.
    - The value after `pass=` is your **secret** (a long numeric string).


The client looks for credentials in the following order:

1. **Direct arguments** when creating the client:

    ```python
    from enappsys import EnAppSys

    client = EnAppSys(
        user="example_user",
        secret="123456789123456789123456789123456789"
    )
    ```
    
2. **Environment variables**:

    ```bash
    export ENAPPSYS_USER=example_user
    export ENAPPSYS_SECRET=123456789123456789123456789123456789
    ```

3. **Credentials file** at your home directory, the default location is: `~/.credentials/enappsys.json`:

    ```json
    {
        "user": "example_user",
        "secret": "123456789123456789123456789123456789"
    }
    ```

    You can also save and specify a custom path:

    ```python
    client = EnAppSys(credentials_file="path/to/credentials.json")
    ```

#### Credentials in logs

The platform authenticates with `user` and `pass` query parameters, so every
request URL contains them. Creating a client therefore attaches a logging filter
to `urllib3.connectionpool` and `aiohttp.client` that replaces the secret with
`<redacted>`, because those libraries log the request line they send and would
otherwise write it into any log with DEBUG enabled.

The username and the rest of the URL are left alone, so the logs stay useful and
a request can still be traced back to an account:

```
https://app.enappsys.com:443 "GET /csvapi?type=ENTSOE_DAY_AHEAD_PRICES&res=qh&user=your-username&pass=<redacted> HTTP/1.1" 200 None
```

## Usage

The EnAppSys client provides several download interfaces, depending on your user permissions.

### Bulk API

The Bulk API is a subscription service that allows you to retrieve time series data.
A **data type** represents a group of related series, and each individual series within that group is referred to as an **entity**.

The web interface for browsing available data types and entities is available at:
[https://app.enappsys.com/#dataservicecsv](https://app.enappsys.com/#dataservicecsv)

Retrieve the `DA_PRICE` and `DA_VOLUME` entities belonging to `EPEX_HR_AUCTION_RESULTS_DE` and convert them to a pandas `DataFrame`. When converting to a `DataFrame`, you can also rename the columns:

```python
day_ahead = client.bulk.get(
    "csv",
    data_type="EPEX_HR_AUCTION_RESULTS_DE",
    entities=["DA_PRICE", "DA_VOLUME"],
    start_dt="2025-01-01T00:00",
    end_dt="2025-01-02T00:00",
    resolution="qh",
    time_zone="CET",
)

df = day_ahead.to_df(rename_columns=["price", "volume"])
```

To retrieve **all entities** for a given `data_type`, omit the `entities` argument or pass `None`:

```python
day_ahead_all = client.bulk.get(
    "csv",
    data_type="EPEX_HR_AUCTION_RESULTS_DE",
    start_dt="2025-01-01T00:00",
    end_dt="2025-01-02T00:00",
    resolution="qh",
    time_zone="CET",
)

df_all = day_ahead_all.to_df()
```

The Bulk API supports multiple response formats:

- `"csv"`
- `"json"`
- `"json_map"`
- `"xml"`

The JSON-based formats optionally include metadata fields:

- `timestamp`: Indicates when the data was first entered into the database or created as a forecast (UTC).
- `last_updated`: Indicates the last time the data was updated in the database (UTC).

These fields can be included when converting the response to a DataFrame:

```python
data = client.bulk.get(
    "json",
    data_type="EPEX_HR_AUCTION_RESULTS_DE",
    start_dt="2025-01-01T00:00",
    end_dt="2025-01-02T00:00",
    resolution="qh",
    time_zone="CET",
)

df = data.to_df(timestamp=True, last_updated=True)
```

#### Large requests

Wide date ranges are split into several requests automatically and stitched back
together, because the platform answers some series much more slowly as the range
grows.

Splitting kicks in above `CHUNK_ROWS` (5,000) rows, counting each entity
separately, since requesting two entities costs as much as two requests. Pass
`chunk_rows` to override it, or `chunk_rows=0` to send one request regardless:

```python
data = client.bulk.get(
    "csv",
    data_type="ENTSOE_AGGREGATED_GENERATION_PER_TYPE",
    entities=["DE.GERMANY_SOLAR"],
    start_dt="2023-01-01T00:00",
    end_dt="2026-01-01T00:00",
    resolution="qh",
    time_zone="UTC",
    chunk_rows=10_000,   # default is 5_000; 0 disables splitting
)
```

On the asynchronous client the chunks are fetched concurrently, so the same
three-year request completes in a matter of seconds.

### Chart API

The Chart API extracts data directly from charts available on the EnAppSys platform.

Each chart is identified by a **code**, which can be found in the page URL.
For example:

```
https://app.enappsys.com/#de/elec/pricing/daprices/chart
```

The chart code is the part between `#` and `/chart`, in this case:

```
de/elec/pricing/daprices
```

Example usage:

```python
day_ahead_chart = client.chart.get(
    "csv",
    code="de/elec/pricing/daprices",
    start_dt="2025-01-01T00:00",
    end_dt="2025-01-02T00:00",
    resolution="qh",
    time_zone="CET",
)

df_day_ahead_chart = day_ahead_chart.to_df()
```

Some charts support the optional `enable_settlement_period` flag:

```python
settlement_chart = client.chart.get(
    "csv",
    code="gb/elec/pricing/daprices",
    resolution="hh",
    start_dt="2026-07-13T10:49",
    end_dt="2026-07-13T18:49",
    time_zone="WET",
    currency="GBP",
    enable_settlement_period=True,
)
df_settlement = settlement_chart.to_df()
```

Some charts also support alternative time window modes via `time_display`:

```python
rolling_chart = client.chart.get(
    "csv",
    code="gb/elec/pricing/daprice/ensemble/forecast",
    resolution="hh",
    time_zone="WET",
    currency="GBP",
    enable_settlement_period=True,
    time_display={
        "mode": "rolling",
        "periodback": "daily",
        "amountback": 4,
        "periodfor": "min",
        "amountfor": 4,
    },
)
df_rolling = rolling_chart.to_df()
```

> [!NOTE]
> Some charts contain non-timeseries data and may have a different structure.
> Below chart types are supported. If you encounter a chart that is not yet supported, please open an issue and include a link to the chart.

### Price Volume Curves

The Price Volume Curve API retrieves auction price-volume curve data for a given timestamp.

```python
hu_price_volume_curve = client.price_volume_curve.get(
    "csv",
    code="hu/elec/ancillary/capacity/afrr/daily/up",
    dt="2025-01-01T00:00",
    time_zone="CET",
    currency="EUR",
)

df_curve = hu_price_volume_curve.to_df()
```

The `dt` parameter represents the auction timestamp for which the curve should be retrieved.

### EPEX Trade Evolution And Contracts

The EPEX service uses the `apxdownload` endpoint and is exposed as `client.epex`. It currently supports settlement trade evolution downloads and contract evolution downloads in `"csv"`, `"json"`, and `"xml"` formats.

Settlement trade evolution is available under `client.epex.settlement`. You can identify a quarter-hour either with `settlement_date` plus `settlement_period`, or with a single `settlement_datetime`:

```python
trade_evo = client.epex.settlement.get(
    "csv",
    code="nl/elec/epex/tradeevo/download",
    settlement_datetime="2025-01-01T12:07",
    max_points=200,
    time_zone="CET",
)

trade_evo_df = trade_evo.to_df(
    rename_columns=["qh_price", "qh_vol", "hh_price", "hh_vol", "hr_price", "hr_vol"],
    unit_in_columns=True,
)
```

Contract evolution is available under `client.epex.contract`:

```python
contract_evo = client.epex.contract.get(
    "csv",
    code="nl/elec/spectron/power/baseload/evolution/download",
    contract="DEC-25",
    max_points="all",
    time_zone="CET",
)

contract_evo_df = contract_evo.to_df(unit_in_columns=True)
```

## Asynchronous

An asynchronous client (`EnAppSysAsync`) is available for non-blocking and concurrent request execution.

The asynchronous interface is currently under active development.
Usage examples and extended documentation will be added in a future release.

## License

This project is licensed under the terms of the MIT license.
