diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..0145f72 --- /dev/null +++ b/.env.test @@ -0,0 +1,4 @@ +#copy and paste this file into a .env file to run scripts locally. You will also need to add the DUNE_API_KEY into the repo settings under "Secrets and Variables" + +#add a dune API key - you can create one under team settings (https://dune.com/settings/teams/manage/{team_name}/api). You must be on the premium plan. +DUNE_API_KEY= \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1596469 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,17 @@ +--- +name: Bug Report +about: Tell us about the data bug or broken query you are seeing +title: "[BUG] Query data quality issue" +labels: 'bug' +assignees: '' + +--- + +**What is the query id, and describe the bug** +A clear and concise description of what the bug is, with query id linked. + +**Examples of the bug** +Please provide links to etherscan or your own queries showing the errors/discrepencies. + +**Screenshots** +If applicable, add screenshots to help explain your problem. diff --git a/.github/ISSUE_TEMPLATE/chart-improvment.md b/.github/ISSUE_TEMPLATE/chart-improvment.md new file mode 100644 index 0000000..93bbab0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/chart-improvment.md @@ -0,0 +1,17 @@ +--- +name: Chart Improvment +about: Suggest an idea for improving a chart +title: "[CHART] Suggestion for improving a chart" +labels: 'enhancements' +assignees: '' + +--- + +**Which query would you like to improve, and why?"** +A clear and concise description of what the problem is, with query id (if this is an improvement to an existing chart) + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Any work or examples you've seen that are similar to the solution** +Links and screenshots to any guiding charts/tables/queries you've seen. diff --git a/.github/ISSUE_TEMPLATE/generic-issue.md b/.github/ISSUE_TEMPLATE/generic-issue.md new file mode 100644 index 0000000..83d5bf8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/generic-issue.md @@ -0,0 +1,11 @@ +--- +name: Generic Question +about: Tell us about your question +title: "[GENERIC] Question" +labels: 'question' +assignees: '' + +--- + +**What is your question?** +I don't quite understand how/why this query does XYZ... diff --git a/.github/ISSUE_TEMPLATE/query-improvement.md b/.github/ISSUE_TEMPLATE/query-improvement.md new file mode 100644 index 0000000..ebd6b60 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/query-improvement.md @@ -0,0 +1,17 @@ +--- +name: Query Improvement +about: Suggest an idea for improving a query +title: "[QUERY] Suggestion for improving a chart" +labels: 'enhancement' +assignees: '' + +--- + +**Which query would you like to improve, and why?"** +A clear and concise description of what the problem is, with query id (if this is an improvement to an existing chart) + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. Please suggest data sources/tables that this enhancement could come from. + +**Any work or examples you've seen that are similar to the solution** +Links and screenshots to any guiding charts/tables/queries you've seen. diff --git a/.github/workflows/push_to_dune.yml b/.github/workflows/push_to_dune.yml new file mode 100644 index 0000000..5d438d7 --- /dev/null +++ b/.github/workflows/push_to_dune.yml @@ -0,0 +1,32 @@ +name: Push to Dune on Commit + +on: + push: + branches: + - main + paths: + - 'queries/**' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Log directory structure + run: | + pwd + ls -R + + - name: pip requirements + run: pip install -r requirements.txt + + - name: Update all queries from Dune, by overwriting queries with repo query text + env: + DUNE_API_KEY: ${{ secrets.DUNE_API_KEY }} + run: python -u scripts/push_to_dune.py diff --git a/.github/workflows/upload_to_dune.yml b/.github/workflows/upload_to_dune.yml new file mode 100644 index 0000000..0c5a3a2 --- /dev/null +++ b/.github/workflows/upload_to_dune.yml @@ -0,0 +1,32 @@ +name: Upload CSVs to Dune on Commit + +on: + push: + branches: + - main + paths: + - 'uploads/**' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Log directory structure + run: | + pwd + ls -R + + - name: pip requirements + run: pip install -r requirements.txt + + - name: Update all queries from Dune, by overwriting queries with repo query text + env: + DUNE_API_KEY: ${{ secrets.DUNE_API_KEY }} + run: python -u scripts/upload_to_dune.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..963a057 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +.env diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e00689f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Dune + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..0873636 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +**Is this linked to an existing issue** +If so, link that issue(s) here. + +**Fill out the following table describing your edits:** + +| Original | Updated | Change | Reasoning | +|---|---|---|---| +| [3237745](https://dune.com/queries/3237745) | [3237938](https://dune.com/queries/3238935) | Remove sandwich traders using dex.sandwiches | We should only care about traders who are not doing MEV | +| `evm_grants.csv` | n/a | added grants for Optimism | captures approved grants only | + +**Provide any other context or screenshots that explain or justify the changes above:** + +--- + +**Note to Contributor:** + +Make sure your PR edits the original `query_id.sql` file with the new query text. If you are proposing adding a new query completely, make sure to add it to `queries.yml` and as a file in `/queries` as well. + +Thanks for contributing! 🙏 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d6f99d --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Dune Query Repo + +A template for creating repos to [manage your Dune queries](https://dune.mintlify.app/api-reference/crud/endpoint/create) and any [CSVs as Dune tables](https://dune.mintlify.app/api-reference/upload/endpoint/upload). + +### Setup Your Repo + +1. Generate an API key from your Dune account and put that in both your `.env` file and [github action secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) (name it `DUNE_API_KEY`). You can create a key under your Dune team settings. *The api key must be from a plus plan for this repo to work.* + +2. Type your intended query ids into the `queries.yml` file. The id can be found from the link `https://dune.com/queries//...`. If you're creating this for a dashboard, go to the dashboard you want to create a repo and click on the "github" button in the top right of your dashboard to see the query ids. + +3. Then, run `pull_from_dune.py` to bring in all queries into `/query_{id}.sql` files within the `/queries` folder. Directions to setup and run this python script are below. + +### Updating Queries or CSV Tables + +1. Make any changes you need to directly in the repo. Any time you push a commit to MAIN branch, `push_to_dune.py` will save your changes into Dune directly. You can run this manually too if you want. + +2. For CSVs, update the files in the `/uploads` folder. `upload_to_dune.py` will run on commit, or can be run manually. The table name in Dune will be `dune.team_name.dataset_`. + +--- + +### Query Management Scripts + +You'll need python and pip installed to run the script commands. If you don't have a package manager set up, then use either [conda](https://www.anaconda.com/download) or [poetry](https://python-poetry.org/) . Then install the required packages: + +``` +pip install -r requirements.txt +``` + +| Script | Action | Command | +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------|---| +| `pull_from_dune.py` | updates/adds queries to your repo based on ids in `queries.yml` | `python scripts/pull_from_dune.py` | +| `push_to_dune.py` | updates queries to Dune based on files in your `/queries` folder | `python scripts/push_to_dune.py` | +| `preview_query.py` | gives you the first 20 rows of results by running a query from your `/queries` folder. Specify the id. This uses Dune API credits | `python scripts/preview_query.py 2615782` | +| `upload_to_dune.py` | uploads/updates any tables from your `/uploads` folder. Must be in CSV format, and under 200MB. | `python scripts/upload_to_dune.py` | + +--- + +### Things to be aware of + +💡: Names of queries are pulled into the file name the first time `pull_from_dune.py` is run. Changing the file name in app or in folder will not affect each other (they aren't synced). **Make sure you leave the `___id.sql` at the end of the file, otherwise the scripts will break!** + +🟧: Make sure to leave in the comment `-- already part of a query repo` at the top of your file. This will hopefully help prevent others from using it in more than one repo. + +🔒: Queries must be owned by the team the API key was created under - otherwise you won't be able to update them from the repo. + +➕: If you want to add a query, add it in Dune app first then pull the query id (from URL `dune.com/queries/{id}/other_stuff`) into `queries.yml` + +🛑: If you accidently merge a PR or push a commit that messes up your query in Dune, you can roll back any changes using [query version history](https://dune.com/docs/app/query-editor/version-history). + +--- + +### For Contributors + +I've set up four types of issues right now: +- `bugs`: This is for data quality issues like miscalculations or broken queries. +- `chart improvements`: This is for suggesting improvements to the visualizations. +- `query improvements`: This is for suggesting improvements to the query itself, such as adding an extra column or table that enhances the results. +- `generic questions`: This is a catch all for other questions or suggestions you may have about the dashboard. + +If you want to contribute, either start an issue or go directly into making a PR (using the same labels as above). Once the PR is merged, the queries will get updated in the frontend. diff --git a/queries.yml b/queries.yml new file mode 100644 index 0000000..a276af1 --- /dev/null +++ b/queries.yml @@ -0,0 +1,2 @@ +query_ids: + - 123456 \ No newline at end of file diff --git a/queries/filler.txt b/queries/filler.txt new file mode 100644 index 0000000..f643c76 --- /dev/null +++ b/queries/filler.txt @@ -0,0 +1 @@ +this file is just here so the folder can exist. Feel free to delete this after you've pulled your queries in. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6888d99 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +dune-client +pyyaml +python-dotenv +pandas \ No newline at end of file diff --git a/scripts/preview_query.py b/scripts/preview_query.py new file mode 100644 index 0000000..0268db6 --- /dev/null +++ b/scripts/preview_query.py @@ -0,0 +1,39 @@ +import os +from dune_client.client import DuneClient +from dotenv import load_dotenv +import sys +import pandas as pd + +dotenv_path = os.path.join(os.path.dirname(__file__), '..', '.env') +load_dotenv(dotenv_path) + +dune = DuneClient.from_env() + +#get id passed in python script invoke +id = sys.argv[1] + +queries_path = os.path.join(os.path.dirname(__file__), '..', 'queries') +files = os.listdir(queries_path) +found_files = [file for file in files if str(id) == file.split('___')[-1].split('.')[0]] + +if len(found_files) != 0: + query_file = os.path.join(os.path.dirname(__file__), '..', 'queries', found_files[0]) + + print('getting 20 line preview for query {}...'.format(id)) + + with open(query_file, 'r', encoding='utf-8') as file: + query_text = file.read() + + print('select * from (\n' + query_text + '\n) limit 20') + + results = dune.run_sql('select * from (\n' + query_text + '\n) limit 20') + # print(results.result.rows) + results = pd.DataFrame(data=results.result.rows) + print('\n') + print(results) + print('\n') + print(results.describe()) + print('\n') + print(results.info()) +else: + print('query id file not found, try again') diff --git a/scripts/pull_from_dune.py b/scripts/pull_from_dune.py new file mode 100644 index 0000000..9b60313 --- /dev/null +++ b/scripts/pull_from_dune.py @@ -0,0 +1,60 @@ +import os +import yaml +from dune_client.client import DuneClient +from dotenv import load_dotenv +import sys +import codecs + +# Set the default encoding to UTF-8 +sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) + +dotenv_path = os.path.join(os.path.dirname(__file__), '..', '.env') +load_dotenv(dotenv_path) + +dune = DuneClient.from_env() + +# Read the queries.yml file +queries_yml = os.path.join(os.path.dirname(__file__), '..', 'queries.yml') +with open(queries_yml, 'r', encoding='utf-8') as file: + data = yaml.safe_load(file) + +# Extract the query_ids from the data +query_ids = [id for id in data['query_ids']] + +for id in query_ids: + query = dune.get_query(id) + print('PROCESSING: query {}, {}'.format(query.base.query_id, query.base.name)) + + # Check if query file exists in /queries folder + queries_path = os.path.join(os.path.dirname(__file__), '..', 'queries') + files = os.listdir(queries_path) + found_files = [file for file in files if str(id) == file.split('___')[-1].split('.')[0]] + + if len(found_files) != 0: + # Update existing file + file_path = os.path.join(os.path.dirname(__file__), '..', 'queries', found_files[0]) + + print('UPDATE: existing query file: {}'.format(found_files[0])) + with open(file_path, 'r+', encoding='utf-8') as file: + #if "query repo:" is in the file, then don't add the text header again + if '-- part of a query repo' in query.sql: + file.write(query.sql) + else: + file.write(f'-- part of a query repo\n-- query name: {query.base.name}\n-- query link: https://dune.com/queries/{query.base.query_id}\n\n\n{query.sql}') + else: + # Create new file and directories if they don't exist + new_file = f'{query.base.name.replace(" ", "_").lower()[:30]}___{query.base.query_id}.sql' + file_path = os.path.join(os.path.dirname(__file__), '..', 'queries', new_file) + os.makedirs(os.path.dirname(file_path), exist_ok=True) + + if '-- part of a query repo' in query.sql: + print('WARNING!!! This query is part of a query repo') + with open(file_path, 'w', encoding='utf-8') as file: + file.write(f'-- WARNING: this query may be part of multiple repos\n{query.sql}') + else: + with open(file_path, 'w', encoding='utf-8') as file: + file.write(f'-- part of a query repo\n-- query name: {query.base.name}\n-- query link: https://dune.com/queries/{query.base.query_id}\n\n\n{query.sql}') + print('CREATE: new query file: {}'.format(new_file)) + + + diff --git a/scripts/push_to_dune.py b/scripts/push_to_dune.py new file mode 100644 index 0000000..69de9b8 --- /dev/null +++ b/scripts/push_to_dune.py @@ -0,0 +1,46 @@ +import os +import yaml +from dune_client.client import DuneClient +from dotenv import load_dotenv +import sys +import codecs + +# Set the default encoding to UTF-8 +sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) + +dotenv_path = os.path.join(os.path.dirname(__file__), '..', '.env') +load_dotenv(dotenv_path) + +dune = DuneClient.from_env() + +# Read the queries.yml file +queries_yml = os.path.join(os.path.dirname(__file__), '..', 'queries.yml') +with open(queries_yml, 'r', encoding='utf-8') as file: + data = yaml.safe_load(file) + +# Extract the query_ids from the data +query_ids = [id for id in data['query_ids']] + +for id in query_ids: + query = dune.get_query(id) + print('PROCESSING: query {}, {}'.format(query.base.query_id, query.base.name)) + + # Check if query file exists in /queries folder + queries_path = os.path.join(os.path.dirname(__file__), '..', 'queries') + files = os.listdir(queries_path) + found_files = [file for file in files if str(id) == file.split('___')[-1].split('.')[0]] + + if len(found_files) != 0: + file_path = os.path.join(os.path.dirname(__file__), '..', 'queries', found_files[0]) + # Read the content of the file + with open(file_path, 'r', encoding='utf-8') as file: + text = file.read() + + # Update existing file + dune.update_query( + query.base.query_id, + query_sql=text, + ) + print('SUCCESS: updated query {} to dune'.format(query.base.query_id)) + else: + print('ERROR: file not found, query id {}'.format(query.base.query_id)) \ No newline at end of file diff --git a/scripts/upload_to_dune.py b/scripts/upload_to_dune.py new file mode 100644 index 0000000..1eec72c --- /dev/null +++ b/scripts/upload_to_dune.py @@ -0,0 +1,33 @@ +import os +from dune_client.client import DuneClient +from dotenv import load_dotenv +import sys +import codecs +import os + +# Set the default encoding to UTF-8 +sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) + +dotenv_path = os.path.join(os.path.dirname(__file__), '..', '.env') +load_dotenv(dotenv_path) + +dune = DuneClient.from_env() + +uploads_path = os.path.join(os.path.dirname(__file__), '..', 'uploads') +files = os.listdir(uploads_path) + +if len(files) == 0: + exit() + +for file in files: + if not file.endswith(".csv"): + continue + file_name = file.split(".")[0].lower().replace(' ', '_') + with open(os.path.join(uploads_path, file), 'r') as file: + table = dune.upload_csv( + data=str(file.read()), + table_name=file_name, + is_private=False + ) + print(f'uploaded table "{file_name}"') + diff --git a/uploads/FEDFUNDS.csv b/uploads/FEDFUNDS.csv new file mode 100644 index 0000000..d063ea3 --- /dev/null +++ b/uploads/FEDFUNDS.csv @@ -0,0 +1,206 @@ +DATE,FEDFUNDS +2007-01-01,5.25 +2007-02-01,5.26 +2007-03-01,5.26 +2007-04-01,5.25 +2007-05-01,5.25 +2007-06-01,5.25 +2007-07-01,5.26 +2007-08-01,5.02 +2007-09-01,4.94 +2007-10-01,4.76 +2007-11-01,4.49 +2007-12-01,4.24 +2008-01-01,3.94 +2008-02-01,2.98 +2008-03-01,2.61 +2008-04-01,2.28 +2008-05-01,1.98 +2008-06-01,2.00 +2008-07-01,2.01 +2008-08-01,2.00 +2008-09-01,1.81 +2008-10-01,0.97 +2008-11-01,0.39 +2008-12-01,0.16 +2009-01-01,0.15 +2009-02-01,0.22 +2009-03-01,0.18 +2009-04-01,0.15 +2009-05-01,0.18 +2009-06-01,0.21 +2009-07-01,0.16 +2009-08-01,0.16 +2009-09-01,0.15 +2009-10-01,0.12 +2009-11-01,0.12 +2009-12-01,0.12 +2010-01-01,0.11 +2010-02-01,0.13 +2010-03-01,0.16 +2010-04-01,0.20 +2010-05-01,0.20 +2010-06-01,0.18 +2010-07-01,0.18 +2010-08-01,0.19 +2010-09-01,0.19 +2010-10-01,0.19 +2010-11-01,0.19 +2010-12-01,0.18 +2011-01-01,0.17 +2011-02-01,0.16 +2011-03-01,0.14 +2011-04-01,0.10 +2011-05-01,0.09 +2011-06-01,0.09 +2011-07-01,0.07 +2011-08-01,0.10 +2011-09-01,0.08 +2011-10-01,0.07 +2011-11-01,0.08 +2011-12-01,0.07 +2012-01-01,0.08 +2012-02-01,0.10 +2012-03-01,0.13 +2012-04-01,0.14 +2012-05-01,0.16 +2012-06-01,0.16 +2012-07-01,0.16 +2012-08-01,0.13 +2012-09-01,0.14 +2012-10-01,0.16 +2012-11-01,0.16 +2012-12-01,0.16 +2013-01-01,0.14 +2013-02-01,0.15 +2013-03-01,0.14 +2013-04-01,0.15 +2013-05-01,0.11 +2013-06-01,0.09 +2013-07-01,0.09 +2013-08-01,0.08 +2013-09-01,0.08 +2013-10-01,0.09 +2013-11-01,0.08 +2013-12-01,0.09 +2014-01-01,0.07 +2014-02-01,0.07 +2014-03-01,0.08 +2014-04-01,0.09 +2014-05-01,0.09 +2014-06-01,0.10 +2014-07-01,0.09 +2014-08-01,0.09 +2014-09-01,0.09 +2014-10-01,0.09 +2014-11-01,0.09 +2014-12-01,0.12 +2015-01-01,0.11 +2015-02-01,0.11 +2015-03-01,0.11 +2015-04-01,0.12 +2015-05-01,0.12 +2015-06-01,0.13 +2015-07-01,0.13 +2015-08-01,0.14 +2015-09-01,0.14 +2015-10-01,0.12 +2015-11-01,0.12 +2015-12-01,0.24 +2016-01-01,0.34 +2016-02-01,0.38 +2016-03-01,0.36 +2016-04-01,0.37 +2016-05-01,0.37 +2016-06-01,0.38 +2016-07-01,0.39 +2016-08-01,0.40 +2016-09-01,0.40 +2016-10-01,0.40 +2016-11-01,0.41 +2016-12-01,0.54 +2017-01-01,0.65 +2017-02-01,0.66 +2017-03-01,0.79 +2017-04-01,0.90 +2017-05-01,0.91 +2017-06-01,1.04 +2017-07-01,1.15 +2017-08-01,1.16 +2017-09-01,1.15 +2017-10-01,1.15 +2017-11-01,1.16 +2017-12-01,1.30 +2018-01-01,1.41 +2018-02-01,1.42 +2018-03-01,1.51 +2018-04-01,1.69 +2018-05-01,1.70 +2018-06-01,1.82 +2018-07-01,1.91 +2018-08-01,1.91 +2018-09-01,1.95 +2018-10-01,2.19 +2018-11-01,2.20 +2018-12-01,2.27 +2019-01-01,2.40 +2019-02-01,2.40 +2019-03-01,2.41 +2019-04-01,2.42 +2019-05-01,2.39 +2019-06-01,2.38 +2019-07-01,2.40 +2019-08-01,2.13 +2019-09-01,2.04 +2019-10-01,1.83 +2019-11-01,1.55 +2019-12-01,1.55 +2020-01-01,1.55 +2020-02-01,1.58 +2020-03-01,0.65 +2020-04-01,0.05 +2020-05-01,0.05 +2020-06-01,0.08 +2020-07-01,0.09 +2020-08-01,0.10 +2020-09-01,0.09 +2020-10-01,0.09 +2020-11-01,0.09 +2020-12-01,0.09 +2021-01-01,0.09 +2021-02-01,0.08 +2021-03-01,0.07 +2021-04-01,0.07 +2021-05-01,0.06 +2021-06-01,0.08 +2021-07-01,0.10 +2021-08-01,0.09 +2021-09-01,0.08 +2021-10-01,0.08 +2021-11-01,0.08 +2021-12-01,0.08 +2022-01-01,0.08 +2022-02-01,0.08 +2022-03-01,0.20 +2022-04-01,0.33 +2022-05-01,0.77 +2022-06-01,1.21 +2022-07-01,1.68 +2022-08-01,2.33 +2022-09-01,2.56 +2022-10-01,3.08 +2022-11-01,3.78 +2022-12-01,4.10 +2023-01-01,4.33 +2023-02-01,4.57 +2023-03-01,4.65 +2023-04-01,4.83 +2023-05-01,5.06 +2023-06-01,5.08 +2023-07-01,5.12 +2023-08-01,5.33 +2023-09-01,5.33 +2023-10-01,5.33 +2023-11-01,5.33 +2023-12-01,5.33 +2024-01-01,5.33