|
1 | | -# Energy Trading Day-Ahead Price Analysis |
| 1 | +# Energy Trading Analysis |
2 | 2 |
|
3 | | -A Python-based tool for exploring and optimizing energy procurement strategies using Danish day-ahead electricity prices (DK2). The centerpiece is an **interactive dashboard** for visualizing price trends, moving averages, and predictions for multiple countries. |
| 3 | + |
4 | 4 |
|
5 | | -## 🖼️ Interactive Dashboard |
| 5 | +Download, analyze, and publish ENTSO-E day-ahead spot prices (and generation mix) per bidding zone/country, with computed features (moving average + Holt‑Winters forecasts) and static HTML dashboards. |
6 | 6 |
|
7 | | - |
| 7 | +Core entry points: |
| 8 | +- CLI: [`main.main`](src/main.py) in [src/main.py](src/main.py) |
| 9 | +- Data pipeline: [`datamanager.DataManager`](src/datamanager.py) in [src/datamanager.py](src/datamanager.py) |
| 10 | +- Analyses: [`analysis.MovingAverageAnalyzer`](src/analysis.py), [`analysis.ForecastAnalyzer`](src/analysis.py), orchestrated by [`analysis.AnalysisRunner`](src/analysis.py) in [src/analysis.py](src/analysis.py) |
| 11 | +- Static reporting: [`plot_bokeh.create_static_dashboard`](src/plot_bokeh.py) + [`plot_bokeh.generate_index_html`](src/plot_bokeh.py) in [src/plot_bokeh.py](src/plot_bokeh.py) |
8 | 12 |
|
9 | | -*Example: Dash web app showing price and moving average for selected country. Explore historical trends, zoom into the last 24 hours, and compare predictive features interactively.* |
| 13 | +--- |
10 | 14 |
|
11 | | -## 📊 Features |
| 15 | +## What it does |
12 | 16 |
|
13 | | -- **Interactive Dashboard**: Explore price and moving average trends for any country using a Dash web app (`src/plot.py`). |
14 | | -- **SciChart.js Stack**: High-performance alternative visualization using Flask + SciChart.js (`src/plot_scichart.py`). |
15 | | -- **Static HTML Reports**: |
16 | | - - **Bokeh (Recommended)**: High-performance, standalone HTML reports suitable for VCS hosting (`src/plot_bokeh.py`). |
17 | | - - **ECharts**: Legacy static reports (`src/plot_echarts.py`). |
18 | | -- **Historical Price Analysis**: Processes 3 years of hourly electricity price data (2023-2025). |
19 | | -- **Procurement Optimization**: Implements adaptive procurement algorithm with configurable parameters. |
20 | | -- **Cost Analysis**: Compares total costs across different procurement frequencies (1-24 times per year). |
21 | | -- **Visualization**: Generates comprehensive charts showing price trends and optimal purchase points. |
| 17 | +1. **Downloads data from ENTSO-E** |
| 18 | + - Spot prices per country code via `EntsoePandasClient` inside [`datamanager.DataManager.download`](src/datamanager.py) → [`datamanager.DataManager.download_by_country_code`](src/datamanager.py). |
| 19 | + - Generation mix per country code via [`datamanager.DataManager.download_generation_by_country_code`](src/datamanager.py). |
22 | 20 |
|
23 | | -## 🚀 Quick Start |
| 21 | +2. **Stores data on disk** |
| 22 | + - Price data is loaded with CSV schema `time, price` by [`datamanager.DataManager.__read_data`](src/datamanager.py). |
| 23 | + - Features are saved as `{country}_{feature}.csv` by [`datamanager.DataManager.save_analysis`](src/datamanager.py) and discovered via [`datamanager.DataManager.__read_features`](src/datamanager.py). |
| 24 | + - `data/features.csv` is (re)generated with `ma` and `forecast` columns by [`datamanager.DataManager.__update_features_file`](src/datamanager.py). |
24 | 25 |
|
25 | | -### Prerequisites |
26 | | -- Python 3.8+ (Windows, macOS, Linux) |
27 | | -- Git (optional) |
| 26 | +3. **Runs analyses** |
| 27 | + - Moving average feature `ma` via [`analysis.MovingAverageAnalyzer.analyze`](src/analysis.py). |
| 28 | + - Forecast feature(s) via Holt‑Winters exponential smoothing in [`analysis.ForecastAnalyzer.analyze`](src/analysis.py). |
28 | 29 |
|
29 | | -### Installation |
| 30 | +4. **Generates static HTML reports** |
| 31 | + - Per-country report: [`plot_bokeh.create_static_dashboard`](src/plot_bokeh.py) (Bokeh, WebGL, downsampling). |
| 32 | + - Dashboard index that links all reports: [`plot_bokeh.generate_index_html`](src/plot_bokeh.py). |
30 | 33 |
|
31 | | -**Simple Setup:** |
32 | | -```bash |
33 | | -git clone <repository-url> |
34 | | -cd EnergyTradingAnalysis |
35 | | -pip install -r requirements.txt |
36 | | -``` |
| 34 | +--- |
37 | 35 |
|
38 | | -**Alternative Methods:** |
39 | | -- **Automated**: Run `./scripts/setup.sh` (Linux/macOS) or `scripts\setup.bat` (Windows) |
40 | | -- **Conda**: `conda env create -f environment.yml && conda activate energy-trading-analysis` |
41 | | -- **Docker**: `docker-compose up --build` |
42 | | -- **Nix**: `nix develop` (Linux/macOS) |
| 36 | +## Repository layout (high-level) |
43 | 37 |
|
44 | | -### Running the Dashboard & Analysis |
| 38 | +- Source code: [src/](src/) |
| 39 | + - Data pipeline: [src/datamanager.py](src/datamanager.py) |
| 40 | + - Analysis: [src/analysis.py](src/analysis.py) |
| 41 | + - Static reporting: [src/plot_bokeh.py](src/plot_bokeh.py) |
| 42 | + - Config: [src/config.py](src/config.py) |
| 43 | + - Utilities: [src/utils.py](src/utils.py) |
| 44 | + - Logging: [src/logger.py](src/logger.py) |
| 45 | + - Exceptions: [src/exceptions.py](src/exceptions.py) |
| 46 | +- Data (CSV): [data/](data/) |
| 47 | +- Generated static site output directory (configured): [doc/](doc/) via `OUTPUT_DIR` in [src/config.py](src/config.py) |
| 48 | +- Prebuilt/served static site directory (also present): [docs/](docs/) and [Dockerfile](Dockerfile) |
45 | 49 |
|
46 | | -From the `src/` directory: |
| 50 | +Note: both [doc/](doc/) and [docs/](docs/) exist. `OUTPUT_DIR` is set to `doc/` in [src/config.py](src/config.py), while the [Dockerfile](Dockerfile) copies `docs/` into nginx. If you want Docker to serve newly generated reports, ensure the generated output ends up in the directory being served. |
47 | 51 |
|
48 | | -```bash |
49 | | -cd src |
50 | | -python main.py plot # launch interactive dashboard (Dash) |
51 | | -python plot_scichart.py # launch high-performance dashboard (SciChart.js) |
52 | | -python plot_bokeh.py [COUNTRY] # generate high-performance static report (e.g., DK_2) |
53 | | -python plot_echarts.py [COUNTRY] # generate legacy static HTML report |
54 | | -python scheduled_procurement.py # scheduling analysis + day-ahead trend plot |
55 | | -python day_prices.py # hourly profile (price by hour) plot |
56 | | -``` |
| 52 | +--- |
| 53 | + |
| 54 | +## Requirements |
| 55 | + |
| 56 | +- Python **3.8+** (setup script enforces this) — see [scripts/setup.sh](scripts/setup.sh) and [scripts/setup.bat](scripts/setup.bat) |
| 57 | +- An **ENTSOE API key** (see configuration below) |
| 58 | +- Python dependencies in [requirements.txt](requirements.txt) (also Nix dev shell in [flake.nix](flake.nix)) |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## Configuration |
| 63 | + |
| 64 | +This project expects an ENTSO-E API key via environment variables. |
| 65 | + |
| 66 | +- `.env.example` shows the expected shape: [.env.example](.env.example) |
| 67 | +- Local overrides: `.env` (not committed) — [.env](.env) |
| 68 | +- Config validation happens in [src/config.py](src/config.py). Missing key raises [`exceptions.ConfigException`](src/exceptions.py). |
| 69 | + |
| 70 | +Required: |
| 71 | +- `ENTSOE_API_KEY`: ENTSO-E Transparency Platform API key (used by `EntsoePandasClient` inside [`datamanager.DataManager.download`](src/datamanager.py)). |
57 | 72 |
|
58 | | -**Generated Output:** |
59 | | -- `output/dash_screenshot.png`: Dashboard screenshot (add manually) |
60 | | -- `output/report_DK_2.html`: Interactive HTML report generated by Bokeh (or ECharts) |
61 | | -- `output/dayaheadprices.png`: Price trends with optimal purchase points (produced by `scheduled_procurement.py`) |
62 | | -- `output/total_cost_vs_nproc.png`: Total cost vs number of procurements (produced by `scheduled_procurement.py`) |
63 | | -- `output/price_by_hour.png`: Average price by hour with error bars (produced by `day_prices.py`) |
| 73 | +--- |
64 | 74 |
|
65 | | -## 📁 Project Structure |
| 75 | +## Quickstart (venv) |
66 | 76 |
|
| 77 | +### 1) Create and install |
| 78 | +Use the provided scripts: |
| 79 | + |
| 80 | +```sh |
| 81 | +./scripts/setup.sh |
67 | 82 | ``` |
68 | | -├── src/ # Source code and analysis scripts |
69 | | -│ ├── main.py # CLI entry point (download, analyze, plot) |
70 | | -│ ├── plot.py # Dash dashboard for interactive exploration |
71 | | -│ ├── plot_scichart.py # High-performance dashboard using SciChart.js |
72 | | -│ ├── plot_bokeh.py # Generate static HTML reports with Bokeh |
73 | | -│ ├── plot_echarts.py # Generate static HTML reports with ECharts |
74 | | -│ ├── datamanager.py # Data loading and feature extraction |
75 | | -│ ├── dataanalysis.py # Analysis functions (moving average, prediction) |
76 | | -│ ├── scheduled_procurement.py # Procurement optimization analysis |
77 | | -│ ├── day_prices.py # Hourly price profile and plot |
78 | | -│ └── utils.py # Utility functions |
79 | | -├── data/ # CSV price data files (2023-2025) |
80 | | -├── output/ # Generated visualizations (PNG files) |
81 | | -├── scripts/ # Setup scripts for different platforms |
82 | | -├── requirements.txt # Python dependencies |
83 | | -├── environment.yml # Conda environment |
84 | | -├── Dockerfile # Docker support |
85 | | -├── docker-compose.yml # Docker orchestration |
86 | | -├── flake.nix # Nix environment |
87 | | -└── README.md # Project documentation |
| 83 | + |
| 84 | +Windows: |
| 85 | + |
| 86 | +```bat |
| 87 | +scripts\setup.bat |
88 | 88 | ``` |
89 | 89 |
|
90 | | -## 🔬 Algorithm Overview |
| 90 | +(They create `venv/` and install from [requirements.txt](requirements.txt).) |
91 | 91 |
|
92 | | -**Data Source**: [ENTSO-E Transparency Platform](https://newtransparency.entsoe.eu/) |
93 | | -- Market: DK2 (Denmark Eastern) day-ahead prices |
94 | | -- Resolution: Hourly data, converted to daily averages |
95 | | -- Period: 2023-2025 |
| 92 | +### 2) Set your ENTSO-E key |
| 93 | +Create `.env` at repo root (or export env var): |
96 | 94 |
|
97 | | -**Procurement Strategy (`sched_proc` function):** |
98 | | -1. **Time Partitioning**: Divides the year into `n_parts` equal segments |
99 | | -2. **Reference Tracking**: Maintains reference price that updates to lower values |
100 | | -3. **Purchase Trigger**: Buys energy when price exceeds `reference + limit` (default: €10/MWh) |
101 | | -4. **Cost Calculation**: Computes total cost for specified energy volume (default: 1000 MWh) |
| 95 | +```sh |
| 96 | +export ENTSOE_API_KEY="your_key_here" |
| 97 | +``` |
102 | 98 |
|
103 | | -**Parameters:** |
104 | | -- `mwhs`: Total energy to procure (default: 1000 MWh) |
105 | | -- `n_parts`: Number of procurement periods (tested: 1, 2, 3, 4, 6, 12, 24) |
106 | | -- `limit`: Price increase threshold (default: €10/MWh) |
| 99 | +### 3) Download data |
| 100 | +Runs ENTSO-E pulls for all configured country codes: |
107 | 101 |
|
108 | | -## 📈 Key Results |
| 102 | +```sh |
| 103 | +python src/main.py download |
| 104 | +``` |
109 | 105 |
|
110 | | -- **Optimal Frequency**: Usually 3-6 procurements per year minimize total costs |
111 | | -- **Cost Savings**: Significant reduction compared to single annual purchase |
112 | | -- **Seasonal Patterns**: Purchase timing typically aligns with seasonal price cycles |
113 | | -- **Diminishing Returns**: Increased procurement frequency shows diminishing cost benefits |
| 106 | +This calls [`datamanager.DataManager.download`](src/datamanager.py), which iterates `country_codes.csv` (see `COUNTRY_CODES_FILE` in [src/config.py](src/config.py)). |
114 | 107 |
|
115 | | -## 🛠️ Usage Examples |
| 108 | +### 4) Analyze data |
| 109 | +Computes moving average + forecasts per country: |
116 | 110 |
|
117 | | -**Basic Analysis:** |
118 | | -```python |
119 | | -buy_indices, total_cost = sched_proc(price_avg) |
| 111 | +```sh |
| 112 | +python src/main.py analyze |
120 | 113 | ``` |
121 | 114 |
|
122 | | -**Custom Parameters:** |
123 | | -```python |
124 | | -buy_indices, total_cost = sched_proc( |
125 | | - price=price_avg, |
126 | | - mwhs=2000, # 2000 MWh total |
127 | | - n_parts=6, # 6 procurements per year |
128 | | - limit=15 # €15/MWh threshold |
129 | | -) |
| 115 | +This calls [`datamanager.DataManager.analysis`](src/datamanager.py) → [`datamanager.DataManager.analysis_by_country_code`](src/datamanager.py), using [`analysis.AnalysisRunner`](src/analysis.py) with [`analysis.MovingAverageAnalyzer`](src/analysis.py) and [`analysis.ForecastAnalyzer`](src/analysis.py). |
| 116 | + |
| 117 | +### 5) Generate static dashboards |
| 118 | +Generate per-country reports and the index dashboard: |
| 119 | + |
| 120 | +```sh |
| 121 | +python src/plot_bokeh.py |
130 | 122 | ``` |
131 | 123 |
|
132 | | -## 🔧 Troubleshooting |
| 124 | +Outputs: |
| 125 | +- `report_{COUNTRY}.html` files into `OUTPUT_DIR` (configured as `doc/`) in [src/config.py](src/config.py) |
| 126 | +- `index.html` dashboard via [`plot_bokeh.generate_index_html`](src/plot_bokeh.py) |
| 127 | + |
| 128 | +Open in a browser: |
| 129 | +- [doc/index.html](doc/index.html) |
| 130 | + |
| 131 | +--- |
| 132 | + |
| 133 | +## Data & file conventions |
133 | 134 |
|
134 | | -**Common Issues:** |
135 | | -- Ensure Python 3.8+ is installed |
136 | | -- Activate virtual environment before running |
137 | | -- Use `python3` if `python` points to Python 2.x |
138 | | -- On Linux/macOS: `chmod +x scripts/setup.sh` if permission denied |
| 135 | +### Prices |
| 136 | +- Loaded by [`datamanager.DataManager.__read_data`](src/datamanager.py) as: |
| 137 | + - `time` (UTC parsed), `price` |
139 | 138 |
|
140 | | -## 📄 License |
| 139 | +### Features |
| 140 | +- Written by [`datamanager.DataManager.save_analysis`](src/datamanager.py) to per-country files: |
| 141 | + - `{country}_ma.csv` (moving average output includes `ma`) |
| 142 | + - `{country}_forecast.csv` (forecast output contains multiple forecast columns) |
| 143 | +- Registry file: `data/features.csv` maintained by [`datamanager.DataManager.__update_features_file`](src/datamanager.py) |
141 | 144 |
|
142 | | -Educational and research use. Price data from ENTSO-E under their terms of use. |
| 145 | +### Utilities used by the pipeline |
| 146 | +- Efficient incremental download uses [`utils.read_last_csv_line`](src/utils.py) (reads last CSV line) in [`datamanager.DataManager.download_by_country_code`](src/datamanager.py). |
| 147 | +- Time-window extraction helper: [`utils.extract_last`](src/utils.py) in [src/utils.py](src/utils.py). |
143 | 148 |
|
144 | | -## 📸 Plots (output/) |
| 149 | +--- |
145 | 150 |
|
146 | | -The repository includes example generated visualizations in the `output/` directory. If you run the scripts above they will be (re)created. |
| 151 | +## Development environments |
147 | 152 |
|
148 | | -- dayaheadprices.png |
| 153 | +### Nix dev shell |
| 154 | +A ready dev shell is defined in [flake.nix](flake.nix) (includes `entsoe-py`, pandas, statsmodels, bokeh, etc.). |
149 | 155 |
|
150 | | - |
| 156 | +### Docker (static site) |
| 157 | +The provided [Dockerfile](Dockerfile) serves static files through nginx and copies [docs/](docs/) into the container. If you want to serve the freshly generated content from [doc/](doc/), align the directories (either generate into `docs/` or serve `doc/`). |
151 | 158 |
|
152 | | -Caption: Daily-averaged day-ahead prices with procurement markers for different numbers of procurements (N_proc). Each marker shows the time and price where the algorithm decided to buy — useful to inspect timing and clustering of purchase points. |
| 159 | +See also: [docker-compose.yml](docker-compose.yml). |
153 | 160 |
|
154 | | -- total_cost_vs_nproc.png |
| 161 | +--- |
155 | 162 |
|
156 | | - |
| 163 | +## Logging & troubleshooting |
157 | 164 |
|
158 | | -Caption: Total procurement cost (€) as a function of the number of procurements per year. Use this to identify the procurement frequency that minimizes total cost. |
| 165 | +- Logging is configured by [`logger.setup_logger`](src/logger.py) in [src/logger.py](src/logger.py) to both console and rotating files under [logs/](logs/). |
| 166 | +- Common failures: |
| 167 | + - Missing `ENTSOE_API_KEY` → [`exceptions.ConfigException`](src/exceptions.py) raised by [src/config.py](src/config.py). |
| 168 | + - Empty/malformed CSV → [`exceptions.DataException`](src/exceptions.py) used by [`utils.read_last_csv_line`](src/utils.py) and [`utils.extract_last`](src/utils.py). |
159 | 169 |
|
160 | | -- price_by_hour.png |
| 170 | +--- |
161 | 171 |
|
162 | | - |
| 172 | +## License |
163 | 173 |
|
164 | | -Caption: Average day-ahead price by hour-of-day with error bars (standard deviation). Highlights intraday patterns and hours with highest/lowest average prices. |
| 174 | +Licensed under **GNU GPL v3.0** — see [LICENSE](LICENSE). |
0 commit comments