diff --git a/.env b/.env index 75f3437..07fb3df 100644 --- a/.env +++ b/.env @@ -11,12 +11,12 @@ LOCAL_PORT=5433 DB_NAME=postgres DB_USER=postgres DB_PASSWORD=SolarFalconBreeze -BACKUP_FILE=/home/boundys/data4/django/agile_predict/.local/backup.sql +BACKUP_DIR=/home/boundys/data4/django/agile_predict/.local +LOG_DIR=/home/boundys/data4/django/agile_predict/logs APP_NAME=prices-db # Local PostgreSQL restore PGUSER=postgres PGPASSWORD=RainbowHenryGibbon DBNAME=agile_predict -WIN_BACKUP_FILE=.local\backup.sql - +WIN_BACKUP_FILE=.local\backup.sql \ No newline at end of file diff --git a/.github/workflows/fly-deploy.yml b/.github/workflows/fly-deploy.yml deleted file mode 100644 index b0c246e..0000000 --- a/.github/workflows/fly-deploy.yml +++ /dev/null @@ -1,18 +0,0 @@ -# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ - -name: Fly Deploy -on: - push: - branches: - - main -jobs: - deploy: - name: Deploy app - runs-on: ubuntu-latest - concurrency: deploy-group # optional: ensure only one action runs at a time - steps: - - uses: actions/checkout@v4 - - uses: superfly/flyctl-actions/setup-flyctl@master - - run: flyctl deploy --remote-only - env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/README.md b/README.md index 8fbacef..7f29bc6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Agile Predict v2.1.0 +# Agile Predict v2.1.1 This model forecasts Octopus Agile electricity prices up to 14 days in advance using a Machine Learning model trained on data from the Balancing Mechanism Reporting System (BRMS), National Grid diff --git a/bin/backup.sh b/bin/backup.sh index 3f38ac8..42b0227 100644 --- a/bin/backup.sh +++ b/bin/backup.sh @@ -1,21 +1,41 @@ #!/bin/bash +# Timestamp for logs and backup file naming +NOW=$(date +"%Y-%m-%d_%H-%M-%S") + # Load environment -if [ -f .env ]; then +if [ -f /home/boundys/data4/django/agile_predict/.env ]; then set -a - . .env + . /home/boundys/data4/django/agile_predict/.env set +a else echo "Missing .env file" exit 1 fi +# Ensure LOG_DIR, BACKUP_DIR, and BACKUPS_TO_KEEP are set +: "${LOG_DIR:?LOG_DIR not set in .env}" +: "${BACKUP_DIR:?BACKUP_DIR not set in .env}" +BACKUPS_TO_KEEP="${BACKUPS_TO_KEEP:-3}" + +mkdir -p "$LOG_DIR" +LOG_FILE="$LOG_DIR/backup_$NOW.log" + +# Redirect all output to log file +exec > >(tee -a "$LOG_FILE") 2>&1 + +echo "================ $(date) ================" +echo "Starting backup script..." + +# Ensure PATH includes location for fly and pg_dump +export PATH="/home/boundys/.fly/bin:/usr/bin:/bin:$PATH" + echo "APP_NAME: $APP_NAME" echo "LOCAL_PORT: $LOCAL_PORT" echo "Starting Fly proxy to database..." # Start Fly proxy -fly proxy ${LOCAL_PORT}:5432 -a $APP_NAME & +/home/boundys/.fly/bin/fly proxy ${LOCAL_PORT}:5432 -a $APP_NAME & PROXY_PID=$! # Wait for proxy to be ready @@ -37,18 +57,38 @@ if ! nc -z localhost "$LOCAL_PORT"; then exit 1 fi - echo "Starting backup..." export PGPASSWORD=$DB_PASSWORD -pg_dump -h localhost -p $LOCAL_PORT -U $DB_USER -d $DB_NAME > "$BACKUP_FILE" +mkdir -p "$BACKUP_DIR" +BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_backup_$NOW.sql" +LATEST_BACKUP_FILE="$BACKUP_DIR/backup.sql" + +/usr/bin/pg_dump -h localhost -p $LOCAL_PORT -U $DB_USER -d $DB_NAME > "$BACKUP_FILE" if [ $? -eq 0 ]; then echo "Backup completed successfully: $BACKUP_FILE" + cp "$BACKUP_FILE" "$LATEST_BACKUP_FILE" + echo "Updated latest backup symlink: $LATEST_BACKUP_FILE" else echo "Backup FAILED" fi +# Cleanup old backups and their logs beyond $BACKUPS_TO_KEEP +BACKUP_PATTERN="$BACKUP_DIR/${DB_NAME}_backup_*.sql" +LOG_PATTERN="$LOG_DIR/backup_*.log" + +BACKUP_LIST=$(ls -1t $BACKUP_PATTERN 2>/dev/null) +LOG_LIST=$(ls -1t $LOG_PATTERN 2>/dev/null) + +DELETE_BACKUPS=$(echo "$BACKUP_LIST" | tail -n +$((BACKUPS_TO_KEEP + 1))) +DELETE_LOGS=$(echo "$LOG_LIST" | tail -n +$((BACKUPS_TO_KEEP + 1))) + +echo "$DELETE_BACKUPS" | xargs -r rm -- +echo "$DELETE_LOGS" | xargs -r rm -- + +echo "Old backups and logs cleaned. Keeping last $BACKUPS_TO_KEEP." + # Kill proxy if still running if kill -0 $PROXY_PID 2>/dev/null; then kill $PROXY_PID diff --git a/bin/update.sh b/bin/update.sh index 6f2a1fd..f92ac71 100644 --- a/bin/update.sh +++ b/bin/update.sh @@ -1,3 +1,3 @@ -/home/boundys/.fly/bin/fly ssh console -C "python manage.py update3 --debug" -a prices +/home/boundys/.fly/bin/fly ssh console -C "python manage.py update --debug" -a prices /home/boundys/.fly/bin/fly app restart prices diff --git a/logs/backup_2025-04-16_14-40-43.log b/logs/backup_2025-04-16_14-40-43.log new file mode 100644 index 0000000..55870ab --- /dev/null +++ b/logs/backup_2025-04-16_14-40-43.log @@ -0,0 +1,19 @@ +================ Wed Apr 16 14:40:43 BST 2025 ================ +Starting backup script... +APP_NAME: prices-db +LOCAL_PORT: 5433 +Starting Fly proxy to database... +Waiting for proxy to be ready... +Waiting for proxy to be ready... +Proxying local port 5433 to remote [prices-db.internal]:5432 +Connection to localhost (127.0.0.1) 5433 port [tcp/*] succeeded! +Proxy is ready! +Connection to localhost (127.0.0.1) 5433 port [tcp/*] succeeded! +Starting backup... +pg_dump: warning: there are circular foreign-key constraints on this table: +pg_dump: detail: nodes +pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints. +pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem. +Backup completed successfully: /home/boundys/data4/django/agile_predict/.local/postgres_backup_2025-04-16_14-40-43.sql +Updated latest backup symlink: /home/boundys/data4/django/agile_predict/.local/backup.sql +Proxy closed. diff --git a/logs/backup_2025-04-16_14-43-01.log b/logs/backup_2025-04-16_14-43-01.log new file mode 100644 index 0000000..72a799d --- /dev/null +++ b/logs/backup_2025-04-16_14-43-01.log @@ -0,0 +1,20 @@ +================ Wed Apr 16 14:43:01 BST 2025 ================ +Starting backup script... +APP_NAME: prices-db +LOCAL_PORT: 5433 +Starting Fly proxy to database... +Waiting for proxy to be ready... +Waiting for proxy to be ready... +Proxying local port 5433 to remote [prices-db.internal]:5432 +Connection to localhost (127.0.0.1) 5433 port [tcp/*] succeeded! +Proxy is ready! +Connection to localhost (127.0.0.1) 5433 port [tcp/*] succeeded! +Starting backup... +pg_dump: warning: there are circular foreign-key constraints on this table: +pg_dump: detail: nodes +pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints. +pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem. +Backup completed successfully: /home/boundys/data4/django/agile_predict/.local/postgres_backup_2025-04-16_14-43-01.sql +Updated latest backup symlink: /home/boundys/data4/django/agile_predict/.local/backup.sql +Old backups and logs cleaned. Keeping last 3. +Proxy closed. diff --git a/logs/backup_2025-04-16_14-46-01.log b/logs/backup_2025-04-16_14-46-01.log new file mode 100644 index 0000000..ff7f2cc --- /dev/null +++ b/logs/backup_2025-04-16_14-46-01.log @@ -0,0 +1,21 @@ +================ Wed Apr 16 14:46:01 BST 2025 ================ +Starting backup script... +APP_NAME: prices-db +LOCAL_PORT: 5433 +Starting Fly proxy to database... +Waiting for proxy to be ready... +Waiting for proxy to be ready... +Waiting for proxy to be ready... +Proxying local port 5433 to remote [prices-db.internal]:5432 +Connection to localhost (127.0.0.1) 5433 port [tcp/*] succeeded! +Proxy is ready! +Connection to localhost (127.0.0.1) 5433 port [tcp/*] succeeded! +Starting backup... +pg_dump: warning: there are circular foreign-key constraints on this table: +pg_dump: detail: nodes +pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints. +pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem. +Backup completed successfully: /home/boundys/data4/django/agile_predict/.local/postgres_backup_2025-04-16_14-46-01.sql +Updated latest backup symlink: /home/boundys/data4/django/agile_predict/.local/backup.sql +Old backups and logs cleaned. Keeping last 3. +Proxy closed. diff --git a/logs/update.log b/logs/update.log new file mode 100644 index 0000000..928a7f5 --- /dev/null +++ b/logs/update.log @@ -0,0 +1,449 @@ +Connecting to fdaa:9:282c:a7b:3ed:1ddf:e2e6:2... +2025-04-16 10:15:09 INFO: Max days: 60 +2025-04-16 10:15:09 INFO: ID | Name | #FD | #AD | Days | +2025-04-16 10:15:09 INFO: ------+------------------+-------+---------+------+ +2025-04-16 10:15:09 INFO: 25 | 2025-04-16 06:15 | 612 | 9180 | 0 | +2025-04-16 10:15:09 INFO: 24 | 2025-04-15 23:06 | 626 | 9390 | 0 | +2025-04-16 10:15:09 INFO: 23 | 2025-04-15 17:14 | 638 | 9570 | 0 | +2025-04-16 10:15:09 INFO: 22 | 2025-04-15 16:15 | 640 | 9600 | 0 | +2025-04-16 10:15:09 INFO: 21 | 2025-04-15 10:15 | 604 | 9060 | 1 | +2025-04-16 10:15:09 INFO: 20 | 2025-04-15 06:15 | 612 | 9180 | 1 | +2025-04-16 10:15:09 INFO: 19 | 2025-04-14 22:15 | 628 | 9420 | 1 | +2025-04-16 10:15:09 INFO: 18 | 2025-04-14 19:21 | 634 | 9510 | 1 | +2025-04-16 10:15:09 INFO: 17 | 2025-04-13 19:37 | 634 | 74 | 2 | +2025-04-16 10:15:09 INFO: 16 | 2025-04-13 16:04 | 642 | 1284 | 2 | +2025-04-16 10:15:09 INFO: 15 | 2025-04-13 15:57 | 642 | 1284 | 2 | +2025-04-16 10:15:09 INFO: 14 | 2025-04-13 15:52 | 642 | 1284 | 2 | +2025-04-16 10:15:09 INFO: 13 | 2025-04-13 15:49 | 642 | 1284 | 2 | +2025-04-16 10:15:09 INFO: 12 | 2025-04-13 15:43 | 642 | 1284 | 2 | +2025-04-16 10:15:09 INFO: 11 | 2025-04-01 21:46 | 628 | 1256 | 14 | +2025-04-16 10:15:09 INFO: +Deleting () + +2025-04-16 10:15:09 INFO: Getting Historic Prices +2025-04-16 10:15:09 INFO: Prices + day_ahead agile +date_time +2023-06-30 00:00:00+01:00 92.80 19.4880 +2023-06-30 00:30:00+01:00 78.75 16.5375 +2023-06-30 01:00:00+01:00 81.90 17.1990 +2023-06-30 01:30:00+01:00 78.75 16.5375 +2023-06-30 02:00:00+01:00 82.85 17.3985 +... ... ... +2025-04-16 20:30:00+01:00 105.00 22.0500 +2025-04-16 21:00:00+01:00 107.50 22.5750 +2025-04-16 21:30:00+01:00 90.30 18.9630 +2025-04-16 22:00:00+01:00 84.30 17.7030 +2025-04-16 22:30:00+01:00 76.25 16.0125 + +[31534 rows x 2 columns] +2025-04-16 10:15:09 INFO: New Prices +Empty DataFrame +Columns: [day_ahead, agile] +Index: [] +2025-04-16 10:15:09 INFO: GB60: +2025-04-15 23:00:00+01:00 91.55 +2025-04-16 00:00:00+01:00 79.23 +2025-04-16 01:00:00+01:00 66.31 +2025-04-16 02:00:00+01:00 53.99 +2025-04-16 03:00:00+01:00 48.67 +2025-04-16 04:00:00+01:00 39.20 +2025-04-16 05:00:00+01:00 37.93 +2025-04-16 06:00:00+01:00 78.22 +2025-04-16 07:00:00+01:00 92.08 +2025-04-16 08:00:00+01:00 87.93 +2025-04-16 09:00:00+01:00 70.57 +2025-04-16 10:00:00+01:00 45.53 +2025-04-16 11:00:00+01:00 35.73 +2025-04-16 12:00:00+01:00 28.12 +2025-04-16 13:00:00+01:00 28.44 +2025-04-16 14:00:00+01:00 40.12 +2025-04-16 15:00:00+01:00 53.41 +2025-04-16 16:00:00+01:00 74.96 +2025-04-16 17:00:00+01:00 81.40 +2025-04-16 18:00:00+01:00 102.29 +2025-04-16 19:00:00+01:00 111.24 +2025-04-16 20:00:00+01:00 93.43 +2025-04-16 21:00:00+01:00 82.58 +2025-04-16 22:00:00+01:00 81.12 +dtype: float64 +2025-04-16 10:15:09 INFO: Merged prices: + day_ahead agile +date_time +2023-06-30 00:00:00+01:00 92.80 19.4880 +2023-06-30 00:30:00+01:00 78.75 16.5375 +2023-06-30 01:00:00+01:00 81.90 17.1990 +2023-06-30 01:30:00+01:00 78.75 16.5375 +2023-06-30 02:00:00+01:00 82.85 17.3985 +... ... ... +2025-04-16 20:30:00+01:00 105.00 22.0500 +2025-04-16 21:00:00+01:00 107.50 22.5750 +2025-04-16 21:30:00+01:00 90.30 18.9630 +2025-04-16 22:00:00+01:00 84.30 17.7030 +2025-04-16 22:30:00+01:00 76.25 16.0125 + +[31534 rows x 2 columns] +2025-04-16 10:15:09 INFO: Getting latest Forecast + https://api.nationalgrideso.com/api/3/action/datastore_search?resource_id=93c3048e-1dab-4057-a2a9-417540583929&limit=1000 + https://api.nationalgrideso.com/api/3/action/datastore_search?resource_id=b2f03146-f05d-4824-a663-3a4f36090c71&limit=1000 + https://api.nationalgrideso.com/api/3/action/datastore_search?resource_id=db6c038f-98af-4570-ab60-24d71ebd0ae5&limit=1000 + https://api.nationalgrideso.com/api/3/action/datastore_search?resource_id=7c0411cd-2714-4bb5-a408-adb065edf34d&limit=5000 + https://api.open-meteo.com/v1/forecast + https://data.elexon.co.uk/bmrs/api/v1/datasets/NDF +2025-04-16 10:15:11 INFO: bm_wind solar ... time day_of_week +2025-04-16 10:30:00+01:00 13916.0 5248.0 ... 10.5 2 +2025-04-16 11:00:00+01:00 13841.0 6072.0 ... 11.0 2 +2025-04-16 11:30:00+01:00 13951.0 6932.0 ... 11.5 2 +2025-04-16 12:00:00+01:00 13956.0 7716.0 ... 12.0 2 +2025-04-16 12:30:00+01:00 13858.0 8310.0 ... 12.5 2 +... ... ... ... ... ... +2025-04-28 22:00:00+01:00 6538.0 0.0 ... 22.0 0 +2025-04-28 22:30:00+01:00 6566.0 0.0 ... 22.5 0 +2025-04-28 23:00:00+01:00 6602.0 0.0 ... 23.0 0 +2025-04-28 23:30:00+01:00 6644.0 0.0 ... 23.5 0 +2025-04-29 00:00:00+01:00 6692.0 0.0 ... 0.0 1 + +[604 rows x 9 columns] +2025-04-16 10:15:11 INFO: id name created_at +0 11 2025-04-01 21:46 2025-04-14 18:09:54.304603+00:00 +1 12 2025-04-13 15:43 2025-04-14 18:10:00.741061+00:00 +2 13 2025-04-13 15:49 2025-04-14 18:10:07.697382+00:00 +3 14 2025-04-13 15:52 2025-04-14 18:10:14.684936+00:00 +4 15 2025-04-13 15:57 2025-04-14 18:10:21.526148+00:00 +5 16 2025-04-13 16:04 2025-04-14 18:10:28.274760+00:00 +6 17 2025-04-13 19:37 2025-04-14 18:11:25.824051+00:00 +7 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 +8 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 +9 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 +10 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 +11 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 +12 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 +13 24 2025-04-15 23:06 2025-04-15 22:06:52.758343+00:00 +14 25 2025-04-16 06:15 2025-04-16 05:15:14.881366+00:00 +2025-04-16 10:15:11 INFO: Forecasts Database: +{ff.to_string()} +2025-04-16 10:15:11 INFO: train_X: + bm_wind solar ... weekend day_ahead +date_time ... +2025-04-16 21:00:00+00:00 10874.0 0.0 ... 0 84.30 +2025-04-16 21:30:00+00:00 10782.0 0.0 ... 0 76.25 +2025-04-01 21:30:00+00:00 14901.0 0.0 ... 0 70.00 +2025-04-01 22:00:00+00:00 15148.0 0.0 ... 0 67.20 +2025-04-01 22:30:00+00:00 15377.0 0.0 ... 0 55.70 +... ... ... ... ... ... +2025-04-16 18:30:00+00:00 11331.0 530.0 ... 0 105.05 +2025-04-16 19:00:00+00:00 11170.0 45.0 ... 0 101.85 +2025-04-16 19:30:00+00:00 11050.0 0.0 ... 0 105.00 +2025-04-16 20:00:00+00:00 10972.0 0.0 ... 0 107.50 +2025-04-16 20:30:00+00:00 10937.0 20.0 ... 0 90.30 + +[193 rows x 8 columns] +2025-04-16 10:15:11 INFO: len(ff) : 15 +2025-04-16 10:15:11 INFO: len(ff_train): 5 +2025-04-16 10:15:11 INFO: len(train_X) : 193 +2025-04-16 10:15:11 INFO: len(test_X) : 1016 +2025-04-16 10:15:11 INFO: Earliest ff : 11 +2025-04-16 10:15:11 INFO: Latest ff : 25 +2025-04-16 10:15:11 INFO: Earliest ff_t : 11 +2025-04-16 10:15:11 INFO: Latest ff_t : 25 +2025-04-16 10:15:11 INFO: train_cols: +2025-04-16 10:15:11 INFO: bm_wind : 3454.00 8777.20 15467.00 +2025-04-16 10:15:11 INFO: solar : 0.00 2386.76 10571.00 +2025-04-16 10:15:11 INFO: demand : 18060.00 25265.01 31810.00 +2025-04-16 10:15:11 INFO: peak : 0.00 0.12 1.00 +2025-04-16 10:15:11 INFO: days_ago : 0.17 4.81 14.52 +2025-04-16 10:15:11 INFO: wind_10m : 6.05 24.75 45.40 +2025-04-16 10:15:11 INFO: weekend : 0.00 0.03 1.00 +2025-04-16 10:15:11 INFO: test_X: + id forecast_id ... peak day_ahead +date_time ... +2025-04-15 22:30:00+00:00 14637 24 ... 0.0 82.00 +2025-04-15 23:00:00+00:00 14638 24 ... 0.0 78.75 +2025-04-15 23:30:00+00:00 14639 24 ... 0.0 78.85 +2025-04-16 00:00:00+00:00 14640 24 ... 0.0 74.15 +2025-04-16 00:30:00+00:00 14641 24 ... 0.0 72.25 +... ... ... ... ... ... +2025-04-16 19:30:00+00:00 14053 23 ... 0.0 105.00 +2025-04-16 20:00:00+00:00 14054 23 ... 0.0 107.50 +2025-04-16 20:30:00+00:00 14055 23 ... 0.0 90.30 +2025-04-16 21:00:00+00:00 14056 23 ... 0.0 84.30 +2025-04-16 21:30:00+00:00 14057 23 ... 0.0 76.25 + +[1016 rows x 22 columns] +2025-04-16 10:15:14 INFO: Forecast from 2025-04-16 10:30:00+01:00 tp 2025-04-29 00:00:00+01:00 +2025-04-16 10:15:14 INFO: Agile to 2025-04-16 22:30:00+01:00 +2025-04-16 10:15:14 INFO: Forecast + bm_wind solar ... day_ahead_low day_ahead_high +2025-04-16 10:30:00+01:00 13916.0 5248.0 ... 70.945318 78.757462 +2025-04-16 11:00:00+01:00 13841.0 6072.0 ... 70.945318 78.757462 +2025-04-16 11:30:00+01:00 13951.0 6932.0 ... 59.063833 70.482971 +2025-04-16 12:00:00+01:00 13956.0 7716.0 ... 36.562767 60.419027 +2025-04-16 12:30:00+01:00 13858.0 8310.0 ... 43.279658 60.789566 +... ... ... ... ... ... +2025-04-28 22:00:00+01:00 6538.0 0.0 ... 61.889986 81.053406 +2025-04-28 22:30:00+01:00 6566.0 0.0 ... 70.457985 78.812744 +2025-04-28 23:00:00+01:00 6602.0 0.0 ... 68.167249 77.041573 +2025-04-28 23:30:00+01:00 6644.0 0.0 ... 67.858875 77.293633 +2025-04-29 00:00:00+01:00 6692.0 0.0 ... 67.858875 76.749825 + +[604 rows x 15 columns] +2025-04-16 10:15:14 INFO: idx0: 2025-04-16 10:30:00+01:00:2025-04-16 22:30:00+01:00 + mult shift +2025-04-16 10:30:00+01:00 0 1 +2025-04-16 11:00:00+01:00 0 1 +2025-04-16 11:30:00+01:00 0 1 +2025-04-16 12:00:00+01:00 0 1 +2025-04-16 12:30:00+01:00 0 1 +2025-04-16 13:00:00+01:00 0 1 +2025-04-16 13:30:00+01:00 0 1 +2025-04-16 14:00:00+01:00 0 1 +2025-04-16 14:30:00+01:00 0 1 +2025-04-16 15:00:00+01:00 0 1 +2025-04-16 15:30:00+01:00 0 1 +2025-04-16 16:00:00+01:00 0 1 +2025-04-16 16:30:00+01:00 0 1 +2025-04-16 17:00:00+01:00 0 1 +2025-04-16 17:30:00+01:00 0 1 +2025-04-16 18:00:00+01:00 0 1 +2025-04-16 18:30:00+01:00 0 1 +2025-04-16 19:00:00+01:00 0 1 +2025-04-16 19:30:00+01:00 0 1 +2025-04-16 20:00:00+01:00 0 1 +2025-04-16 20:30:00+01:00 0 1 +2025-04-16 21:00:00+01:00 0 1 +2025-04-16 21:30:00+01:00 0 1 +2025-04-16 22:00:00+01:00 0 1 +2025-04-16 22:30:00+01:00 0 1 +2025-04-16 10:15:14 INFO: idx1: 2025-04-16 23:00:00+01:00:2025-04-29 00:00:00+01:00 + mult shift +2025-04-16 23:00:00+01:00 1 0 +2025-04-16 23:30:00+01:00 1 0 +2025-04-17 00:00:00+01:00 1 0 +2025-04-17 00:30:00+01:00 1 0 +2025-04-17 01:00:00+01:00 1 0 +... ... ... +2025-04-28 22:00:00+01:00 1 0 +2025-04-28 22:30:00+01:00 1 0 +2025-04-28 23:00:00+01:00 1 0 +2025-04-28 23:30:00+01:00 1 0 +2025-04-29 00:00:00+01:00 1 0 + +[579 rows x 2 columns] +2025-04-16 10:15:14 INFO: Scale factors + mult shift +2025-04-16 10:30:00+01:00 0 1 +2025-04-16 11:00:00+01:00 0 1 +2025-04-16 11:30:00+01:00 0 1 +2025-04-16 12:00:00+01:00 0 1 +2025-04-16 12:30:00+01:00 0 1 +... ... ... +2025-04-28 22:00:00+01:00 1 0 +2025-04-28 22:30:00+01:00 1 0 +2025-04-28 23:00:00+01:00 1 0 +2025-04-28 23:30:00+01:00 1 0 +2025-04-29 00:00:00+01:00 1 0 + +[604 rows x 2 columns] +2025-04-16 10:15:14 INFO: Scale Factors: + mult shift day_ahead agile +2025-04-16 10:30:00+01:00 0 1 63.45 13.3245 +2025-04-16 11:00:00+01:00 0 1 52.50 11.0250 +2025-04-16 11:30:00+01:00 0 1 44.10 9.2610 +2025-04-16 12:00:00+01:00 0 1 27.10 5.6910 +2025-04-16 12:30:00+01:00 0 1 23.60 4.9560 +... ... ... ... ... +2025-04-28 22:00:00+01:00 1 0 0.00 0.0000 +2025-04-28 22:30:00+01:00 1 0 0.00 0.0000 +2025-04-28 23:00:00+01:00 1 0 0.00 0.0000 +2025-04-28 23:30:00+01:00 1 0 0.00 0.0000 +2025-04-29 00:00:00+01:00 1 0 0.00 0.0000 + +[604 rows x 4 columns] +2025-04-16 10:15:14 INFO: mult shift ... day_ahead_low day_ahead_high +2025-04-16 10:30:00+01:00 0 1 ... 62.450000 64.450000 +2025-04-16 11:00:00+01:00 0 1 ... 51.500000 53.500000 +2025-04-16 11:30:00+01:00 0 1 ... 43.100000 45.100000 +2025-04-16 12:00:00+01:00 0 1 ... 26.100000 28.100000 +2025-04-16 12:30:00+01:00 0 1 ... 22.600000 24.600000 +... ... ... ... ... ... +2025-04-28 22:00:00+01:00 1 0 ... 61.889986 81.053406 +2025-04-28 22:30:00+01:00 1 0 ... 70.457985 78.812744 +2025-04-28 23:00:00+01:00 1 0 ... 68.167249 77.041573 +2025-04-28 23:30:00+01:00 1 0 ... 67.858875 77.293633 +2025-04-29 00:00:00+01:00 1 0 ... 67.858875 76.749825 + +[604 rows x 7 columns] +2025-04-16 10:15:14 INFO: Final forecast from 2025-04-16 10:30:00+01:00 to 2025-04-29 00:00:00+01:00 +2025-04-16 10:15:14 INFO: Forecast + bm_wind solar emb_wind ... rad demand day_ahead +2025-04-16 10:30:00+01:00 13916.0 5248.0 4123.0 ... 170.0 26888.0 63.450000 +2025-04-16 11:00:00+01:00 13841.0 6072.0 4195.0 ... 161.5 26097.0 52.500000 +2025-04-16 11:30:00+01:00 13951.0 6932.0 4242.0 ... 184.0 24528.0 44.100000 +2025-04-16 12:00:00+01:00 13956.0 7716.0 4289.0 ... 298.5 24500.0 27.100000 +2025-04-16 12:30:00+01:00 13858.0 8310.0 4392.0 ... 171.5 24025.0 23.600000 +... ... ... ... ... ... ... ... +2025-04-28 22:00:00+01:00 6538.0 0.0 1500.0 ... 0.0 28295.0 81.053406 +2025-04-28 22:30:00+01:00 6566.0 0.0 1516.0 ... 0.0 26885.0 78.812744 +2025-04-28 23:00:00+01:00 6602.0 0.0 1532.0 ... 0.0 25506.0 77.041573 +2025-04-28 23:30:00+01:00 6644.0 0.0 1552.0 ... 0.0 23916.0 77.293633 +2025-04-29 00:00:00+01:00 6692.0 0.0 1572.0 ... 0.0 22841.0 76.749825 + +[604 rows x 8 columns] +2025-04-16 10:15:22 INFO: 26: 2025-04-16 10:15 +2025-04-16 10:15:22 INFO: 25: 2025-04-16 06:15 +2025-04-16 10:15:22 INFO: 24: 2025-04-15 23:06 +2025-04-16 10:15:22 INFO: 23: 2025-04-15 17:14 +2025-04-16 10:15:22 INFO: 22: 2025-04-15 16:15 +2025-04-16 10:15:22 INFO: 21: 2025-04-15 10:15 +2025-04-16 10:15:22 INFO: 20: 2025-04-15 06:15 +2025-04-16 10:15:22 INFO: 19: 2025-04-14 22:15 +2025-04-16 10:15:22 INFO: 18: 2025-04-14 19:21 +2025-04-16 10:15:22 INFO: 17: 2025-04-13 19:37 +2025-04-16 10:15:22 INFO: 16: 2025-04-13 16:04 +2025-04-16 10:15:22 INFO: 15: 2025-04-13 15:57 +2025-04-16 10:15:22 INFO: 14: 2025-04-13 15:52 +2025-04-16 10:15:22 INFO: 13: 2025-04-13 15:49 +2025-04-16 10:15:22 INFO: 12: 2025-04-13 15:43 +2025-04-16 10:15:22 INFO: 11: 2025-04-01 21:46 +Restarting machine 17814469b9d798 +No health checks found +Machine 17814469b9d798 restarted successfully! +2025-04-16 14:06:31 INFO: id name created_at +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 +2025-04-16 14:08:31 INFO: id name created_at +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 +2025-04-16 14:08:47 INFO: + +Added Forecast: 25: 2025-04-16 14:08 +2025-04-16 15:01:44 INFO: id name created_at +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 +2025-04-16 15:02:02 INFO: + +Added Forecast: 26: 2025-04-16 15:01 +2025-04-16 15:02:41 INFO: id name created_at +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 +2025-04-16 15:07:17 INFO: id name created_at +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 +2025-04-16 15:09:36 INFO: id name created_at +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 +2025-04-16 15:09:41 INFO: Cross-val scrore: [-14.6491809 -6.45852132 -12.3239967 -15.04279345 -29.85792956] +2025-04-16 15:09:57 INFO: + +Added Forecast: 27: 2025-04-16 15:09 +2025-04-16 15:19:48 INFO: id name created_at mean stdev +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 None None +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 None None +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 None None +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 None None +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 None None +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 None None +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 None None +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 None None +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 None None +9 27 2025-04-16 15:09 2025-04-16 14:09:45.215338+00:00 None None +2025-04-16 15:19:53 INFO: Cross-val scrore: [-14.6491809 -6.45852132 -12.3239967 -15.04279345 -29.85792956] +2025-04-16 15:20:36 INFO: id name created_at mean stdev +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 None None +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 None None +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 None None +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 None None +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 None None +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 None None +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 None None +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 None None +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 None None +9 27 2025-04-16 15:09 2025-04-16 14:09:45.215338+00:00 None None +2025-04-16 15:20:42 INFO: Cross-val scrore: [-14.6491809 -6.45852132 -12.3239967 -15.04279345 -29.85792956] +2025-04-16 15:21:00 INFO: + +Added Forecast: 28: 2025-04-16 15:20 +2025-04-16 15:21:17 INFO: id name created_at mean stdev +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 NaN NaN +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 NaN NaN +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 NaN NaN +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 NaN NaN +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 NaN NaN +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 NaN NaN +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 NaN NaN +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 NaN NaN +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 NaN NaN +9 27 2025-04-16 15:09 2025-04-16 14:09:45.215338+00:00 NaN NaN +10 28 2025-04-16 15:20 2025-04-16 14:20:46.422498+00:00 -15.666484 7.730198 +2025-04-16 15:21:23 INFO: Cross-val scrore: [-14.80727976 -6.61297107 -12.27491292 -14.55917733 -29.94994601] +2025-04-16 15:21:40 INFO: + +Added Forecast: 29: 2025-04-16 15:21 +2025-04-16 15:39:27 INFO: id name created_at mean stdev +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 None None +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 None None +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 None None +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 None None +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 None None +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 None None +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 None None +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 None None +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 None None +9 27 2025-04-16 15:09 2025-04-16 14:09:45.215338+00:00 None None +2025-04-16 15:39:32 INFO: Cross-val scrore: [-14.6491809 -6.45852132 -12.3239967 -15.04279345 -29.85792956] +2025-04-16 15:39:48 INFO: + +Added Forecast: 30: 2025-04-16 15:39 +2025-04-16 15:40:14 INFO: id name created_at mean stdev +0 18 2025-04-14 19:21 2025-04-14 18:21:21.668951+00:00 NaN NaN +1 19 2025-04-14 22:15 2025-04-14 21:15:13.688685+00:00 NaN NaN +2 20 2025-04-15 06:15 2025-04-15 05:15:20.771691+00:00 NaN NaN +3 21 2025-04-15 10:15 2025-04-15 09:15:13.805469+00:00 NaN NaN +4 22 2025-04-15 16:15 2025-04-15 15:15:54.357581+00:00 NaN NaN +5 23 2025-04-15 17:14 2025-04-15 16:14:14.069566+00:00 NaN NaN +6 24 2025-04-15 22:54 2025-04-15 21:54:36.185845+00:00 NaN NaN +7 25 2025-04-16 14:08 2025-04-16 13:08:35.352505+00:00 NaN NaN +8 26 2025-04-16 15:01 2025-04-16 14:01:48.755874+00:00 NaN NaN +9 27 2025-04-16 15:09 2025-04-16 14:09:45.215338+00:00 NaN NaN +10 30 2025-04-16 15:39 2025-04-16 14:39:36.016846+00:00 15.666484 7.730198 +2025-04-16 15:40:19 INFO: Cross-val scrore: [-14.81128377 -6.61297107 -12.27491292 -14.55917733 -29.94994601] +2025-04-16 15:40:36 INFO: + +Added Forecast: 31: 2025-04-16 15:40 diff --git a/prices/management/commands/update3.py b/prices/management/commands/update.py similarity index 90% rename from prices/management/commands/update3.py rename to prices/management/commands/update.py index ba214ca..3f64ea1 100644 --- a/prices/management/commands/update3.py +++ b/prices/management/commands/update.py @@ -1,5 +1,6 @@ import xgboost as xg from sklearn.metrics import mean_squared_error as MSE +from sklearn.model_selection import cross_val_score from sklearn.neighbors import KernelDensity from sklearn.model_selection import train_test_split @@ -61,12 +62,11 @@ def add_arguments(self, parser): ) parser.add_argument( - "--no_day_of_week", - action="store_true", + "--max_days", ) parser.add_argument( - "--nordpool", + "--no_day_of_week", action="store_true", ) @@ -90,9 +90,9 @@ def add_arguments(self, parser): def handle(self, *args, **options): # Setup logging - local_dir = os.path.join(os.getcwd(), ".local") - os.makedirs(local_dir, exist_ok=True) - log_file = os.path.join(local_dir, "train_forecast.log") + log_dir = os.path.join(os.getcwd(), "logs") + os.makedirs(log_dir, exist_ok=True) + log_file = os.path.join(log_dir, "update.log") logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -111,12 +111,11 @@ def handle(self, *args, **options): debug = options.get("debug", False) min_fd = int(options.get("min_fd", 600) or 600) - min_ad = int(options.get("min_ad", 0) or 0) + min_ad = int(options.get("min_ad", 1500) or 1500) + max_days = int(options.get("max_days", 60) or 60) no_ranges = options.get("no_ranges", False) - use_gb60 = options.get("gb60", False) - drop_cols = ["emb_wind"] if options.get("no_day_of_week", False): drop_cols += ["day_of_week"] @@ -129,14 +128,32 @@ def handle(self, *args, **options): ignore_forecast = [int(x) for x in options.get("ignore_forecast", [])] # Clean any invalid forecasts - for f in Forecasts.objects.all(): - q = ForecastData.objects.filter(forecast=f) - a = AgileData.objects.filter(forecast=f) - + if debug: + logger.info(f"Max days: {max_days}") + + logger.info(f" ID | Name | #FD | #AD | Days |") + logger.info(f"------+------------------+-------+---------+------+") + keep = [] + for f in Forecasts.objects.all().order_by("-created_at"): + fd = ForecastData.objects.filter(forecast=f) + ad = AgileData.objects.filter(forecast=f) + dt = pd.to_datetime(f.name).tz_localize("GB") + days = (pd.Timestamp.now(tz="GB") - dt).days + if fd.count() < min_fd or ad.count() < min_ad: + fail = " <- Fail" + else: + if days < max_days * 2: + keep.append(f.id) + fail = "" + else: + fail = "<- Old" if debug: - logger.info(f"{f.id} {f.name} {q.count()} {a.count()}") - if q.count() < min_fd or a.count() < min_ad: - f.delete() + logger.info(f"{f.id:5d} | {f.name} | {fd.count():5d} | {ad.count():7d} | {days:4d} | {fail}") + + forecasts_to_delete = Forecasts.objects.exclude(id__in=keep) + if debug: + logger.info(f"\nDeleting ({forecasts_to_delete})\n") + forecasts_to_delete.delete() prices, start = model_to_df(PriceHistory) @@ -245,8 +262,6 @@ def handle(self, *args, **options): df["dt"] = (df.index - df["created_at"]).dt.total_seconds() / 3600 / 24 df["peak"] = ((df["time"] >= 16) & (df["time"] < 19)).astype(float) - max_days = 60 - features = [ "bm_wind", "solar", @@ -287,6 +302,12 @@ def handle(self, *args, **options): colsample_bytree=1, ) + scores = cross_val_score( + xg_model, train_X, train_y, cv=5, scoring="neg_root_mean_squared_error" + ) + + logger.info(f"Cross-val scrore: {scores}") + xg_model.fit(train_X, train_y, sample_weight=sample_weights, verbose=True) # Drop the training data set @@ -400,8 +421,10 @@ def handle(self, *args, **options): ) ) sfs.append( - pd.DataFrame(index=fc.index.difference(sfs[0].index.union(sfs[1].index))), - data={"mult": 1, "shift": 0}, + pd.DataFrame( + index=fc.index.difference(sfs[0].index.union(sfs[1].index)), + data={"mult": 1, "shift": 0}, + ) ) else: sfs.append(pd.DataFrame(index=fc.index.difference(sfs[0].index), data={"mult": 1, "shift": 0})) @@ -478,7 +501,7 @@ def handle(self, *args, **options): logger.info(f"Final forecast from {fc.index[0]} to {fc.index[-1]}") logger.info(f"Forecast\n{fc}") - this_forecast = Forecasts(name=new_name) + this_forecast = Forecasts(name=new_name, mean=-np.mean(scores), stdev=np.std(scores)) this_forecast.save() fc["forecast"] = this_forecast ag["forecast"] = this_forecast diff --git a/prices/migrations/0028_forecasts_mean_forecasts_stdev_and_more.py b/prices/migrations/0028_forecasts_mean_forecasts_stdev_and_more.py new file mode 100644 index 0000000..06e5953 --- /dev/null +++ b/prices/migrations/0028_forecasts_mean_forecasts_stdev_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.20 on 2025-04-16 14:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("prices", "0027_delete_updateerrors"), + ] + + operations = [ + migrations.AddField( + model_name="forecasts", + name="mean", + field=models.FloatField(null=True), + ), + migrations.AddField( + model_name="forecasts", + name="stdev", + field=models.FloatField(null=True), + ), + migrations.AlterField( + model_name="forecastdata", + name="day_ahead", + field=models.FloatField(null=True), + ), + ] diff --git a/prices/models.py b/prices/models.py index 1da52bd..2a650bc 100644 --- a/prices/models.py +++ b/prices/models.py @@ -5,6 +5,8 @@ class Forecasts(models.Model): name = models.CharField(unique=True, max_length=64) created_at = models.DateTimeField(auto_now_add=True) + mean = models.FloatField(null=True) + stdev = models.FloatField(null=True) def __str__(self): return self.name diff --git a/prices/views.py b/prices/views.py index c397369..f3c6940 100644 --- a/prices/views.py +++ b/prices/views.py @@ -225,7 +225,7 @@ def update_chart(self, context, **kwargs): marker={"symbol": 104, "size": 10}, mode="lines", line=dict(width=width), - name="Prediction", + name=f"Prediction ({pd.to_datetime(f.name).tz_localize('GB').strftime('%d-%b %H:%M')})", hovertemplate=hover_template_price, ) ] @@ -240,7 +240,7 @@ def update_chart(self, context, **kwargs): line=dict(width=1, color="red"), name="Low", showlegend=False, - hovertemplate=hover_template_price + hovertemplate=hover_template_price, ), go.Scatter( x=df.index, @@ -367,7 +367,7 @@ def update_chart(self, context, **kwargs): legend=legend, height=height, template="plotly_dark", - hovermode='x', + hovermode="x", ) figure.update_layout(**layout)