From d6fd646402be51a5e31f2d7f37d8c50da8f52fc0 Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:02:49 -0700 Subject: [PATCH] [V5] `openbb-deribit`: Refactor provider for V5 (#7674) * refactor openbb-deribit for v5 * restructure the optimizer around the published combos data and add integration test suites --- .github/workflows/publish-openbb-deribit.yml | 165 + .../workflows/test-unit-openbb-deribit.yml | 118 + openbb_platform/providers/deribit/README.md | 342 +- .../providers/deribit/integration/__init__.py | 1 + .../providers/deribit/integration/cases.py | 107 + .../providers/deribit/integration/conftest.py | 72 + .../deribit/integration/test_deribit_api.py | 143 + .../integration/test_deribit_python.py | 148 + .../deribit/openbb_deribit/__init__.py | 88 +- .../deribit/openbb_deribit/assets/apps.json | 586 +++ .../deribit/openbb_deribit/deribit_router.py | 83 + .../openbb_deribit/models/announcements.py | 145 + .../openbb_deribit/models/apr_history.py | 113 + .../openbb_deribit/models/block_rfq_trades.py | 212 + .../openbb_deribit/models/book_summary.py | 334 ++ .../deribit/openbb_deribit/models/combos.py | 175 + .../openbb_deribit/models/currencies.py | 155 + .../openbb_deribit/models/delivery_prices.py | 134 + .../openbb_deribit/models/expirations.py | 142 + .../openbb_deribit/models/funding_chart.py | 177 + .../models/funding_rate_history.py | 208 + .../openbb_deribit/models/futures_curve.py | 245 +- .../models/futures_historical.py | 239 +- .../openbb_deribit/models/futures_info.py | 232 +- .../models/futures_instruments.py | 347 +- .../models/historical_volatility.py | 159 + .../openbb_deribit/models/index_historical.py | 129 + .../openbb_deribit/models/index_price.py | 118 + .../openbb_deribit/models/instruments.py | 430 ++ .../openbb_deribit/models/options_chains.py | 724 ++- .../openbb_deribit/models/order_book.py | 261 + .../openbb_deribit/models/settlements.py | 244 + .../deribit/openbb_deribit/models/ticker.py | 401 ++ .../openbb_deribit/models/trade_volumes.py | 222 + .../deribit/openbb_deribit/models/trades.py | 354 ++ .../openbb_deribit/models/volatility_index.py | 216 + .../openbb_deribit/routers/__init__.py | 1 + .../deribit/openbb_deribit/routers/futures.py | 181 + .../deribit/openbb_deribit/routers/index.py | 116 + .../deribit/openbb_deribit/routers/market.py | 256 + .../deribit/openbb_deribit/routers/options.py | 992 ++++ .../deribit/openbb_deribit/routers/rates.py | 146 + .../openbb_deribit/routers/reference.py | 321 ++ .../openbb_deribit/routers/volatility.py | 90 + .../deribit/openbb_deribit/utils/cache.py | 146 + .../deribit/openbb_deribit/utils/choices.py | 86 + .../deribit/openbb_deribit/utils/client.py | 132 + .../deribit/openbb_deribit/utils/constants.py | 102 + .../deribit/openbb_deribit/utils/helpers.py | 797 +-- .../openbb_deribit/utils/options/__init__.py | 1 + .../openbb_deribit/utils/options/chain.py | 337 ++ .../utils/options/create_payoff.py | 145 + .../utils/options/create_smile.py | 209 + .../utils/options/create_stats.py | 143 + .../utils/options/create_surface.py | 199 + .../utils/options/create_term_structure.py | 191 + .../utils/options/data_handler.py | 144 + .../openbb_deribit/utils/options/greeks.py | 79 + .../openbb_deribit/utils/options/legs.py | 248 + .../openbb_deribit/utils/options/optimizer.py | 764 +++ .../utils/options/strategies.py | 162 + .../openbb_deribit/utils/options/theme.py | 119 + .../deribit/openbb_deribit/utils/session.py | 116 + .../deribit/openbb_deribit/utils/websocket.py | 85 + openbb_platform/providers/deribit/poetry.lock | 1974 ------- .../providers/deribit/pyproject.toml | 104 +- .../providers/deribit/tests/conftest.py | 160 + .../deribit/tests/data/announcements.json | 26 + .../deribit/tests/data/apr_history.json | 25 + .../deribit/tests/data/block_rfq_trades.json | 343 ++ .../tests/data/book_summary_future.json | 251 + .../tests/data/book_summary_instrument.json | 23 + .../deribit/tests/data/combo_details.json | 17 + .../deribit/tests/data/combo_ids.json | 202 + .../providers/deribit/tests/data/combos.json | 342 ++ .../deribit/tests/data/contract_size.json | 3 + .../deribit/tests/data/currencies.json | 162 + .../deribit/tests/data/delivery_prices.json | 25 + .../deribit/tests/data/expirations_flat.json | 32 + .../tests/data/expirations_grouped.json | 280 + .../deribit/tests/data/funding_chart.json | 306 ++ .../tests/data/funding_rate_history.json | 170 + .../tests/data/historical_volatility.json | 242 + .../deribit/tests/data/index_chart_data.json | 242 + .../deribit/tests/data/index_price.json | 4 + .../deribit/tests/data/index_price_names.json | 202 + .../deribit/tests/data/instrument.json | 35 + .../tests/data/instruments_future.json | 457 ++ .../tests/data/instruments_option.json | 2870 ++++++++++ .../deribit/tests/data/option_tickers.json | 452 ++ .../deribit/tests/data/order_book.json | 73 + .../deribit/tests/data/server_time.json | 1 + .../tests/data/settlements_currency.json | 55 + .../tests/data/settlements_instrument.json | 55 + .../providers/deribit/tests/data/status.json | 3 + .../tests/data/supported_index_names.json | 192 + .../deribit/tests/data/ticker_future.json | 28 + .../deribit/tests/data/trade_volumes.json | 313 ++ .../deribit/tests/data/trades_currency.json | 85 + .../deribit/tests/data/trades_instrument.json | 80 + .../deribit/tests/data/tradingview_chart.json | 73 + .../deribit/tests/data/volatility_index.json | 110 + ...ibit_futures_curve_fetcher_urllib3_v2.yaml | 528 -- ...futures_historical_fetcher_urllib3_v2.yaml | 916 ---- ...ribit_futures_info_fetcher_urllib3_v2.yaml | 132 - ...utures_instruments_fetcher_urllib3_v2.yaml | 79 - .../test_get_options_symbols_urllib3_v2.yaml | 265 - .../providers/deribit/tests/test_cache.py | 191 + .../providers/deribit/tests/test_choices.py | 106 + .../providers/deribit/tests/test_client.py | 225 + .../deribit/tests/test_deribit_fetchers.py | 166 - .../providers/deribit/tests/test_helpers.py | 476 ++ .../providers/deribit/tests/test_models.py | 1406 +++++ .../deribit/tests/test_options_analysis.py | 790 +++ .../deribit/tests/test_options_chains.py | 193 + .../deribit/tests/test_options_charts.py | 378 ++ .../deribit/tests/test_options_views.py | 971 ++++ .../deribit/tests/test_registration.py | 116 + .../providers/deribit/tests/test_router.py | 263 + .../providers/deribit/tests/test_routes.py | 159 + .../providers/deribit/tests/test_session.py | 211 + .../providers/deribit/tests/test_websocket.py | 130 + .../deribit/tests/test_widget_contract.py | 962 ++++ openbb_platform/providers/deribit/uv.lock | 4647 +++++++++++++++++ 124 files changed, 32530 insertions(+), 5074 deletions(-) create mode 100644 .github/workflows/publish-openbb-deribit.yml create mode 100644 .github/workflows/test-unit-openbb-deribit.yml create mode 100644 openbb_platform/providers/deribit/integration/__init__.py create mode 100644 openbb_platform/providers/deribit/integration/cases.py create mode 100644 openbb_platform/providers/deribit/integration/conftest.py create mode 100644 openbb_platform/providers/deribit/integration/test_deribit_api.py create mode 100644 openbb_platform/providers/deribit/integration/test_deribit_python.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/assets/apps.json create mode 100644 openbb_platform/providers/deribit/openbb_deribit/deribit_router.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/announcements.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/apr_history.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/block_rfq_trades.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/book_summary.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/combos.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/currencies.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/delivery_prices.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/expirations.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/funding_chart.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/funding_rate_history.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/historical_volatility.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/index_historical.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/index_price.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/instruments.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/order_book.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/settlements.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/ticker.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/trade_volumes.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/trades.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/models/volatility_index.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/__init__.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/futures.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/index.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/market.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/options.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/rates.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/reference.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/routers/volatility.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/cache.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/choices.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/client.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/constants.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/__init__.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/chain.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/create_payoff.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/create_smile.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/create_stats.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/create_surface.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/create_term_structure.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/data_handler.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/greeks.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/legs.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/optimizer.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/strategies.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/options/theme.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/session.py create mode 100644 openbb_platform/providers/deribit/openbb_deribit/utils/websocket.py delete mode 100644 openbb_platform/providers/deribit/poetry.lock create mode 100644 openbb_platform/providers/deribit/tests/conftest.py create mode 100644 openbb_platform/providers/deribit/tests/data/announcements.json create mode 100644 openbb_platform/providers/deribit/tests/data/apr_history.json create mode 100644 openbb_platform/providers/deribit/tests/data/block_rfq_trades.json create mode 100644 openbb_platform/providers/deribit/tests/data/book_summary_future.json create mode 100644 openbb_platform/providers/deribit/tests/data/book_summary_instrument.json create mode 100644 openbb_platform/providers/deribit/tests/data/combo_details.json create mode 100644 openbb_platform/providers/deribit/tests/data/combo_ids.json create mode 100644 openbb_platform/providers/deribit/tests/data/combos.json create mode 100644 openbb_platform/providers/deribit/tests/data/contract_size.json create mode 100644 openbb_platform/providers/deribit/tests/data/currencies.json create mode 100644 openbb_platform/providers/deribit/tests/data/delivery_prices.json create mode 100644 openbb_platform/providers/deribit/tests/data/expirations_flat.json create mode 100644 openbb_platform/providers/deribit/tests/data/expirations_grouped.json create mode 100644 openbb_platform/providers/deribit/tests/data/funding_chart.json create mode 100644 openbb_platform/providers/deribit/tests/data/funding_rate_history.json create mode 100644 openbb_platform/providers/deribit/tests/data/historical_volatility.json create mode 100644 openbb_platform/providers/deribit/tests/data/index_chart_data.json create mode 100644 openbb_platform/providers/deribit/tests/data/index_price.json create mode 100644 openbb_platform/providers/deribit/tests/data/index_price_names.json create mode 100644 openbb_platform/providers/deribit/tests/data/instrument.json create mode 100644 openbb_platform/providers/deribit/tests/data/instruments_future.json create mode 100644 openbb_platform/providers/deribit/tests/data/instruments_option.json create mode 100644 openbb_platform/providers/deribit/tests/data/option_tickers.json create mode 100644 openbb_platform/providers/deribit/tests/data/order_book.json create mode 100644 openbb_platform/providers/deribit/tests/data/server_time.json create mode 100644 openbb_platform/providers/deribit/tests/data/settlements_currency.json create mode 100644 openbb_platform/providers/deribit/tests/data/settlements_instrument.json create mode 100644 openbb_platform/providers/deribit/tests/data/status.json create mode 100644 openbb_platform/providers/deribit/tests/data/supported_index_names.json create mode 100644 openbb_platform/providers/deribit/tests/data/ticker_future.json create mode 100644 openbb_platform/providers/deribit/tests/data/trade_volumes.json create mode 100644 openbb_platform/providers/deribit/tests/data/trades_currency.json create mode 100644 openbb_platform/providers/deribit/tests/data/trades_instrument.json create mode 100644 openbb_platform/providers/deribit/tests/data/tradingview_chart.json create mode 100644 openbb_platform/providers/deribit/tests/data/volatility_index.json delete mode 100644 openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_curve_fetcher_urllib3_v2.yaml delete mode 100644 openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_historical_fetcher_urllib3_v2.yaml delete mode 100644 openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_info_fetcher_urllib3_v2.yaml delete mode 100644 openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_instruments_fetcher_urllib3_v2.yaml delete mode 100644 openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_get_options_symbols_urllib3_v2.yaml create mode 100644 openbb_platform/providers/deribit/tests/test_cache.py create mode 100644 openbb_platform/providers/deribit/tests/test_choices.py create mode 100644 openbb_platform/providers/deribit/tests/test_client.py delete mode 100644 openbb_platform/providers/deribit/tests/test_deribit_fetchers.py create mode 100644 openbb_platform/providers/deribit/tests/test_helpers.py create mode 100644 openbb_platform/providers/deribit/tests/test_models.py create mode 100644 openbb_platform/providers/deribit/tests/test_options_analysis.py create mode 100644 openbb_platform/providers/deribit/tests/test_options_chains.py create mode 100644 openbb_platform/providers/deribit/tests/test_options_charts.py create mode 100644 openbb_platform/providers/deribit/tests/test_options_views.py create mode 100644 openbb_platform/providers/deribit/tests/test_registration.py create mode 100644 openbb_platform/providers/deribit/tests/test_router.py create mode 100644 openbb_platform/providers/deribit/tests/test_routes.py create mode 100644 openbb_platform/providers/deribit/tests/test_session.py create mode 100644 openbb_platform/providers/deribit/tests/test_websocket.py create mode 100644 openbb_platform/providers/deribit/tests/test_widget_contract.py create mode 100644 openbb_platform/providers/deribit/uv.lock diff --git a/.github/workflows/publish-openbb-deribit.yml b/.github/workflows/publish-openbb-deribit.yml new file mode 100644 index 00000000000..8e044468312 --- /dev/null +++ b/.github/workflows/publish-openbb-deribit.yml @@ -0,0 +1,165 @@ +name: ๐Ÿ“ฆ publish openbb-deribit + +on: + pull_request: + types: [closed] + branches: [develop, v5] + workflow_dispatch: + +permissions: + contents: read + +jobs: + version-check: + name: Verify version bump + if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/openbb-deribit-v')) || github.event_name == 'workflow_dispatch' }} + runs-on: ubuntu-latest + outputs: + version: ${{ steps.parse.outputs.version }} + defaults: + run: + shell: bash + working-directory: openbb_platform/providers/deribit + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.merge_commit_sha || github.sha }} + + - name: Parse [project].version from pyproject.toml + id: parse + run: | + VERSION=$(grep -m1 -E '^version[[:space:]]*=' pyproject.toml | sed -E 's/^version[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/') + if [ -z "$VERSION" ]; then + echo "::error::Could not parse [project].version from pyproject.toml." + exit 1 + fi + echo "Local version: $VERSION" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Fail if version already exists on PyPI + env: + VERSION: ${{ steps.parse.outputs.version }} + run: | + STATUS=$(curl -sS -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/openbb-deribit/${VERSION}/json") + case "$STATUS" in + 200) + echo "::error::openbb-deribit ${VERSION} is already published on PyPI." + echo "Bump openbb_platform/providers/deribit/pyproject.toml [project].version and re-merge a fresh release branch." + exit 1 + ;; + 404) + echo "openbb-deribit ${VERSION} is not yet on PyPI โ€” proceeding." + ;; + *) + echo "::error::Unexpected status $STATUS from PyPI JSON API. Aborting to avoid a silent skip." + exit 1 + ;; + esac + + - name: Verify release branch name matches pyproject version + if: github.event_name == 'pull_request' + env: + VERSION: ${{ steps.parse.outputs.version }} + BRANCH: ${{ github.event.pull_request.head.ref }} + run: | + EXPECTED="release/openbb-deribit-v${VERSION}" + if [ "$BRANCH" != "$EXPECTED" ]; then + echo "::error::Release branch '${BRANCH}' does not match pyproject version '${VERSION}'." + echo "Expected branch name: '${EXPECTED}'." + echo "Either rename the release branch or update [project].version in pyproject.toml so they agree." + exit 1 + fi + echo "Branch '${BRANCH}' matches pyproject version '${VERSION}'." + + build: + name: Build sdist + wheel + needs: version-check + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: openbb_platform/providers/deribit + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.merge_commit_sha || github.sha }} + + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - run: python -m pip install --upgrade pip uv + + - name: Build sdist + wheel + run: uv build --no-sources + + - uses: actions/upload-artifact@v4 + with: + name: openbb-deribit-dist + path: openbb_platform/providers/deribit/dist/ + if-no-files-found: error + retention-days: 7 + + test: + name: Test (${{ matrix.os }} / Python ${{ matrix.python_version }}) + needs: build + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-latest, python_version: "3.10" } + - { os: ubuntu-latest, python_version: "3.11" } + - { os: ubuntu-latest, python_version: "3.12" } + - { os: ubuntu-latest, python_version: "3.13" } + - { os: ubuntu-latest, python_version: "3.14" } + - { os: macos-latest, python_version: "3.10" } + - { os: macos-latest, python_version: "3.14" } + - { os: windows-latest, python_version: "3.10" } + - { os: windows-latest, python_version: "3.14" } + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + working-directory: openbb_platform/providers/deribit + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.merge_commit_sha || github.sha }} + + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python_version }} + allow-prereleases: true + + - uses: actions/download-artifact@v4 + with: + name: openbb-deribit-dist + path: openbb_platform/providers/deribit/dist + + - run: python -m pip install --upgrade pip uv + + - name: Install the dev group then the openbb-deribit wheel + run: | + uv pip install --system --group dev + uv pip install --system dist/openbb_deribit-*.whl + + - name: Test the installed wheel + run: | + rm -rf openbb_deribit + pytest tests --cov=openbb_deribit --cov-report=term-missing --cov-fail-under=100 + + publish: + name: Publish to PyPI + needs: test + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + name: openbb-deribit-dist + path: dist + + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist diff --git a/.github/workflows/test-unit-openbb-deribit.yml b/.github/workflows/test-unit-openbb-deribit.yml new file mode 100644 index 00000000000..0a593a99867 --- /dev/null +++ b/.github/workflows/test-unit-openbb-deribit.yml @@ -0,0 +1,118 @@ +name: ๐Ÿงช test openbb-deribit + +on: + pull_request: + branches: [develop, main, v5] + paths: + - "openbb_platform/providers/deribit/**" + - ".github/workflows/test-unit-openbb-deribit.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: test-openbb-deribit-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build sdist + wheel + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: openbb_platform/providers/deribit + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - run: python -m pip install --upgrade pip uv + + - name: Build sdist + wheel + run: uv build --no-sources + + - uses: actions/upload-artifact@v4 + with: + name: openbb-deribit-dist + path: openbb_platform/providers/deribit/dist/ + if-no-files-found: error + retention-days: 7 + + lint: + name: Lint (ruff + ty) + needs: build + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: openbb_platform/providers/deribit + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - run: python -m pip install --upgrade pip uv + + - name: Install openbb-deribit in editable mode with the dev group + run: uv pip install --system -e . --group dev + + - name: ruff check + run: ruff check --no-fix openbb_deribit tests integration + + - name: ruff format --check + run: ruff format --check openbb_deribit tests integration + + - name: ty check + run: ty check openbb_deribit + + test: + name: Test (${{ matrix.os }} / Python ${{ matrix.python_version }}) + needs: build + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-latest, python_version: "3.10" } + - { os: ubuntu-latest, python_version: "3.11" } + - { os: ubuntu-latest, python_version: "3.12" } + - { os: ubuntu-latest, python_version: "3.13" } + - { os: ubuntu-latest, python_version: "3.14" } + - { os: macos-latest, python_version: "3.10" } + - { os: macos-latest, python_version: "3.14" } + - { os: windows-latest, python_version: "3.10" } + - { os: windows-latest, python_version: "3.14" } + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + working-directory: openbb_platform/providers/deribit + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python_version }} + allow-prereleases: true + + - uses: actions/download-artifact@v4 + with: + name: openbb-deribit-dist + path: openbb_platform/providers/deribit/dist + + - run: python -m pip install --upgrade pip uv + + - name: Install the dev group then the openbb-deribit wheel + run: | + uv pip install --system --group dev + uv pip install --system dist/openbb_deribit-*.whl + + - name: Test the installed wheel + run: | + rm -rf openbb_deribit + pytest tests --cov=openbb_deribit --cov-report=term-missing --cov-fail-under=100 diff --git a/openbb_platform/providers/deribit/README.md b/openbb_platform/providers/deribit/README.md index c391522e763..83fea5fafa9 100644 --- a/openbb_platform/providers/deribit/README.md +++ b/openbb_platform/providers/deribit/README.md @@ -1,45 +1,305 @@ # OpenBB Deribit Provider Extension +This package adds the `openbb-deribit` provider and router extension to the Open Data Platform by +OpenBB. + +It implements the complete public market data surface of [Deribit](https://docs.deribit.com/) โ€” the +crypto derivatives exchange listing options, futures, perpetuals, spot pairs, and multi-leg combos โ€” +as OpenBB Platform endpoints. No API key is required, and nothing here trades. + +## Installation + +Install from PyPI with: + +```sh +pip install openbb-deribit +``` + +Then build the Python static assets by running: + +```sh +openbb-build +``` + +## Quick Start + +The fastest way to get started is by connecting to the OpenBB Workspace as a custom backend. + +### Start Server + +```sh +openbb-api +``` + +This starts the FastAPI server over localhost on port 6900. + +### Add to Workspace + +See the documentation [here](https://docs.openbb.co/python/quickstart/workspace) for more details. +The extension ships an `apps.json` describing a ready-made dashboard with Markets, Futures, Options, +Analysis, Volatility, and Reference tabs. + +## Conditional Registration + +Five of the Deribit models implement OpenBB standard models โ€” `FuturesCurve`, `FuturesHistorical`, +`FuturesInfo`, `FuturesInstruments`, and `OptionsChains` โ€” which are normally served by the +`openbb-derivatives` extension. When it is installed, Deribit registers as a provider under its +existing commands and no Deribit-specific route is added for them. + +When it is absent, the same data is served from this extension's own router under +`/deribit/futures/...` and `/deribit/options/...`, so installing `openbb-deribit` on its own still +exposes every dataset. The bundled `apps.json` resolves its widget IDs the same way. + +Everything else is Deribit-specific and always lives under `/deribit/...`. + ## Coverage -- obb.derivatives.options.chains - - Support for symbols: - - BTC - - ETH - - XRP - - SOL - - BNB - - PAXG -- obb.derivatives.futures.curve - - Support for symbols: - - BTC - - ETH - - PAXG -- obb.derivatives.futures.historical - - Support for symbols (Perpetual Futures): - - ADAUSDC - - ALGOUSDC - - AVAXUSDC - - BCHUSDC - - BNBUSDC - - BTC - - BTCUSDC - - BTCUSDT - - DOGEUSDC - - DOTUSDC - - ETH - - ETHUSDC - - ETHUSDT - - LINKUSDC - - LTCUSDC - - NEARUSDC - - PAXGUSDC - - SOLUSDC - - TRXUSDC - - UNIUSDC - - XRPUSDC - - Additional symbols include all Deribit instrument names of current open contracts for BTC, ETH, and PAXG futures. -- obb.derivatives.futures.instruments - - Reference data for all current futures instruments. -- obb.derivatives.futures.info - - Key stats by symbol (multiple items allowed). +### Endpoints + +```python +from openbb import obb + +obb.deribit +# /deribit +# index +# delivery_prices +# historical +# price +# market +# block_rfq_trades +# book_summary +# order_book +# settlements +# ticker +# trade_volumes +# trades +# rates +# apr_history +# funding_chart +# funding_history +# funding_value <- utility endpoint, returns a single rate +# reference +# announcements +# combo_choices <- utility endpoint serving choices to Workspace widgets +# combos +# contract_size <- utility endpoint +# currencies +# currency_choices <- utility endpoint +# expirations +# index_choices <- utility endpoint +# instrument_choices <- utility endpoint +# instruments +# server_time <- utility endpoint +# status <- utility endpoint +# volatility +# index +# realized +# futures +# curve +# curve_choices <- utility endpoint +# historical +# info +# instruments +# perpetual_choices <- utility endpoint +# options +# chains +# optimizer +# payoff +# smile +# spreads +# stats +# straddle +# strangle +# surface +# term_structure +# underlying_choices <- utility endpoint +``` + +Every public data method the exchange documents is reachable. The methods that exist only to open a +websocket session โ€” `auth`, `hello`, `subscribe`, `set_heartbeat`, `test` โ€” are not exposed, because +they carry no data. + +### Datasets + +- **Options chains** โ€” the full chain of an underlying, with all five greeks, mark/bid/ask implied + volatility, and open interest. Read over a websocket subscription, which is the only way the + exchange publishes greeks. BTC and ETH contracts are quoted in the underlying, so their premiums + are carried to USD at the index level each quote was published against. +- **Order books** โ€” the resting depth of any instrument, one row per level, by name or by numeric + instrument identifier. +- **Quotes** โ€” the live ticker of any instrument, and the whole-currency book summary carrying the + top of book and trailing session statistics of every instrument at once. +- **Trades** โ€” the prints that crossed, by instrument or across a currency, optionally over a span. + Option prints carry the volatility they implied. +- **Block RFQ** โ€” the block requests that traded, one row per leg, with the index levels each + structure was marked against. +- **Futures** โ€” the term structure of any underlying with a dated curve, priced off each contract's + mark, optionally against how the curve stood some hours ago. Candles are available for any listed + instrument, not only futures. +- **Funding and yield** โ€” the hourly funding a perpetual has paid, the funding accrued over the + current window, the single rate for a span, and the daily yield of each yield-bearing currency. +- **Volatility** โ€” the DVOL volatility index as candles, and the volatility an asset has actually + realized. Both accept several at once, and both offer only what the reading means something for: + DVOL is computed for BTC and ETH alone, and a dollar-pegged token has no volatility against the + dollar to realize, so USDC and USDT are not offered on either. EURR is, because a euro token + measured against the dollar reads the euro rate โ€” around 5 percent against their 0.5. + + This is separate from a currency contracts are *settled* in. USDC margins a real book on Deribit, + so it stays on the instrument, book summary, settlement and trade views, and the dashboard binds + the two meanings to two different controls rather than one. + +### Options Are Listed By Underlying, Not By Settlement Currency + +Only BTC and ETH are both a settlement currency and an underlying, which is the only reason the +exchange's by-currency listings read sensibly for them. USDC settles options written on seven +different underlyings, so asking for `kind=option` by USDC returns 3,656 contracts across AVAX, +BTC, ETH, HYPE, SOL, TRON and XRP โ€” a settlement rail, not a chain. USDT and EURR settle no options +at all. + +So a listing of option contracts scoped to a currency that only settles them is refused, and says +to ask by the underlying instead. Every one of those underlyings is offered by name, and naming an +instrument directly still works. The same views keep USDC for what it genuinely carries โ€” 174 +futures, 47 spot pairs โ€” and settlements, which are a settlement-currency fact, keep every +currency the exchange collateralises in. +- **Indices** โ€” the current level of any of the 344 published indices, its history over a span, and + the level each index has delivered at. +- **Settlements** โ€” the settlements, deliveries, and bankruptcies the exchange has processed. +- **Reference** โ€” the full contract specification of every listed instrument, every currency the + exchange lists, the expirations listed on each currency, every multi-leg combo, and the exchange's + own announcements. + +## Options Analysis + +The chain is also the input to an analysis suite built on it. Every figure below is computed from +the quotes the exchange publishes โ€” there is no second data source, and no assumption about a rate +or a dividend the exchange does not quote. + +- **Strategy optimizer** โ€” builds every Deribit combo structure the chosen expiration can form, + bought and sold: call and put spreads (CS, PS), straddles and strangles (STRD, STRG), risk + reversals and synthetics (RR, REV), 1x2 ratio spreads (CSR12, PSR12), butterflies and iron + butterflies (CBUT, PBUT, IBUT), condors and iron condors (CCOND, PCOND, ICOND), calendars and + diagonals against the expiration about a month later (CCAL, PCAL, CDIAG, PDIAG), and single + calls and puts. A position Deribit lists as a combo book is priced from that book's bid or offer, + and every listed combo on the expiration is ranked even outside the strike window. Each position + is sized so its worst loss across a threefold move either way is the budget, which is what lets + credit structures rank alongside debit ones. The best position of each structure is returned, + ranked by what it returns if the underlying reaches a price by a date. +- **Payoff** โ€” the profit and loss of one ranked strategy, drawn only over the prices where the + position actually responds. A payoff is a straight line everywhere outside its strikes, so the + range is taken from the position rather than from a fixed span either side of spot, and the + target and breakevens are added when they sit within reach of it. +- **Straddle, strangle, vertical spreads** โ€” the standard structures priced at every listed + expiration, each with its cost, its cost as a share of spot, its breakevens, and its bounds. +- **Volatility smile** โ€” implied volatility or skew against strike for calls and puts, up to five + expirations, optionally only out of the money. Drawn the same way as the Cboe smile. +- **Volatility term structure** โ€” implied volatility or price at the nearest out-of-the-money + strike, a chosen strike, or a moneyness, across every expiration. Drawn the same way as Cboe's. +- **Volatility surface** โ€” implied volatility, a greek, or delta and gamma exposure, raised over + days to expiration and strike, for either side of the chain. Drawn the same way as Cboe's. +- **Statistics** โ€” call and put open interest or volume by expiration, or by strike for one + expiration, as values, shares of the total, or put/call ratios. Drawn the same way as Cboe's. + +### Priced and Sized in the Quote Currency + +Budgets and profits are expressed in what the underlying is quoted in โ€” USD for the coin-margined +roots, USDC for the linear ones โ€” so a budget means the same thing whichever contract it buys, and +the numbers are readable without converting anything. + +BTC and ETH options are still settled in the coin, and that is not hidden. The premium is coin the +account no longer holds, so what it cost depends on what the coin is worth at the price being +valued: at the price the position was opened at the loss is exactly the premium, above it more, +below it less. This is why an inverse position's loss is not the flat line a linear payoff has, and +the chart says which contract settles in what. + +The settlement style, contract size and tick size are read from the exchange's own instrument +specification rather than assumed. The tick matters for sizing: far out of the money the published +mark can run below the smallest price the instrument trades in โ€” a TRON contract marked at 3.1e-07 +against a tick of 5e-05 โ€” and sizing a budget against a price nobody can pay invents a position +hundreds of times larger than the money would really buy. A contract that is priced at all is +entered at no less than one tick; a contract with no price is left out. + +### Greeks Are Modelled, Not Subscribed + +The `options.chains` table reads the greeks the exchange itself publishes, which means subscribing +to every contract over a websocket โ€” about twelve seconds for BTC. The analysis views do not need +the exchange's greeks, only its quotes and volatilities, and those come from three REST calls in +well under a second. They read that path instead and model the greeks from the quoted volatility. + +Measured against the greeks Deribit publishes, across all 972 BTC contracts: delta agrees to a +median of 0.0001, gamma to 0.000002, vega to 0.03. Theta follows the exchange's own two +conventions rather than the bare derivative โ€” scaled down inside the last day, where the +derivative diverges but a contract cannot decay faster than it expires, and floored at the +contract's own value, because nothing decays by more than it is worth. Both rules bind on real +contracts; together they bring the worst disagreement across the chain from 193 per day to 0.11. + +The whole nine-widget Analysis tab loads in under three seconds cold and about one warm, against +fourteen seconds before. The last read of a chain is held briefly and shared, so nine views of one +underlying read it once. + +### Choices Are Read Live + +The exchange lists 52 currencies, 344 indices, and roughly 5,800 instruments, and the set changes +week to week. Nothing in this extension freezes those into a literal: the underlyings with listed +options, the underlyings with a futures curve, the perpetuals, the indices, and the combo +identifiers are all read from the exchange when asked for. The utility endpoints marked above serve +them to Workspace widgets as `[{label, value}]`. + +## Example + +```python +from openbb import obb + +# The full BTC option chain, with greeks. +chains = obb.deribit.options.chains(symbol="BTC", provider="deribit") + +# The BTC futures term structure, against where it stood a day ago. +curve = obb.deribit.futures.curve(symbol="BTC", hours_ago=24, provider="deribit") + +# The resting depth of the BTC perpetual. +book = obb.deribit.market.order_book( + symbol="BTC-PERPETUAL", depth=20, provider="deribit" +) + +# The top of book of every BTC instrument at once. +summary = obb.deribit.market.book_summary( + currency="BTC", kind="option", provider="deribit" +) + +# The hourly funding the BTC perpetual has paid. +funding = obb.deribit.rates.funding_history(symbol="BTC-PERPETUAL", provider="deribit") + +# The DVOL volatility index, as daily candles. +dvol = obb.deribit.volatility.index(currency="BTC", interval="1d", provider="deribit") + +# Every instrument the exchange lists, with its full specification. +instruments = obb.deribit.reference.instruments(currency="any", provider="deribit") + +# The best position of every combo structure for a view of 100,000 BTC by year end, +# each risking 5,000 USD. +ranked = obb.deribit.options.optimizer( + symbol="BTC", target_price=100000, target_date="2026-12-25", budget=5000 +) + +# The payoff of the best of them. +diagram = obb.deribit.options.payoff( + symbol="BTC", target_price=100000, target_date="2026-12-25" +) + +# At-the-money volatility against time to expiration. +term = obb.deribit.options.term_structure(symbol="BTC") +``` + +When `openbb-derivatives` is installed, the five shared models are reached through the standard +namespace instead โ€” `obb.derivatives.options.chains(provider="deribit")`, +`obb.derivatives.futures.curve(provider="deribit")`, and so on. + +## Notes + +Responses are cached on disk with a lifetime chosen per endpoint: a day for the currency and index +directories, an hour for the instrument listings, minutes for the historical series, and not at all +for quotes, books, and the server clock. The cache file is swept once per process and capped at +128 MiB. + +Perpetuals can be named either in full โ€” `SOL_USDC-PERPETUAL` โ€” or by their shortened root, +`SOLUSDC`. + diff --git a/openbb_platform/providers/deribit/integration/__init__.py b/openbb_platform/providers/deribit/integration/__init__.py new file mode 100644 index 00000000000..245885d29b8 --- /dev/null +++ b/openbb_platform/providers/deribit/integration/__init__.py @@ -0,0 +1 @@ +"""Live Deribit integration tests.""" diff --git a/openbb_platform/providers/deribit/integration/cases.py b/openbb_platform/providers/deribit/integration/cases.py new file mode 100644 index 00000000000..7bf2b43429e --- /dev/null +++ b/openbb_platform/providers/deribit/integration/cases.py @@ -0,0 +1,107 @@ +"""The Deribit commands and parameters both consumption surfaces are exercised with.""" + +SHARED = { + ("futures", "curve"), + ("futures", "historical"), + ("futures", "info"), + ("futures", "instruments"), + ("options", "chains"), +} + +MODELS = [ + ("futures", "curve", {"symbol": "BTC"}), + ( + "futures", + "historical", + { + "symbol": "BTC-PERPETUAL", + "start_date": "2026-01-01", + "end_date": "2026-02-01", + }, + ), + ("futures", "info", {"symbol": "BTC-PERPETUAL"}), + ("futures", "instruments", {}), + ("index", "delivery_prices", {"index_name": "btc_usd", "limit": 5}), + ("index", "historical", {"index_name": "btc_usd", "span": "1h"}), + ("index", "price", {"index_name": "btc_usd,eth_usd"}), + ("market", "block_rfq_trades", {"limit": 10}), + ("market", "book_summary", {"currency": "BTC", "kind": "future"}), + ("market", "order_book", {"symbol": "BTC-PERPETUAL", "depth": 5}), + ("market", "settlements", {"currency": "BTC", "limit": 5}), + ("market", "ticker", {"symbol": "BTC-PERPETUAL"}), + ("market", "trade_volumes", {}), + ("market", "trades", {"symbol": "BTC-PERPETUAL", "limit": 5}), + ("options", "chains", {"symbol": "BTC"}), + ("rates", "apr_history", {"currency": "usde", "limit": 5}), + ("rates", "funding_chart", {"symbol": "BTC-PERPETUAL"}), + ("rates", "funding_history", {"symbol": "BTC-PERPETUAL"}), + ("reference", "announcements", {"limit": 3}), + ("reference", "combos", {"currency": "BTC"}), + ("reference", "currencies", {}), + ("reference", "expirations", {"currency": "BTC", "kind": "any"}), + ("reference", "instruments", {"currency": "BTC", "kind": "future"}), + ("volatility", "index", {"currency": "BTC", "interval": "1d"}), + ("volatility", "realized", {"currency": "BTC"}), +] + +FEEDS = [ + ("futures", "curve_choices", {}), + ("futures", "perpetual_choices", {}), + ("options", "underlying_choices", {}), + ("options", "expiration_choices", {"symbol": "BTC"}), + ("options", "strike_choices", {"symbol": "BTC"}), + ("options", "strategy_choices", {"symbol": "BTC"}), + ("rates", "funding_value", {"symbol": "BTC-PERPETUAL"}), + ("reference", "combo_choices", {"currency": "BTC"}), + ("reference", "contract_size", {"symbol": "BTC-PERPETUAL"}), + ("reference", "currency_choices", {}), + ("reference", "index_choices", {}), + ("reference", "index_choices", {"supported": True}), + ("reference", "instrument_choices", {"kind": "future"}), + ("reference", "server_time", {}), + ("reference", "status", {}), +] + +TABLES = [ + ("optimizer", {"symbol": "BTC", "target_price": 100000, "limit": 5}), + ("straddle", {"symbol": "BTC"}), + ("strangle", {"symbol": "BTC", "moneyness": 5}), + ("spreads", {"symbol": "BTC", "moneyness": 5}), + ("straddle", {"symbol": "ETH"}), +] + +CHARTS = [ + ("smile", {"symbol": "BTC"}), + ("smile", {"symbol": "BTC", "otm": True, "skew": True}), + ("smile", {"symbol": "ETH"}), + ("stats", {"symbol": "BTC"}), + ("stats", {"symbol": "BTC", "metric": "volume", "unit": "pcr"}), + ("stats", {"symbol": "BTC", "by": "strike", "unit": "percent"}), + ("stats", {"symbol": "SOL_USDC"}), + ("surface", {"symbol": "BTC"}), + ("surface", {"symbol": "BTC", "metric": "gex", "option_type": "calls"}), + ("surface", {"symbol": "BTC", "dte_min": 7, "dte_max": 90, "oi": True}), + ("term_structure", {"symbol": "BTC"}), + ("term_structure", {"symbol": "BTC", "metric": "price", "moneyness": 5}), + ("payoff", {"symbol": "BTC"}), + ("payoff", {"symbol": "BTC", "target_price": 100000, "budget": 1000}), +] + + +def namespace(router: str, name: str) -> str: + """Return where a command is served: its own namespace or the shared one.""" + from openbb_deribit import DERIVATIVES_INSTALLED + + return ( + "derivatives" + if DERIVATIVES_INSTALLED and (router, name) in SHARED + else "deribit" + ) + + +def case_id(case: tuple) -> str: + """Name a parametrized case by its command and parameters.""" + *path, params = case + written = ",".join(f"{key}={value}" for key, value in params.items()) + + return ".".join(path) + (f"[{written}]" if written else "") diff --git a/openbb_platform/providers/deribit/integration/conftest.py b/openbb_platform/providers/deribit/integration/conftest.py new file mode 100644 index 00000000000..b3d4ba729fa --- /dev/null +++ b/openbb_platform/providers/deribit/integration/conftest.py @@ -0,0 +1,72 @@ +"""Serve the OpenBB REST API for the Deribit integration tests.""" + +import pytest + + +def _free_port() -> int: + """Return a local port nothing is listening on.""" + import socket + + with socket.socket() as probe: + probe.bind(("127.0.0.1", 0)) + + return probe.getsockname()[1] + + +@pytest.fixture(scope="session") +def api(): + """Run the REST API on a free local port. + + Yields + ------ + dict + The base URL of the API and the authorization headers to call it with. + + Raises + ------ + RuntimeError + If the server does not start within a minute. + """ + import base64 + import threading + import time + + import uvicorn + from openbb_core.api.rest_api import app + from openbb_core.env import Env + + from openbb_deribit.utils.constants import API_PREFIX + + port = _free_port() + server = uvicorn.Server( + uvicorn.Config(app, host="127.0.0.1", port=port, log_level="warning") + ) + thread = threading.Thread(target=server.run, daemon=True) + thread.start() + deadline = time.monotonic() + 60 + + while not server.started: + if time.monotonic() > deadline: + raise RuntimeError("The OpenBB REST API did not start within 60 seconds.") + + time.sleep(0.25) + + credentials = base64.b64encode( + f"{Env().API_USERNAME}:{Env().API_PASSWORD}".encode("ascii") + ).decode("ascii") + + yield { + "base": f"http://127.0.0.1:{port}{API_PREFIX}", + "headers": {"Authorization": f"Basic {credentials}"}, + } + + server.should_exit = True + thread.join(timeout=15) + + +@pytest.fixture(scope="session") +def obb(): + """Return the Python interface, built with this extension installed.""" + from openbb import obb as interface + + return interface diff --git a/openbb_platform/providers/deribit/integration/test_deribit_api.py b/openbb_platform/providers/deribit/integration/test_deribit_api.py new file mode 100644 index 00000000000..facfed9a96b --- /dev/null +++ b/openbb_platform/providers/deribit/integration/test_deribit_api.py @@ -0,0 +1,143 @@ +"""Deribit through the REST API, against the live exchange.""" + +import pytest + +from .cases import CHARTS, FEEDS, MODELS, TABLES, case_id, namespace + +pytestmark = pytest.mark.integration + + +def _get(api: dict, path: str, params: dict): + """Call one route and return its decoded body. + + Raises + ------ + AssertionError + If the route does not answer 200. + """ + import requests + + written = { + key: str(value).lower() if isinstance(value, bool) else value + for key, value in params.items() + } + response = requests.get( + f"{api['base']}{path}", params=written, headers=api["headers"], timeout=120 + ) + + assert response.status_code == 200, f"{path} {params}: {response.text}" + + return response.json() + + +class TestModels: + """Every model-backed route returns rows.""" + + @pytest.mark.parametrize("case", MODELS, ids=case_id) + def test_returns_rows(self, api, case): + """The route answers with an OBBject body holding at least one row.""" + router, name, params = case + body = _get( + api, + f"/{namespace(router, name)}/{router}/{name}", + {"provider": "deribit", **params}, + ) + + assert body["provider"] == "deribit" + assert body["results"] + + def test_an_unoffered_depth_is_refused(self, api): + """A book depth Deribit does not return is refused before the exchange.""" + import requests + + response = requests.get( + f"{api['base']}/deribit/market/order_book", + params={"provider": "deribit", "symbol": "BTC-PERPETUAL", "depth": "7"}, + headers=api["headers"], + timeout=60, + ) + + assert response.status_code in (400, 422) + assert "not 7" in response.text + + +class TestFeeds: + """Every choice feed and scalar route answers.""" + + @pytest.mark.parametrize("case", FEEDS, ids=case_id) + def test_answers(self, api, case): + """The route returns something to offer.""" + router, name, params = case + body = _get(api, f"/deribit/{router}/{name}", params) + + assert body is not None + assert body not in ([], {}) + + +class TestAnalysisTables: + """The options analysis routes return ranked or priced rows.""" + + @pytest.mark.parametrize( + "case", TABLES, ids=lambda case: case_id(("options", *case)) + ) + def test_returns_rows(self, api, case): + """The route answers with rows.""" + name, params = case + body = _get(api, f"/deribit/options/{name}", params) + + assert body["results"] + + def test_the_optimizer_codes_every_row(self, api): + """Each ranked position names the Deribit combo it is.""" + body = _get( + api, "/deribit/options/optimizer", {"symbol": "BTC", "target_price": 100000} + ) + + assert all(row["code"] for row in body["results"]) + + +class TestCharts: + """Every chart route draws, and hands back its rows on request.""" + + @pytest.mark.parametrize( + "case", CHARTS, ids=lambda case: case_id(("options", *case)) + ) + def test_draws_a_figure(self, api, case): + """The route answers with a Plotly figure and its display config.""" + name, params = case + figure = _get(api, f"/deribit/options/{name}", params) + + assert figure["data"] + assert figure["layout"]["title"]["text"] + assert figure["config"]["responsive"] is True + + @pytest.mark.parametrize( + "name", sorted({name for name, _params in CHARTS}), ids=str + ) + def test_raw_returns_rows(self, api, name): + """With raw set, the route answers with the rows behind the chart.""" + rows = _get(api, f"/deribit/options/{name}", {"symbol": "BTC", "raw": True}) + + assert isinstance(rows, list) + assert rows + + def test_the_payoff_draws_a_ranked_position(self, api): + """The legs the optimizer prints draw back as that position.""" + view = {"symbol": "BTC", "target_price": 100000} + row = _get(api, "/deribit/options/optimizer", view)["results"][-1] + figure = _get(api, "/deribit/options/payoff", {**view, "legs": row["legs"]}) + + assert row["strategy"] in figure["layout"]["title"]["text"] + + def test_an_unlisted_underlying_is_a_404(self, api): + """An underlying Deribit lists no options on answers 404.""" + import requests + + response = requests.get( + f"{api['base']}/deribit/options/smile", + params={"symbol": "NOPE"}, + headers=api["headers"], + timeout=60, + ) + + assert response.status_code == 404 diff --git a/openbb_platform/providers/deribit/integration/test_deribit_python.py b/openbb_platform/providers/deribit/integration/test_deribit_python.py new file mode 100644 index 00000000000..ca68d8ea711 --- /dev/null +++ b/openbb_platform/providers/deribit/integration/test_deribit_python.py @@ -0,0 +1,148 @@ +"""Deribit through the Python interface, against the live exchange.""" + +import pytest +from openbb_core.app.model.obbject import OBBject + +from .cases import CHARTS, FEEDS, MODELS, TABLES, case_id, namespace + +pytestmark = pytest.mark.integration + + +def _command(obb, router: str, name: str): + """Return the Python command for a route, wherever it is served.""" + return getattr(getattr(getattr(obb, namespace(router, name)), router), name) + + +def _field(row, name: str): + """Read one field of a result row, whether a model or a mapping.""" + return row[name] if isinstance(row, dict) else getattr(row, name) + + +class TestModels: + """Every model-backed command returns rows.""" + + @pytest.mark.parametrize("case", MODELS, ids=case_id) + def test_returns_rows(self, obb, case): + """The command answers with an OBBject holding at least one row.""" + router, name, params = case + result = _command(obb, router, name)(provider="deribit", **params) + + assert isinstance(result, OBBject) + assert result.provider == "deribit" + assert result.results + + def test_a_result_converts_to_a_frame(self, obb): + """The rows convert to a DataFrame for Python users.""" + frame = _command(obb, "market", "ticker")( + symbol="BTC-PERPETUAL", provider="deribit" + ).to_df() + + assert not frame.empty + + def test_an_unoffered_depth_is_refused(self, obb): + """A book depth Deribit does not return is refused before the exchange.""" + from openbb_core.app.model.abstract.error import OpenBBError + + with pytest.raises(OpenBBError, match="not 7"): + obb.deribit.market.order_book( + symbol="BTC-PERPETUAL", depth=7, provider="deribit" + ) + + +class TestFeeds: + """Every choice feed and scalar answers.""" + + @pytest.mark.parametrize("case", FEEDS, ids=case_id) + def test_answers(self, obb, case): + """The feed returns something to offer.""" + router, name, params = case + answer = _command(obb, router, name)(**params) + + assert answer is not None + assert answer not in ([], {}) + + def test_status_reports_locked_currencies(self, obb): + """The platform status names what is locked.""" + assert "locked" in obb.deribit.reference.status() + + +class TestAnalysisTables: + """The options analysis tables return ranked or priced rows.""" + + @pytest.mark.parametrize( + "case", TABLES, ids=lambda case: case_id(("options", *case)) + ) + def test_returns_rows(self, obb, case): + """The table answers with rows.""" + name, params = case + result = getattr(obb.deribit.options, name)(**params) + + assert isinstance(result, OBBject) + assert result.results + + def test_the_optimizer_codes_every_row(self, obb): + """Each ranked position names the Deribit combo it is.""" + result = obb.deribit.options.optimizer(symbol="BTC", target_price=100000) + codes = [_field(row, "code") for row in result.results] + + assert all(codes) + assert len(set(codes)) > 1 + + +class TestCharts: + """Every chart draws, and every chart hands back its rows on request.""" + + @pytest.mark.parametrize( + "case", CHARTS, ids=lambda case: case_id(("options", *case)) + ) + def test_draws_a_figure(self, obb, case): + """The chart answers with a Plotly figure.""" + name, params = case + figure = getattr(obb.deribit.options, name)(**params) + + assert isinstance(figure, dict) + assert figure["data"] + assert figure["layout"]["title"]["text"] + + @pytest.mark.parametrize( + "name", sorted({name for name, _params in CHARTS}), ids=str + ) + def test_raw_returns_rows(self, obb, name): + """With raw set, the chart answers with the rows behind it.""" + rows = getattr(obb.deribit.options, name)(symbol="BTC", raw=True) + + assert isinstance(rows, list) + assert rows + + def test_the_smile_draws_chosen_expirations(self, obb): + """Expirations picked from the feed are drawn as calls and puts each.""" + offered = obb.deribit.options.expiration_choices(symbol="BTC") + chosen = [item["value"] for item in offered[2:4]] + figure = obb.deribit.options.smile(symbol="BTC", expirations=",".join(chosen)) + + assert len(figure["data"]) == 2 * len(chosen) + + def test_stats_for_one_expiration(self, obb): + """An expiration picked from the feed is drawn by strike.""" + offered = obb.deribit.options.expiration_choices(symbol="BTC") + figure = obb.deribit.options.stats(symbol="BTC", date=offered[1]["value"]) + + assert "By Strike" in figure["layout"]["title"]["text"] + + def test_term_structure_at_a_chosen_strike(self, obb): + """A strike picked from the feed titles the term structure.""" + offered = obb.deribit.options.strike_choices(symbol="BTC") + strike = offered[len(offered) // 2]["value"] + figure = obb.deribit.options.term_structure(symbol="BTC", strike=strike) + + assert f"${strike}" in figure["layout"]["title"]["text"] + + def test_the_payoff_draws_a_ranked_position(self, obb): + """The legs the optimizer prints draw back as that position.""" + ranked = obb.deribit.options.optimizer(symbol="BTC", target_price=100000) + row = ranked.results[-1] + figure = obb.deribit.options.payoff( + symbol="BTC", target_price=100000, legs=_field(row, "legs") + ) + + assert _field(row, "strategy") in figure["layout"]["title"]["text"] diff --git a/openbb_platform/providers/deribit/openbb_deribit/__init__.py b/openbb_platform/providers/deribit/openbb_deribit/__init__.py index ac4d98966bb..fba896ee6c1 100644 --- a/openbb_platform/providers/deribit/openbb_deribit/__init__.py +++ b/openbb_platform/providers/deribit/openbb_deribit/__init__.py @@ -1,24 +1,94 @@ """OpenBB Deribit Provider Module.""" +from importlib.util import find_spec + from openbb_core.provider.abstract.provider import Provider + +from openbb_deribit.models.announcements import DeribitAnnouncementsFetcher +from openbb_deribit.models.apr_history import DeribitAprHistoryFetcher +from openbb_deribit.models.block_rfq_trades import DeribitBlockRfqTradesFetcher +from openbb_deribit.models.book_summary import DeribitBookSummaryFetcher +from openbb_deribit.models.combos import DeribitCombosFetcher +from openbb_deribit.models.currencies import DeribitCurrenciesFetcher +from openbb_deribit.models.delivery_prices import DeribitDeliveryPricesFetcher +from openbb_deribit.models.expirations import DeribitExpirationsFetcher +from openbb_deribit.models.funding_chart import DeribitFundingChartFetcher +from openbb_deribit.models.funding_rate_history import ( + DeribitFundingRateHistoryFetcher, +) from openbb_deribit.models.futures_curve import DeribitFuturesCurveFetcher from openbb_deribit.models.futures_historical import DeribitFuturesHistoricalFetcher from openbb_deribit.models.futures_info import DeribitFuturesInfoFetcher from openbb_deribit.models.futures_instruments import DeribitFuturesInstrumentsFetcher +from openbb_deribit.models.historical_volatility import ( + DeribitHistoricalVolatilityFetcher, +) +from openbb_deribit.models.index_historical import DeribitIndexHistoricalFetcher +from openbb_deribit.models.index_price import DeribitIndexPriceFetcher +from openbb_deribit.models.instruments import DeribitInstrumentsFetcher from openbb_deribit.models.options_chains import DeribitOptionsChainsFetcher +from openbb_deribit.models.order_book import DeribitOrderBookFetcher +from openbb_deribit.models.settlements import DeribitSettlementsFetcher +from openbb_deribit.models.ticker import DeribitTickerFetcher +from openbb_deribit.models.trade_volumes import DeribitTradeVolumesFetcher +from openbb_deribit.models.trades import DeribitTradesFetcher +from openbb_deribit.models.volatility_index import DeribitVolatilityIndexFetcher + +DERIVATIVES_INSTALLED = find_spec("openbb_derivatives") is not None +CHARTING_INSTALLED = find_spec("openbb_charting") is not None + + +def _key(standard: str, alias: str, installed: bool) -> str: + """Return the standard key when the extension owning it is installed, else the alias.""" + return standard if installed else alias + + +_fetcher_dict: dict = { + "DeribitAnnouncements": DeribitAnnouncementsFetcher, + "DeribitAprHistory": DeribitAprHistoryFetcher, + "DeribitBlockRfqTrades": DeribitBlockRfqTradesFetcher, + "DeribitBookSummary": DeribitBookSummaryFetcher, + "DeribitCombos": DeribitCombosFetcher, + "DeribitCurrencies": DeribitCurrenciesFetcher, + "DeribitDeliveryPrices": DeribitDeliveryPricesFetcher, + "DeribitExpirations": DeribitExpirationsFetcher, + "DeribitFundingChart": DeribitFundingChartFetcher, + "DeribitFundingRateHistory": DeribitFundingRateHistoryFetcher, + "DeribitHistoricalVolatility": DeribitHistoricalVolatilityFetcher, + "DeribitIndexHistorical": DeribitIndexHistoricalFetcher, + "DeribitIndexPrice": DeribitIndexPriceFetcher, + "DeribitInstruments": DeribitInstrumentsFetcher, + "DeribitOrderBook": DeribitOrderBookFetcher, + "DeribitSettlements": DeribitSettlementsFetcher, + "DeribitTicker": DeribitTickerFetcher, + "DeribitTradeVolumes": DeribitTradeVolumesFetcher, + "DeribitTrades": DeribitTradesFetcher, + "DeribitVolatilityIndex": DeribitVolatilityIndexFetcher, + _key("FuturesCurve", "DeribitFuturesCurve", DERIVATIVES_INSTALLED): ( + DeribitFuturesCurveFetcher + ), + _key("FuturesHistorical", "DeribitFuturesHistorical", DERIVATIVES_INSTALLED): ( + DeribitFuturesHistoricalFetcher + ), + _key("FuturesInfo", "DeribitFuturesInfo", DERIVATIVES_INSTALLED): ( + DeribitFuturesInfoFetcher + ), + _key("FuturesInstruments", "DeribitFuturesInstruments", DERIVATIVES_INSTALLED): ( + DeribitFuturesInstrumentsFetcher + ), + _key("OptionsChains", "DeribitOptionsChains", DERIVATIVES_INSTALLED): ( + DeribitOptionsChainsFetcher + ), +} deribit_provider = Provider( name="deribit", website="https://deribit.com/", - description="""Unofficial Python client for public data published by Deribit.""", + description="""Deribit is a crypto derivatives exchange listing futures, perpetuals, +options, and spot pairs. This extension covers its public market data API, which +needs no credentials and carries no trading.""", credentials=None, - fetcher_dict={ - "FuturesCurve": DeribitFuturesCurveFetcher, - "FuturesHistorical": DeribitFuturesHistoricalFetcher, - "FuturesInfo": DeribitFuturesInfoFetcher, - "FuturesInstruments": DeribitFuturesInstrumentsFetcher, - "OptionsChains": DeribitOptionsChainsFetcher, - }, + fetcher_dict=_fetcher_dict, repr_name="Deribit Public Data", - instructions="This provider does not require any credentials and is not meant for trading.", + instructions="This provider needs no credentials and is not meant for trading.", ) diff --git a/openbb_platform/providers/deribit/openbb_deribit/assets/apps.json b/openbb_platform/providers/deribit/openbb_deribit/assets/apps.json new file mode 100644 index 00000000000..27e1a368f50 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/assets/apps.json @@ -0,0 +1,586 @@ +[ + { + "name": "Deribit", + "img": "https://www.deribit.com//share-image.png", + "img_dark": "", + "img_light": "", + "description": "Order books, options chains, funding, and volatility indexes from Deribit's public market data API.", + "allowCustomization": true, + "groups": [ + { + "name": "Instrument", + "type": "endpointParam", + "paramName": "symbol", + "defaultValue": "BTC-PERPETUAL", + "widgetIds": [ + "deribit_market_ticker_deribit_obb", + "deribit_market_order_book_deribit_obb", + "deribit_market_trades_deribit_obb", + "deribit_market_book_summary_deribit_obb", + "deribit_market_settlements_deribit_obb", + "derivatives_futures_historical_deribit_obb", + "derivatives_futures_info_deribit_obb" + ] + }, + { + "name": "Index", + "type": "endpointParam", + "paramName": "index_name", + "defaultValue": "btc_usd", + "widgetIds": [ + "deribit_index_price_deribit_obb", + "deribit_index_historical_deribit_obb", + "deribit_index_delivery_prices_deribit_obb" + ] + }, + { + "name": "Settlement Currency", + "type": "param", + "paramName": "currency", + "defaultValue": "BTC", + "widgetIds": [ + "deribit_reference_expirations_deribit_obb", + "deribit_reference_combos_deribit_obb" + ] + }, + { + "name": "Volatility Asset", + "type": "param", + "paramName": "currency", + "defaultValue": "BTC", + "widgetIds": [ + "deribit_volatility_realized_deribit_obb", + "deribit_volatility_index_deribit_obb" + ] + }, + { + "name": "Underlying", + "type": "endpointParam", + "paramName": "symbol", + "defaultValue": "BTC", + "widgetIds": [ + "derivatives_options_chains_deribit_obb", + "deribit_options_optimizer_custom_obb", + "deribit_options_payoff_custom_obb", + "deribit_options_smile_custom_obb", + "deribit_options_term_structure_custom_obb", + "deribit_options_surface_custom_obb", + "deribit_options_stats_custom_obb", + "deribit_options_straddle_custom_obb", + "deribit_options_strangle_custom_obb", + "deribit_options_spreads_custom_obb" + ] + }, + { + "name": "Strategy", + "type": "endpointParam", + "paramName": "legs", + "widgetIds": [ + "deribit_options_optimizer_custom_obb", + "deribit_options_payoff_custom_obb" + ] + } + ], + "tabs": { + "markets": { + "id": "markets", + "name": "Markets", + "layout": [ + { + "i": "deribit_market_ticker_deribit_obb", + "x": 0, + "y": 0, + "w": 40, + "h": 8, + "state": { + "params": { + "symbol": "BTC-PERPETUAL" + } + }, + "groups": [ + "Instrument" + ] + }, + { + "i": "deribit_market_order_book_deribit_obb", + "x": 0, + "y": 8, + "w": 20, + "h": 16, + "state": { + "params": { + "symbol": "BTC-PERPETUAL", + "depth": "20" + } + }, + "groups": [ + "Instrument" + ] + }, + { + "i": "deribit_market_trades_deribit_obb", + "x": 20, + "y": 8, + "w": 20, + "h": 16, + "state": { + "params": { + "symbol": "BTC-PERPETUAL", + "limit": 100 + } + }, + "groups": [ + "Instrument" + ] + }, + { + "i": "deribit_market_book_summary_deribit_obb", + "x": 0, + "y": 24, + "w": 40, + "h": 15, + "state": { + "params": { + "currency": "BTC", + "kind": "future" + } + }, + "groups": [ + "Instrument" + ] + }, + { + "i": "deribit_market_trade_volumes_deribit_obb", + "x": 0, + "y": 39, + "w": 40, + "h": 12, + "state": { + "params": { + "extended": true + } + } + } + ] + }, + "futures": { + "id": "futures", + "name": "Futures", + "layout": [ + { + "i": "derivatives_futures_curve_deribit_obb", + "x": 0, + "y": 0, + "w": 40, + "h": 15, + "state": { + "params": { + "symbol": "BTC" + } + } + }, + { + "i": "derivatives_futures_historical_deribit_obb", + "x": 0, + "y": 15, + "w": 40, + "h": 15, + "state": { + "params": { + "symbol": "BTC-PERPETUAL", + "interval": "1d" + } + }, + "groups": [ + "Instrument" + ] + }, + { + "i": "derivatives_futures_info_deribit_obb", + "x": 0, + "y": 30, + "w": 40, + "h": 8, + "state": { + "params": { + "symbol": "BTC-PERPETUAL" + } + }, + "groups": [ + "Instrument" + ] + }, + { + "i": "deribit_rates_funding_history_deribit_obb", + "x": 0, + "y": 38, + "w": 40, + "h": 14, + "state": { + "params": { + "symbol": "BTC-PERPETUAL" + } + } + }, + { + "i": "derivatives_futures_instruments_deribit_obb", + "x": 0, + "y": 52, + "w": 40, + "h": 15 + } + ] + }, + "options": { + "id": "options", + "name": "Options", + "layout": [ + { + "i": "derivatives_options_chains_deribit_obb", + "x": 0, + "y": 0, + "w": 40, + "h": 20, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + }, + { + "i": "deribit_reference_expirations_deribit_obb", + "x": 0, + "y": 20, + "w": 20, + "h": 14, + "state": { + "params": { + "currency": "BTC", + "kind": "any" + } + }, + "groups": [ + "Settlement Currency" + ] + }, + { + "i": "deribit_reference_combos_deribit_obb", + "x": 20, + "y": 20, + "w": 20, + "h": 14, + "state": { + "params": { + "currency": "BTC" + } + }, + "groups": [ + "Settlement Currency" + ] + }, + { + "i": "deribit_market_block_rfq_trades_deribit_obb", + "x": 0, + "y": 34, + "w": 40, + "h": 15, + "state": { + "params": { + "limit": 50 + } + } + } + ] + }, + "analysis": { + "id": "analysis", + "name": "Analysis", + "layout": [ + { + "i": "deribit_options_optimizer_custom_obb", + "x": 0, + "y": 0, + "w": 40, + "h": 18, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying", + "Strategy" + ] + }, + { + "i": "deribit_options_payoff_custom_obb", + "x": 0, + "y": 18, + "w": 40, + "h": 18, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying", + "Strategy" + ] + }, + { + "i": "deribit_options_smile_custom_obb", + "x": 0, + "y": 36, + "w": 40, + "h": 14, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + }, + { + "i": "deribit_options_term_structure_custom_obb", + "x": 0, + "y": 50, + "w": 40, + "h": 15, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + }, + { + "i": "deribit_options_surface_custom_obb", + "x": 0, + "y": 65, + "w": 40, + "h": 20, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + }, + { + "i": "deribit_options_stats_custom_obb", + "x": 0, + "y": 72, + "w": 40, + "h": 16, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + }, + { + "i": "deribit_options_straddle_custom_obb", + "x": 0, + "y": 88, + "w": 20, + "h": 16, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + }, + { + "i": "deribit_options_strangle_custom_obb", + "x": 20, + "y": 88, + "w": 20, + "h": 16, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + }, + { + "i": "deribit_options_spreads_custom_obb", + "x": 0, + "y": 104, + "w": 40, + "h": 16, + "state": { + "params": { + "symbol": "BTC" + } + }, + "groups": [ + "Underlying" + ] + } + ] + }, + "volatility": { + "id": "volatility", + "name": "Volatility", + "layout": [ + { + "i": "deribit_volatility_index_deribit_obb", + "x": 0, + "y": 0, + "w": 20, + "h": 15, + "state": { + "params": { + "currency": "BTC", + "interval": "1d" + } + }, + "groups": [ + "Volatility Asset" + ] + }, + { + "i": "deribit_volatility_realized_deribit_obb", + "x": 20, + "y": 0, + "w": 20, + "h": 15, + "state": { + "params": { + "currency": "BTC" + } + }, + "groups": [ + "Volatility Asset" + ] + } + ] + }, + "reference": { + "id": "reference", + "name": "Reference", + "layout": [ + { + "i": "deribit_index_price_deribit_obb", + "x": 0, + "y": 0, + "w": 20, + "h": 8, + "state": { + "params": { + "index_name": "btc_usd" + } + }, + "groups": [ + "Index" + ] + }, + { + "i": "deribit_index_historical_deribit_obb", + "x": 20, + "y": 0, + "w": 20, + "h": 8, + "state": { + "params": { + "index_name": "btc_usd", + "span": "1d" + } + }, + "groups": [ + "Index" + ] + }, + { + "i": "deribit_index_delivery_prices_deribit_obb", + "x": 0, + "y": 8, + "w": 40, + "h": 14, + "state": { + "params": { + "index_name": "btc_usd" + } + }, + "groups": [ + "Index" + ] + }, + { + "i": "deribit_market_settlements_deribit_obb", + "x": 0, + "y": 22, + "w": 40, + "h": 14, + "state": { + "params": { + "symbol": "BTC-PERPETUAL" + } + }, + "groups": [ + "Instrument" + ] + }, + { + "i": "deribit_rates_apr_history_deribit_obb", + "x": 0, + "y": 36, + "w": 40, + "h": 14, + "state": { + "params": { + "currency": "usde" + } + } + }, + { + "i": "deribit_reference_currencies_deribit_obb", + "x": 0, + "y": 50, + "w": 40, + "h": 12 + }, + { + "i": "deribit_reference_instruments_deribit_obb", + "x": 0, + "y": 62, + "w": 40, + "h": 15, + "state": { + "params": { + "currency": "any", + "kind": "future" + } + } + }, + { + "i": "deribit_reference_announcements_deribit_obb", + "x": 0, + "y": 77, + "w": 40, + "h": 12 + } + ] + } + } + } +] diff --git a/openbb_platform/providers/deribit/openbb_deribit/deribit_router.py b/openbb_platform/providers/deribit/openbb_deribit/deribit_router.py new file mode 100644 index 00000000000..c61f860bb19 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/deribit_router.py @@ -0,0 +1,83 @@ +"""Deribit Router.""" + +import json +from pathlib import Path + +from openbb_core.app.router import Router + +from openbb_deribit import DERIVATIVES_INSTALLED +from openbb_deribit.routers.futures import router as futures_router +from openbb_deribit.routers.index import router as index_router +from openbb_deribit.routers.market import router as market_router +from openbb_deribit.routers.options import router as options_router +from openbb_deribit.routers.rates import router as rates_router +from openbb_deribit.routers.reference import router as reference_router +from openbb_deribit.routers.volatility import router as volatility_router + +router = Router(prefix="", description="Deribit provider router.") + +for _subrouter in ( + reference_router, + market_router, + index_router, + rates_router, + volatility_router, + futures_router, + options_router, +): + router.include_router(_subrouter) + +_APPS_JSON = Path(__file__).parent / "assets" / "apps.json" + +_STANDALONE_WIDGET_IDS: dict[str, tuple[str, bool]] = { + "derivatives_futures_curve_deribit_obb": ( + "deribit_futures_curve_deribit_obb", + DERIVATIVES_INSTALLED, + ), + "derivatives_futures_historical_deribit_obb": ( + "deribit_futures_historical_deribit_obb", + DERIVATIVES_INSTALLED, + ), + "derivatives_futures_info_deribit_obb": ( + "deribit_futures_info_deribit_obb", + DERIVATIVES_INSTALLED, + ), + "derivatives_futures_instruments_deribit_obb": ( + "deribit_futures_instruments_deribit_obb", + DERIVATIVES_INSTALLED, + ), + "derivatives_options_chains_deribit_obb": ( + "deribit_options_chains_deribit_obb", + DERIVATIVES_INSTALLED, + ), +} + + +def _resolve_widget_id(widget_id: str) -> str: + """Return the widget ID that resolves against the active install.""" + standalone, installed = _STANDALONE_WIDGET_IDS.get(widget_id, (widget_id, True)) + + return widget_id if installed else standalone + + +@router.api_router.get("/apps.json", include_in_schema=False) +async def deribit_apps() -> list: + """Serve the bundled Deribit dashboard template (``assets/apps.json``). + + A parameter group names the widgets it binds, so its identifiers are + resolved against the active install the same way the layout's are. + """ + apps = json.loads(_APPS_JSON.read_text(encoding="utf-8")) + + for app in apps: + for tab in app.get("tabs", {}).values(): + for widget in tab.get("layout", []): + widget["i"] = _resolve_widget_id(widget["i"]) + + for group in app.get("groups", []): + group["widgetIds"] = [ + _resolve_widget_id(widget_id) + for widget_id in group.get("widgetIds", []) + ] + + return apps diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/announcements.py b/openbb_platform/providers/deribit/openbb_deribit/models/announcements.py new file mode 100644 index 00000000000..efd2443ed50 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/announcements.py @@ -0,0 +1,145 @@ +"""Deribit Announcements Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + + +class DeribitAnnouncementsQueryParams(QueryParams): + """Deribit Announcements Query. + + Source: https://docs.deribit.com/api-reference/account-management/public-get_announcements + """ + + start_date: Any | None = Field( + default=None, + description="Return announcements published on or after this date.", + ) + limit: int = Field( + default=50, description="The number of announcements to return.", ge=1 + ) + + +class DeribitAnnouncementsData(Data): + """Deribit Announcements Data.""" + + __alias_dict__ = {"date": "publication_timestamp"} + + date: datetime = Field( + description="When the announcement was published.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + title: str = Field( + description="The title of the announcement.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + body: str = Field( + description="The body of the announcement, as HTML.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "cellDataType": "text"} + }, + ) + important: bool | None = Field( + default=None, + description="Whether the exchange flagged the announcement.", + json_schema_extra={ + "x-widget_config": { + "chartDataType": "excluded", + "cellDataType": "boolean", + } + }, + ) + confirmation: bool | None = Field( + default=None, + description="Whether the announcement needs acknowledging.", + json_schema_extra={ + "x-widget_config": { + "chartDataType": "excluded", + "cellDataType": "boolean", + "hide": True, + } + }, + ) + id: int | None = Field( + default=None, + description="The identifier of the announcement.", + json_schema_extra={ + "x-widget_config": { + "headerName": "ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + +class DeribitAnnouncementsFetcher( + Fetcher[DeribitAnnouncementsQueryParams, list[DeribitAnnouncementsData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitAnnouncementsQueryParams: + """Transform the query.""" + return DeribitAnnouncementsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitAnnouncementsQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange published no announcements over the span. + """ + from openbb_deribit.utils.client import request + from openbb_deribit.utils.helpers import to_timestamp + + data = await request( + "get_announcements", + { + "start_timestamp": ( + to_timestamp(query.start_date) if query.start_date else None + ), + "count": query.limit, + }, + ) + + if not data: + raise EmptyDataError("Deribit published no announcements over the span.") + + return data + + @staticmethod + def transform_data( + query: DeribitAnnouncementsQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitAnnouncementsData]: + """Transform the data to the model.""" + return [ + DeribitAnnouncementsData.model_validate(record) + for record in sorted( + data, key=lambda d: d.get("publication_timestamp") or 0, reverse=True + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/apr_history.py b/openbb_platform/providers/deribit/openbb_deribit/models/apr_history.py new file mode 100644 index 00000000000..665d68b2a11 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/apr_history.py @@ -0,0 +1,113 @@ +"""Deribit APR History Model.""" + +from datetime import date as dateType +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import AprCurrencies + + +class DeribitAprHistoryQueryParams(QueryParams): + """Deribit APR History Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_apr_history + """ + + currency: AprCurrencies = Field( + default="usde", description="The yield-bearing currency." + ) + limit: int = Field(default=365, description="The number of days to return.", ge=1) + + +class DeribitAprHistoryData(Data): + """Deribit APR History Data.""" + + __alias_dict__ = {"date": "day"} + + date: dateType = Field( + description="The day the rate applied to.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + currency: str = Field( + description="The currency earning the rate.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + apr: float = Field( + description="The annual percentage rate the currency earned.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": { + "headerName": "APR", + "chartDataType": "series", + }, + }, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Read the day number as a date.""" + from openbb_deribit.utils.helpers import from_day_number + + return from_day_number(v) + + +class DeribitAprHistoryFetcher( + Fetcher[DeribitAprHistoryQueryParams, list[DeribitAprHistoryData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitAprHistoryQueryParams: + """Transform the query.""" + return DeribitAprHistoryQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitAprHistoryQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the currency earned nothing over the span. + """ + from openbb_deribit.utils.client import request + + result = await request( + "get_apr_history", + {"currency": query.currency.lower(), "limit": query.limit}, + ) + data = [ + {"currency": query.currency.upper(), **record} + for record in (result or {}).get("data") or [] + ] + + if not data: + raise EmptyDataError( + f"Deribit published no yield history for {query.currency.upper()}." + ) + + return data + + @staticmethod + def transform_data( + query: DeribitAprHistoryQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitAprHistoryData]: + """Transform the data to the model.""" + return [ + DeribitAprHistoryData.model_validate(record) + for record in sorted(data, key=lambda d: d.get("day") or 0) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/block_rfq_trades.py b/openbb_platform/providers/deribit/openbb_deribit/models/block_rfq_trades.py new file mode 100644 index 00000000000..c477ed7c9d1 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/block_rfq_trades.py @@ -0,0 +1,212 @@ +"""Deribit Block RFQ Trades Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + BLOCK_RFQ_MAX_COUNT, + BLOCK_RFQ_MIN_COUNT, + AnyListingCurrencies, +) + + +class DeribitBlockRfqTradesQueryParams(QueryParams): + """Deribit Block RFQ Trades Query. + + Source: https://docs.deribit.com/api-reference/block-rfq/public-get_block_rfq_trades + """ + + currency: AnyListingCurrencies = Field( + default="any", description="The settlement currency of the quoted legs." + ) + limit: int = Field( + default=BLOCK_RFQ_MAX_COUNT, + description=f"The number of requests to return, between" + f" {BLOCK_RFQ_MIN_COUNT} and {BLOCK_RFQ_MAX_COUNT}.", + ge=BLOCK_RFQ_MIN_COUNT, + le=BLOCK_RFQ_MAX_COUNT, + ) + + +class DeribitBlockRfqTradesData(Data): + """Deribit Block RFQ Trades Data.""" + + timestamp: datetime = Field( + description="When the request traded.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "category", "pinned": "left"} + }, + ) + rfq_id: int | None = Field( + default=None, + description="The identifier of the request.", + json_schema_extra={ + "x-widget_config": { + "headerName": "RFQ ID", + "cellDataType": "text", + "chartDataType": "excluded", + } + }, + ) + combo_id: str | None = Field( + default=None, + description="The combo the request was quoted as.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Combo ID", + "chartDataType": "excluded", + } + }, + ) + direction: str | None = Field( + default=None, + description="Which side the taker was on.", + json_schema_extra={ + "x-widget_config": {"headerName": "Side", "chartDataType": "excluded"} + }, + ) + amount: float | None = Field( + default=None, + description="The size of the request.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + mark_price: float | None = Field( + default=None, + description="The price the exchange marked the structure at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + leg: str | None = Field( + default=None, + description="The instrument the leg trades.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + leg_direction: str | None = Field( + default=None, + description="Which side the leg was traded on.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + leg_price: float | None = Field( + default=None, + description="The price the leg traded at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + leg_ratio: float | None = Field( + default=None, + description="How many of the leg one structure holds.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + index_prices: dict | None = Field( + default=None, + description="The index prices the structure was marked against.", + json_schema_extra={ + "x-widget_config": { + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + + @field_validator("timestamp", mode="before", check_fields=False) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + +class DeribitBlockRfqTradesFetcher( + Fetcher[DeribitBlockRfqTradesQueryParams, list[DeribitBlockRfqTradesData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitBlockRfqTradesQueryParams: + """Transform the query.""" + return DeribitBlockRfqTradesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitBlockRfqTradesQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If no block request traded. + """ + from openbb_deribit.utils.client import request + from openbb_deribit.utils.helpers import normalize_currency + + result = await request( + "get_block_rfq_trades", + { + "currency": normalize_currency(query.currency), + "count": query.limit, + }, + use_cache=False, + ) + data = (result or {}).get("block_rfqs") or [] + + if not data: + raise EmptyDataError("Deribit reports no block requests traded.") + + return data + + @staticmethod + def transform_data( + query: DeribitBlockRfqTradesQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitBlockRfqTradesData]: + """Transform the data to the model.""" + records: list[dict] = [] + + for rfq in data: + context = { + "timestamp": rfq.get("timestamp"), + "rfq_id": rfq.get("id"), + "combo_id": rfq.get("combo_id"), + "direction": rfq.get("direction"), + "amount": rfq.get("amount"), + "mark_price": rfq.get("mark_price"), + "index_prices": rfq.get("index_prices"), + } + + for leg in rfq.get("legs") or [{}]: + records.append( + { + **context, + "leg": leg.get("instrument_name"), + "leg_direction": leg.get("direction"), + "leg_price": leg.get("price"), + "leg_ratio": leg.get("ratio"), + } + ) + + return [ + DeribitBlockRfqTradesData.model_validate(record) + for record in sorted( + records, + key=lambda d: (d["timestamp"] or 0, d["rfq_id"] or 0), + reverse=True, + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/book_summary.py b/openbb_platform/providers/deribit/openbb_deribit/models/book_summary.py new file mode 100644 index 00000000000..2ff3bf001df --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/book_summary.py @@ -0,0 +1,334 @@ +"""Deribit Book Summary Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator, model_validator + +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + SYMBOL_STYLE, + InstrumentKinds, + ListingCurrencies, +) + + +class DeribitBookSummaryQueryParams(QueryParams): + """Deribit Book Summary Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_book_summary_by_currency + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + symbol: str | None = Field( + default=None, + description="One or more instrument names. When given, the currency and" + + " kind are ignored.", + ) + currency: ListingCurrencies = Field( + default="BTC", description="The settlement currency of the instruments." + ) + kind: InstrumentKinds | None = Field( + default=None, description="The kind of instrument. Default is all of them." + ) + + @model_validator(mode="after") + def _options_are_listed_by_underlying(self): + """Refuse a listing of options scoped to a currency that only settles them.""" + from openbb_deribit.utils.helpers import reject_options_by_settlement + + if not self.symbol: + reject_options_by_settlement(self.currency, self.kind) + + return self + + +class DeribitBookSummaryData(Data): + """Deribit Book Summary Data.""" + + __alias_dict__ = { + "symbol": "instrument_name", + "last_price": "last", + "bid": "bid_price", + "ask": "ask_price", + "mid": "mid_price", + "change_percent": "price_change", + "implied_volatility": "mark_iv", + "timestamp": "creation_timestamp", + } + + symbol: str = Field( + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + timestamp: datetime | None = Field( + default=None, + description="When the summary was published.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + base_currency: str | None = Field( + default=None, + description="The currency the instrument is based on.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + quote_currency: str | None = Field( + default=None, + description="The currency the instrument is quoted in.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + last_price: float | None = Field( + default=None, + description="The price the instrument last traded at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + bid: float | None = Field( + default=None, + description="The highest price bid.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + ask: float | None = Field( + default=None, + description="The lowest price offered.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + mid: float | None = Field( + default=None, + description="The midpoint of the best bid and offer.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + mark_price: float | None = Field( + default=None, + description="The price the exchange marks positions at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + high: float | None = Field( + default=None, + description="The highest price of the last 24 hours.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + low: float | None = Field( + default=None, + description="The lowest price of the last 24 hours.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + change_percent: float | None = Field( + default=None, + description="The price change over the last 24 hours.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + volume: float | None = Field( + default=None, + description="The volume of the last 24 hours, in base currency.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + volume_notional: float | None = Field( + default=None, + description="The volume of the last 24 hours, in quote currency.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + volume_usd: float | None = Field( + default=None, + description="The volume of the last 24 hours, in USD.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": { + "headerName": "Volume USD", + "chartDataType": "excluded", + }, + }, + ) + open_interest: float | None = Field( + default=None, + description="The contracts left outstanding.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + current_funding: float | None = Field( + default=None, + description="The funding rate a perpetual is paying right now.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + funding_8h: float | None = Field( + default=None, + description="The funding rate a perpetual paid over eight hours.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": { + "headerName": "Funding 8H", + "chartDataType": "excluded", + }, + }, + ) + estimated_delivery_price: float | None = Field( + default=None, + description="What the instrument would deliver at right now.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + interest_rate: float | None = Field( + default=None, + description="The rate the exchange prices the option's greeks at.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + implied_volatility: float | None = Field( + default=None, + description="The volatility implied by the mark price.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": { + "headerName": "Mark IV", + "chartDataType": "excluded", + }, + }, + ) + underlying_index: str | None = Field( + default=None, + description="The future or index the option is priced against.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + underlying_price: float | None = Field( + default=None, + description="The price of the underlying the option is priced against.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + + @field_validator("timestamp", mode="before", check_fields=False) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) if v else None + + @field_validator( + "current_funding", + "funding_8h", + "interest_rate", + mode="before", + check_fields=False, + ) + @classmethod + def as_percent(cls, v): + """Return a rate the exchange sends as a fraction in percent units.""" + return v * 100 if v is not None else None + + +class DeribitBookSummaryFetcher( + Fetcher[DeribitBookSummaryQueryParams, list[DeribitBookSummaryData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitBookSummaryQueryParams: + """Transform the query.""" + return DeribitBookSummaryQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitBookSummaryQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange published no summary for the query. + """ + from openbb_deribit.utils.client import gather, request + from openbb_deribit.utils.helpers import get_perpetual_symbols + + if query.symbol: + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + results = await gather( + [ + ( + "get_book_summary_by_instrument", + {"instrument_name": perpetuals.get(symbol, symbol)}, + ) + for symbol in symbols + ] + ) + data = [ + record + for result in results + if isinstance(result, list) + for record in result + ] + else: + data = await request( + "get_book_summary_by_currency", + {"currency": query.currency.upper(), "kind": query.kind}, + ) + + if not data: + raise EmptyDataError("Deribit published no summary for the query.") + + return data + + @staticmethod + def transform_data( + query: DeribitBookSummaryQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitBookSummaryData]: + """Transform the data to the model.""" + return [ + DeribitBookSummaryData.model_validate(record) + for record in sorted(data, key=lambda d: str(d.get("instrument_name"))) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/combos.py b/openbb_platform/providers/deribit/openbb_deribit/models/combos.py new file mode 100644 index 00000000000..e124f2b7b3d --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/combos.py @@ -0,0 +1,175 @@ +"""Deribit Combos Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + COMBO_CHOICES_ENDPOINT, + SYMBOL_STYLE, + AnyListingCurrencies, +) + + +class DeribitCombosQueryParams(QueryParams): + """Deribit Combos Query. + + Source: https://docs.deribit.com/api-reference/combo-books/public-get_combos + """ + + __json_schema_extra__ = { + "combo_id": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": COMBO_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + currency: AnyListingCurrencies = Field( + default="BTC", description="The currency the combos settle in." + ) + combo_id: str | None = Field( + default=None, + description="One or more combo identifiers. When given, the currency is" + + " ignored.", + ) + + +class DeribitCombosData(Data): + """Deribit Combos Data.""" + + combo_id: str = Field( + description="The identifier of the combo.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Combo ID", + "chartDataType": "category", + "pinned": "left", + } + }, + ) + instrument_id: int | None = Field( + default=None, + description="The numeric identifier of the combo.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Instrument ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + state: str | None = Field( + default=None, + description="Whether the combo is still tradeable.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + leg: str = Field( + description="The instrument the leg trades.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + amount: float | None = Field( + default=None, + description="The size of the leg. A negative amount is a sold leg.", + json_schema_extra={"x-widget_config": {"chartDataType": "series"}}, + ) + creation_timestamp: datetime | None = Field( + default=None, + description="When the combo was listed.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + state_timestamp: datetime | None = Field( + default=None, + description="When the combo last changed state.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + + @field_validator( + "creation_timestamp", "state_timestamp", mode="before", check_fields=False + ) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) if v else None + + +class DeribitCombosFetcher(Fetcher[DeribitCombosQueryParams, list[DeribitCombosData]]): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitCombosQueryParams: + """Transform the query.""" + return DeribitCombosQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitCombosQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange lists no matching combo. + """ + from openbb_deribit.utils.client import gather, request + from openbb_deribit.utils.helpers import normalize_currency + + if query.combo_id: + ids = [c.strip() for c in query.combo_id.split(",") if c.strip()] + results = await gather( + [("get_combo_details", {"combo_id": c}) for c in ids] + ) + data = [result for result in results if isinstance(result, dict) and result] + else: + data = await request( + "get_combos", {"currency": normalize_currency(query.currency)} + ) + + if not data: + raise EmptyDataError("Deribit lists no combo matching the query.") + + return data + + @staticmethod + def transform_data( + query: DeribitCombosQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitCombosData]: + """Transform the data to the model.""" + records: list[dict] = [] + + for combo in data: + for leg in combo.get("legs") or []: + records.append( + { + "combo_id": combo.get("id"), + "instrument_id": combo.get("instrument_id"), + "state": combo.get("state"), + "leg": leg.get("instrument_name"), + "amount": leg.get("amount"), + "creation_timestamp": combo.get("creation_timestamp"), + "state_timestamp": combo.get("state_timestamp"), + } + ) + + return [ + DeribitCombosData.model_validate(record) + for record in sorted(records, key=lambda d: (d["combo_id"], d["leg"])) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/currencies.py b/openbb_platform/providers/deribit/openbb_deribit/models/currencies.py new file mode 100644 index 00000000000..8d1cc8c2083 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/currencies.py @@ -0,0 +1,155 @@ +"""Deribit Currencies Model.""" + +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field + + +class DeribitCurrenciesQueryParams(QueryParams): + """Deribit Currencies Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_currencies + """ + + +class DeribitCurrenciesData(Data): + """Deribit Currencies Data.""" + + currency: str = Field( + description="The abbreviation of the currency.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "category", "pinned": "left"} + }, + ) + currency_long: str | None = Field( + default=None, + description="The full name of the currency.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "headerName": "Name"} + }, + ) + coin_type: str | None = Field( + default=None, + description="The type of the currency.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + currency_uuid: str | None = Field( + default=None, + description="The unique identifier of the currency.", + json_schema_extra={ + "x-widget_config": { + "chartDataType": "excluded", + "headerName": "Currency UUID", + "hide": True, + } + }, + ) + network_currency: str | None = Field( + default=None, + description="The currency the network fee is charged in.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + apr: float | None = Field( + default=None, + description="The annual percentage rate earned by holding the currency.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "series", "headerName": "APR"}, + }, + ) + decimals: int | None = Field( + default=None, + description="The number of decimal places the currency is held to.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + onchain_operations_precision: int | None = Field( + default=None, + description="The number of decimal places an on-chain operation is held to.", + json_schema_extra={ + "x-widget_config": { + "chartDataType": "excluded", + "headerName": "On-chain Precision", + } + }, + ) + min_confirmations: int | None = Field( + default=None, + description="The confirmations a deposit needs before it is credited.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + min_withdrawal_fee: float | None = Field( + default=None, + description="The smallest fee a withdrawal is charged.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + withdrawal_fee: float | None = Field( + default=None, + description="The fee a withdrawal is charged.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + network_fee: float | None = Field( + default=None, + description="The fee the network itself charges.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + in_cross_collateral_pool: bool | None = Field( + default=None, + description="Whether the currency counts towards cross collateral.", + json_schema_extra={ + "x-widget_config": { + "chartDataType": "excluded", + "cellDataType": "boolean", + } + }, + ) + + +class DeribitCurrenciesFetcher( + Fetcher[DeribitCurrenciesQueryParams, list[DeribitCurrenciesData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitCurrenciesQueryParams: + """Transform the query.""" + return DeribitCurrenciesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitCurrenciesQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange listed no currencies. + """ + from openbb_deribit.utils.helpers import get_currencies + + data = await get_currencies() + + if not data: + raise EmptyDataError("Deribit listed no currencies.") + + return data + + @staticmethod + def transform_data( + query: DeribitCurrenciesQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitCurrenciesData]: + """Transform the data to the model.""" + return [ + DeribitCurrenciesData.model_validate(record) + for record in sorted(data, key=lambda d: str(d.get("currency"))) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/delivery_prices.py b/openbb_platform/providers/deribit/openbb_deribit/models/delivery_prices.py new file mode 100644 index 00000000000..f26823fb69b --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/delivery_prices.py @@ -0,0 +1,134 @@ +"""Deribit Delivery Prices Model.""" + +from datetime import date as dateType +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field + +from openbb_deribit.utils.constants import ( + INDEX_CHOICES_ENDPOINT, + SYMBOL_STYLE, +) + + +class DeribitDeliveryPricesQueryParams(QueryParams): + """Deribit Delivery Prices Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_delivery_prices + """ + + __json_schema_extra__ = { + "index_name": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INDEX_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + index_name: str = Field( + default="btc_usd", + description="One or more index names, such as 'btc_usd'.", + ) + limit: int = Field( + default=100, description="The number of settlements to return.", ge=1 + ) + offset: int = Field( + default=0, + description="How many of the most recent settlements to skip.", + ge=0, + ) + + +class DeribitDeliveryPricesData(Data): + """Deribit Delivery Prices Data.""" + + date: dateType = Field( + description="The session the index delivered on.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + index_name: str = Field( + description="The name of the index.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + delivery_price: float = Field( + description="The level the index delivered at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + + +class DeribitDeliveryPricesFetcher( + Fetcher[DeribitDeliveryPricesQueryParams, list[DeribitDeliveryPricesData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitDeliveryPricesQueryParams: + """Transform the query.""" + return DeribitDeliveryPricesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitDeliveryPricesQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the indexes has delivered. + """ + from openbb_deribit.utils.client import gather + + names = [n.strip().lower() for n in query.index_name.split(",") if n.strip()] + results = await gather( + [ + ( + "get_delivery_prices", + { + "index_name": name, + "count": query.limit, + "offset": query.offset, + }, + ) + for name in names + ] + ) + data = [ + {"index_name": name, **record} + for name, result in zip(names, results) + if isinstance(result, dict) + for record in result.get("data") or [] + ] + + if not data: + raise EmptyDataError( + f"Deribit published no delivery prices for {', '.join(names)}." + ) + + return data + + @staticmethod + def transform_data( + query: DeribitDeliveryPricesQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitDeliveryPricesData]: + """Transform the data to the model.""" + return [ + DeribitDeliveryPricesData.model_validate(record) + for record in sorted(data, key=lambda d: (d["date"], d["index_name"])) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/expirations.py b/openbb_platform/providers/deribit/openbb_deribit/models/expirations.py new file mode 100644 index 00000000000..cc0a0e0fed5 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/expirations.py @@ -0,0 +1,142 @@ +"""Deribit Expirations Model.""" + +from datetime import date as dateType +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field + +from openbb_deribit.utils.constants import ExpirationCurrencies, ExpirationKinds + + +class DeribitExpirationsQueryParams(QueryParams): + """Deribit Expirations Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_expirations + """ + + currency: ExpirationCurrencies = Field( + default="grouped", + description="The currency to list expirations for. 'any' pools every" + + " currency together, and 'grouped' keeps them apart.", + ) + kind: ExpirationKinds = Field(default="any", description="The kind of instrument.") + currency_pair: str | None = Field( + default=None, + description="Narrow the listing to one currency pair, such as 'btc_usd'.", + ) + + +class DeribitExpirationsData(Data): + """Deribit Expirations Data.""" + + currency: str = Field( + description="The currency the contracts settle in.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "pinned": "left"} + }, + ) + kind: str = Field( + description="The kind of instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + code: str = Field( + description="The expiration as it appears in instrument names.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + expiration: dateType | None = Field( + default=None, + description="The expiration date. Perpetual contracts carry none.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + + +class DeribitExpirationsFetcher( + Fetcher[DeribitExpirationsQueryParams, list[DeribitExpirationsData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitExpirationsQueryParams: + """Transform the query.""" + return DeribitExpirationsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitExpirationsQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> dict: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange lists no expirations for the query. + """ + from openbb_deribit.utils.helpers import get_expirations + + data = await get_expirations(query.currency, query.kind, query.currency_pair) + + if not data: + raise EmptyDataError("Deribit lists no expirations matching the query.") + + return data + + @staticmethod + def transform_data( + query: DeribitExpirationsQueryParams, + data: dict, + **kwargs: Any, + ) -> list[DeribitExpirationsData]: + """Transform the data to the model. + + Raises + ------ + EmptyDataError + If every currency in the response listed nothing. + """ + from pandas import to_datetime + + by_currency = ( + {query.currency: data} + if all(isinstance(value, list) for value in data.values()) + else data + ) + records: list[dict] = [] + + for currency, kinds in by_currency.items(): + for kind, codes in kinds.items(): + for code in codes: + records.append( + { + "currency": currency.upper(), + "kind": kind, + "code": code, + "expiration": ( + None + if code == "PERPETUAL" + else to_datetime(code).date() + ), + } + ) + + if not records: + raise EmptyDataError("Deribit lists no expirations matching the query.") + + return [ + DeribitExpirationsData.model_validate(record) + for record in sorted( + records, + key=lambda d: ( + d["currency"], + d["kind"], + d["expiration"] or dateType.max, + ), + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/funding_chart.py b/openbb_platform/providers/deribit/openbb_deribit/models/funding_chart.py new file mode 100644 index 00000000000..89097ba6629 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/funding_chart.py @@ -0,0 +1,177 @@ +"""Deribit Funding Chart Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + PERPETUAL_CHOICES_ENDPOINT, + SYMBOL_STYLE, + FundingLengths, +) + + +class DeribitFundingChartQueryParams(QueryParams): + """Deribit Funding Chart Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_funding_chart_data + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": PERPETUAL_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + symbol: str = Field( + default="BTC-PERPETUAL", + description="One or more perpetual instrument names. A perpetual can also" + + " be given by its shortened root, such as 'SOLUSDC'.", + ) + length: FundingLengths = Field( + default="8h", description="How far back the series runs." + ) + + +class DeribitFundingChartData(Data): + """Deribit Funding Chart Data.""" + + __alias_dict__ = {"date": "timestamp"} + + date: datetime = Field( + description="When the rate was published.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + symbol: str = Field( + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + interest_8h: float | None = Field( + default=None, + description="The funding rate over the trailing eight hours.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": { + "headerName": "Interest 8H", + "chartDataType": "series", + }, + }, + ) + index_price: float | None = Field( + default=None, + description="The level of the index when the rate was published.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + current_interest: float | None = Field( + default=None, + description="The funding rate accrued so far in the current interval.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + @field_validator( + "interest_8h", + "current_interest", + mode="before", + check_fields=False, + ) + @classmethod + def as_percent(cls, v): + """Return a rate the exchange sends as a fraction in percent units.""" + return v * 100 if v is not None else None + + +class DeribitFundingChartFetcher( + Fetcher[DeribitFundingChartQueryParams, list[DeribitFundingChartData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitFundingChartQueryParams: + """Transform the query.""" + return DeribitFundingChartQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitFundingChartQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the instruments published funding over the span. + """ + from openbb_deribit.utils.client import gather + from openbb_deribit.utils.helpers import get_perpetual_symbols + + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + resolved = [perpetuals.get(symbol, symbol) for symbol in symbols] + results = await gather( + [ + ( + "get_funding_chart_data", + {"instrument_name": symbol, "length": query.length}, + ) + for symbol in resolved + ] + ) + data = [ + { + "symbol": symbol, + "current_interest": result.get("current_interest"), + **record, + } + for symbol, result in zip(resolved, results) + if isinstance(result, dict) + for record in result.get("data") or [] + ] + + if not data: + raise EmptyDataError( + f"Deribit published no funding over the span for {', '.join(resolved)}." + ) + + return data + + @staticmethod + def transform_data( + query: DeribitFundingChartQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitFundingChartData]: + """Transform the data to the model.""" + return [ + DeribitFundingChartData.model_validate(record) + for record in sorted( + data, key=lambda d: (d.get("timestamp") or 0, d["symbol"]) + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/funding_rate_history.py b/openbb_platform/providers/deribit/openbb_deribit/models/funding_rate_history.py new file mode 100644 index 00000000000..23c3d8526d6 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/funding_rate_history.py @@ -0,0 +1,208 @@ +"""Deribit Funding Rate History Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + PERPETUAL_CHOICES_ENDPOINT, + SYMBOL_STYLE, +) + + +class DeribitFundingRateHistoryQueryParams(QueryParams): + """Deribit Funding Rate History Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_funding_rate_history + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": PERPETUAL_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + symbol: str = Field( + default="BTC-PERPETUAL", + description="One or more perpetual instrument names. A perpetual can also" + + " be given by its shortened root, such as 'SOLUSDC'.", + ) + start_date: Any | None = Field( + default=None, description="Return rates from this date onwards." + ) + end_date: Any | None = Field( + default=None, description="Return rates up to this date." + ) + + +class DeribitFundingRateHistoryData(Data): + """Deribit Funding Rate History Data.""" + + __alias_dict__ = {"date": "timestamp"} + + date: datetime = Field( + description="The hour the rate applied to.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + symbol: str = Field( + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + interest_1h: float | None = Field( + default=None, + description="The funding rate over the hour.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": { + "headerName": "Interest 1H", + "chartDataType": "excluded", + }, + }, + ) + interest_8h: float | None = Field( + default=None, + description="The funding rate over the trailing eight hours.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": { + "headerName": "Interest 8H", + "chartDataType": "series", + }, + }, + ) + index_price: float | None = Field( + default=None, + description="The level of the index at the end of the hour.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + prev_index_price: float | None = Field( + default=None, + description="The level of the index at the start of the hour.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": { + "headerName": "Previous Index Price", + "chartDataType": "excluded", + }, + }, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + @field_validator( + "interest_1h", + "interest_8h", + mode="before", + check_fields=False, + ) + @classmethod + def as_percent(cls, v): + """Return a rate the exchange sends as a fraction in percent units.""" + return v * 100 if v is not None else None + + +class DeribitFundingRateHistoryFetcher( + Fetcher[DeribitFundingRateHistoryQueryParams, list[DeribitFundingRateHistoryData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query( + params: dict[str, Any], + ) -> DeribitFundingRateHistoryQueryParams: + """Transform the query.""" + return DeribitFundingRateHistoryQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitFundingRateHistoryQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the instruments paid funding over the span. + """ + from datetime import ( + datetime as dt, + timedelta, + timezone, + ) + + from openbb_deribit.utils.client import gather + from openbb_deribit.utils.helpers import get_perpetual_symbols, to_timestamp + + now = dt.now(timezone.utc) + start = ( + to_timestamp(query.start_date) + if query.start_date + else to_timestamp(now - timedelta(days=7)) + ) + end = to_timestamp(query.end_date) if query.end_date else to_timestamp(now) + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + resolved = [perpetuals.get(symbol, symbol) for symbol in symbols] + results = await gather( + [ + ( + "get_funding_rate_history", + { + "instrument_name": symbol, + "start_timestamp": start, + "end_timestamp": end, + }, + ) + for symbol in resolved + ] + ) + data = [ + {"symbol": symbol, **record} + for symbol, result in zip(resolved, results) + if isinstance(result, list) + for record in result + ] + + if not data: + raise EmptyDataError( + f"Deribit published no funding over the span for {', '.join(resolved)}." + ) + + return data + + @staticmethod + def transform_data( + query: DeribitFundingRateHistoryQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitFundingRateHistoryData]: + """Transform the data to the model.""" + return [ + DeribitFundingRateHistoryData.model_validate(record) + for record in sorted( + data, key=lambda d: (d.get("timestamp") or 0, d["symbol"]) + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/futures_curve.py b/openbb_platform/providers/deribit/openbb_deribit/models/futures_curve.py index cef7eded052..6c6324317b6 100644 --- a/openbb_platform/providers/deribit/openbb_deribit/models/futures_curve.py +++ b/openbb_platform/providers/deribit/openbb_deribit/models/futures_curve.py @@ -1,105 +1,142 @@ """Deribit Futures Curve Model.""" -# pylint: disable=unused-argument - +from datetime import date as dateType from typing import Any -from openbb_core.app.model.abstract.error import OpenBBError from openbb_core.provider.abstract.fetcher import Fetcher from openbb_core.provider.standard_models.futures_curve import ( FuturesCurveData, FuturesCurveQueryParams, ) from openbb_core.provider.utils.descriptions import ( + DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS, ) from openbb_core.provider.utils.errors import EmptyDataError -from openbb_deribit.utils.helpers import ( - DERIBIT_FUTURES_CURVE_SYMBOLS, - FuturesCurveSymbols, -) from pydantic import Field, field_validator, model_validator +from openbb_deribit.utils.constants import ( + CURVE_CHOICES_ENDPOINT, + SYMBOL_STYLE, +) + class DeribitFuturesCurveQueryParams(FuturesCurveQueryParams): - """ - Deribit Futures Curve Query. + """Deribit Futures Curve Query. - Source: https://docs.deribit.com/?shell#public-ticker + Source: https://docs.deribit.com/api-reference/market-data/public-ticker """ __json_schema_extra__ = { "symbol": { "multiple_items_allowed": False, - "choices": DERIBIT_FUTURES_CURVE_SYMBOLS, - }, - "hours_ago": { - "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": CURVE_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, }, + "hours_ago": {"multiple_items_allowed": True}, } - symbol: FuturesCurveSymbols = Field( + symbol: str = Field( default="BTC", description=QUERY_DESCRIPTIONS.get("symbol", "") - + " Default is 'btc' Supported symbols are: ['btc', 'eth', 'paxg']", + + " The underlying root, as it appears in the instrument name.", ) - hours_ago: int | list[int] | str | None = Field( + hours_ago: str | None = Field( default=None, - description="Compare the current curve with the specified number of hours ago. Default is None.", + description="Compare the current curve against how it stood this many" + + " hours ago.", ) - @field_validator("symbol", mode="before", check_fields=False) - @classmethod - def validate_symbol(cls, v): - """Validate the symbol.""" - symbol = v.upper() - if symbol not in DERIBIT_FUTURES_CURVE_SYMBOLS: - raise ValueError( - f"Invalid Deribit symbol, {symbol}. Supported symbols are: {', '.join(DERIBIT_FUTURES_CURVE_SYMBOLS)}" - ) - return symbol - @field_validator("hours_ago", mode="before", check_fields=False) @classmethod - def _validate_hours_ago(cls, v): - """Validate hours ago.""" - if isinstance(v, str): - return v - if isinstance(v, int): - return v - if isinstance(v, list): - return ",".join([str(i) for i in v]) - return None + def validate_hours_ago(cls, v): + """Read one or more hour counts as a comma-separated string.""" + if v is None: + return None + + return ",".join(str(hour) for hour in v) if isinstance(v, list) else str(v) @model_validator(mode="before") @classmethod - def _validate_model(cls, values): - """Validate the model.""" - if values.get("date"): + def validate_model(cls, values): + """Reject a date, which the exchange does not serve a curve for. + + Raises + ------ + ValueError + If a date was given. + """ + if isinstance(values, dict) and values.get("date"): raise ValueError( - "Date field is not supported for Deribit provider. Use 'hours_ago' instead." + "Deribit serves no curve as of a date. Use 'hours_ago' instead." ) + return values class DeribitFuturesCurveData(FuturesCurveData): """Deribit Futures Curve Data.""" + date: dateType | None = Field( + default=None, + description=DATA_DESCRIPTIONS.get("date", ""), + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + expiration: str = Field( + description="Futures expiration month.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "category", "pinned": "left"} + }, + ) + price: float | None = Field( + default=None, + description="The price of the futures contract, taken from the mark.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + symbol: str | None = Field( + default=None, + description="The name of the contract.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + last_price: float | None = Field( + default=None, + description="The price the contract last traded at, which on a thin" + + " contract can be a long way from the mark the curve is built on.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) hours_ago: int | None = Field( default=None, - description="The number of hours ago represented by the price." - + " Only available when hours_ago is set in the query.", + description="How many hours back the price was read, when the query" + + " asked for one.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, ) class DeribitFuturesCurveFetcher( Fetcher[DeribitFuturesCurveQueryParams, list[DeribitFuturesCurveData]] ): - """Deribit Futures Curve Fetcher.""" + """Transform the query, extract and transform the data from the Deribit endpoint. + + The current curve is priced off each contract's mark, because a thin contract's + last trade can be a long way from where the exchange marks it. A curve read + some hours back is priced off traded closes instead, the only history the + exchange publishes, so it carries only the contracts that had traded by then. + """ + + require_credentials = False @staticmethod def transform_query(params: dict[str, Any]) -> DeribitFuturesCurveQueryParams: - """Transform query params.""" + """Transform the query.""" return DeribitFuturesCurveQueryParams(**params) @staticmethod @@ -107,79 +144,83 @@ class DeribitFuturesCurveFetcher( query: DeribitFuturesCurveQueryParams, credentials: dict[str, str] | None, **kwargs: Any, - ) -> list: - """Extract the raw data.""" - # pylint: disable=import-outside-toplevel - import asyncio # noqa + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the contracts on the curve published a quote. + """ from openbb_deribit.utils.helpers import ( - get_futures_curve_symbols, - get_ticker_data, get_futures_curve_by_hours_ago, + get_futures_curve_symbols, + get_tickers, ) - try: - symbols = await get_futures_curve_symbols(query.symbol) - tasks = [get_ticker_data(s) for s in symbols] - data = await asyncio.gather(*tasks, return_exceptions=True) + symbols = await get_futures_curve_symbols(query.symbol) + data = await get_tickers(symbols) - if query.hours_ago is not None: - num_hours = query.hours_ago + if query.hours_ago: + for hour in [int(h) for h in query.hours_ago.split(",") if h.strip()]: + data.extend(await get_futures_curve_by_hours_ago(query.symbol, hour)) - hours_ago = ( - [int(d) for d in num_hours.split(",")] - if isinstance(num_hours, str) - else [int(num_hours)] if isinstance(num_hours, int) else num_hours - ) + if not data: + raise EmptyDataError( + f"Deribit published no quote for the {query.symbol} curve." + ) - for hours in hours_ago: - hours_data = await get_futures_curve_by_hours_ago( - query.symbol, hours - ) - if hours_data: - data.extend(hours_data) - return data - except Exception as e: # pylint: disable=broad-except - raise OpenBBError( - f"Failed to get futures curve -> {e.__class__.__name__ if hasattr(e, '__class__') else e}: {e.args}" - ) from e + return data @staticmethod def transform_data( - query: DeribitFuturesCurveQueryParams, data: list, **kwargs: Any + query: DeribitFuturesCurveQueryParams, + data: list[dict], + **kwargs: Any, ) -> list[DeribitFuturesCurveData]: - """Transform the data.""" - # pylint: disable=import-outside-toplevel - from datetime import datetime # noqa + """Transform the data to the model. + + Raises + ------ + EmptyDataError + If every contract on the curve came back unpriced. + """ + from datetime import datetime, timezone + from pandas import to_datetime - if not data: - raise EmptyDataError("No data found") + records: list[dict] = [] - futures_curve: list[DeribitFuturesCurveData] = [] + for record in data: + symbol = str(record.get("instrument_name") or "") + code = symbol.split("-")[1] if "-" in symbol else "" + price = record.get("mark_price") or record.get("last_price") - for d in data: - if not d: + if not symbol or price is None: continue - ins_name = d.get("instrument_name", "") - exp = ins_name.split("-")[1] - hours_ago = d.get("hours_ago", 0) - exp = ( - datetime.today().strftime("%Y-%m-%d") - if exp == "PERPETUAL" - else to_datetime(exp).strftime("%Y-%m-%d") + records.append( + { + "symbol": symbol, + "expiration": ( + datetime.now(timezone.utc).strftime("%Y-%m-%d") + if code == "PERPETUAL" + else to_datetime(code).strftime("%Y-%m-%d") + ), + "price": price, + "last_price": record.get("last_price"), + "hours_ago": record.get("hours_ago") if query.hours_ago else None, + } ) - price = d.get("last_price", d.get("mark_price")) + if not records: + raise EmptyDataError( + f"Deribit published no priced contract on the {query.symbol} curve." + ) - result = {"expiration": exp, "price": price} - if query.hours_ago: - result["hours_ago"] = hours_ago - - if price: - futures_curve.append(DeribitFuturesCurveData.model_validate(result)) - - if not futures_curve: - raise EmptyDataError("No data found.") - - return sorted(futures_curve, key=lambda x: x.expiration) + return [ + DeribitFuturesCurveData.model_validate(record) + for record in sorted( + records, key=lambda d: (d["hours_ago"] or 0, d["expiration"]) + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/futures_historical.py b/openbb_platform/providers/deribit/openbb_deribit/models/futures_historical.py index 979dec6bacc..11862201ecb 100644 --- a/openbb_platform/providers/deribit/openbb_deribit/models/futures_historical.py +++ b/openbb_platform/providers/deribit/openbb_deribit/models/futures_historical.py @@ -1,91 +1,87 @@ """Deribit Futures Historical Model.""" -# pylint: disable=unused-argument - -from datetime import datetime, timedelta +from datetime import ( + date as dateType, + datetime, + timedelta, +) from typing import Any -from openbb_core.app.model.abstract.error import OpenBBError from openbb_core.provider.abstract.fetcher import Fetcher from openbb_core.provider.standard_models.futures_historical import ( FuturesHistoricalData, FuturesHistoricalQueryParams, ) -from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS +from openbb_core.provider.utils.descriptions import ( + DATA_DESCRIPTIONS, + QUERY_DESCRIPTIONS, +) from openbb_core.provider.utils.errors import EmptyDataError -from openbb_deribit.utils.helpers import DERIBIT_INTERVALS, DeribitIntervals -from pydantic import Field, field_validator, model_validator +from pydantic import Field, model_validator + +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + INTERVALS, + SYMBOL_STYLE, + Intervals, +) + +INTERVAL_LOOKBACK = { + "1m": 30, + "3m": 60, + "5m": 60, + "10m": 90, + "15m": 90, + "30m": 90, +} +DEFAULT_LOOKBACK = 364 class DeribitFuturesHistoricalQueryParams(FuturesHistoricalQueryParams): - """ - Deribit Futures historical Price Query. + """Deribit Futures Historical Query. - Source: https://docs.deribit.com/?shell#public-get_tradingview_chart_data + Source: https://docs.deribit.com/api-reference/market-data/public-get_tradingview_chart_data """ __json_schema_extra__ = { - "symbol": {"multiple_items_allowed": True}, + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + }, "interval": { "multiple_items_allowed": False, - "choices": DERIBIT_INTERVALS, + "choices": list(INTERVALS), }, + "expiration": {"x-widget_config": {"exclude": True}}, } - interval: DeribitIntervals = Field( + symbol: str = Field( + description=QUERY_DESCRIPTIONS.get("symbol", "") + + " Any listed instrument works, not only futures. A perpetual can also" + + " be given by its shortened root, such as 'SOLUSDC'." + ) + interval: Intervals = Field( default="1d", description=QUERY_DESCRIPTIONS.get("interval", "") ) - @field_validator("symbol", mode="before", check_fields=False) - @classmethod - def _validate_symbol(cls, v): - """Validate the symbol.""" - # pylint: disable=import-outside-toplevel - from openbb_core.provider.utils.helpers import run_async - from openbb_deribit.utils.helpers import ( - get_futures_symbols, - get_perpetual_symbols, - ) - - if not v: - raise ValueError("Symbol is required.") - - futures_symbols = run_async(get_futures_symbols) - perpetual_symbols = run_async(get_perpetual_symbols) - all_symbols = list(perpetual_symbols) + futures_symbols - symbols = v.upper().split(",") - new_symbols: list = [] - - for symbol in symbols: - if symbol not in all_symbols: - raise ValueError( - f"Invalid Deribit symbol: {symbol}. Supported symbols are: {', '.join(all_symbols)}" - ) - if symbol in perpetual_symbols: - new_symbols.append(perpetual_symbols[symbol]) - else: - new_symbols.append(symbol) - - return ",".join(new_symbols) - @model_validator(mode="before") @classmethod - def _validate_model(cls, values): - """Validate the model.""" - interval = values.get("interval") + def validate_model(cls, values): + """Fill in a span the interval can be served over.""" + if not isinstance(values, dict): + return values + now = datetime.today() if not values.get("start_date"): - if interval == "1m": - start = now - timedelta(days=30) - elif interval in ["3m", "5m"]: - start = now - timedelta(days=60) - elif interval in ["15m", "30m"]: - start = now - timedelta(days=90) - else: - start = now - timedelta(days=364) - - values["start_date"] = start.strftime("%Y-%m-%d") + lookback = INTERVAL_LOOKBACK.get( + str(values.get("interval") or "1d"), DEFAULT_LOOKBACK + ) + values["start_date"] = (now - timedelta(days=lookback)).strftime("%Y-%m-%d") if not values.get("end_date"): values["end_date"] = (now + timedelta(days=1)).strftime("%Y-%m-%d") @@ -96,16 +92,72 @@ class DeribitFuturesHistoricalQueryParams(FuturesHistoricalQueryParams): class DeribitFuturesHistoricalData(FuturesHistoricalData): """Deribit Futures Historical Data.""" - volume_notional: float = Field(description="Trading volume in quote currency.") + date: datetime | dateType = Field( + description=DATA_DESCRIPTIONS.get("date", ""), + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + open: float | None = Field( + default=None, + description=DATA_DESCRIPTIONS.get("open", ""), + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + high: float | None = Field( + default=None, + description=DATA_DESCRIPTIONS.get("high", ""), + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + low: float | None = Field( + default=None, + description=DATA_DESCRIPTIONS.get("low", ""), + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + close: float = Field( + description=DATA_DESCRIPTIONS.get("close", ""), + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + volume: float | None = Field( + default=None, + description=DATA_DESCRIPTIONS.get("volume", ""), + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + symbol: str | None = Field( + default=None, + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + volume_notional: float | None = Field( + default=None, + description="The volume of the candle, in quote currency.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) class DeribitFuturesHistoricalFetcher( Fetcher[DeribitFuturesHistoricalQueryParams, list[DeribitFuturesHistoricalData]] ): - """Deribit Futures Historical Fetcher.""" + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False @staticmethod - def transform_query(params: dict[str, Any]) -> DeribitFuturesHistoricalQueryParams: + def transform_query( + params: dict[str, Any], + ) -> DeribitFuturesHistoricalQueryParams: """Transform the query.""" return DeribitFuturesHistoricalQueryParams(**params) @@ -114,42 +166,51 @@ class DeribitFuturesHistoricalFetcher( query: DeribitFuturesHistoricalQueryParams, credentials: dict[str, str] | None, **kwargs: Any, - ) -> list: - """Extract the raw data.""" - # pylint: disable=import-outside-toplevel - from openbb_deribit.utils.helpers import get_ohlc_data + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. - symbols = query.symbol.split(",") - results: list = [] + Raises + ------ + EmptyDataError + If none of the instruments published candles over the span. + """ + from openbb_deribit.utils.helpers import get_ohlc_data, get_perpetual_symbols + + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + results: list[dict] = [] for symbol in symbols: - try: - data = await get_ohlc_data( - symbol=symbol, - interval=query.interval, - start_date=query.start_date, - end_date=query.end_date, + results.extend( + await get_ohlc_data( + perpetuals.get(symbol, symbol), + query.start_date, + query.end_date, + query.interval, ) - if data: - results.extend(data) - except OpenBBError as e: - raise e from e + ) if not results: - raise EmptyDataError("No data found.") + raise EmptyDataError( + f"Deribit published no candles over the span for {', '.join(symbols)}." + ) - return sorted(results, key=lambda x: x["date"]) + return results @staticmethod def transform_data( - query: DeribitFuturesHistoricalQueryParams, data: list, **kwargs: Any + query: DeribitFuturesHistoricalQueryParams, + data: list[dict], + **kwargs: Any, ) -> list[DeribitFuturesHistoricalData]: - """Transform the data.""" - symbols = query.symbol.split(",") - if len(symbols) == 1: - results: list[DeribitFuturesHistoricalData] = [] - for d in data: - _ = d.pop("symbol", None) - results.append(DeribitFuturesHistoricalData.model_validate(d)) - return [DeribitFuturesHistoricalData.model_validate(d) for d in data] - return [DeribitFuturesHistoricalData.model_validate(d) for d in data] + """Transform the data to the model.""" + single = len([s for s in query.symbol.split(",") if s.strip()]) == 1 + + return [ + DeribitFuturesHistoricalData.model_validate( + {key: value for key, value in record.items() if key != "symbol"} + if single + else record + ) + for record in sorted(data, key=lambda d: (d["date"], d["symbol"])) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/futures_info.py b/openbb_platform/providers/deribit/openbb_deribit/models/futures_info.py index b07bcbc6fb5..74d357ff8b7 100644 --- a/openbb_platform/providers/deribit/openbb_deribit/models/futures_info.py +++ b/openbb_platform/providers/deribit/openbb_deribit/models/futures_info.py @@ -1,187 +1,59 @@ -"""Deribit Futures Info Models.""" +"""Deribit Futures Info Model.""" -# pylint: disable=unused-argument - -from datetime import datetime -from typing import Any, Literal +from typing import Any from openbb_core.provider.abstract.fetcher import Fetcher from openbb_core.provider.standard_models.futures_info import ( FuturesInfoData, FuturesInfoQueryParams, ) -from openbb_core.provider.utils.descriptions import ( - DATA_DESCRIPTIONS, - QUERY_DESCRIPTIONS, +from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import ConfigDict, Field + +from openbb_deribit.models.ticker import DeribitTickerData +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + SYMBOL_STYLE, ) -from pydantic import ConfigDict, Field, field_validator class DeribitFuturesInfoQueryParams(FuturesInfoQueryParams): - """Deribit Futures Instruments Query.""" + """Deribit Futures Info Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-ticker + """ __json_schema_extra__ = { - "symbol": {"multiple_items_allowed": True}, + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } } symbol: str = Field( description=QUERY_DESCRIPTIONS.get("symbol", "") - + " Perpetual contracts can be referenced by their currency pair - i.e, SOLUSDC" - + " - or by their official Deribit symbol - i.e, SOL_USDC-PERPETUAL" - + " For a list of currently available instruments, use `derivatives.futures.instruments()`" + + " A perpetual can be given by its currency pair - SOLUSDC - or by its" + + " full instrument name - SOL_USDC-PERPETUAL." ) - @field_validator("symbol", mode="before", check_fields=False) - @classmethod - def _validate_symbol(cls, v): - """Validate the symbol.""" - # pylint: disable=import-outside-toplevel - from openbb_core.provider.utils.helpers import run_async - from openbb_deribit.utils.helpers import ( - get_futures_symbols, - get_perpetual_symbols, - ) - symbols = v.split(",") - new_symbols: list = [] - perpetual_symbols = run_async(get_perpetual_symbols) - futures_symbols = run_async(get_futures_symbols) - all_symbols = futures_symbols + list(perpetual_symbols) - - for s in symbols: - if s not in all_symbols: - raise ValueError( - f"Invalid symbol: {s}. Valid symbols are: {all_symbols}" - ) - if s in perpetual_symbols: - new_symbols.append(perpetual_symbols[s]) - else: - new_symbols.append(s) - - return ",".join(new_symbols) - - -class DeribitFuturesInfoData(FuturesInfoData): +class DeribitFuturesInfoData(DeribitTickerData, FuturesInfoData): """Deribit Futures Info Data.""" - __alias_dict__ = { - "symbol": "instrument_name", - "change_percent": "price_change", - } model_config = ConfigDict(extra="ignore") - symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", "")) - state: Literal["open", "closed"] = Field( - description="The state of the order book. Possible values are open and closed." - ) - open_interest: float = Field( - description="The total amount of outstanding contracts in the corresponding amount units." - ) - index_price: float = Field( - description="Current index (reference) price", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - best_ask_amount: float | None = Field( - default=None, - description="It represents the requested order size of all best asks", - ) - best_ask_price: float | None = Field( - default=None, - description="The current best ask price, null if there aren't any asks", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - best_bid_price: float | None = Field( - default=None, - description="The current best bid price, null if there aren't any bids", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - best_bid_amount: float | None = Field( - default=None, - description="It represents the requested order size of all best bids", - ) - last_price: float | None = Field( - default=None, - description="The price for the last trade", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - high: float | None = Field( - default=None, - description="Highest price during 24h", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - low: float | None = Field( - default=None, - description="Lowest price during 24h", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - change_percent: float | None = Field( - default=None, - description="24-hour price change expressed as a percentage, null if there weren't any trades", - json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100}, - ) - volume: float | None = Field( - default=None, - description="Volume during last 24h in base currency", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - volume_usd: float | None = Field( - default=None, - description="Volume in USD", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - mark_price: float = Field( - description="The mark price for the instrument", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - settlement_price: float | None = Field( - default=None, - description="The settlement price for the instrument. Only when state = open", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - delivery_price: float | None = Field( - default=None, - description="The settlement price for the instrument. Only when state = closed.", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - estimated_delivery_price: float = Field( - description="Estimated delivery price for the market.", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - current_funding: float | None = Field( - default=None, - description="Current funding (perpetual only)", - ) - funding_8h: float | None = Field( - default=None, - description="Funding 8h (perpetual only)", - ) - interest_value: float | None = Field( - default=None, - description="Value used to calculate realized_funding in positions (perpetual only)", - ) - max_price: float = Field( - description="The maximum price for the future." - + " Any buy orders submitted higher than this price, will be clamped to this maximum.", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - min_price: float = Field( - description="The minimum price for the future." - + " Any sell orders submitted lower than this price will be clamped to this minimum.", - json_schema_extra={"x-unit_measurement": "currency"}, - ) - timestamp: datetime = Field(description="The timestamp of the data.") - - @field_validator("change_percent", mode="before", check_fields=False) - @classmethod - def _validate_change_percent(cls, v): - """Validate the change percent.""" - return v / 100 if v else v - class DeribitFuturesInfoFetcher( Fetcher[DeribitFuturesInfoQueryParams, list[DeribitFuturesInfoData]] ): - """Deribit Futures Info Fetcher.""" + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False @staticmethod def transform_query(params: dict[str, Any]) -> DeribitFuturesInfoQueryParams: @@ -193,32 +65,40 @@ class DeribitFuturesInfoFetcher( query: DeribitFuturesInfoQueryParams, credentials: dict[str, str] | None, **kwargs: Any, - ) -> list: - """Extract data from the response.""" - # pylint: disable=import-outside-toplevel - import asyncio # noqa - from openbb_core.provider.utils.errors import EmptyDataError, OpenBBError - from openbb_deribit.utils.helpers import get_ticker_data + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. - result: list = [] - symbols = query.symbol.split(",") - try: - tasks = [get_ticker_data(symbol) for symbol in symbols] - for task in asyncio.as_completed(tasks, timeout=10): - result.append(await task) - except Exception as e: # pylint: disable=broad-except - raise OpenBBError(f"Error fetching data: {e}") from e + Raises + ------ + EmptyDataError + If none of the instruments published a quote. + """ + from openbb_deribit.utils.helpers import get_perpetual_symbols, get_tickers - if not result: - raise EmptyDataError("No data found for the given symbol(s).") + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + resolved = [perpetuals.get(symbol, symbol) for symbol in symbols] + data = await get_tickers(resolved) - return sorted(result, key=lambda x: symbols.index(x["instrument_name"])) + if not data: + raise EmptyDataError( + f"Deribit published no quote for {', '.join(resolved)}." + ) + + return sorted( + data, + key=lambda d: ( + resolved.index(d["instrument_name"]) + if d["instrument_name"] in resolved + else len(resolved) + ), + ) @staticmethod def transform_data( query: DeribitFuturesInfoQueryParams, - data: list, + data: list[dict], **kwargs: Any, ) -> list[DeribitFuturesInfoData]: - """Transform the data.""" - return [DeribitFuturesInfoData(**d) for d in data] + """Transform the data to the model.""" + return [DeribitFuturesInfoData.model_validate(record) for record in data] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/futures_instruments.py b/openbb_platform/providers/deribit/openbb_deribit/models/futures_instruments.py index 58a7fcd5438..9d135edbb50 100644 --- a/openbb_platform/providers/deribit/openbb_deribit/models/futures_instruments.py +++ b/openbb_platform/providers/deribit/openbb_deribit/models/futures_instruments.py @@ -1,104 +1,301 @@ """Deribit Futures Instruments Model.""" -# pylint: disable=unused-argument - from datetime import datetime from typing import Any -from openbb_core.app.model.abstract.error import OpenBBError from openbb_core.provider.abstract.fetcher import Fetcher from openbb_core.provider.standard_models.futures_instruments import ( FuturesInstrumentsData, FuturesInstrumentsQueryParams, ) from openbb_core.provider.utils.descriptions import DATA_DESCRIPTIONS +from openbb_core.provider.utils.errors import EmptyDataError from pydantic import ConfigDict, Field, field_validator +from openbb_deribit.models.instruments import PERPETUAL_EXPIRATION + class DeribitFuturesInstrumentsQueryParams(FuturesInstrumentsQueryParams): - """Deribit Futures Instruments Query.""" + """Deribit Futures Instruments Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_instruments + """ class DeribitFuturesInstrumentData(FuturesInstrumentsData): """Deribit Futures Instrument Data.""" - __alias_dict__ = { - "symbol": "instrument_name", - } + __alias_dict__ = {"symbol": "instrument_name"} model_config = ConfigDict(extra="ignore") - instrument_id: int = Field(description="Deribit Instrument ID") - symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", "")) - base_currency: str = Field(description="The underlying currency being traded.") - counter_currency: str = Field(description="Counter currency for the instrument.") - quote_currency: str = Field( - description="The currency in which the instrument prices are quoted." + symbol: str = Field( + description=DATA_DESCRIPTIONS.get("symbol", ""), + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + instrument_id: int = Field( + description="The numeric identifier of the instrument.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Instrument ID", + "cellDataType": "text", + "chartDataType": "excluded", + }, + }, + ) + kind: str | None = Field( + default=None, + description="The kind of instrument.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + instrument_type: str | None = Field( + default=None, + description="Whether the instrument is linear or reversed.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + future_type: str | None = Field( + default=None, + description="Whether the future is linear or reversed.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + state: str | None = Field( + default=None, + description="Whether the instrument's book is open.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + base_currency: str | None = Field( + default=None, + description="The currency the instrument is based on.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + quote_currency: str | None = Field( + default=None, + description="The currency the instrument is quoted in.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + counter_currency: str | None = Field( + default=None, + description="The counter currency of the instrument.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) settlement_currency: str | None = Field( - default=None, description="Settlement currency for the instrument." + default=None, + description="The currency the instrument settles in.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) - future_type: str = Field(description="Type of the instrument. linear or reversed") settlement_period: str | None = Field( - default=None, description="The settlement period." + default=None, + description="How often the instrument settles.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) - price_index: str = Field( - description="Name of price index that is used for this instrument" + underlying_type: str | None = Field( + default=None, + description="The asset class of the underlying.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) - contract_size: float = Field(description="Contract size for instrument.") - is_active: bool = Field( - description="Indicates if the instrument can currently be traded." + product_group: str | None = Field( + default=None, + description="The margin group the instrument belongs to.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) - creation_timestamp: datetime = Field( - description="The time when the instrument was first created (milliseconds since the UNIX epoch)." + price_index: str | None = Field( + default=None, + description="The index the instrument is priced against.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + lot_size: int | None = Field( + default=None, + description="The number of contracts in one lot.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + min_trade_amount: float | None = Field( + default=None, + description="The smallest amount that can be traded.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + tick_size: float | None = Field( + default=None, + description="The smallest price increment.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_leverage: int | None = Field( + default=None, + description="The highest leverage the instrument allows.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_non_default_leverage: float | None = Field( + default=None, + description="The highest leverage available outside the default tier.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_liquidation_commission: float | None = Field( + default=None, + description="The largest commission a liquidation is charged.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + maker_commission: float | None = Field( + default=None, + description="The commission charged to a resting order.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + taker_commission: float | None = Field( + default=None, + description="The commission charged to an aggressing order.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + block_trade_commission: float | None = Field( + default=None, + description="The commission charged to a block trade.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + block_trade_min_trade_amount: float | None = Field( + default=None, + description="The smallest amount a block trade can be.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + block_trade_tick_size: float | None = Field( + default=None, + description="The smallest price increment of a block trade.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + is_active: bool | None = Field( + default=None, + description="Whether the instrument can be traded right now.", + json_schema_extra={ + "x-widget_config": {"cellDataType": "boolean", "chartDataType": "excluded"}, + }, + ) + contract_size: float | None = Field( + default=None, + description="The size of one contract.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "series"}, + }, + ) + index_id: int | None = Field( + default=None, + description="The numeric identifier of the price index.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Index ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + tick_size_steps: list[dict] | None = Field( + default=None, + description="The price levels above which a different tick size applies.", + json_schema_extra={ + "x-widget_config": { + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + base_currency_uuid: str | None = Field( + default=None, + description="The unique identifier of the base currency.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Base Currency UUID", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + quote_currency_uuid: str | None = Field( + default=None, + description="The unique identifier of the quote currency.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Quote Currency UUID", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + creation_timestamp: datetime | None = Field( + default=None, + description="When the instrument was listed.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) expiration_timestamp: datetime | None = Field( default=None, - description="The time when the instrument will expire (milliseconds since the UNIX epoch).", - ) - tick_size: float = Field( - description="Specifies minimal price change and, as follows, the number of decimal places for instrument prices." - ) - min_trade_amount: float = Field( - description="Minimum amount for trading, in USD units." - ) - max_leverage: int = Field(description="Maximal leverage for instrument.") - max_liquidation_commission: float = Field( - description="Maximal liquidation trade commission for instrument." - ) - block_trade_commission: float = Field( - description="Block Trade commission for instrument." - ) - block_trade_min_trade_amount: float = Field( - description="Minimum amount for block trading." - ) - block_trade_tick_size: float = Field( - description="Specifies minimal price change for block trading." - ) - - maker_commission: float | None = Field( - default=None, description="Maker commission for instrument." - ) - taker_commission: float | None = Field( - default=None, description="Taker commission for instrument." + description="When the instrument expires. Perpetual contracts carry none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) @field_validator("expiration_timestamp", mode="before", check_fields=False) @classmethod - def _validate_expiration(cls, v): - """Validate the expiration timestamp.""" - if int(v) == 32503708800000: - return None - return v + def validate_expiration(cls, v): + """Read the expiration, dropping the sentinel a perpetual carries.""" + return None if not v or int(v) == PERPETUAL_EXPIRATION else v class DeribitFuturesInstrumentsFetcher( Fetcher[DeribitFuturesInstrumentsQueryParams, list[DeribitFuturesInstrumentData]] ): - """Deribit Futures Instruments Fetcher.""" + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False @staticmethod - def transform_query(params: dict[str, Any]) -> DeribitFuturesInstrumentsQueryParams: + def transform_query( + params: dict[str, Any], + ) -> DeribitFuturesInstrumentsQueryParams: """Transform the query.""" return DeribitFuturesInstrumentsQueryParams(**params) @@ -107,27 +304,37 @@ class DeribitFuturesInstrumentsFetcher( query: DeribitFuturesInstrumentsQueryParams, credentials: dict[str, str] | None, **kwargs: Any, - ) -> list: - """Extract data from Deribit API.""" - # pylint: disable=import-outside-toplevel + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange lists no futures. + """ from openbb_deribit.utils.helpers import get_instruments - try: - data = await get_instruments("all", "future") - except Exception as e: # pylint: disable=broad-except - raise OpenBBError(f"Error fetching data: {e}") from e + data = await get_instruments("any", "future") + if not data: - raise OpenBBError( - "There was an error with the request and it was returned empty." - ) + raise EmptyDataError("Deribit lists no futures.") return data @staticmethod def transform_data( query: DeribitFuturesInstrumentsQueryParams, - data: list, + data: list[dict], **kwargs: Any, ) -> list[DeribitFuturesInstrumentData]: - """Transform the data.""" - return [DeribitFuturesInstrumentData.model_validate(d) for d in data] + """Transform the data to the model.""" + return [ + DeribitFuturesInstrumentData.model_validate(record) + for record in sorted( + data, + key=lambda d: ( + d.get("expiration_timestamp") or 0, + str(d.get("instrument_name")), + ), + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/historical_volatility.py b/openbb_platform/providers/deribit/openbb_deribit/models/historical_volatility.py new file mode 100644 index 00000000000..074bb2528a5 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/historical_volatility.py @@ -0,0 +1,159 @@ +"""Deribit Historical Volatility Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import REALIZED_VOLATILITY_CURRENCIES + + +class DeribitHistoricalVolatilityQueryParams(QueryParams): + """Deribit Historical Volatility Query. + + A currency pegged to the unit it is measured against has no volatility to + realize: USDC and USDT read a few tenths of a percent, which is the peg + moving rather than the asset, so neither is offered. EURR is a euro token + measured against the dollar, so what it reads is the euro rate, and it is. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_historical_volatility + """ + + __json_schema_extra__ = { + "currency": { + "multiple_items_allowed": True, + "x-widget_config": { + "options": [ + {"label": name, "value": name} + for name in REALIZED_VOLATILITY_CURRENCIES + ] + }, + } + } + + currency: str = Field( + default="BTC", description="The currency whose realized volatility to read." + ) + + @field_validator("currency", mode="before") + @classmethod + def _listed_currencies(cls, value): + """Accept one currency or several, and refuse any the exchange omits. + + Raises + ------ + ValueError + If a currency the exchange does not publish this for is asked for. + """ + chosen = [ + item.strip().upper() for item in str(value).split(",") if item.strip() + ] + unlisted = [ + item for item in chosen if item not in REALIZED_VOLATILITY_CURRENCIES + ] + + if unlisted: + raise ValueError( + f"Deribit publishes no realized volatility for {unlisted}." + f" It publishes {list(REALIZED_VOLATILITY_CURRENCIES)}." + ) + + return ",".join(chosen) + + +class DeribitHistoricalVolatilityData(Data): + """Deribit Historical Volatility Data.""" + + date: datetime = Field( + description="When the reading was published.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + currency: str = Field( + description="The currency the reading belongs to.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + volatility: float = Field( + description="The realized volatility over the trailing window.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + +class DeribitHistoricalVolatilityFetcher( + Fetcher[ + DeribitHistoricalVolatilityQueryParams, + list[DeribitHistoricalVolatilityData], + ] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query( + params: dict[str, Any], + ) -> DeribitHistoricalVolatilityQueryParams: + """Transform the query.""" + return DeribitHistoricalVolatilityQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitHistoricalVolatilityQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the currency published no readings. + """ + from openbb_deribit.utils.client import gather + + currencies = [c.strip().upper() for c in query.currency.split(",") if c.strip()] + results = await gather( + [ + ("get_historical_volatility", {"currency": currency}) + for currency in currencies + ] + ) + data = [ + {"date": point[0], "currency": currency, "volatility": point[1]} + for currency, result in zip(currencies, results) + if isinstance(result, list) + for point in result + ] + + if not data: + raise EmptyDataError( + f"Deribit published no realized volatility for {', '.join(currencies)}." + ) + + return data + + @staticmethod + def transform_data( + query: DeribitHistoricalVolatilityQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitHistoricalVolatilityData]: + """Transform the data to the model.""" + return [ + DeribitHistoricalVolatilityData.model_validate(record) + for record in sorted(data, key=lambda d: (d["date"], d["currency"])) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/index_historical.py b/openbb_platform/providers/deribit/openbb_deribit/models/index_historical.py new file mode 100644 index 00000000000..4af9fae1c14 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/index_historical.py @@ -0,0 +1,129 @@ +"""Deribit Index Historical Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + INDEX_CHOICES_ENDPOINT, + SYMBOL_STYLE, + IndexChartRanges, +) + + +class DeribitIndexHistoricalQueryParams(QueryParams): + """Deribit Index Historical Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_index_chart_data + """ + + __json_schema_extra__ = { + "index_name": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INDEX_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + index_name: str = Field( + default="btc_usd", + description="One or more index names, such as 'btc_usd'.", + ) + span: IndexChartRanges = Field( + default="1d", description="How far back the series runs." + ) + + +class DeribitIndexHistoricalData(Data): + """Deribit Index Historical Data.""" + + date: datetime = Field( + description="When the level was published.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + index_name: str = Field( + description="The name of the index.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + price: float = Field( + description="The level of the index.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + +class DeribitIndexHistoricalFetcher( + Fetcher[DeribitIndexHistoricalQueryParams, list[DeribitIndexHistoricalData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitIndexHistoricalQueryParams: + """Transform the query.""" + return DeribitIndexHistoricalQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitIndexHistoricalQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the indexes published a series. + """ + from openbb_deribit.utils.client import gather + + names = [n.strip().lower() for n in query.index_name.split(",") if n.strip()] + results = await gather( + [ + ("get_index_chart_data", {"index_name": name, "range": query.span}) + for name in names + ] + ) + data = [ + {"date": point[0], "index_name": name, "price": point[1]} + for name, result in zip(names, results) + if isinstance(result, list) + for point in result + ] + + if not data: + raise EmptyDataError(f"Deribit published no series for {', '.join(names)}.") + + return data + + @staticmethod + def transform_data( + query: DeribitIndexHistoricalQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitIndexHistoricalData]: + """Transform the data to the model.""" + return [ + DeribitIndexHistoricalData.model_validate(record) + for record in sorted(data, key=lambda d: (d["date"], d["index_name"])) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/index_price.py b/openbb_platform/providers/deribit/openbb_deribit/models/index_price.py new file mode 100644 index 00000000000..8463a389c7c --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/index_price.py @@ -0,0 +1,118 @@ +"""Deribit Index Price Model.""" + +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field + +from openbb_deribit.utils.constants import ( + INDEX_CHOICES_ENDPOINT, + SYMBOL_STYLE, +) + + +class DeribitIndexPriceQueryParams(QueryParams): + """Deribit Index Price Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_index_price + """ + + __json_schema_extra__ = { + "index_name": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INDEX_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + index_name: str = Field( + default="btc_usd", + description="One or more index names, such as 'btc_usd'.", + ) + + +class DeribitIndexPriceData(Data): + """Deribit Index Price Data.""" + + index_name: str = Field( + description="The name of the index.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "category", "pinned": "left"} + }, + ) + index_price: float | None = Field( + default=None, + description="The current level of the index.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + estimated_delivery_price: float | None = Field( + default=None, + description="What instruments on the index would deliver at right now.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + + +class DeribitIndexPriceFetcher( + Fetcher[DeribitIndexPriceQueryParams, list[DeribitIndexPriceData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitIndexPriceQueryParams: + """Transform the query.""" + return DeribitIndexPriceQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitIndexPriceQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the indexes published a level. + """ + from openbb_deribit.utils.client import gather + + names = [n.strip().lower() for n in query.index_name.split(",") if n.strip()] + results = await gather( + [("get_index_price", {"index_name": name}) for name in names], + use_cache=False, + ) + + data = [ + {"index_name": name, **result} + for name, result in zip(names, results) + if isinstance(result, dict) and result + ] + + if not data: + raise EmptyDataError(f"Deribit published no level for {', '.join(names)}.") + + return data + + @staticmethod + def transform_data( + query: DeribitIndexPriceQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitIndexPriceData]: + """Transform the data to the model.""" + return [DeribitIndexPriceData.model_validate(record) for record in data] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/instruments.py b/openbb_platform/providers/deribit/openbb_deribit/models/instruments.py new file mode 100644 index 00000000000..0a197880a76 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/instruments.py @@ -0,0 +1,430 @@ +"""Deribit Instruments Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator, model_validator + +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + SYMBOL_STYLE, + AnyListingCurrencies, + InstrumentKinds, +) + +PERPETUAL_EXPIRATION = 32503708800000 + + +class DeribitInstrumentsQueryParams(QueryParams): + """Deribit Instruments Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_instruments + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + symbol: str | None = Field( + default=None, + description="One or more instrument names. When given, the currency and" + + " kind are ignored.", + ) + currency: AnyListingCurrencies = Field( + default="any", description="The settlement currency of the instruments." + ) + kind: InstrumentKinds | None = Field( + default=None, + description="The kind of instrument. Default is all of them.", + ) + expired: bool = Field( + default=False, + description="When True, returns instruments that have already expired.", + ) + + @model_validator(mode="after") + def _options_are_listed_by_underlying(self): + """Refuse a listing of options scoped to a currency that only settles them.""" + from openbb_deribit.utils.helpers import reject_options_by_settlement + + if not self.symbol: + reject_options_by_settlement(self.currency, self.kind) + + return self + + +class DeribitInstrumentsData(Data): + """Deribit Instruments Data.""" + + __alias_dict__ = {"symbol": "instrument_name"} + + symbol: str = Field( + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + instrument_id: int | None = Field( + default=None, + description="The numeric identifier of the instrument.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Instrument ID", + "cellDataType": "text", + "chartDataType": "excluded", + }, + }, + ) + kind: str | None = Field( + default=None, + description="The kind of instrument.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + instrument_type: str | None = Field( + default=None, + description="Whether the instrument is linear or reversed.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + future_type: str | None = Field( + default=None, + description="Whether the future is linear or reversed.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + state: str | None = Field( + default=None, + description="Whether the instrument's book is open.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + base_currency: str | None = Field( + default=None, + description="The currency the instrument is based on.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + quote_currency: str | None = Field( + default=None, + description="The currency the instrument is quoted in.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + counter_currency: str | None = Field( + default=None, + description="The counter currency of the instrument.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + settlement_currency: str | None = Field( + default=None, + description="The currency the instrument settles in.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + settlement_period: str | None = Field( + default=None, + description="How often the instrument settles.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + underlying_type: str | None = Field( + default=None, + description="The asset class of the underlying.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + product_group: str | None = Field( + default=None, + description="The margin group the instrument belongs to.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + price_index: str | None = Field( + default=None, + description="The index the instrument is priced against.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + lot_size: int | None = Field( + default=None, + description="The number of contracts in one lot.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + min_trade_amount: float | None = Field( + default=None, + description="The smallest amount that can be traded.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + tick_size: float | None = Field( + default=None, + description="The smallest price increment.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_leverage: int | None = Field( + default=None, + description="The highest leverage the instrument allows.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_non_default_leverage: float | None = Field( + default=None, + description="The highest leverage available outside the default tier.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_liquidation_commission: float | None = Field( + default=None, + description="The largest commission a liquidation is charged.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + maker_commission: float | None = Field( + default=None, + description="The commission charged to a resting order.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + taker_commission: float | None = Field( + default=None, + description="The commission charged to an aggressing order.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + block_trade_commission: float | None = Field( + default=None, + description="The commission charged to a block trade.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + block_trade_min_trade_amount: float | None = Field( + default=None, + description="The smallest amount a block trade can be.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + block_trade_tick_size: float | None = Field( + default=None, + description="The smallest price increment of a block trade.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + option_type: str | None = Field( + default=None, + description="Whether the option is a call or a put.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + strike: float | None = Field( + default=None, + description="The strike price of the option.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + is_active: bool | None = Field( + default=None, + description="Whether the instrument can be traded right now.", + json_schema_extra={ + "x-widget_config": {"cellDataType": "boolean", "chartDataType": "excluded"}, + }, + ) + contract_size: float | None = Field( + default=None, + description="The size of one contract.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "series"}, + }, + ) + is_csr: bool | None = Field( + default=None, + description="Whether the instrument is a cross settled routed pair.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Is CSR", + "cellDataType": "boolean", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + is_cbe_routed: bool | None = Field( + default=None, + description="Whether the instrument routes to Coinbase Exchange liquidity.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Is CBE Routed", + "cellDataType": "boolean", + "chartDataType": "excluded", + }, + }, + ) + index_id: int | None = Field( + default=None, + description="The numeric identifier of the price index.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Index ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + tick_size_steps: list[dict] | None = Field( + default=None, + description="The price levels above which a different tick size applies.", + json_schema_extra={ + "x-widget_config": { + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + base_currency_uuid: str | None = Field( + default=None, + description="The unique identifier of the base currency.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Base Currency UUID", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + quote_currency_uuid: str | None = Field( + default=None, + description="The unique identifier of the quote currency.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Quote Currency UUID", + "chartDataType": "excluded", + "hide": True, + }, + }, + ) + creation_timestamp: datetime | None = Field( + default=None, + description="When the instrument was listed.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + expiration_timestamp: datetime | None = Field( + default=None, + description="When the instrument expires. Perpetual and spot instruments carry none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + + @field_validator( + "creation_timestamp", "expiration_timestamp", mode="before", check_fields=False + ) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime, dropping the perpetual sentinel.""" + from openbb_deribit.utils.helpers import from_timestamp + + return None if not v or int(v) == PERPETUAL_EXPIRATION else from_timestamp(v) + + +class DeribitInstrumentsFetcher( + Fetcher[DeribitInstrumentsQueryParams, list[DeribitInstrumentsData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitInstrumentsQueryParams: + """Transform the query.""" + return DeribitInstrumentsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitInstrumentsQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange lists no matching instrument. + """ + from openbb_deribit.utils.client import gather + from openbb_deribit.utils.helpers import get_instruments + + if query.symbol: + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + results = await gather( + [("get_instrument", {"instrument_name": s}) for s in symbols] + ) + data = [result for result in results if isinstance(result, dict) and result] + else: + data = await get_instruments(query.currency, query.kind, query.expired) + + if not data: + raise EmptyDataError("Deribit lists no instrument matching the query.") + + return data + + @staticmethod + def transform_data( + query: DeribitInstrumentsQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitInstrumentsData]: + """Transform the data to the model.""" + return [ + DeribitInstrumentsData.model_validate(record) + for record in sorted( + data, + key=lambda d: ( + str(d.get("kind")), + d.get("expiration_timestamp") or 0, + str(d.get("instrument_name")), + ), + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/options_chains.py b/openbb_platform/providers/deribit/openbb_deribit/models/options_chains.py index 379c2e07135..e46d4f455ac 100644 --- a/openbb_platform/providers/deribit/openbb_deribit/models/options_chains.py +++ b/openbb_platform/providers/deribit/openbb_deribit/models/options_chains.py @@ -1,44 +1,58 @@ """Deribit Options Chains Model.""" -# pylint: disable=unused-argument - -from datetime import datetime +from datetime import ( + date as dateType, + datetime, +) from typing import Any -from openbb_core.app.model.abstract.error import OpenBBError from openbb_core.provider.abstract.fetcher import Fetcher from openbb_core.provider.standard_models.options_chains import ( OptionsChainsData, OptionsChainsQueryParams, ) from openbb_core.provider.utils.errors import EmptyDataError -from openbb_deribit.utils.helpers import DERIBIT_OPTIONS_SYMBOLS from pydantic import Field, field_validator +from openbb_deribit.utils.constants import ( + SYMBOL_STYLE, + UNDERLYING_CHOICES_ENDPOINT, +) + +INVERSE_QUOTED = ("BTC", "ETH") +PRICE_FIELDS = ( + "last_price", + "settlement_price", + "mark_price", + "min_price", + "max_price", + "best_ask_price", + "best_bid_price", + "high", + "low", +) + class DeribitOptionsChainsQueryParams(OptionsChainsQueryParams): - """Deribit Options Chains Query Parameters Model.""" + """Deribit Options Chains Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-ticker + """ __json_schema_extra__ = { "symbol": { "multiple_items_allowed": False, - "choices": DERIBIT_OPTIONS_SYMBOLS, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": UNDERLYING_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, } } - @field_validator("symbol", mode="before", check_fields=False) - @classmethod - def _validate_symbol(cls, v): - """Validate the symbol.""" - if v.upper() not in DERIBIT_OPTIONS_SYMBOLS: - raise ValueError( - f"Invalid Deribit symbol. Supported symbols are: {', '.join(DERIBIT_OPTIONS_SYMBOLS)}", - ) - return v - class DeribitOptionsChainsData(OptionsChainsData): - """Deribit Options Chains Data Model.""" + """Deribit Options Chains Data.""" __alias_dict__ = { "contract_symbol": "instrument_name", @@ -57,60 +71,459 @@ class DeribitOptionsChainsData(OptionsChainsData): __doc__ = OptionsChainsData.__doc__ - bid_iv: list[float | None] = Field( - default_factory=list, - description="The implied volatility of the bid price.", - json_schema_extra={"x-unit_measurement": "decimal"}, + contract_symbol: list[str] = Field( + description="The symbol of the contract.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Contract", + "chartDataType": "excluded", + "pinned": "left", + }, + }, ) - ask_iv: list[float | None] = Field( - default_factory=list, - description="The implied volatility of the ask price.", - json_schema_extra={"x-unit_measurement": "decimal"}, + expiration: list[dateType] = Field( + description="The expiration date of the contract.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) - interest_rate: list[float | None] = Field( - default_factory=list, - description="The interest rate used by Deribit to calculate greeks.", + dte: list[int | None] = Field( + description="The number of days until expiration.", + json_schema_extra={ + "x-widget_config": {"headerName": "DTE", "chartDataType": "excluded"}, + }, ) - underlying_spot_price: list[float] = Field( - description="The spot price of the underlying asset." - " The underlying asset is the specific future or index that the option is based on.", - json_schema_extra={"x-unit_measurement": "currency"}, + strike: list[float] = Field( + description="The strike price of the contract.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "category"}, + }, ) - settlement_price: list[float | None] = Field( - default_factory=list, - description="The settlement price of the contract.", - json_schema_extra={"x-unit_measurement": "currency"}, + option_type: list[str] = Field( + description="Whether the contract is a call or a put.", + json_schema_extra={ + "x-widget_config": {"headerName": "Type", "chartDataType": "excluded"}, + }, ) - min_price: list[float | None] = Field( - default_factory=list, - description="The minimum price allowed.", - json_schema_extra={"x-unit_measurement": "currency"}, + mark: list[float | None] = Field( + description="The mark price of the contract.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, ) - max_price: list[float | None] = Field( - default_factory=list, - description="The maximum price allowed.", - json_schema_extra={"x-unit_measurement": "currency"}, + bid: list[float | None] = Field( + description="The highest price bid.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + bid_size: list[int | float | None] = Field( + description="The size resting at the best bid.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + ask: list[float | None] = Field( + description="The lowest price offered.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + ask_size: list[int | float | None] = Field( + description="The size resting at the best offer.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + last_trade_price: list[float | None] = Field( + description="The price the contract last traded at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + open_interest: list[int | float | None] = Field( + description="The contracts left outstanding.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + volume: list[int | float | None] = Field( + description="The volume of the session.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) volume_notional: list[float | None] = Field( + description="The notional volume of the contract, in USD or USDC.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + high: list[float | None] = Field( + description="The highest price of the session.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + low: list[float | None] = Field( + description="The lowest price of the session.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + change_percent: list[float | None] = Field( + description="The price change over the session.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + implied_volatility: list[float | None] = Field( + description="The volatility implied by the mark price.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"headerName": "Mark IV", "chartDataType": "excluded"}, + }, + ) + bid_iv: list[float | None] = Field( + description="The volatility implied by the bid price.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"headerName": "Bid IV", "chartDataType": "excluded"}, + }, + ) + ask_iv: list[float | None] = Field( + description="The volatility implied by the ask price.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"headerName": "Ask IV", "chartDataType": "excluded"}, + }, + ) + delta: list[float | None] = Field( + description="The sensitivity to the underlying's price.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + gamma: list[float | None] = Field( + description="The sensitivity of delta to the underlying's price.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + theta: list[float | None] = Field( + description="The sensitivity to the passage of time.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + vega: list[float | None] = Field( + description="The sensitivity to implied volatility.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + rho: list[float | None] = Field( + description="The sensitivity to the interest rate.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + interest_rate: list[float | None] = Field( + description="The rate the exchange prices the greeks at.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + settlement_price: list[float | None] = Field( + description="The price the contract last settled at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + min_price: list[float | None] = Field( + description="The lowest price an order will be accepted at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_price: list[float | None] = Field( + description="The highest price an order will be accepted at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + underlying_symbol: list[str | None] = Field( + description="The future or index the contract is priced against.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + underlying_price: list[float | None] = Field( + description="The price of the underlying the contract is priced against.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + underlying_spot_price: list[float] = Field( + description="The spot price of the underlying the option is based on.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + contract_size: list[int | float | None] = Field( + description="The size of one contract, in units of the underlying.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + is_inverse: list[bool | None] = Field( default_factory=list, - description="The notional trading volume of the contract, as USD or USDC.", - json_schema_extra={"x-unit_measurement": "currency"}, + description="Whether the contract settles in the underlying rather than" + + " in the quote currency, which changes how its payoff is valued.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Is Inverse", + "cellDataType": "boolean", + "chartDataType": "excluded", + } + }, ) timestamp: list[datetime] = Field( - description="The datetime of the data, as America/New_York time.", + description="When the quote was published, as America/New_York time.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded"}, + }, ) + eod_date: list[dateType | None] = Field( + default_factory=list, + description="The end-of-day date. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": { + "headerName": "EOD Date", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + theoretical_price: list[float | None] = Field( + default_factory=list, + description="The theoretical price of the contract. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + last_trade_size: list[int | float | None] = Field( + default_factory=list, + description="The size of the last trade. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + last_trade_time: list[datetime | None] = Field( + default_factory=list, + description="When the last trade printed. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + tick: list[str | None] = Field( + default_factory=list, + description="The direction of the last price move. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + bid_time: list[datetime | None] = Field( + default_factory=list, + description="When the bid was published. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + bid_exchange: list[str | None] = Field( + default_factory=list, + description="The exchange the bid came from. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + ask_time: list[datetime | None] = Field( + default_factory=list, + description="When the offer was published. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + ask_exchange: list[str | None] = Field( + default_factory=list, + description="The exchange the offer came from. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + open: list[float | None] = Field( + default_factory=list, + description="The opening price of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + open_bid: list[float | None] = Field( + default_factory=list, + description="The opening bid of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + open_ask: list[float | None] = Field( + default_factory=list, + description="The opening offer of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + bid_high: list[float | None] = Field( + default_factory=list, + description="The highest bid of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + ask_high: list[float | None] = Field( + default_factory=list, + description="The highest offer of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + bid_low: list[float | None] = Field( + default_factory=list, + description="The lowest bid of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + ask_low: list[float | None] = Field( + default_factory=list, + description="The lowest offer of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close: list[float | None] = Field( + default_factory=list, + description="The closing price of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_size: list[int | float | None] = Field( + default_factory=list, + description="The size of the closing trade. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_time: list[datetime | None] = Field( + default_factory=list, + description="When the closing trade printed. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_bid: list[float | None] = Field( + default_factory=list, + description="The closing bid of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_bid_size: list[int | float | None] = Field( + default_factory=list, + description="The size at the closing bid. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_bid_time: list[datetime | None] = Field( + default_factory=list, + description="When the closing bid was published. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_ask: list[float | None] = Field( + default_factory=list, + description="The closing offer of the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_ask_size: list[int | float | None] = Field( + default_factory=list, + description="The size at the closing offer. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + close_ask_time: list[datetime | None] = Field( + default_factory=list, + description="When the closing offer was published. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + prev_close: list[float | None] = Field( + default_factory=list, + description="The closing price of the previous session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + change: list[float | None] = Field( + default_factory=list, + description="The price change over the session. Deribit publishes none.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + + @field_validator( + "interest_rate", + mode="before", + check_fields=False, + ) + @classmethod + def as_percent(cls, v): + """Return a rate the exchange sends as a fraction in percent units.""" + return v * 100 if v is not None else None class DeribitOptionsChainsFetcher( Fetcher[DeribitOptionsChainsQueryParams, DeribitOptionsChainsData] ): - """Deribit Options Chains Fetcher.""" + """Transform the query, extract and transform the data from the Deribit endpoint.""" require_credentials = False @staticmethod def transform_query(params: dict[str, Any]) -> DeribitOptionsChainsQueryParams: - """Transform the query parameters.""" + """Transform the query.""" return DeribitOptionsChainsQueryParams(**params) @staticmethod @@ -119,126 +532,87 @@ class DeribitOptionsChainsFetcher( credentials: dict[str, str] | None, **kwargs: Any, ) -> list[dict]: - """Extract the data.""" - # pylint: disable=import-outside-toplevel - import asyncio # noqa - import json - import websockets - from openbb_deribit.utils.helpers import get_options_symbols - from pandas import to_datetime - from websockets.asyncio.client import connect + """Return the raw data from the Deribit websocket. + + Raises + ------ + OpenBBError + If every expiration failed. + EmptyDataError + If the subscriptions returned nothing at all. + """ + import asyncio from warnings import warn - # We need to identify each option contract in order to fetch the chains data. - symbols_dict: dict[str, str] = {} + from openbb_core.app.model.abstract.error import OpenBBError + from pandas import to_datetime - try: - symbols_dict = await get_options_symbols(query.symbol) # type: ignore - except OpenBBError as e: - raise OpenBBError(e) from e + from openbb_deribit.utils.helpers import ( + flatten_ticker, + get_instruments, + get_options_symbols, + ) + from openbb_deribit.utils.websocket import subscribe_tickers - # For each expiration, we need to create a websocket connection to fetch the data. - # We subscribe to each contract symbol and break the connection when we have all the data for an expiry. - # If it takes too long, we break the connection and return an error message. - results: list = [] + by_expiration = await get_options_symbols(query.symbol) + specs = { + str(d["instrument_name"]): d for d in await get_instruments("any", "option") + } messages: set = set() + subscriptions = await asyncio.gather( + *( + subscribe_tickers(symbols, messages) + for symbols in by_expiration.values() + ), + return_exceptions=True, + ) + today = to_datetime("today").date() + results: list[dict] = [] - async def call_api(expiration): - """Call the Deribit API.""" - symbols = symbols_dict[expiration] - received_symbols: set = set() - msg = { - "jsonrpc": "2.0", - "id": 3600, - "method": "public/subscribe", - "params": {"channels": ["ticker." + d + ".100ms" for d in symbols]}, - } - async with connect("wss://www.deribit.com/ws/api/v2") as websocket: - await websocket.send(json.dumps(msg)) - try: - await asyncio.wait_for( - receive_data(websocket, symbols, received_symbols), timeout=2.0 - ) - except asyncio.TimeoutError: - messages.add(f"Timeout reached for {expiration}, data incomplete.") + for subscription in subscriptions: + if not isinstance(subscription, dict): + continue - async def receive_data(websocket, symbols, received_symbols): - """Receive the data from the websocket with a timeout.""" - while True: - try: - response = await websocket.recv() - except websockets.ConnectionClosed: - break - data = json.loads(response) + for symbol, ticker in subscription.items(): + parts = symbol.split("-") + strike = parts[2].replace("d", ".") + flat = flatten_ticker(ticker) + flat.pop("state", None) + published = flat.pop("timestamp", None) - if "params" not in data: - continue + if flat.get("underlying_index") == "index_price": + flat["underlying_index"] = parts[0].replace("_", "-") - if "error" in data and data.get("error"): - messages.add(f"Error while receiving data -> {data['error']}") - break + if flat.get("estimated_delivery_price") == flat.get("index_price"): + flat.pop("estimated_delivery_price", None) - res = data.get("params", {}).get("data", {}) - symbol = res.get("instrument_name") - - # While we are handling the data, we will parse the message. - if symbol not in received_symbols: - received_symbols.add(symbol) - stats = res.pop("stats", {}) - greeks = res.pop("greeks", {}) - timestamp = res.pop("timestamp", None) - underlying_symbol = res.get("underlying_index") - - if underlying_symbol == "index_price": - res["underlying_index"] = symbol.split("-")[0].replace("_", "-") - - res["timestamp"] = to_datetime( - timestamp, unit="ms", utc=True - ).tz_convert("America/New_York") - - if res.get("estimated_delivery_price") == res.get("index_price"): - _ = res.pop("estimated_delivery_price", None) - - _ = res.pop("state", None) - result = { - "expiration": to_datetime(symbol.split("-")[1]).date(), - "strike": ( - float(symbol.split("-")[2].replace("d", ".")) - if "d" in symbol.split("-")[2] - else int(symbol.split("-")[2]) - ), - "option_type": ( - "call" - if symbol.endswith("-C") - else "put" if symbol.endswith("-P") else None - ), - **res, - **stats, - **greeks, + expiration = to_datetime(parts[1]).date() + spec = specs.get(symbol, {}) + results.append( + { + "expiration": expiration, + "strike": float(strike), + "option_type": "call" if symbol.endswith("-C") else "put", + "dte": (expiration - today).days, + "contract_size": spec.get("contract_size"), + "is_inverse": spec.get("instrument_type") == "reversed", + "timestamp": to_datetime( + published, unit="ms", utc=True + ).tz_convert("America/New_York"), + **flat, } - result["dte"] = ( - result["expiration"] - to_datetime("today").date() - ).days - results.append(result) - - if len(received_symbols) == len(symbols): - await websocket.close() - break - - tasks = [ - asyncio.create_task(call_api(expiration)) for expiration in symbols_dict - ] - await asyncio.gather(*tasks, return_exceptions=True) + ) if messages and not results: - raise OpenBBError(", ".join(messages)) + raise OpenBBError(", ".join(sorted(messages))) - if results and messages: - for message in messages: - warn(message) + for message in sorted(messages): + warn(message) - if not results and not messages: - raise EmptyDataError("All requests returned empty with no error messages.") + if not results: + raise EmptyDataError( + f"Deribit published no option quotes for {query.symbol}." + ) return results @@ -248,34 +622,22 @@ class DeribitOptionsChainsFetcher( data: list[dict], **kwargs: Any, ) -> DeribitOptionsChainsData: - """Transform the data.""" - # pylint: disable=import-outside-toplevel + """Transform the data to the model. + + Contracts on BTC and ETH are quoted in the underlying, so their prices are + carried to USD at the index level the quote was published against. + """ from numpy import nan from pandas import DataFrame - df = DataFrame(data) + frame = DataFrame(data) + inverse = bool(frame["is_inverse"].any()) - # For BTC and ETH options, we need to convert price units to USD. - for col in df.columns: - if col in [ - "last_price", - "settlement_price", - "mark_price", - "min_price", - "max_price", - "best_ask_price", - "best_bid_price", - "high", - "low", - ] and query.symbol.upper() in ["BTC", "ETH"]: - df[col] = (df[col].astype(float) * df["index_price"]).round(2) - elif col in ["price_change", "mark_iv", "bid_iv", "ask_iv"]: - df[col] = df[col].astype(float) / 100 + for column in frame.columns: + if column in PRICE_FIELDS and inverse: + frame[column] = frame[column].astype(float) * frame["index_price"] - df = df.replace({nan: None}) - df = df.sort_values(["expiration", "strike", "option_type"]) - df = df.reset_index(drop=True) - df["contract_size"] = 1 - results = df.to_dict(orient="list") - - return DeribitOptionsChainsData.model_validate(results) + frame = frame.replace({nan: None}) + frame = frame.sort_values(["expiration", "strike", "option_type"]) + frame = frame.reset_index(drop=True) + return DeribitOptionsChainsData.model_validate(frame.to_dict(orient="list")) diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/order_book.py b/openbb_platform/providers/deribit/openbb_deribit/models/order_book.py new file mode 100644 index 00000000000..68c0c4014eb --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/order_book.py @@ -0,0 +1,261 @@ +"""Deribit Order Book Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + ORDER_BOOK_DEPTHS, + SYMBOL_STYLE, +) + + +class DeribitOrderBookQueryParams(QueryParams): + """Deribit Order Book Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_order_book + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + }, + "depth": { + "x-widget_config": { + "options": [ + {"label": str(depth), "value": depth} for depth in ORDER_BOOK_DEPTHS + ] + } + }, + } + + symbol: str | None = Field( + default=None, + description="One or more instrument names. A perpetual can also be given" + + " by its shortened root, such as 'SOLUSDC'.", + ) + instrument_id: int | None = Field( + default=None, + description="The numeric identifier of one instrument, used in place of" + + " its name.", + ) + depth: int = Field( + default=10, + description="The number of levels to return on each side: 1, 5, 10, 20, 50," + + " 100, 1000, or 10000.", + ) + + @field_validator("depth", check_fields=False) + @classmethod + def validate_depth(cls, v): + """Refuse a depth the exchange does not offer. + + Raises + ------ + ValueError + If the depth is not one Deribit accepts. + """ + if v not in ORDER_BOOK_DEPTHS: + raise ValueError( + f"Deribit returns a book {', '.join(map(str, ORDER_BOOK_DEPTHS))}" + f" levels deep, not {v}." + ) + + return v + + +class DeribitOrderBookData(Data): + """Deribit Order Book Data.""" + + symbol: str = Field( + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + timestamp: datetime | None = Field( + default=None, + description="When the book was published.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + side: str = Field( + description="Whether the level is a bid or an ask.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + level: int = Field( + description="How far the level sits from the top of the book.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + price: float = Field( + description="The price of the level.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "category", "pinned": "left"}, + }, + ) + size: float = Field( + description="The size resting at the level.", + json_schema_extra={"x-widget_config": {"chartDataType": "series"}}, + ) + mark_price: float | None = Field( + default=None, + description="The price the exchange marks positions at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + index_price: float | None = Field( + default=None, + description="The price of the index the instrument is priced against.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + change_id: int | None = Field( + default=None, + description="The revision of the book the level came from.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Change ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + + @field_validator("timestamp", mode="before", check_fields=False) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) if v else None + + +class DeribitOrderBookFetcher( + Fetcher[DeribitOrderBookQueryParams, list[DeribitOrderBookData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitOrderBookQueryParams: + """Transform the query. + + Raises + ------ + OpenBBError + If neither a symbol nor an instrument ID was given. + """ + from openbb_core.app.model.abstract.error import OpenBBError + + query = DeribitOrderBookQueryParams(**params) + + if not query.symbol and query.instrument_id is None: + raise OpenBBError("Give either a symbol or an instrument ID.") + + return query + + @staticmethod + async def aextract_data( + query: DeribitOrderBookQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the instruments published a book. + """ + from openbb_deribit.utils.client import gather + from openbb_deribit.utils.helpers import get_perpetual_symbols + + calls: list[tuple[str, dict[str, Any] | None]] + + if query.symbol: + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + calls = [ + ( + "get_order_book", + { + "instrument_name": perpetuals.get(symbol, symbol), + "depth": query.depth, + }, + ) + for symbol in symbols + ] + else: + calls = [ + ( + "get_order_book_by_instrument_id", + { + "instrument_id": query.instrument_id, + "depth": query.depth, + }, + ) + ] + + results = await gather(calls, use_cache=False) + data = [result for result in results if isinstance(result, dict) and result] + + if not data: + raise EmptyDataError("Deribit published no book for the query.") + + return data + + @staticmethod + def transform_data( + query: DeribitOrderBookQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitOrderBookData]: + """Transform the data to the model. + + Raises + ------ + EmptyDataError + If every book came back with no resting orders. + """ + records: list[dict] = [] + + for book in data: + context = { + "symbol": book.get("instrument_name"), + "timestamp": book.get("timestamp"), + "mark_price": book.get("mark_price"), + "index_price": book.get("index_price"), + "change_id": book.get("change_id"), + } + + for side in ("bids", "asks"): + for level, entry in enumerate(book.get(side) or [], start=1): + records.append( + { + **context, + "side": side[:-1], + "level": level, + "price": entry[0], + "size": entry[1], + } + ) + + if not records: + raise EmptyDataError("Deribit published no resting orders for the query.") + + return [DeribitOrderBookData.model_validate(record) for record in records] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/settlements.py b/openbb_platform/providers/deribit/openbb_deribit/models/settlements.py new file mode 100644 index 00000000000..1c0f60db424 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/settlements.py @@ -0,0 +1,244 @@ +"""Deribit Settlements Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + MAX_SETTLEMENT_COUNT, + SYMBOL_STYLE, + Currencies, + SettlementTypes, +) + + +class DeribitSettlementsQueryParams(QueryParams): + """Deribit Settlements Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_last_settlements_by_currency + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + symbol: str | None = Field( + default=None, + description="One or more instrument names. When given, the currency is" + + " ignored.", + ) + currency: Currencies = Field( + default="BTC", description="The settlement currency of the instruments." + ) + settlement_type: SettlementTypes | None = Field( + default=None, + description="The kind of settlement. Default is all of them.", + ) + start_date: Any | None = Field( + default=None, description="Search back no further than this date." + ) + limit: int = Field( + default=100, + description="The number of settlements to return, at most" + f" {MAX_SETTLEMENT_COUNT}.", + ge=1, + le=MAX_SETTLEMENT_COUNT, + ) + + +class DeribitSettlementsData(Data): + """Deribit Settlements Data.""" + + __alias_dict__ = {"symbol": "instrument_name", "settlement_type": "type"} + + timestamp: datetime = Field( + description="When the instrument settled.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "category", "pinned": "left"} + }, + ) + symbol: str | None = Field( + default=None, + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + settlement_type: str | None = Field( + default=None, + description="Whether the event was a settlement or a delivery.", + json_schema_extra={ + "x-widget_config": {"headerName": "Type", "chartDataType": "excluded"} + }, + ) + position: float | None = Field( + default=None, + description="The size that settled.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + index_price: float | None = Field( + default=None, + description="The level of the index at settlement.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + mark_price: float | None = Field( + default=None, + description="The price the instrument settled at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + profit_loss: float | None = Field( + default=None, + description="The profit and loss the settlement realized.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Profit & Loss", + "chartDataType": "excluded", + "renderFn": "greenRed", + } + }, + ) + session_profit_loss: float | None = Field( + default=None, + description="The profit and loss of the whole session.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Session Profit & Loss", + "chartDataType": "excluded", + "renderFn": "greenRed", + } + }, + ) + funding: float | None = Field( + default=None, + description="The funding the session paid.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + session_bankruptcy: float | None = Field( + default=None, + description="The loss the session could not cover.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + session_tax: float | None = Field( + default=None, + description="The socialised loss the session was charged.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + session_tax_rate: float | None = Field( + default=None, + description="The rate the socialised loss was charged at.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + socialized: float | None = Field( + default=None, + description="The loss spread across other accounts.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + + @field_validator("timestamp", mode="before", check_fields=False) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + +class DeribitSettlementsFetcher( + Fetcher[DeribitSettlementsQueryParams, list[DeribitSettlementsData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitSettlementsQueryParams: + """Transform the query.""" + return DeribitSettlementsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitSettlementsQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If nothing settled over the span. + """ + from openbb_deribit.utils.client import gather, request + from openbb_deribit.utils.helpers import get_perpetual_symbols, to_timestamp + + shared = { + "type": query.settlement_type, + "count": query.limit, + "search_start_timestamp": ( + to_timestamp(query.start_date) if query.start_date else None + ), + } + + if query.symbol: + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + results = await gather( + [ + ( + "get_last_settlements_by_instrument", + { + "instrument_name": perpetuals.get(symbol, symbol), + **shared, + }, + ) + for symbol in symbols + ] + ) + data = [ + record + for result in results + if isinstance(result, dict) + for record in result.get("settlements") or [] + ] + else: + result = await request( + "get_last_settlements_by_currency", + {"currency": query.currency.upper(), **shared}, + ) + data = (result or {}).get("settlements") or [] + + if not data: + raise EmptyDataError("Deribit reports nothing settled over the span.") + + return data + + @staticmethod + def transform_data( + query: DeribitSettlementsQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitSettlementsData]: + """Transform the data to the model.""" + return [ + DeribitSettlementsData.model_validate(record) + for record in sorted( + data, key=lambda d: d.get("timestamp") or 0, reverse=True + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/ticker.py b/openbb_platform/providers/deribit/openbb_deribit/models/ticker.py new file mode 100644 index 00000000000..212e47e4602 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/ticker.py @@ -0,0 +1,401 @@ +"""Deribit Ticker Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + SYMBOL_STYLE, +) + + +class DeribitTickerQueryParams(QueryParams): + """Deribit Ticker Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-ticker + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + symbol: str = Field( + description="One or more instrument names. A perpetual can also be given" + + " by its shortened root, such as 'SOLUSDC'." + ) + + +class DeribitTickerData(Data): + """Deribit Ticker Data.""" + + __alias_dict__ = { + "symbol": "instrument_name", + "change_percent": "price_change", + "implied_volatility": "mark_iv", + "bid": "best_bid_price", + "ask": "best_ask_price", + "bid_size": "best_bid_amount", + "ask_size": "best_ask_amount", + "last_price": "last_price", + } + + symbol: str = Field( + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + timestamp: datetime | None = Field( + default=None, + description="When the quote was published.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + state: str | None = Field( + default=None, + description="Whether the instrument's book is open.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + combo_state: str | None = Field( + default=None, + description="Whether the combo is still tradeable.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + last_price: float | None = Field( + default=None, + description="The price the instrument last traded at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + bid: float | None = Field( + default=None, + description="The highest price bid.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + ask: float | None = Field( + default=None, + description="The lowest price offered.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + bid_size: float | None = Field( + default=None, + description="The size resting at the best bid.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + ask_size: float | None = Field( + default=None, + description="The size resting at the best offer.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + implied_bid: float | None = Field( + default=None, + description="The bid implied by the legs of a combo.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + implied_ask: float | None = Field( + default=None, + description="The offer implied by the legs of a combo.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + mark_price: float | None = Field( + default=None, + description="The price the exchange marks positions at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + index_price: float | None = Field( + default=None, + description="The price of the index the instrument is priced against.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + settlement_price: float | None = Field( + default=None, + description="The price the instrument last settled at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + delivery_price: float | None = Field( + default=None, + description="The price an expired instrument was delivered at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + estimated_delivery_price: float | None = Field( + default=None, + description="What the instrument would deliver at right now.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + min_price: float | None = Field( + default=None, + description="The lowest price an order will be accepted at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + max_price: float | None = Field( + default=None, + description="The highest price an order will be accepted at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + high: float | None = Field( + default=None, + description="The highest price of the last 24 hours.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + low: float | None = Field( + default=None, + description="The lowest price of the last 24 hours.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + change_percent: float | None = Field( + default=None, + description="The price change over the last 24 hours.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + volume: float | None = Field( + default=None, + description="The volume of the last 24 hours, in base currency.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + volume_notional: float | None = Field( + default=None, + description="The volume of the last 24 hours, in quote currency.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + volume_usd: float | None = Field( + default=None, + description="The volume of the last 24 hours, in USD.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": { + "headerName": "Volume USD", + "chartDataType": "excluded", + }, + }, + ) + open_interest: float | None = Field( + default=None, + description="The contracts left outstanding.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + current_funding: float | None = Field( + default=None, + description="The funding rate a perpetual is paying right now.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + funding_8h: float | None = Field( + default=None, + description="The funding rate a perpetual paid over eight hours.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": { + "headerName": "Funding 8H", + "chartDataType": "excluded", + }, + }, + ) + interest_value: float | None = Field( + default=None, + description="The value realized funding on a perpetual is derived from.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + interest_rate: float | None = Field( + default=None, + description="The rate the exchange prices the option's greeks at.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + underlying_index: str | None = Field( + default=None, + description="The future or index the option is priced against.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + underlying_price: float | None = Field( + default=None, + description="The price of the underlying the option is priced against.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + implied_volatility: float | None = Field( + default=None, + description="The volatility implied by the mark price.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"headerName": "Mark IV", "chartDataType": "excluded"}, + }, + ) + bid_iv: float | None = Field( + default=None, + description="The volatility implied by the best bid.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"headerName": "Bid IV", "chartDataType": "excluded"}, + }, + ) + ask_iv: float | None = Field( + default=None, + description="The volatility implied by the best offer.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"headerName": "Ask IV", "chartDataType": "excluded"}, + }, + ) + delta: float | None = Field( + default=None, + description="The sensitivity to the underlying's price.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + gamma: float | None = Field( + default=None, + description="The sensitivity of delta to the underlying's price.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + vega: float | None = Field( + default=None, + description="The sensitivity to implied volatility.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + theta: float | None = Field( + default=None, + description="The sensitivity to the passage of time.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + rho: float | None = Field( + default=None, + description="The sensitivity to the interest rate.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + + @field_validator("timestamp", mode="before", check_fields=False) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) if v else None + + @field_validator( + "current_funding", + "funding_8h", + "interest_rate", + mode="before", + check_fields=False, + ) + @classmethod + def as_percent(cls, v): + """Return a rate the exchange sends as a fraction in percent units.""" + return v * 100 if v is not None else None + + +class DeribitTickerFetcher(Fetcher[DeribitTickerQueryParams, list[DeribitTickerData]]): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitTickerQueryParams: + """Transform the query.""" + return DeribitTickerQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitTickerQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If none of the instruments published a quote. + """ + from openbb_deribit.utils.client import gather + from openbb_deribit.utils.helpers import get_perpetual_symbols + + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + resolved = [perpetuals.get(symbol, symbol) for symbol in symbols] + results = await gather( + [("ticker", {"instrument_name": s}) for s in resolved], use_cache=False + ) + data = [result for result in results if isinstance(result, dict) and result] + + if not data: + raise EmptyDataError( + f"Deribit published no quote for {', '.join(resolved)}." + ) + + return data + + @staticmethod + def transform_data( + query: DeribitTickerQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitTickerData]: + """Transform the data to the model.""" + from openbb_deribit.utils.helpers import flatten_ticker + + return [ + DeribitTickerData.model_validate(flatten_ticker(record)) for record in data + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/trade_volumes.py b/openbb_platform/providers/deribit/openbb_deribit/models/trade_volumes.py new file mode 100644 index 00000000000..9080dc2d6b6 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/trade_volumes.py @@ -0,0 +1,222 @@ +"""Deribit Trade Volumes Model.""" + +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field + +DAY_VOLUMES = ("futures_volume", "calls_volume", "puts_volume", "spot_volume") + + +def _traded(record: dict) -> float: + """Return everything that traded in one currency over the last day.""" + return sum(float(record.get(field) or 0) for field in DAY_VOLUMES) + + +class DeribitTradeVolumesQueryParams(QueryParams): + """Deribit Trade Volumes Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_trade_volumes + """ + + extended: bool = Field( + default=True, + description="When True, adds the seven and thirty day volumes to the" + + " trailing twenty-four hours.", + ) + + +class DeribitTradeVolumesData(Data): + """Deribit Trade Volumes Data.""" + + currency: str = Field( + description="The currency the volume was traded in.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "category", "pinned": "left"} + }, + ) + currency_pair: str | None = Field( + default=None, + description="The index the currency is priced against.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + futures_volume: float | None = Field( + default=None, + description="The futures volume of the last 24 hours.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Futures Volume", + "chartDataType": "series", + } + }, + ) + calls_volume: float | None = Field( + default=None, + description="The call volume of the last 24 hours.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Calls Volume", + "chartDataType": "excluded", + } + }, + ) + puts_volume: float | None = Field( + default=None, + description="The put volume of the last 24 hours.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Puts Volume", + "chartDataType": "excluded", + } + }, + ) + spot_volume: float | None = Field( + default=None, + description="The spot volume of the last 24 hours.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Spot Volume", + "chartDataType": "excluded", + } + }, + ) + futures_volume_7d: float | None = Field( + default=None, + description="The futures volume of the last seven days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Futures Volume 7D", + "chartDataType": "excluded", + } + }, + ) + calls_volume_7d: float | None = Field( + default=None, + description="The call volume of the last seven days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Calls Volume 7D", + "chartDataType": "excluded", + } + }, + ) + puts_volume_7d: float | None = Field( + default=None, + description="The put volume of the last seven days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Puts Volume 7D", + "chartDataType": "excluded", + } + }, + ) + spot_volume_7d: float | None = Field( + default=None, + description="The spot volume of the last seven days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Spot Volume 7D", + "chartDataType": "excluded", + } + }, + ) + futures_volume_30d: float | None = Field( + default=None, + description="The futures volume of the last thirty days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Futures Volume 30D", + "chartDataType": "excluded", + } + }, + ) + calls_volume_30d: float | None = Field( + default=None, + description="The call volume of the last thirty days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Calls Volume 30D", + "chartDataType": "excluded", + } + }, + ) + puts_volume_30d: float | None = Field( + default=None, + description="The put volume of the last thirty days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Puts Volume 30D", + "chartDataType": "excluded", + } + }, + ) + spot_volume_30d: float | None = Field( + default=None, + description="The spot volume of the last thirty days.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Spot Volume 30D", + "chartDataType": "excluded", + } + }, + ) + + +class DeribitTradeVolumesFetcher( + Fetcher[DeribitTradeVolumesQueryParams, list[DeribitTradeVolumesData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitTradeVolumesQueryParams: + """Transform the query.""" + return DeribitTradeVolumesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitTradeVolumesQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the exchange published no volumes. + """ + from openbb_deribit.utils.client import request + + data = await request("get_trade_volumes", {"extended": query.extended}) + + if not data: + raise EmptyDataError("Deribit published no trade volumes.") + + return data + + @staticmethod + def transform_data( + query: DeribitTradeVolumesQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitTradeVolumesData]: + """Transform the data to the model. + + The busiest currencies come first. Most of the currencies the exchange + lists trade nothing on a given day, and ordering them by name buries the + handful that did under the ones that did not. Volumes are counted in + each currency's own units, so the ranking is by activity rather than by + value. + """ + return [ + DeribitTradeVolumesData.model_validate(record) + for record in sorted( + data, + key=lambda d: (-_traded(d), str(d.get("currency"))), + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/trades.py b/openbb_platform/providers/deribit/openbb_deribit/models/trades.py new file mode 100644 index 00000000000..46ba4875c0e --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/trades.py @@ -0,0 +1,354 @@ +"""Deribit Trades Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator, model_validator + +from openbb_deribit.utils.constants import ( + INSTRUMENT_CHOICES_ENDPOINT, + MAX_TRADE_COUNT, + SYMBOL_STYLE, + ListingCurrencies, + Sorting, + TradeKinds, +) + + +class DeribitTradesQueryParams(QueryParams): + """Deribit Trades Query. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_last_trades_by_instrument + """ + + __json_schema_extra__ = { + "symbol": { + "multiple_items_allowed": True, + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": INSTRUMENT_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + }, + } + } + + symbol: str | None = Field( + default=None, + description="One or more instrument names. When given, the currency is" + + " ignored.", + ) + currency: ListingCurrencies = Field( + default="BTC", description="The settlement currency of the instruments." + ) + kind: TradeKinds = Field( + default="future", + description="The kind of instrument. Only used when reading by currency," + + " where the exchange rejects a request that leaves it out, and where" + + " spot is not offered at all. Read spot trades by symbol instead.", + ) + start_date: Any | None = Field( + default=None, description="Return trades from this date onwards." + ) + end_date: Any | None = Field( + default=None, description="Return trades up to this date." + ) + limit: int = Field( + default=100, + description=f"The number of trades to return, at most {MAX_TRADE_COUNT}.", + ge=1, + le=MAX_TRADE_COUNT, + ) + sorting: Sorting = Field( + default="desc", description="The order the trades are returned in." + ) + + @model_validator(mode="after") + def _options_are_listed_by_underlying(self): + """Refuse a listing of options scoped to a currency that only settles them.""" + from openbb_deribit.utils.helpers import reject_options_by_settlement + + if not self.symbol: + reject_options_by_settlement(self.currency, self.kind) + + return self + + +class DeribitTradesData(Data): + """Deribit Trades Data.""" + + __alias_dict__ = {"symbol": "instrument_name"} + + timestamp: datetime = Field( + description="When the trade printed.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "category", "pinned": "left"} + }, + ) + symbol: str = Field( + description="The name of the instrument.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + direction: str | None = Field( + default=None, + description="Which side the aggressor was on.", + json_schema_extra={ + "x-widget_config": {"headerName": "Side", "chartDataType": "excluded"} + }, + ) + price: float = Field( + description="The price the trade printed at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + amount: float | None = Field( + default=None, + description="The size of the trade, in base currency.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + contracts: float | None = Field( + default=None, + description="The size of the trade, in contracts.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + iv: float | None = Field( + default=None, + description="The volatility the option trade implies.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"headerName": "IV", "chartDataType": "excluded"}, + }, + ) + mark_price: float | None = Field( + default=None, + description="The price the exchange marked the instrument at.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + index_price: float | None = Field( + default=None, + description="The price of the index at the time of the trade.", + json_schema_extra={ + "x-unit_measurement": "currency", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + tick_direction: int | None = Field( + default=None, + description="How the price moved against the previous trade. 0 and 1 are" + + " upticks, 2 and 3 are downticks.", + json_schema_extra={ + "x-widget_config": {"chartDataType": "excluded", "hide": True} + }, + ) + trade_id: str | None = Field( + default=None, + description="The identifier of the trade.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Trade ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + trade_seq: int | None = Field( + default=None, + description="The sequence number of the trade.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Trade Seq", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + block_trade_id: str | None = Field( + default=None, + description="The block trade the print belongs to.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Block Trade ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + liquidation: str | None = Field( + default=None, + description="Which side of the trade was liquidated.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + starbase_match_id: int | None = Field( + default=None, + description="The matching engine's identifier for the fill.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Match ID", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + starbase_timestamp: int | None = Field( + default=None, + description="When the matching engine recorded the fill, in nanoseconds" + + " since the epoch.", + json_schema_extra={ + "x-widget_config": { + "headerName": "Match Timestamp", + "cellDataType": "text", + "chartDataType": "excluded", + "hide": True, + } + }, + ) + + @field_validator("timestamp", mode="before", check_fields=False) + @classmethod + def validate_timestamp(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + +class DeribitTradesFetcher(Fetcher[DeribitTradesQueryParams, list[DeribitTradesData]]): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitTradesQueryParams: + """Transform the query.""" + return DeribitTradesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitTradesQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + OpenBBError + If the symbol is one of the spot pairs the exchange routes to Coinbase, + which publish no trade history here. + EmptyDataError + If the exchange printed no trades over the span. + """ + from openbb_core.app.model.abstract.error import OpenBBError + + from openbb_deribit.utils.client import gather, request + from openbb_deribit.utils.constants import ROUTED_SPOT_ERROR + from openbb_deribit.utils.helpers import get_perpetual_symbols, to_timestamp + + window = { + "start_timestamp": ( + to_timestamp(query.start_date) if query.start_date else None + ), + "end_timestamp": to_timestamp(query.end_date) if query.end_date else None, + } + timed = window["start_timestamp"] and window["end_timestamp"] + shared = {"count": query.limit, "sorting": query.sorting} + + if query.symbol: + symbols = [s.strip().upper() for s in query.symbol.split(",") if s.strip()] + perpetuals = await get_perpetual_symbols() + method = ( + "get_last_trades_by_instrument_and_time" + if timed + else "get_last_trades_by_instrument" + ) + results = await gather( + [ + ( + method, + { + "instrument_name": perpetuals.get(symbol, symbol), + **window, + **shared, + }, + ) + for symbol in symbols + ], + use_cache=False, + ) + data = [ + trade + for result in results + if isinstance(result, dict) + for trade in result.get("trades") or [] + ] + + if not data and any( + ROUTED_SPOT_ERROR in str(result) + for result in results + if isinstance(result, Exception) + ): + raise OpenBBError( + f"Deribit publishes no trade history for {', '.join(symbols)}." + " Spot pairs it routes to Coinbase carry none." + ) + else: + method = ( + "get_last_trades_by_currency_and_time" + if timed + else "get_last_trades_by_currency" + ) + try: + result = await request( + method, + { + "currency": query.currency.upper(), + "kind": query.kind, + **window, + **shared, + }, + use_cache=False, + ) + except OpenBBError as error: + if ROUTED_SPOT_ERROR not in str(error): + raise + + raise OpenBBError( + "Deribit publishes no trade history for the spot pairs it" + " routes to Coinbase. Read the other kinds by currency, or" + " a specific spot pair by symbol." + ) from error + + data = (result or {}).get("trades") or [] + + if not data: + raise EmptyDataError("Deribit printed no trades matching the query.") + + return data + + @staticmethod + def transform_data( + query: DeribitTradesQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitTradesData]: + """Transform the data to the model.""" + return [ + DeribitTradesData.model_validate(record) + for record in sorted( + data, + key=lambda d: d.get("timestamp") or 0, + reverse=query.sorting != "asc", + ) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/models/volatility_index.py b/openbb_platform/providers/deribit/openbb_deribit/models/volatility_index.py new file mode 100644 index 00000000000..f3082dd9b01 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/models/volatility_index.py @@ -0,0 +1,216 @@ +"""Deribit Volatility Index Model.""" + +from datetime import datetime +from typing import Any + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +from openbb_deribit.utils.constants import ( + VOLATILITY_INDEX_CURRENCIES, + VolatilityResolutions, +) + + +class DeribitVolatilityIndexQueryParams(QueryParams): + """Deribit Volatility Index Query. + + Deribit computes DVOL for BTC and ETH only. Asking for it on a pegged + currency returns an empty series, so none are offered. + + Source: https://docs.deribit.com/api-reference/market-data/public-get_volatility_index_data + """ + + __json_schema_extra__ = { + "currency": { + "multiple_items_allowed": True, + "x-widget_config": { + "options": [ + {"label": name, "value": name} + for name in VOLATILITY_INDEX_CURRENCIES + ] + }, + } + } + + currency: str = Field( + default="BTC", description="The currency whose volatility index to read." + ) + + @field_validator("currency", mode="before") + @classmethod + def _listed_currencies(cls, value): + """Accept one currency or several, and refuse any the exchange omits. + + Raises + ------ + ValueError + If a currency the exchange does not publish this for is asked for. + """ + chosen = [ + item.strip().upper() for item in str(value).split(",") if item.strip() + ] + unlisted = [item for item in chosen if item not in VOLATILITY_INDEX_CURRENCIES] + + if unlisted: + raise ValueError( + f"Deribit publishes no volatility index for {unlisted}." + f" It publishes {list(VOLATILITY_INDEX_CURRENCIES)}." + ) + + return ",".join(chosen) + + start_date: Any | None = Field( + default=None, description="Return candles from this date onwards." + ) + end_date: Any | None = Field( + default=None, description="Return candles up to this date." + ) + interval: VolatilityResolutions = Field( + default="1d", description=QUERY_DESCRIPTIONS.get("interval", "") + ) + + +class DeribitVolatilityIndexData(Data): + """Deribit Volatility Index Data.""" + + date: datetime = Field( + description="When the candle closed.", + json_schema_extra={"x-widget_config": {"chartDataType": "category"}}, + ) + currency: str = Field( + description="The currency the index belongs to.", + json_schema_extra={"x-widget_config": {"chartDataType": "excluded"}}, + ) + open: float = Field( + description="The level the candle opened at.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + high: float = Field( + description="The highest level of the candle.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + low: float = Field( + description="The lowest level of the candle.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "excluded"}, + }, + ) + close: float = Field( + description="The level the candle closed at.", + json_schema_extra={ + "x-unit_measurement": "percent", + "x-widget_config": {"chartDataType": "series"}, + }, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Read the timestamp as a datetime.""" + from openbb_deribit.utils.helpers import from_timestamp + + return from_timestamp(v) + + +class DeribitVolatilityIndexFetcher( + Fetcher[DeribitVolatilityIndexQueryParams, list[DeribitVolatilityIndexData]] +): + """Transform the query, extract and transform the data from the Deribit endpoint.""" + + require_credentials = False + + @staticmethod + def transform_query(params: dict[str, Any]) -> DeribitVolatilityIndexQueryParams: + """Transform the query.""" + return DeribitVolatilityIndexQueryParams(**params) + + @staticmethod + async def aextract_data( + query: DeribitVolatilityIndexQueryParams, + credentials: dict[str, str] | None, + **kwargs: Any, + ) -> list[dict]: + """Return the raw data from the Deribit endpoint. + + Raises + ------ + EmptyDataError + If the index published no candles over the span. + """ + from datetime import ( + datetime as dt, + timedelta, + timezone, + ) + + from openbb_deribit.utils.client import gather + from openbb_deribit.utils.constants import VOLATILITY_RESOLUTION_MAP + from openbb_deribit.utils.helpers import to_timestamp + + now = dt.now(timezone.utc) + start = ( + to_timestamp(query.start_date) + if query.start_date + else to_timestamp(now - timedelta(days=30)) + ) + end = to_timestamp(query.end_date) if query.end_date else to_timestamp(now) + currencies = [c.strip().upper() for c in query.currency.split(",") if c.strip()] + results = await gather( + [ + ( + "get_volatility_index_data", + { + "currency": currency, + "start_timestamp": start, + "end_timestamp": end, + "resolution": VOLATILITY_RESOLUTION_MAP[query.interval], + }, + ) + for currency in currencies + ] + ) + data = [ + { + "date": candle[0], + "currency": currency, + "open": candle[1], + "high": candle[2], + "low": candle[3], + "close": candle[4], + } + for currency, result in zip(currencies, results) + if isinstance(result, dict) + for candle in result.get("data") or [] + ] + + if not data: + raise EmptyDataError( + f"Deribit published no volatility index candles for" + f" {', '.join(currencies)} over the span." + ) + + return data + + @staticmethod + def transform_data( + query: DeribitVolatilityIndexQueryParams, + data: list[dict], + **kwargs: Any, + ) -> list[DeribitVolatilityIndexData]: + """Transform the data to the model.""" + return [ + DeribitVolatilityIndexData.model_validate(record) + for record in sorted(data, key=lambda d: (d["date"], d["currency"])) + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/__init__.py b/openbb_platform/providers/deribit/openbb_deribit/routers/__init__.py new file mode 100644 index 00000000000..71476273149 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/__init__.py @@ -0,0 +1 @@ +"""Deribit sub-routers.""" diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/futures.py b/openbb_platform/providers/deribit/openbb_deribit/routers/futures.py new file mode 100644 index 00000000000..56c74761d27 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/futures.py @@ -0,0 +1,181 @@ +"""Deribit Futures sub-router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query as OBBQuery +from openbb_core.app.router import Router + +from openbb_deribit import DERIVATIVES_INSTALLED + +router = Router(prefix="/futures", description="Deribit futures data.") + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Get the underlyings with a listed futures curve.", + parameters={}, + ) + ], +) +async def curve_choices() -> list[dict[str, str]]: + """``[{label, value}]`` of every underlying with a listed futures curve.""" + from openbb_deribit.utils.choices import futures_root_choices + + return await futures_root_choices() + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[APIEx(description="Get the listed perpetuals.", parameters={})], +) +async def perpetual_choices() -> list[dict[str, str]]: + """``[{label, value}]`` of every perpetual the exchange lists.""" + from openbb_deribit.utils.choices import perpetual_choices as choices + + return await choices() + + +if not DERIVATIVES_INSTALLED: + + @router.command( + model="DeribitFuturesInstruments", + widget_config={ + "name": "Deribit Futures Instruments", + "description": "The contract specification of every listed future.", + "category": "Crypto", + "subCategory": "Futures", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + }, + examples=[ + APIEx( + description="Every future Deribit lists.", + parameters={"provider": "deribit"}, + ) + ], + ) + async def instruments( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> OBBject: + """Get the contract specification of every listed Deribit future.""" + return await OBBject.from_query(OBBQuery(**locals())) + + @router.command( + model="DeribitFuturesCurve", + widget_config={ + "name": "Deribit Futures Curve", + "description": "The term structure of an underlying's dated futures," + + " optionally against how it stood some hours ago.", + "category": "Crypto", + "subCategory": "Futures", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + "params": [{"paramName": "symbol", "value": "BTC"}], + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="The current BTC futures term structure.", + parameters={"symbol": "BTC", "provider": "deribit"}, + ), + APIEx( + description="The curve against where it stood a day ago.", + parameters={"symbol": "BTC", "hours_ago": 24, "provider": "deribit"}, + ), + ], + ) + async def curve( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> OBBject: + """Get the term structure of a Deribit underlying's dated futures.""" + return await OBBject.from_query(OBBQuery(**locals())) + + @router.command( + model="DeribitFuturesHistorical", + widget_config={ + "name": "Deribit Historical Prices", + "description": "Candles for any listed instrument, not only futures.", + "category": "Crypto", + "subCategory": "Futures", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + "params": [{"paramName": "symbol", "value": "BTC-PERPETUAL"}], + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="Daily candles for the BTC perpetual.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ), + APIEx( + description="Hourly candles over a named span.", + parameters={ + "symbol": "BTC-PERPETUAL", + "interval": "1h", + "start_date": "2026-09-01", + "end_date": "2026-09-15", + "provider": "deribit", + }, + ), + ], + ) + async def historical( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> OBBject: + """Get candles for any listed Deribit instrument.""" + return await OBBject.from_query(OBBQuery(**locals())) + + @router.command( + model="DeribitFuturesInfo", + widget_config={ + "name": "Deribit Futures Info", + "description": "The live quote and session statistics of a perpetual or" + + " dated future.", + "category": "Crypto", + "subCategory": "Futures", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 8}, + "params": [{"paramName": "symbol", "value": "BTC-PERPETUAL"}], + }, + examples=[ + APIEx( + description="The live quote of the BTC perpetual.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ) + ], + ) + async def info( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> OBBject: + """Get the live quote and session statistics of a Deribit future.""" + return await OBBject.from_query(OBBQuery(**locals())) diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/index.py b/openbb_platform/providers/deribit/openbb_deribit/routers/index.py new file mode 100644 index 00000000000..0ae246980fa --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/index.py @@ -0,0 +1,116 @@ +"""Deribit Index sub-router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query as OBBQuery +from openbb_core.app.router import Router + +router = Router(prefix="/index", description="Deribit index data.") + + +@router.command( + model="DeribitIndexPrice", + widget_config={ + "name": "Deribit Index Price", + "description": "The current level of an index, and what it would deliver at.", + "category": "Crypto", + "subCategory": "Index", + "source": ["Deribit"], + "gridData": {"w": 20, "h": 6}, + "params": [{"paramName": "index_name", "value": "btc_usd"}], + }, + examples=[ + APIEx( + description="The current level of the BTC index.", + parameters={"index_name": "btc_usd", "provider": "deribit"}, + ), + APIEx( + description="Multiple indexes are comma-separated.", + parameters={"index_name": "btc_usd,eth_usd", "provider": "deribit"}, + ), + ], +) +async def price( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the current level of a Deribit index.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitIndexHistorical", + widget_config={ + "name": "Deribit Index History", + "description": "The published level of an index over a span.", + "category": "Crypto", + "subCategory": "Index", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 14}, + "params": [{"paramName": "index_name", "value": "btc_usd"}], + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="The BTC index over the last day.", + parameters={"index_name": "btc_usd", "provider": "deribit"}, + ), + APIEx( + description="The whole published history.", + parameters={"index_name": "btc_usd", "span": "all", "provider": "deribit"}, + ), + ], +) +async def historical( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the published level of a Deribit index over a span.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitDeliveryPrices", + widget_config={ + "name": "Deribit Delivery Prices", + "description": "The level each index has delivered at.", + "category": "Crypto", + "subCategory": "Index", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 14}, + "params": [{"paramName": "index_name", "value": "btc_usd"}], + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="The level the BTC index has delivered at.", + parameters={"index_name": "btc_usd", "provider": "deribit"}, + ) + ], +) +async def delivery_prices( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the level a Deribit index has delivered at.""" + return await OBBject.from_query(OBBQuery(**locals())) diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/market.py b/openbb_platform/providers/deribit/openbb_deribit/routers/market.py new file mode 100644 index 00000000000..806ec64d0c5 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/market.py @@ -0,0 +1,256 @@ +"""Deribit Market sub-router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query as OBBQuery +from openbb_core.app.router import Router + +router = Router(prefix="/market", description="Deribit market data.") + + +@router.command( + model="DeribitBookSummary", + widget_config={ + "name": "Deribit Book Summary", + "description": "The top of book and trailing session statistics of every" + + " instrument in a currency.", + "category": "Crypto", + "subCategory": "Market Data", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + "params": [{"paramName": "currency", "value": "BTC"}], + }, + examples=[ + APIEx( + description="Every BTC instrument's top of book.", + parameters={"currency": "BTC", "provider": "deribit"}, + ), + APIEx( + description="Only the futures.", + parameters={"currency": "BTC", "kind": "future", "provider": "deribit"}, + ), + APIEx( + description="One named instrument.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ), + ], +) +async def book_summary( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the top of book and session statistics of Deribit instruments.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitOrderBook", + widget_config={ + "name": "Deribit Order Book", + "description": "The resting depth of one instrument, one row per level.", + "category": "Crypto", + "subCategory": "Market Data", + "source": ["Deribit"], + "gridData": {"w": 20, "h": 15}, + "params": [{"paramName": "symbol", "value": "BTC-PERPETUAL"}], + "data": { + "table": { + "chartView": {"enabled": False}, + } + }, + }, + examples=[ + APIEx( + description="The resting depth of the BTC perpetual.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ), + APIEx( + description="A book read by its numeric instrument identifier.", + parameters={"instrument_id": 210838, "provider": "deribit"}, + ), + ], +) +async def order_book( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the resting depth of a Deribit instrument, one row per level.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitTicker", + widget_config={ + "name": "Deribit Ticker", + "description": "The live quote, session statistics, and greeks of one" + + " instrument.", + "category": "Crypto", + "subCategory": "Market Data", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 8}, + "params": [{"paramName": "symbol", "value": "BTC-PERPETUAL"}], + }, + examples=[ + APIEx( + description="The live quote of the BTC perpetual.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ), + APIEx( + description="Multiple instruments are comma-separated.", + parameters={"symbol": "BTC-PERPETUAL,ETH-PERPETUAL", "provider": "deribit"}, + ), + ], +) +async def ticker( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the live quote, session statistics, and greeks of a Deribit instrument.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitTrades", + widget_config={ + "name": "Deribit Trades", + "description": "The prints that crossed, by instrument or across a currency.", + "category": "Crypto", + "subCategory": "Market Data", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + "params": [{"paramName": "symbol", "value": "BTC-PERPETUAL"}], + "data": { + "table": { + "chartView": {"enabled": False}, + } + }, + }, + examples=[ + APIEx( + description="The most recent prints on the BTC perpetual.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ), + APIEx( + description="Every BTC option print over a span.", + parameters={ + "currency": "BTC", + "kind": "option", + "start_date": "2026-09-01", + "end_date": "2026-09-02", + "provider": "deribit", + }, + ), + ], +) +async def trades( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the Deribit prints that crossed, by instrument or across a currency.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitTradeVolumes", + widget_config={ + "name": "Deribit Trade Volumes", + "description": "Traded volume by currency and product, over one, seven," + + " and thirty days.", + "category": "Crypto", + "subCategory": "Market Data", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 12}, + }, + examples=[ + APIEx( + description="Traded volume by currency and product.", + parameters={"provider": "deribit"}, + ) + ], +) +async def trade_volumes( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get Deribit's traded volume by currency and product.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitBlockRfqTrades", + widget_config={ + "name": "Deribit Block RFQ Trades", + "description": "The block requests that traded, one row per leg.", + "category": "Crypto", + "subCategory": "Market Data", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + }, + examples=[ + APIEx( + description="The block requests that most recently traded.", + parameters={"provider": "deribit"}, + ) + ], +) +async def block_rfq_trades( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the Deribit block requests that traded, one row per leg.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitSettlements", + widget_config={ + "name": "Deribit Settlements", + "description": "The settlements, deliveries, and bankruptcies the exchange" + + " has processed.", + "category": "Crypto", + "subCategory": "Market Data", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + "params": [{"paramName": "currency", "value": "BTC"}], + }, + examples=[ + APIEx( + description="The most recent BTC settlements.", + parameters={"currency": "BTC", "provider": "deribit"}, + ), + APIEx( + description="Only the deliveries of one instrument.", + parameters={ + "symbol": "BTC-PERPETUAL", + "settlement_type": "settlement", + "provider": "deribit", + }, + ), + ], +) +async def settlements( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the settlements, deliveries, and bankruptcies Deribit has processed.""" + return await OBBject.from_query(OBBQuery(**locals())) diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/options.py b/openbb_platform/providers/deribit/openbb_deribit/routers/options.py new file mode 100644 index 00000000000..d7b435f421e --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/options.py @@ -0,0 +1,992 @@ +"""Deribit Options sub-router.""" + +import logging +from typing import Annotated, Any, Literal + +from fastapi import Query as FastAPIQuery +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query as OBBQuery +from openbb_core.app.router import Router + +from openbb_deribit import DERIVATIVES_INSTALLED +from openbb_deribit.utils.constants import ( + EXPIRATION_CHOICES_ENDPOINT, + STRATEGY_CHOICES_ENDPOINT, + STRIKE_CHOICES_ENDPOINT, + SYMBOL_STYLE, + UNDERLYING_CHOICES_ENDPOINT, +) + +_logger = logging.getLogger(__name__) + +router = Router(prefix="/options", description="Deribit options data and analysis.") + +_UNDERLYING = { + "x-widget_config": { + "groupId": "deribit_underlying", + "type": "endpoint", + "optionsEndpoint": UNDERLYING_CHOICES_ENDPOINT, + "style": SYMBOL_STYLE, + } +} + + +def _underlying() -> Any: + """Build the shared, group-linked underlying parameter.""" + return FastAPIQuery( + description="The underlying root, as it appears in the instrument name.", + json_schema_extra=_UNDERLYING, + ) + + +def _excluded_query(description: str = "") -> Any: + """Build a query parameter that is accepted but hidden from the widget UI.""" + return FastAPIQuery( + description=description, + json_schema_extra={"x-widget_config": {"exclude": True}}, + ) + + +def _expiry_query(description: str, multi: bool = False) -> Any: + """Build an expiration query parameter backed by the expiration choices.""" + config: dict = { + "type": "endpoint", + "optionsEndpoint": EXPIRATION_CHOICES_ENDPOINT, + "optionsParams": {"symbol": "$symbol"}, + "style": SYMBOL_STYLE, + } + + if multi: + config["multiSelect"] = True + + return FastAPIQuery( + description=description, json_schema_extra={"x-widget_config": config} + ) + + +def _strike_query(description: str) -> Any: + """Build a strike query parameter backed by the strike choices.""" + return FastAPIQuery( + description=description, + json_schema_extra={ + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": STRIKE_CHOICES_ENDPOINT, + "optionsParams": {"symbol": "$symbol"}, + "style": SYMBOL_STYLE, + } + }, + ) + + +def _choice_query(description: str, options: list[tuple[str, str]]) -> Any: + """Build a query parameter rendered as a labelled dropdown.""" + return FastAPIQuery( + description=description, + json_schema_extra={ + "x-widget_config": { + "options": [ + {"label": label, "value": value} for label, value in options + ] + } + }, + ) + + +def _legs_query(description: str) -> Any: + """Build the strategy parameter the optimizer's Legs column groups by.""" + return FastAPIQuery( + description=description, + json_schema_extra={ + "x-widget_config": { + "type": "endpoint", + "optionsEndpoint": STRATEGY_CHOICES_ENDPOINT, + "optionsParams": { + "symbol": "$symbol", + "target_price": "$target_price", + "target_date": "$target_date", + "budget": "$budget", + }, + "style": SYMBOL_STYLE, + } + }, + ) + + +def _column(field: str, header: str, **extra) -> dict: + """Describe one grid column.""" + return {"field": field, "headerName": header, **extra} + + +_MONEY = {"cellDataType": "number"} +_COUNT = {"cellDataType": "number"} +_NORMALIZED = {"cellDataType": "number", "formatterFn": "normalizedPercent"} + +_PERCENT = {"cellDataType": "number", "formatterFn": "percent"} + +_STRATEGY_TAIL = [ + _column("cost", "Cost", **_MONEY), + _column("cost_percent", "Cost %", **_NORMALIZED), + _column("max_loss", "Max Loss", **_MONEY), + _column("breakeven_lower", "Breakeven Lower", **_MONEY), + _column("breakeven_upper", "Breakeven Upper", **_MONEY), +] + +CHAIN_COLUMNS = [ + _column("contract_symbol", "Contract", pinned="left", cellDataType="text"), + _column("expiration", "Expiration", cellDataType="text"), + _column("dte", "DTE", **_COUNT), + _column("strike", "Strike", **_MONEY), + _column("option_type", "Type", cellDataType="text"), + _column("open_interest", "OI", **_COUNT), + _column("volume", "Volume", **_COUNT), + _column("volume_notional", "Volume Notional", **_MONEY), + _column("bid", "Bid", **_MONEY), + _column("bid_size", "Bid Size", **_COUNT), + _column("ask", "Ask", **_MONEY), + _column("ask_size", "Ask Size", **_COUNT), + _column("mark", "Mark", **_MONEY), + _column("last_trade_price", "Last", **_MONEY), + _column("high", "High", **_MONEY), + _column("low", "Low", **_MONEY), + _column("change_percent", "Change %", **_PERCENT, renderFn="greenRed"), + _column("implied_volatility", "Mark IV", **_PERCENT), + _column("bid_iv", "Bid IV", **_PERCENT), + _column("ask_iv", "Ask IV", **_PERCENT), + _column("delta", "Delta", cellDataType="number"), + _column("gamma", "Gamma", cellDataType="number"), + _column("theta", "Theta", cellDataType="number"), + _column("vega", "Vega", cellDataType="number"), + _column("rho", "Rho", cellDataType="number"), + _column("settlement_price", "Settlement Price", **_MONEY), + _column("min_price", "Min Price", **_MONEY), + _column("max_price", "Max Price", **_MONEY), + _column("interest_rate", "Interest Rate", **_PERCENT), + _column("contract_size", "Contract Size", **_COUNT), + _column("is_inverse", "Is Inverse", cellDataType="boolean"), + _column("underlying_symbol", "Underlying", cellDataType="text"), + _column("underlying_price", "Underlying Forward", **_MONEY), + _column("underlying_spot_price", "Underlying Spot", **_MONEY), + _column("timestamp", "Timestamp", cellDataType="text"), +] + +OPTIMIZER_COLUMNS = [ + _column("strategy", "Strategy", pinned="left", cellDataType="text"), + _column("code", "Combo Type", cellDataType="text"), + _column( + "legs", + "Legs", + cellDataType="text", + formatterFn="none", + renderFn="cellOnClick", + renderFnParams={ + "actionType": "groupBy", + "groupBy": {"paramName": "legs"}, + }, + ), + _column("combo", "Listed Combo", cellDataType="text"), + _column("contracts", "Contracts", **_COUNT), + _column("cost", "Net Premium", **_MONEY), + _column("expected_profit", "Expected Profit", **_MONEY, renderFn="greenRed"), + _column("expected_return", "Expected Return", **_NORMALIZED, renderFn="greenRed"), + _column("max_profit", "Max Profit", **_MONEY), + _column("max_loss", "Max Loss", **_MONEY), + _column("breakeven_lower", "Breakeven Lower", **_MONEY), + _column("breakeven_upper", "Breakeven Upper", **_MONEY), +] + +STRADDLE_COLUMNS = [ + _column("expiration", "Expiration", pinned="left", cellDataType="text"), + _column("dte", "DTE", **_COUNT), + _column("underlying_price", "Underlying Price", **_MONEY), + _column("strike", "Strike", **_MONEY), + _column("call_premium", "Call Premium", **_MONEY), + _column("put_premium", "Put Premium", **_MONEY), +] + _STRATEGY_TAIL + +STRANGLE_COLUMNS = [ + _column("expiration", "Expiration", pinned="left", cellDataType="text"), + _column("dte", "DTE", **_COUNT), + _column("underlying_price", "Underlying Price", **_MONEY), + _column("put_strike", "Put Strike", **_MONEY), + _column("call_strike", "Call Strike", **_MONEY), + _column("call_premium", "Call Premium", **_MONEY), + _column("put_premium", "Put Premium", **_MONEY), +] + _STRATEGY_TAIL + +SPREAD_COLUMNS = [ + _column("strategy", "Strategy", pinned="left", cellDataType="text"), + _column("expiration", "Expiration", cellDataType="text"), + _column("dte", "DTE", **_COUNT), + _column("underlying_price", "Underlying Price", **_MONEY), + _column("bought_strike", "Bought Strike", **_MONEY), + _column("sold_strike", "Sold Strike", **_MONEY), + _column("bought_premium", "Bought Premium", **_MONEY), + _column("sold_premium", "Sold Premium", **_MONEY), + _column("cost", "Cost", **_MONEY), + _column("cost_percent", "Cost %", **_NORMALIZED), + _column("max_profit", "Max Profit", **_MONEY), + _column("max_loss", "Max Loss", **_MONEY), + _column("breakeven", "Breakeven", **_MONEY), +] + + +def _table_widget(name: str, description: str, columns: list, height: int = 16) -> dict: + """Build the Workspace table-widget config for an analysis view.""" + return { + "name": f"Deribit {name}", + "description": description, + "category": "Crypto", + "subCategory": "Options", + "source": ["Deribit"], + "gridData": {"w": 40, "h": height}, + "data": {"table": {"columnsDefs": columns}}, + } + + +def _chart_widget(name: str, description: str, height: int = 18) -> dict: + """Build the Workspace chart-widget config for a Plotly route.""" + return { + "widget_config": { + "name": f"Deribit {name}", + "description": description, + "type": "chart", + "raw": True, + "category": "Crypto", + "subCategory": "Options", + "source": ["Deribit"], + "gridData": {"w": 40, "h": height}, + "refetchInterval": False, + } + } + + +async def _loaded(symbol: str) -> tuple: + """Return the chain, its spot price, and how it settles.""" + from openbb_deribit.utils.options.chain import ( + is_inverse, + load_chain, + settlement_currency, + underlying_price, + ) + + frame = await load_chain(symbol) + + return ( + frame, + underlying_price(frame), + is_inverse(frame), + settlement_currency(frame, symbol), + ) + + +def _figure_json(output: Any, raw: bool) -> Any: + """Return the drawn figure, or the rows behind it when raw is asked for. + + Parameters + ---------- + output : Any + The OBBject a view returned. + raw : bool + Whether the widget's raw toggle is on. + + Returns + ------- + Any + The Plotly figure as plain JSON, or the list of rows. + """ + import json + + from openbb_deribit.utils.options.theme import CHART_CONFIG + + if raw or output.chart is None: + return [row.model_dump() for row in (output.results or [])] + + figure_json = json.loads(output.chart.fig.to_json()) + figure_json["config"] = CHART_CONFIG + + return figure_json + + +async def _chart_json(builder, symbol: str, theme: str, raw: bool, **kwargs) -> Any: + """Load the chain, build the view, and return rows or figure JSON. + + Raises + ------ + HTTPException + If the exchange lists no options on the underlying. + """ + from fastapi import HTTPException + from openbb_core.app.model.abstract.error import OpenBBError + + from openbb_deribit.utils.options.data_handler import load_symbol + + try: + data = await load_symbol(symbol) + except OpenBBError as exc: + _logger.exception("Loading the Deribit option chain for %s failed", symbol) + + raise HTTPException( + status_code=404, + detail=f"Deribit lists no options on {symbol}.", + ) from exc + + return _figure_json(builder(data, theme=theme, **kwargs), raw) + + +async def _chain_or_404(symbol: str) -> tuple: + """Load a chain, answering 404 when the exchange lists none. + + Raises + ------ + HTTPException + If the exchange publishes no option quotes for the underlying. + """ + from fastapi import HTTPException + from openbb_core.app.model.abstract.error import OpenBBError + + try: + return await _loaded(symbol) + except OpenBBError as exc: + _logger.exception("Loading the Deribit option chain for %s failed", symbol) + + raise HTTPException( + status_code=404, + detail=f"Deribit lists no options on {symbol}.", + ) from exc + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx(description="Get the underlyings with listed options.", parameters={}) + ], +) +async def underlying_choices() -> list[dict[str, str]]: + """``[{label, value}]`` of every underlying with listed options.""" + from openbb_deribit.utils.choices import options_root_choices + + return await options_root_choices() + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Get the expirations listed on an underlying.", + parameters={"symbol": "BTC"}, + ) + ], +) +async def expiration_choices( + symbol: Annotated[str, _underlying()] = "BTC", +) -> list[dict[str, str]]: + """``[{label, value}]`` of every expiration the underlying lists.""" + from openbb_deribit.utils.options.chain import expirations, load_chain + + frame = await load_chain(symbol) + + return [ + { + "label": f"{day} ({int(frame[frame['expiration'] == day]['dte'].iloc[0])}d)", + "value": str(day), + } + for day in expirations(frame) + ] + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Get the strikes listed on an underlying.", + parameters={"symbol": "BTC"}, + ) + ], +) +async def strike_choices( + symbol: Annotated[str, _underlying()] = "BTC", +) -> list[dict]: + """``[{label, value}]`` of every strike the underlying lists.""" + from openbb_core.app.model.abstract.error import OpenBBError + + from openbb_deribit.utils.options.data_handler import get_strikes, load_symbol + + try: + data = await load_symbol(symbol) + except OpenBBError: + return [] + + return get_strikes(data) + + +if not DERIVATIVES_INSTALLED: + + @router.command( + model="DeribitOptionsChains", + widget_config={ + "name": "Deribit Options Chains", + "description": "The full option chain of an underlying, with greeks and" + + " implied volatility.", + "category": "Crypto", + "subCategory": "Options", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 20}, + "params": [{"paramName": "symbol", "value": "BTC"}], + "data": {"table": {"columnsDefs": CHAIN_COLUMNS}}, + }, + examples=[ + APIEx( + description="The full BTC option chain.", + parameters={"symbol": "BTC", "provider": "deribit"}, + ) + ], + ) + async def chains( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, + ) -> OBBject: + """Get the full Deribit option chain of an underlying, with greeks.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +def _quote(symbol: str) -> str: + """Return the currency a position in this underlying is priced in.""" + return "USDC" if symbol.upper().endswith("_USDC") else "USD" + + +async def _combos(symbol: str) -> list: + """Return the combo books listed on an underlying.""" + from openbb_deribit.utils.options.chain import load_combos + + return await load_combos(symbol) + + +def _ranked_row(item: dict) -> dict: + """Describe one ranked strategy as a grid row.""" + crossings = item.get("breakevens") or [] + + return { + "strategy": item["strategy"], + "code": item["code"], + "legs": item["legs"], + "combo": item["combo"], + "contracts": item["contracts"], + "cost": item["cost"], + "expected_profit": item["expected_profit"], + "expected_return": item["expected_return"], + "max_profit": item["max_profit"], + "max_loss": item["max_loss"], + "breakeven_lower": crossings[0] if crossings else None, + "breakeven_upper": crossings[-1] if len(crossings) > 1 else None, + } + + +@router.command( + methods=["GET"], + widget_config=_table_widget( + "Strategy Optimizer", + "Every Deribit combo structure the expiration can form, bought and sold," + " priced from its listed combo book where one trades, sized to the" + " capital it puts at risk, and ranked by what it returns if the" + " underlying reaches a price.", + OPTIMIZER_COLUMNS, + 18, + ), + examples=[ + APIEx( + description="Rank strategies for a move to 100,000 by year end.", + parameters={ + "symbol": "BTC", + "target_price": 100000, + "target_date": "2026-12-25", + }, + ) + ], +) +async def optimizer( + symbol: Annotated[str, _underlying()] = "BTC", + target_price: Annotated[ + float | None, + FastAPIQuery(description="The price the underlying is expected to reach."), + ] = None, + target_date: Annotated[ + str | None, + FastAPIQuery(description="When it is expected to reach it."), + ] = None, + budget: Annotated[ + float, + FastAPIQuery( + description="The capital to put at risk, in the quote currency. Each" + " position is sized so its worst loss across a threefold move either" + " way is this much." + ), + ] = 5000.0, + limit: Annotated[ + int, + FastAPIQuery( + description="How many structures to return, each at its best strikes." + ), + ] = 40, + legs: Annotated[ + str | None, + _legs_query( + "The strategy picked from the Legs column, shared with the payoff" + " chart. It does not change the ranking." + ), + ] = None, +) -> OBBject: + """Rank every combo structure against a view on the underlying.""" + from openbb_deribit.utils.options.chain import nearest_expiration + from openbb_deribit.utils.options.optimizer import rank + + frame, spot, inverse, settles = await _chain_or_404(symbol) + target = float(target_price) if target_price else spot + expiration = nearest_expiration(frame, target_date or frame["expiration"].min()) + ranked = rank( + frame, expiration, spot, target, budget, inverse, limit, await _combos(symbol) + ) + + return OBBject( + results=[_ranked_row(item) for item in ranked], + provider="deribit", + extra={ + "results_metadata": { + "symbol": symbol, + "underlying_price": spot, + "target_price": target, + "expiration": str(expiration), + "quote_currency": _quote(symbol), + "settlement_currency": settles, + } + }, + ) + + +@router.command( + methods=["GET"], + widget_config=_table_widget( + "Straddle", + "A long straddle at the money, priced at every expiration.", + STRADDLE_COLUMNS, + ), + examples=[APIEx(description="Price the BTC straddles.", parameters={})], +) +async def straddle(symbol: Annotated[str, _underlying()] = "BTC") -> OBBject: + """Price a long straddle at each expiration.""" + from openbb_deribit.utils.options.strategies import straddles + + frame, spot, _inverse, _settles = await _chain_or_404(symbol) + + return OBBject(results=straddles(frame, spot), provider="deribit") + + +@router.command( + methods=["GET"], + widget_config=_table_widget( + "Strangle", + "A long strangle out of the money, priced at every expiration.", + STRANGLE_COLUMNS, + ), + examples=[APIEx(description="Price the BTC strangles.", parameters={})], +) +async def strangle( + symbol: Annotated[str, _underlying()] = "BTC", + moneyness: Annotated[ + float, + FastAPIQuery(description="How far out of the money each leg sits, in percent."), + ] = 5.0, +) -> OBBject: + """Price a long strangle at each expiration.""" + from openbb_deribit.utils.options.strategies import strangles + + frame, spot, _inverse, _settles = await _chain_or_404(symbol) + + return OBBject( + results=strangles(frame, spot, abs(moneyness) / 100), provider="deribit" + ) + + +@router.command( + methods=["GET"], + widget_config=_table_widget( + "Vertical Spreads", + "A bull call and a bear put spread at every expiration.", + SPREAD_COLUMNS, + ), + examples=[APIEx(description="Price the BTC vertical spreads.", parameters={})], +) +async def spreads( + symbol: Annotated[str, _underlying()] = "BTC", + moneyness: Annotated[ + float, + FastAPIQuery(description="How far out the sold leg sits, in percent."), + ] = 5.0, +) -> OBBject: + """Price a bull call and a bear put spread at each expiration.""" + from openbb_deribit.utils.options.strategies import verticals + + frame, spot, _inverse, _settles = await _chain_or_404(symbol) + + return OBBject( + results=verticals(frame, spot, abs(moneyness) / 100), provider="deribit" + ) + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Get the strategies the optimizer ranks for a view.", + parameters={"symbol": "BTC"}, + ) + ], +) +async def strategy_choices( + symbol: Annotated[str, _underlying()] = "BTC", + target_price: Annotated[float | None, FastAPIQuery()] = None, + target_date: Annotated[str | None, FastAPIQuery()] = None, + budget: Annotated[float, FastAPIQuery()] = 5000.0, +) -> list[dict[str, str]]: + """``[{label, value}]`` of the strategies the optimizer ranks for a view.""" + from openbb_deribit.utils.options.chain import nearest_expiration + from openbb_deribit.utils.options.optimizer import SHORTLIST, rank + + frame, spot, inverse, _settles = await _loaded(symbol) + target = float(target_price) if target_price else spot + expiration = nearest_expiration(frame, target_date or frame["expiration"].min()) + ranked = rank( + frame, + expiration, + spot, + target, + budget, + inverse, + SHORTLIST, + await _combos(symbol), + ) + + return [ + {"label": f"{item['strategy']} โ€” {item['legs']}", "value": item["legs"]} + for item in ranked + ] + + +async def payoff_chart( + symbol: Annotated[str, _underlying()] = "BTC", + target_price: Annotated[ + float | None, + FastAPIQuery(description="The price the underlying is expected to reach."), + ] = None, + target_date: Annotated[ + str | None, FastAPIQuery(description="When it is expected to reach it.") + ] = None, + budget: Annotated[ + float, + FastAPIQuery( + description="The capital to put at risk, in the quote currency. Each" + " position is sized so its worst loss across a threefold move either" + " way is this much." + ), + ] = 5000.0, + legs: Annotated[ + str | None, + _legs_query( + "The contracts to draw, as 'Buy SYMBOL / Sell 2 SYMBOL'. Click the" + " Legs cell of a strategy in the optimizer to set it, or pick one" + " here. Left empty, the best strategy for the view is drawn." + ), + ] = None, + raw: Annotated[bool, _excluded_query()] = False, + theme: Annotated[str, _excluded_query()] = "dark", +): + """Draw what one position returns across a range of underlying prices. + + Raises + ------ + HTTPException + If the expiration lists no strategy, or does not list the position. + """ + from fastapi import HTTPException + + from openbb_deribit.utils.options.chain import nearest_expiration, quotes_at + from openbb_deribit.utils.options.create_payoff import create_payoff + from openbb_deribit.utils.options.optimizer import draw, rank, rebuild, score + + frame, spot, inverse, settles = await _chain_or_404(symbol) + target = float(target_price) if target_price else spot + expiration = nearest_expiration(frame, target_date or frame["expiration"].min()) + books = await _combos(symbol) + + if legs: + try: + built = rebuild(legs, quotes_at(frame), books, spot, inverse) + except ValueError as exc: + raise HTTPException(status_code=404, detail=str(exc)) from exc + + scored = score(built, spot, target, inverse) + + if scored is None: + raise HTTPException( + status_code=404, + detail="That position puts nothing at risk, so a budget does not" + " size it. Pick one the optimizer ranked.", + ) + + chosen = draw(scored, spot, target, budget, inverse) + else: + ranked = rank(frame, expiration, spot, target, budget, inverse, 1, books) + + if not ranked: + raise HTTPException( + status_code=404, + detail=f"No strategy on the {expiration} expiration can be sized" + f" to {budget:,.0f} {_quote(symbol)}.", + ) + + chosen = ranked[0] + + output = create_payoff( + { + **chosen, + "spot": spot, + "target": target, + "quote": _quote(symbol), + "settles": settles, + }, + theme, + ) + + return _figure_json(output, raw) + + +async def smile_chart( + symbol: Annotated[str, _underlying()] = "BTC", + expirations: Annotated[ + str | None, + _expiry_query("Up to five expiration dates (comma-separated).", multi=True), + ] = None, + otm: Annotated[ + bool, FastAPIQuery(description="Show only out-of-the-money contracts.") + ] = False, + skew: Annotated[ + bool, FastAPIQuery(description="Plot skew relative to ATM instead of IV.") + ] = False, + raw: Annotated[bool, _excluded_query()] = False, + theme: Annotated[str, _excluded_query()] = "dark", +): + """Implied-volatility smile / skew across strikes.""" + from openbb_deribit.utils.options.create_smile import create_smile + + return await _chart_json( + create_smile, symbol, theme, raw, expirations=expirations, otm=otm, skew=skew + ) + + +async def surface_chart( + symbol: Annotated[str, _underlying()] = "BTC", + metric: Annotated[ + Literal[ + "implied_volatility", "delta", "gamma", "theta", "vega", "rho", "dex", "gex" + ], + _choice_query( + "The surface metric.", + [ + ("Implied Volatility", "implied_volatility"), + ("Delta", "delta"), + ("Gamma", "gamma"), + ("Theta", "theta"), + ("Vega", "vega"), + ("Rho", "rho"), + ("Delta Exposure", "dex"), + ("Gamma Exposure", "gex"), + ], + ), + ] = "implied_volatility", + option_type: Annotated[ + Literal["otm", "itm", "puts", "calls"], + _choice_query( + "Which side of the chain to plot.", + [ + ("Out-Of-The-Money", "otm"), + ("In-The-Money", "itm"), + ("Puts", "puts"), + ("Calls", "calls"), + ], + ), + ] = "otm", + dte_min: Annotated[ + int | None, FastAPIQuery(description="Minimum days to expiry.") + ] = None, + dte_max: Annotated[ + int | None, FastAPIQuery(description="Maximum days to expiry.") + ] = None, + moneyness: Annotated[ + float | None, + FastAPIQuery(description="Percent from the underlying price to include."), + ] = None, + oi: Annotated[ + bool, FastAPIQuery(description="Drop contracts with no open interest.") + ] = False, + volume: Annotated[ + bool, FastAPIQuery(description="Drop contracts that have not traded.") + ] = False, + raw: Annotated[bool, _excluded_query()] = False, + theme: Annotated[str, _excluded_query()] = "dark", +): + """Implied-volatility or greeks 3-D surface over DTE and strike.""" + from openbb_deribit.utils.options.create_surface import create_surface + + dte_range = [dte_min or 0, dte_max or 5000] if (dte_min or dte_max) else None + + return await _chart_json( + create_surface, + symbol, + theme, + raw, + option_type=option_type, + metric=metric, + dte_range=dte_range, + moneyness=moneyness, + oi=oi, + volume=volume, + ) + + +async def stats_chart( + symbol: Annotated[str, _underlying()] = "BTC", + by: Annotated[ + Literal["strike", "expiration"], + _choice_query( + "Aggregate by strike or expiration.", + [("Expiration", "expiration"), ("Strike", "strike")], + ), + ] = "expiration", + metric: Annotated[ + Literal["oi", "volume"], + _choice_query( + "Open interest or volume.", + [("Open Interest", "oi"), ("Volume", "volume")], + ), + ] = "oi", + date: Annotated[ + str | None, + _expiry_query("Expiry to view by strike (switches 'by' to strike)."), + ] = None, + unit: Annotated[ + Literal["value", "percent", "pcr"], + _choice_query( + "Raw values, share of total, or put/call ratio.", + [("Value", "value"), ("Percent", "percent"), ("Put/Call Ratio", "pcr")], + ), + ] = "value", + raw: Annotated[bool, _excluded_query()] = False, + theme: Annotated[str, _excluded_query()] = "dark", +): + """Open-interest or volume statistics by strike or expiration.""" + from openbb_deribit.utils.options.create_stats import create_stats + + return await _chart_json( + create_stats, symbol, theme, raw, by=by, metric=metric, date=date, unit=unit + ) + + +async def term_structure_chart( + symbol: Annotated[str, _underlying()] = "BTC", + strike: Annotated[ + float | None, _strike_query("Target strike. Default is nearest OTM per expiry.") + ] = None, + moneyness: Annotated[ + float | None, + FastAPIQuery(description="Percent out-of-the-money instead of a fixed strike."), + ] = None, + metric: Annotated[ + Literal["iv", "price"], + _choice_query( + "Implied volatility or price.", + [("Implied Volatility", "iv"), ("Price", "price")], + ), + ] = "iv", + option_type: Annotated[ + Literal["both", "calls", "puts"], + _choice_query( + "Which side of the chain to plot.", + [("Both", "both"), ("Calls", "calls"), ("Puts", "puts")], + ), + ] = "both", + raw: Annotated[bool, _excluded_query()] = False, + theme: Annotated[str, _excluded_query()] = "dark", +): + """Price or IV term structure across expirations.""" + from openbb_deribit.utils.options.create_term_structure import create_term_structure + + return await _chart_json( + create_term_structure, + symbol, + theme, + raw, + strike=strike, + moneyness=moneyness, + metric=metric, + option_type=option_type, + ) + + +CHART_ROUTES = ( + ( + "/payoff", + payoff_chart, + "Options Payoff", + "What one ranked strategy returns across a range of underlying prices.", + 18, + ), + ( + "/smile", + smile_chart, + "Volatility Smile", + "Implied volatility smile or skew across strikes, per expiration.", + 16, + ), + ( + "/term_structure", + term_structure_chart, + "Volatility Term Structure", + "Price or implied volatility at one strike across expirations.", + 16, + ), + ( + "/surface", + surface_chart, + "Volatility Surface", + "Implied volatility or a greek as a surface over DTE and strike.", + 20, + ), + ( + "/stats", + stats_chart, + "Options Statistics", + "Open interest and volume, split between calls and puts.", + 16, + ), +) + +for _path, _endpoint, _name, _description, _height in CHART_ROUTES: + router.api_router.add_api_route( + path=_path, + endpoint=_endpoint, + methods=["GET"], + openapi_extra=_chart_widget(_name, _description, _height), + ) diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/rates.py b/openbb_platform/providers/deribit/openbb_deribit/routers/rates.py new file mode 100644 index 00000000000..4062493e678 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/rates.py @@ -0,0 +1,146 @@ +"""Deribit Rates sub-router.""" + +from typing import Annotated + +from fastapi import Query as FastAPIQuery +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query as OBBQuery +from openbb_core.app.router import Router + +router = Router(prefix="/rates", description="Deribit funding and yield data.") + + +@router.command( + model="DeribitFundingRateHistory", + widget_config={ + "name": "Deribit Funding Rate History", + "description": "The hourly funding a perpetual has paid.", + "category": "Crypto", + "subCategory": "Rates", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 14}, + "params": [{"paramName": "symbol", "value": "BTC-PERPETUAL"}], + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="The funding the BTC perpetual has paid over the last week.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ) + ], +) +async def funding_history( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the hourly funding a Deribit perpetual has paid.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitFundingChart", + widget_config={"exclude": True}, + examples=[ + APIEx( + description="The funding the BTC perpetual has accrued over eight hours.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ) + ], +) +async def funding_chart( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the funding a Deribit perpetual has accrued over the current window.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitAprHistory", + widget_config={ + "name": "Deribit Yield History", + "description": "The daily yield a yield-bearing currency has earned.", + "category": "Crypto", + "subCategory": "Rates", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 14}, + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="The yield USDe has earned.", + parameters={"currency": "usde", "provider": "deribit"}, + ) + ], +) +async def apr_history( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the daily yield a Deribit yield-bearing currency has earned.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Read the funding one perpetual paid over a span.", + parameters={ + "symbol": "BTC-PERPETUAL", + "start_date": "2026-09-01", + "end_date": "2026-09-08", + }, + ) + ], +) +async def funding_value( + symbol: Annotated[ + str, FastAPIQuery(description="The perpetual instrument name.") + ] = "BTC-PERPETUAL", + start_date: Annotated[ + str | None, FastAPIQuery(description="The first date of the span.") + ] = None, + end_date: Annotated[ + str | None, FastAPIQuery(description="The last date of the span.") + ] = None, +) -> float: + """Get the funding one perpetual paid over a span, as a single rate.""" + from datetime import datetime, timedelta, timezone + + from openbb_deribit.utils.client import request + from openbb_deribit.utils.helpers import get_perpetual_symbols, to_timestamp + + now = datetime.now(timezone.utc) + perpetuals = await get_perpetual_symbols() + + return await request( + "get_funding_rate_value", + { + "instrument_name": perpetuals.get(symbol.upper(), symbol), + "start_timestamp": to_timestamp(start_date or now - timedelta(days=7)), + "end_timestamp": to_timestamp(end_date or now), + }, + ) diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/reference.py b/openbb_platform/providers/deribit/openbb_deribit/routers/reference.py new file mode 100644 index 00000000000..198f1c5a528 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/reference.py @@ -0,0 +1,321 @@ +"""Deribit Reference sub-router.""" + +from typing import Annotated + +from fastapi import Query as FastAPIQuery +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query as OBBQuery +from openbb_core.app.router import Router + +router = Router(prefix="/reference", description="Deribit exchange reference data.") + + +@router.command( + model="DeribitCurrencies", + widget_config={ + "name": "Deribit Currencies", + "description": "Every currency the exchange lists, with its yield and" + + " withdrawal terms.", + "category": "Crypto", + "subCategory": "Reference", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 12}, + }, + examples=[ + APIEx( + description="List every currency Deribit lists.", + parameters={"provider": "deribit"}, + ) + ], +) +async def currencies( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get every currency Deribit lists, with its yield and withdrawal terms.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitInstruments", + widget_config={ + "name": "Deribit Instruments", + "description": "The full contract specification of every listed instrument.", + "category": "Crypto", + "subCategory": "Reference", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 15}, + }, + examples=[ + APIEx( + description="Every instrument the exchange lists.", + parameters={"provider": "deribit"}, + ), + APIEx( + description="Only the options settling in USDC.", + parameters={"currency": "USDC", "kind": "option", "provider": "deribit"}, + ), + APIEx( + description="The specification of one named instrument.", + parameters={"symbol": "BTC-PERPETUAL", "provider": "deribit"}, + ), + ], +) +async def instruments( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the full contract specification of every listed Deribit instrument.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitExpirations", + widget_config={ + "name": "Deribit Expirations", + "description": "The expirations listed on each currency.", + "category": "Crypto", + "subCategory": "Reference", + "source": ["Deribit"], + "gridData": {"w": 20, "h": 12}, + }, + examples=[ + APIEx( + description="Every expiration listed, kept apart by currency.", + parameters={"provider": "deribit"}, + ), + APIEx( + description="The option expirations listed on BTC.", + parameters={"currency": "BTC", "kind": "option", "provider": "deribit"}, + ), + ], +) +async def expirations( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the expirations Deribit lists on each currency.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitCombos", + widget_config={ + "name": "Deribit Combos", + "description": "The listed multi-leg structures, one row per leg.", + "category": "Crypto", + "subCategory": "Reference", + "source": ["Deribit"], + "gridData": {"w": 30, "h": 12}, + }, + examples=[ + APIEx( + description="Every combo listed on BTC.", + parameters={"currency": "BTC", "provider": "deribit"}, + ), + APIEx( + description="The legs of one named combo.", + parameters={ + "combo_id": "BTC-STRG-30OCT26-66000_90000", + "provider": "deribit", + }, + ), + ], +) +async def combos( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the multi-leg structures Deribit lists, one row per leg.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitAnnouncements", + widget_config={ + "name": "Deribit Announcements", + "description": "What the exchange has announced.", + "category": "Crypto", + "subCategory": "Reference", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 12}, + }, + examples=[ + APIEx( + description="The most recent exchange announcements.", + parameters={"provider": "deribit"}, + ) + ], +) +async def announcements( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get what Deribit has announced.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Read the contract size of one instrument.", + parameters={"symbol": "BTC-PERPETUAL"}, + ) + ], +) +async def contract_size( + symbol: Annotated[ + str, FastAPIQuery(description="The instrument name.") + ] = "BTC-PERPETUAL", +) -> float | None: + """Get the size of one contract of an instrument.""" + from openbb_deribit.utils.client import request + + result = await request("get_contract_size", {"instrument_name": symbol}) + + return (result or {}).get("contract_size") + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[APIEx(description="Read the exchange's platform status.", parameters={})], +) +async def status() -> dict: + """Get whether the exchange has locked the platform.""" + from openbb_deribit.utils.client import request + + return await request("status", use_cache=False) + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[APIEx(description="Read the exchange's server time.", parameters={})], +) +async def server_time() -> int: + """Get the exchange's server time, in milliseconds since the epoch.""" + from openbb_deribit.utils.client import request + + return await request("get_time", use_cache=False) + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[APIEx(description="Get currency choices.", parameters={})], +) +async def currency_choices() -> list[dict[str, str]]: + """``[{label, value}]`` of every currency the exchange lists.""" + from openbb_deribit.utils.choices import currency_choices as choices + + return await choices() + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx(description="Get the names of every published index.", parameters={}) + ], +) +async def index_choices( + supported: Annotated[ + bool, + FastAPIQuery( + description="When True, returns only the indexes instruments settle" + + " against, rather than every published index." + ), + ] = False, + kind: Annotated[ + str, + FastAPIQuery( + description="Narrow the supported names to spot or derivative indexes." + ), + ] = "all", +) -> list[dict[str, str]]: + """``[{label, value}]`` of every index the exchange publishes.""" + from openbb_deribit.utils.choices import ( + _choices, + index_choices as choices, + ) + from openbb_deribit.utils.client import request + + if not supported: + return await choices() + + names = await request("get_supported_index_names", {"type": kind}) + + return _choices(sorted(str(name) for name in names)) + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Get instrument choices for one kind.", + parameters={"kind": "future"}, + ) + ], +) +async def instrument_choices( + kind: Annotated[ + str | None, + FastAPIQuery(description="The kind of instrument. Default is all of them."), + ] = None, + currency: Annotated[ + str, FastAPIQuery(description="The settlement currency of the instruments.") + ] = "any", + expired: Annotated[ + bool, FastAPIQuery(description="When True, returns expired instruments.") + ] = False, +) -> list[dict[str, str]]: + """``[{label, value}]`` of every instrument the exchange lists.""" + from openbb_deribit.utils.choices import instrument_choices as choices + + return await choices(kind, currency, expired) + + +@router.command( + methods=["GET"], + widget_config={"exclude": True}, + examples=[ + APIEx( + description="Get the combo identifiers listed on one currency.", + parameters={"currency": "BTC"}, + ) + ], +) +async def combo_choices( + currency: Annotated[ + str, FastAPIQuery(description="The currency the combos settle in.") + ] = "BTC", + state: Annotated[ + str | None, + FastAPIQuery(description="Narrow to active or inactive combos."), + ] = None, +) -> list[dict[str, str]]: + """``[{label, value}]`` of every combo identifier the exchange lists.""" + from openbb_deribit.utils.choices import combo_choices as choices + + return await choices(currency, state) diff --git a/openbb_platform/providers/deribit/openbb_deribit/routers/volatility.py b/openbb_platform/providers/deribit/openbb_deribit/routers/volatility.py new file mode 100644 index 00000000000..09da427003c --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/routers/volatility.py @@ -0,0 +1,90 @@ +"""Deribit Volatility sub-router.""" + +from openbb_core.app.model.command_context import CommandContext +from openbb_core.app.model.example import APIEx +from openbb_core.app.model.obbject import OBBject +from openbb_core.app.provider_interface import ( + ExtraParams, + ProviderChoices, + StandardParams, +) +from openbb_core.app.query import Query as OBBQuery +from openbb_core.app.router import Router + +router = Router(prefix="/volatility", description="Deribit volatility data.") + + +@router.command( + model="DeribitHistoricalVolatility", + widget_config={ + "name": "Deribit Realized Volatility", + "description": "The volatility a currency has actually realized.", + "category": "Crypto", + "subCategory": "Volatility", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 14}, + "params": [{"paramName": "currency", "value": "BTC"}], + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="The volatility BTC has realized.", + parameters={"currency": "BTC", "provider": "deribit"}, + ) + ], +) +async def realized( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the volatility a Deribit currency has actually realized.""" + return await OBBject.from_query(OBBQuery(**locals())) + + +@router.command( + model="DeribitVolatilityIndex", + widget_config={ + "name": "Deribit Volatility Index", + "description": "The DVOL index, as candles over a span.", + "category": "Crypto", + "subCategory": "Volatility", + "source": ["Deribit"], + "gridData": {"w": 40, "h": 14}, + "params": [{"paramName": "currency", "value": "BTC"}], + "data": { + "table": { + "chartView": {"enabled": True, "chartType": "line"}, + } + }, + }, + examples=[ + APIEx( + description="The BTC volatility index over the last month.", + parameters={"currency": "BTC", "provider": "deribit"}, + ), + APIEx( + description="Daily candles over a named span.", + parameters={ + "currency": "ETH", + "interval": "1d", + "start_date": "2026-01-01", + "end_date": "2026-06-30", + "provider": "deribit", + }, + ), + ], +) +async def index( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get the Deribit DVOL volatility index, as candles over a span.""" + return await OBBject.from_query(OBBQuery(**locals())) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/cache.py b/openbb_platform/providers/deribit/openbb_deribit/utils/cache.py new file mode 100644 index 00000000000..20970290e44 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/cache.py @@ -0,0 +1,146 @@ +"""Deribit shared response cache.""" + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from aiohttp_client_cache import SQLiteBackend + +MINUTE = 60 +HOUR = 60 * 60 +DAY = 60 * 60 * 24 + +CACHE_NAME = "deribit" +CACHE_TTL_DEFAULT = 5 * MINUTE +CACHE_MAX_BYTES = 128 * 1024 * 1024 +CACHE_SWEEP_PASSES = 3 + +URL_CACHE_TTL = { + "*/public/get_currencies*": DAY, + "*/public/get_index_price_names*": DAY, + "*/public/get_supported_index_names*": DAY, + "*/public/get_instruments*": HOUR, + "*/public/get_instrument*": HOUR, + "*/public/get_contract_size*": DAY, + "*/public/get_expirations*": HOUR, + "*/public/get_combo_ids*": HOUR, + "*/public/get_combos*": HOUR, + "*/public/get_combo_details*": HOUR, + "*/public/get_delivery_prices*": HOUR, + "*/public/get_apr_history*": HOUR, + "*/public/get_announcements*": HOUR, + "*/public/get_trade_volumes*": 15 * MINUTE, + "*/public/get_historical_volatility*": 15 * MINUTE, + "*/public/get_volatility_index_data*": 15 * MINUTE, + "*/public/get_funding_rate_history*": 15 * MINUTE, + "*/public/get_funding_chart_data*": 5 * MINUTE, + "*/public/get_funding_rate_value*": 5 * MINUTE, + "*/public/get_tradingview_chart_data*": 5 * MINUTE, + "*/public/get_index_chart_data*": 5 * MINUTE, + "*/public/get_last_settlements_by_currency*": 5 * MINUTE, + "*/public/get_last_settlements_by_instrument*": 5 * MINUTE, + "*/public/get_block_rfq_trades*": MINUTE, + "*/public/get_last_trades_by_currency*": MINUTE, + "*/public/get_last_trades_by_instrument*": MINUTE, + "*/public/get_book_summary_by_currency*": MINUTE, + "*/public/get_book_summary_by_instrument*": MINUTE, + "*/public/get_index_price*": MINUTE, + "*/public/get_order_book*": 0, + "*/public/ticker*": 0, + "*/public/get_time*": 0, + "*/public/status*": 0, +} + +_swept = False + + +def cache_path() -> str: + """Return the file the cached responses are kept in.""" + from openbb_core.app.utils import get_user_cache_directory + + return f"{get_user_cache_directory()}/http/{CACHE_NAME}" + + +def get_cache_backend() -> "SQLiteBackend": + """Build the on-disk response cache, with a TTL per endpoint. + + Returns + ------- + SQLiteBackend + The shared response cache. + """ + from aiohttp_client_cache import SQLiteBackend + + return SQLiteBackend( + cache_name=cache_path(), + expire_after=CACHE_TTL_DEFAULT, + urls_expire_after=URL_CACHE_TTL, + allowed_codes=(200,), + allowed_methods=("GET",), + include_headers=False, + ) + + +async def _oldest(backend: "SQLiteBackend", count: int) -> set: + """Return the keys of the entries written longest ago.""" + keys: set = set() + + async for key in backend.responses.keys(): + keys.add(key) + + if len(keys) >= count: + break + + return keys + + +async def _vacuum(backend: "SQLiteBackend") -> None: + """Give back the pages the deleted responses were held in.""" + from aiohttp_client_cache.backends.sqlite import SQLiteCache + + stored = backend.responses + + if not isinstance(stored, SQLiteCache): + return + + async with stored.get_connection(commit=True) as connection: + await connection.execute("VACUUM") + + +async def sweep_cache(backend: "SQLiteBackend") -> None: + """Drop what has expired, then the oldest of what is left if still too big. + + An expiry marks a response stale rather than deleting it, so the file grows + by every request ever made until something clears it out. It is swept once + per process, and what the sweep leaves over the limit is trimmed by the + share of the file that overshoots it. + + Parameters + ---------- + backend : SQLiteBackend + The cache to sweep. + """ + from pathlib import Path + + global _swept # noqa: PLW0603 + + if _swept: + return + + _swept = True + await backend.delete_expired_responses() + await _vacuum(backend) + stored = Path(f"{cache_path()}.sqlite") + + for _ in range(CACHE_SWEEP_PASSES): + if not stored.exists() or stored.stat().st_size <= CACHE_MAX_BYTES: + return + + held = await backend.responses.size() + overshoot = 1 - CACHE_MAX_BYTES / stored.stat().st_size + dropped = await _oldest(backend, max(1, round(held * overshoot))) + + if not dropped: + return + + await backend.responses.bulk_delete(dropped) + await _vacuum(backend) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/choices.py b/openbb_platform/providers/deribit/openbb_deribit/utils/choices.py new file mode 100644 index 00000000000..bef28ad00db --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/choices.py @@ -0,0 +1,86 @@ +"""Choice feeds built from what the exchange lists right now.""" + + +def _choices(values, labels: dict | None = None) -> list[dict[str, str]]: + """Pair each value with a label for a widget parameter.""" + named = labels or {} + + return [{"label": named.get(value, value), "value": value} for value in values] + + +async def currency_choices() -> list[dict[str, str]]: + """Return every currency the exchange lists.""" + from openbb_deribit.utils.helpers import get_currencies + + currencies = await get_currencies() + + return _choices( + sorted(str(d["currency"]) for d in currencies), + { + str(d["currency"]): str(d.get("currency_long") or d["currency"]) + for d in currencies + }, + ) + + +async def index_choices(extended: bool = False) -> list[dict[str, str]]: + """Return the name of every published index.""" + from openbb_deribit.utils.helpers import get_index_names + + names = await get_index_names(extended) + + return _choices( + sorted( + str(name["name"]) if isinstance(name, dict) else str(name) for name in names + ) + ) + + +async def instrument_choices( + kind: str | None = None, + currency: str = "any", + expired: bool = False, +) -> list[dict[str, str]]: + """Return every instrument of one kind, labelled by its name.""" + from openbb_deribit.utils.helpers import get_instruments + + instruments = await get_instruments(currency, kind, expired) + + return _choices(sorted(str(d["instrument_name"]) for d in instruments)) + + +async def perpetual_choices() -> list[dict[str, str]]: + """Return every perpetual, labelled by its instrument name.""" + from openbb_deribit.utils.helpers import get_perpetual_symbols + + perpetuals = await get_perpetual_symbols() + + return [{"label": name, "value": name} for name in sorted(perpetuals.values())] + + +async def options_root_choices() -> list[dict[str, str]]: + """Return every underlying with listed options.""" + from openbb_deribit.utils.helpers import get_options_roots + + return _choices(await get_options_roots()) + + +async def futures_root_choices() -> list[dict[str, str]]: + """Return every underlying with a listed futures curve.""" + from openbb_deribit.utils.helpers import get_futures_roots + + return _choices(await get_futures_roots()) + + +async def combo_choices( + currency: str = "BTC", state: str | None = None +) -> list[dict[str, str]]: + """Return the identifier of every combo listed on one currency.""" + from openbb_deribit.utils.client import request + from openbb_deribit.utils.helpers import normalize_currency + + combos = await request( + "get_combo_ids", {"currency": normalize_currency(currency), "state": state} + ) + + return _choices(sorted(str(combo) for combo in combos)) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/client.py b/openbb_platform/providers/deribit/openbb_deribit/utils/client.py new file mode 100644 index 00000000000..b2f479758de --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/client.py @@ -0,0 +1,132 @@ +"""Transport for the Deribit public JSON-RPC API.""" + +from typing import Any + +BASE_URL = "https://www.deribit.com/api/v2" +REQUEST_TIMEOUT = 60 +TOO_MANY_REQUESTS = 429 + + +def build_url(method: str, params: dict[str, Any] | None = None) -> str: + """Return the request URL for one public method. + + Parameters + ---------- + method : str + The method name, without the 'public/' prefix. + params : dict or None + The query parameters. Entries with a value of None are dropped, and + booleans are sent as the lower-case literals the API expects. + + Returns + ------- + str + The fully-qualified request URL. + """ + from urllib.parse import urlencode + + query = { + key: str(value).lower() if isinstance(value, bool) else value + for key, value in (params or {}).items() + if value is not None + } + url = f"{BASE_URL}/public/{method}" + + return f"{url}?{urlencode(query)}" if query else url + + +async def request( + method: str, + params: dict[str, Any] | None = None, + use_cache: bool = True, +) -> Any: + """Call one public method and return its result. + + Parameters + ---------- + method : str + The method name, without the 'public/' prefix. + params : dict or None + The query parameters. + use_cache : bool + Whether to read and write the on-disk response cache. + + Returns + ------- + Any + The 'result' member of the JSON-RPC response. + + Raises + ------ + OpenBBError + If the API reports an error, or the response cannot be read. + """ + from openbb_core.app.model.abstract.error import OpenBBError + + from openbb_deribit.utils.session import get_cached_session, get_session + + url = build_url(method, params) + session = await get_cached_session() if use_cache else await get_session() + + try: + response = await session.get(url, timeout=REQUEST_TIMEOUT) + + if response.status != 200: + raise OpenBBError( + f"Deribit answered public/{method} with HTTP {response.status}." + + ( + " The request rate was exceeded; narrow the query or retry." + if response.status == TOO_MANY_REQUESTS + else "" + ) + ) + + payload = await response.json(content_type=None) + except OpenBBError: + raise + except Exception as error: # noqa: BLE001 + raise OpenBBError( + f"Deribit request failed for public/{method} ->" + f" {error.__class__.__name__}: {error}" + ) from error + + if not isinstance(payload, dict): + raise OpenBBError(f"Unexpected Deribit response for public/{method}.") + + if payload.get("error"): + detail = payload["error"] + message = detail.get("message") if isinstance(detail, dict) else detail + reason = detail.get("data") if isinstance(detail, dict) else None + raise OpenBBError( + f"Deribit returned an error for public/{method} -> {message}" + + (f" {reason}" if reason else "") + ) + + return payload.get("result") + + +async def gather( + calls: "list[tuple[str, dict[str, Any] | None]]", + use_cache: bool = True, +) -> list: + """Call several public methods at once. + + Parameters + ---------- + calls : list[tuple[str, dict or None]] + Each method name with its query parameters. + use_cache : bool + Whether to read and write the on-disk response cache. + + Returns + ------- + list + The result of each call, in the order it was given, with an exception + in place of any result that could not be read. + """ + import asyncio + + return await asyncio.gather( + *(request(method, params, use_cache) for method, params in calls), + return_exceptions=True, + ) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/constants.py b/openbb_platform/providers/deribit/openbb_deribit/utils/constants.py new file mode 100644 index 00000000000..31b1891b808 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/constants.py @@ -0,0 +1,102 @@ +"""Fixed enumerations and choice endpoints of the Deribit public API.""" + +from typing import Literal + +from openbb_core.app.service.system_service import SystemService + +API_PREFIX = ( + SystemService() + .system_settings.python_settings.model_dump() + .get("api_settings", {}) + .get("prefix", "") + or "/api/v1" +) + +CURRENCY_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/reference/currency_choices" +INDEX_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/reference/index_choices" +INSTRUMENT_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/reference/instrument_choices" +COMBO_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/reference/combo_choices" +CURVE_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/futures/curve_choices" +PERPETUAL_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/futures/perpetual_choices" +UNDERLYING_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/options/underlying_choices" +EXPIRATION_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/options/expiration_choices" +STRATEGY_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/options/strategy_choices" +STRIKE_CHOICES_ENDPOINT = f"{API_PREFIX}/deribit/options/strike_choices" + +SYMBOL_STYLE = {"popupWidth": 600} + +CURRENCIES = ("BTC", "ETH", "USDC", "USDT", "EURR") +Currencies = Literal["BTC", "ETH", "USDC", "USDT", "EURR"] +AnyCurrencies = Literal["BTC", "ETH", "USDC", "USDT", "EURR", "any"] + +VOLATILITY_INDEX_CURRENCIES = ("BTC", "ETH") +REALIZED_VOLATILITY_CURRENCIES = ("BTC", "ETH", "EURR") + +UNDERLYING_CURRENCIES = ("BTC", "ETH") +LISTING_CURRENCIES = ("BTC", "ETH", "USDC") +ListingCurrencies = Literal["BTC", "ETH", "USDC"] +AnyListingCurrencies = Literal["BTC", "ETH", "USDC", "any"] +OPTION_KINDS = ("option", "option_combo") + +INSTRUMENT_KINDS = ("future", "option", "spot", "future_combo", "option_combo") +InstrumentKinds = Literal["future", "option", "spot", "future_combo", "option_combo"] +TradeKinds = Literal["future", "option", "future_combo", "option_combo", "combo"] +ROUTED_SPOT_ERROR = "not_supported_for_coinbase_routed_spot" +ExpirationKinds = Literal["future", "option", "any"] +ExpirationCurrencies = Literal["BTC", "ETH", "any", "grouped"] + +SettlementTypes = Literal["settlement", "delivery", "bankruptcy"] +ComboStates = Literal["active", "inactive"] +IndexNameTypes = Literal["all", "spot", "derivative"] +Sorting = Literal["asc", "desc", "default"] +FundingLengths = Literal["8h", "24h", "1m"] +IndexChartRanges = Literal["1h", "1d", "2d", "1m", "1y", "all"] +AprCurrencies = Literal["usde", "steth", "usdc", "build"] + +ORDER_BOOK_DEPTHS = (1, 5, 10, 20, 50, 100, 1000, 10000) + +INTERVALS = ( + "1m", + "3m", + "5m", + "10m", + "15m", + "30m", + "1h", + "2h", + "3h", + "6h", + "12h", + "1d", +) +Intervals = Literal[ + "1m", "3m", "5m", "10m", "15m", "30m", "1h", "2h", "3h", "6h", "12h", "1d" +] +INTERVAL_MAP = { + "1m": "1", + "3m": "3", + "5m": "5", + "10m": "10", + "15m": "15", + "30m": "30", + "1h": "60", + "2h": "120", + "3h": "180", + "6h": "360", + "12h": "720", + "1d": "1D", +} + +VOLATILITY_RESOLUTIONS = ("1m", "1h", "12h", "1d") +VolatilityResolutions = Literal["1m", "1h", "12h", "1d"] +VOLATILITY_RESOLUTION_MAP = { + "1m": "60", + "1h": "3600", + "12h": "43200", + "1d": "1D", +} + +BLOCK_RFQ_MIN_COUNT = 10 +BLOCK_RFQ_MAX_COUNT = 50 +MAX_TRADE_COUNT = 1000 +MAX_SETTLEMENT_COUNT = 1000 diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/helpers.py b/openbb_platform/providers/deribit/openbb_deribit/utils/helpers.py index e647abffcba..0cc4f359914 100644 --- a/openbb_platform/providers/deribit/openbb_deribit/utils/helpers.py +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/helpers.py @@ -1,402 +1,535 @@ -"""Deribit Helpers Module.""" +"""Shared accessors for the Deribit public API.""" -from typing import Literal +from typing import Any -from async_lru import alru_cache -from openbb_core.app.model.abstract.error import OpenBBError - -DERIBIT_OPTIONS_SYMBOLS = ["BTC", "ETH", "SOL", "XRP", "BNB", "PAXG"] -OptionsSymbols = Literal["BTC", "ETH", "SOL", "XRP", "BNB", "PAXG"] -CURRENCIES = ["BTC", "ETH", "USDC", "USDT", "EURR", "all"] -Currencies = Literal["BTC", "ETH", "USDC", "USDT", "EURR", "all"] -DERIVATIVE_TYPES = ["future", "option", "spot", "future_combo", "option_combo"] -DerivativeTypes = Literal["future", "option", "spot", "future_combo", "option_combo"] -DERIBIT_FUTURES_CURVE_SYMBOLS = ["BTC", "ETH", "PAXG"] -FuturesCurveSymbols = Literal["BTC", "ETH", "PAXG"] -BASE_URL = "https://www.deribit.com" - -DERIBIT_INTERVALS = [ - "1m", - "3m", - "5m", - "10m", - "15m", - "30m", - "1h", - "2h", - "3h", - "6h", - "12h", - "1d", -] -DeribitIntervals = Literal[ - "1m", "3m", "5m", "10m", "15m", "30m", "1h", "2h", "3h", "6h", "12h", "1d" -] -INTERVAL_MAP = { - "1m": "1", - "3m": "3", - "5m": "5", - "10m": "10", - "15m": "15", - "30m": "30", - "1h": "60", - "2h": "120", - "3h": "180", - "6h": "360", - "12h": "720", - "1d": "1D", -} +MAX_CHART_WINDOWS = 15 +CHART_WINDOW_SIZE = 5000 +SECONDS_PER_DAY = 86400 +CURVE_LOOKBACK_HOURS = 24 -@alru_cache(maxsize=64) -async def get_instruments( - currency: Currencies = "BTC", - derivative_type: DerivativeTypes | None = None, - expired: bool = False, -) -> list[dict]: - """ - Get Deribit instruments. +def to_timestamp(value: Any) -> int: + """Return a date, datetime, or string as milliseconds since the epoch.""" + from pandas import Timestamp, to_datetime + + stamp = value if isinstance(value, Timestamp) else to_datetime(value, utc=True) + + if stamp.tzinfo is None: + stamp = stamp.tz_localize("UTC") + + return int(stamp.timestamp() * 1000) + + +def from_timestamp(value: Any) -> Any: + """Return milliseconds since the epoch as a UTC datetime.""" + from pandas import to_datetime + + return ( + to_datetime(value, unit="ms", origin="unix", utc=True) + if value is not None + else None + ) + + +def from_day_number(value: Any) -> Any: + """Return a count of whole days since the epoch as a date.""" + from pandas import to_datetime + + return ( + to_datetime( + int(value) * SECONDS_PER_DAY, unit="s", origin="unix", utc=True + ).date() + if value is not None + else None + ) + + +def reject_options_by_settlement(currency: str, kind: "str | None") -> None: + """Refuse a listing of option contracts scoped to a settlement currency. + + Only BTC and ETH are both a settlement currency and an underlying. USDC + settles options written on seven different underlyings, so a listing scoped + to it returns a mixture of unrelated assets rather than a chain, and USDT + settles no options at all. Options are listed by the underlying they are + written on, or by instrument name. Parameters ---------- - currency : Currencies - The currency to get instruments for. Default is "BTC". - derivative_type : Optional[DerivativeTypes] - The type of derivative to get instruments for. Default is None, which gets all types. + currency : str + The settlement currency asked for. + kind : str or None + The kind of instrument asked for. + + Raises + ------ + ValueError + If option contracts are asked for by a currency that only settles them. + """ + from openbb_deribit.utils.constants import OPTION_KINDS, UNDERLYING_CURRENCIES + + if currency.upper() in UNDERLYING_CURRENCIES or kind not in OPTION_KINDS: + return + + raise ValueError( + f"{currency.upper()} settles options, it is not an underlying, so there" + " are no options on it to list. Ask for options by the underlying they" + " are written on, such as BTC_USDC or SOL_USDC, using the symbol" + " parameter." + ) + + +def normalize_currency(currency: str) -> str: + """Return a currency as the exchange spells it, leaving the wildcard alone.""" + return "any" if currency.lower() in ("any", "all") else currency.upper() + + +async def get_currencies() -> list[dict]: + """Return every currency the exchange lists.""" + from openbb_deribit.utils.client import request + + return await request("get_currencies") + + +async def get_instruments( + currency: str = "any", + kind: str | None = None, + expired: bool = False, +) -> list[dict]: + """Return the instruments of one currency, optionally of one kind. + + Parameters + ---------- + currency : str + The settlement currency, or 'any' for all of them. + kind : str or None + One of the instrument kinds, or None for all of them. + expired : bool + When True, returns instruments that have already expired. Returns ------- list[dict] - A list of instrument dictionaries. + Every matching instrument. """ - # pylint: disable=import-outside-toplevel - from openbb_core.provider.utils.helpers import amake_request + from openbb_deribit.utils.client import request - if currency != "all" and currency.upper() not in CURRENCIES: - raise ValueError( - f"Currency {currency} not supported. Supported currencies are: {', '.join(CURRENCIES)}" - ) - if derivative_type and derivative_type not in DERIVATIVE_TYPES: - raise ValueError( - f"Kind {derivative_type} not supported. Supported kinds are: {', '.join(DERIVATIVE_TYPES)}" - ) - - url = f"{BASE_URL}/api/v2/public/get_instruments?currency={currency.upper() if currency != 'all' else 'any'}" - - if derivative_type is not None: - url += f"&kind={derivative_type}" - - if expired: - url += f"&expired={str(expired).lower()}" - - try: - response = await amake_request(url) - return response.get("result", []) # type: ignore - except Exception as e: # pylint: disable=broad-except - raise OpenBBError( - f"Failed to get instruments -> {e.__class__.__name__}: {e}" - ) from e - - -async def get_options_symbols(symbol: OptionsSymbols = "BTC") -> dict: - """ - Get a dictionary of contract symbols by expiry. - - Parameters - ---------- - symbol : OptionsSymbols - The underlying symbol to get options for. Default is "btc". - - Returns - ------- - dict[str, str] - A dictionary of contract symbols by expiry date. - """ - # pylint: disable=import-outside-toplevel - from pandas import to_datetime - - if symbol.upper() not in DERIBIT_OPTIONS_SYMBOLS: - raise ValueError( - f"Invalid Deribit symbol. Supported symbols are: {', '.join(DERIBIT_OPTIONS_SYMBOLS)}", - ) - - currency = ( - "USDC" if symbol.upper() in ["BNB", "PAXG", "SOL", "XRP"] else symbol.upper() + return await request( + "get_instruments", + { + "currency": normalize_currency(currency), + "kind": kind, + "expired": expired, + }, ) - instruments = await get_instruments(currency, "option") - expirations: dict = {} - all_options = list( - set( - d.get("instrument_name") - for d in instruments - if d.get("instrument_name").startswith(symbol) - and d.get("instrument_name").endswith(("-C", "-P")) - ) + + +async def get_instrument(symbol: str) -> dict: + """Return the definition of one instrument.""" + from openbb_deribit.utils.client import request + + return await request("get_instrument", {"instrument_name": symbol}) + + +def flatten_ticker(data: dict) -> dict: + """Return a ticker with its nested statistics and greeks at the top level.""" + flat = dict(data) + stats = flat.pop("stats", None) or {} + greeks = flat.pop("greeks", None) or {} + + return {**flat, **stats, **greeks} + + +async def get_ticker(symbol: str) -> dict: + """Return the quote and statistics of one instrument.""" + from openbb_deribit.utils.client import request + + return flatten_ticker(await request("ticker", {"instrument_name": symbol})) + + +async def get_tickers(symbols: list[str]) -> list[dict]: + """Return the quotes of many instruments, skipping the ones that fail.""" + from openbb_deribit.utils.client import gather + + results = await gather([("ticker", {"instrument_name": s}) for s in symbols]) + + return [ + flatten_ticker(result) + for result in results + if isinstance(result, dict) and result + ] + + +async def get_index_names(extended: bool = False) -> list: + """Return the names of every published index.""" + from openbb_deribit.utils.client import request + + return await request("get_index_price_names", {"extended": extended}) + + +async def get_expirations( + currency: str = "any", + kind: str = "any", + currency_pair: str | None = None, +) -> dict: + """Return the expirations listed for one currency and kind.""" + from openbb_deribit.utils.client import request + + return await request( + "get_expirations", + {"currency": currency, "kind": kind, "currency_pair": currency_pair}, ) - for item in sorted( - list( - set( - ( - to_datetime(d.split("-")[1]).date().strftime("%Y-%m-%d"), - d.split("-")[1], - ) - for d in all_options - ) - ) - ): - expirations[item[0]] = item[1] - - return {k: [d for d in all_options if v in d] for k, v in expirations.items()} -async def get_futures_curve_symbols(symbol: FuturesCurveSymbols = "BTC") -> list[str]: - """ - Get a list of futures symbols for a given symbol. +def _roots(instruments: list[dict]) -> dict[str, list[dict]]: + """Group instruments by the root of their name.""" + grouped: dict[str, list[dict]] = {} - Parameters - ---------- - symbol : FuturesCurveSymbols - The symbol to get futures symbols for. + for instrument in instruments: + root = str(instrument.get("instrument_name") or "").split("-")[0] - Returns - ------- - list[str] - A list of futures symbols. - """ - symbol = symbol.upper() # type: ignore - if symbol not in DERIBIT_FUTURES_CURVE_SYMBOLS: - raise ValueError( - f"Invalid Deribit symbol. Supported symbols are: {', '.join(DERIBIT_FUTURES_CURVE_SYMBOLS)}", - ) + if root: + grouped.setdefault(root, []).append(instrument) - currency = "USDC" if symbol == "PAXG" else symbol - instruments = await get_instruments(currency, "future") - - symbols: list = [] - for d in instruments: - ins_name = d.get("instrument_name", "") - if ins_name.startswith(symbol): - symbols.append(ins_name) - - return symbols + return grouped -async def get_ticker_data(symbol: str) -> dict: - """ - Get ticker data. +async def get_options_roots() -> list[str]: + """Return every underlying the exchange currently lists options on.""" + return sorted(_roots(await get_instruments("any", "option"))) + + +async def get_futures_roots() -> list[str]: + """Return every underlying the exchange currently lists dated futures on.""" + grouped = _roots(await get_instruments("any", "future")) + + return sorted( + root + for root, listed in grouped.items() + if sum(1 for d in listed if d.get("settlement_period") != "perpetual") > 1 + ) + + +async def get_options_symbols(symbol: str = "BTC") -> dict[str, list[str]]: + """Return the option symbols of one underlying, keyed by expiration. Parameters ---------- symbol : str - The symbol to get ticker data for. + The underlying root, as it appears in the instrument name. Returns ------- - dict - The ticker data. + dict[str, list[str]] + Every contract symbol, keyed by its expiration date. + + Raises + ------ + OpenBBError + If the exchange lists no options on the underlying. """ - # pylint: disable=import-outside-toplevel - from openbb_core.provider.utils.helpers import amake_request + from openbb_core.app.model.abstract.error import OpenBBError + from pandas import to_datetime - url = f"{BASE_URL}/api/v2/public/ticker?instrument_name={symbol}" + root = symbol.upper() + grouped = _roots(await get_instruments("any", "option")) + listed = grouped.get(root) - try: - response = await amake_request(url) - if response.get("error"): # type: ignore[union-attr] - raise OpenBBError(response.get("error")) # type: ignore[union-attr] - data = response.get("result", {}) # type: ignore[union-attr] - stats = data.pop("stats", {}) - return {**data, **stats} + if not listed: + raise OpenBBError( + f"Deribit lists no options on {root}." + f" Underlyings with listed options are: {', '.join(sorted(grouped))}" + ) - except Exception as e: # pylint: disable=broad-except - raise OpenBBError(f"Failed to get ticker data -> {e}: {e.args[0]}") from e + symbols = sorted( + str(d["instrument_name"]) + for d in listed + if str(d.get("instrument_name", "")).endswith(("-C", "-P")) + ) + expirations: dict[str, list[str]] = {} + + for contract in symbols: + code = contract.split("-")[1] + expiration = to_datetime(code).date().strftime("%Y-%m-%d") + expirations.setdefault(expiration, []).append(contract) + + return dict(sorted(expirations.items())) -async def get_perpetual_symbols() -> dict: - """ - Get perpetual symbols. +async def get_futures_curve_symbols(symbol: str = "BTC") -> list[str]: + """Return the dated future symbols of one underlying. + + Parameters + ---------- + symbol : str + The underlying root, as it appears in the instrument name. Returns ------- - dict - A dictionary of short symbols to full perpetual symbols. + list[str] + Every contract symbol on the curve, nearest expiration first. + + Raises + ------ + OpenBBError + If the exchange lists no curve for the underlying. """ - instruments = await get_instruments("all", "future") + from openbb_core.app.model.abstract.error import OpenBBError + + root = symbol.upper() + grouped = _roots(await get_instruments("any", "future")) + listed = [ + d + for d in grouped.get(root, []) + if d.get("settlement_period") != "perpetual" + and str(d.get("instrument_name", "")).split("-")[0] == root + ] + + if not listed: + roots = await get_futures_roots() + + raise OpenBBError( + f"Deribit lists no futures curve for {root}." + f" Underlyings with a curve are: {', '.join(roots)}" + ) + + return [ + str(d["instrument_name"]) + for d in sorted(listed, key=lambda d: d.get("expiration_timestamp") or 0) + ] + + +async def get_perpetual_symbols() -> dict[str, str]: + """Return every perpetual symbol, keyed by its shortened root.""" + instruments = await get_instruments("any", "future") + return { - d["instrument_name"].split("-")[0].replace("_", ""): d["instrument_name"] + str(d["instrument_name"]).split("-")[0].replace("_", ""): str( + d["instrument_name"] + ) for d in instruments if d.get("settlement_period") == "perpetual" } -async def get_ohlc_data( - symbol: str, start_date: str, end_date: str, interval: DeribitIntervals = "1d" -) -> list[dict]: - """Get OHLC data for a given symbol. Enter dates in the format 'YYYY-MM-DD'.""" - # pylint: disable=import-outside-toplevel - import asyncio # noqa - from openbb_core.provider.utils.errors import EmptyDataError - from openbb_core.provider.utils.helpers import amake_request - from pandas import DataFrame, date_range, to_datetime +async def get_futures_symbols() -> list[str]: + """Return every listed future symbol.""" + instruments = await get_instruments("any", "future") - new_interval = INTERVAL_MAP.get(interval, interval) - - all_instruments = await get_instruments("all", None) - all_symbols = { - d.get("instrument_name"): d.get("creation_timestamp") for d in all_instruments - } - creation_date = all_symbols.get(symbol, 0) - use_start = to_datetime(start_date).timestamp() * 1000 > creation_date - - if creation_date == 0: - raise ValueError(f"Symbol {symbol} not found") - - def generate_urls(symbol, start_date, end_date, interval, window_size=5000): - """Generate urls for historical data breaking it down into requests of length window_size.""" - interval_period = f"{interval}min" if interval.lower() != "1d" else "1d" - interval = "1D" if interval.lower() == "1d" else interval - dates = date_range( - start=start_date if use_start else creation_date, - end=end_date, - freq=interval_period, - ) - windows = [ - (dates[i], dates[min(i + window_size, len(dates) - 1)]) - for i in range(0, len(dates), window_size) - ] - urls: list = [] - for start, end in windows: - start_timestamp = int(start.timestamp() * 1000) - end_timestamp = int(end.timestamp() * 1000) - url = ( - "https://www.deribit.com/api/v2/public/get_tradingview_chart_data?" - f"instrument_name={symbol}&start_timestamp={start_timestamp}&" - f"end_timestamp={end_timestamp}&resolution={interval}" - ) - urls.append(url) - - return urls - - results: list = [] - - async def get_one(url): - """Get data from one url.""" - json_response = await amake_request(url) - if json_response.get("error"): # type: ignore[union-attr] - raise ValueError(json_response["error"]) # type: ignore[call-overload] - if json_response.get("result"): # type: ignore[union-attr] - result = json_response["result"] # type: ignore[call-overload] - df = DataFrame(result) - df = ( - df.drop(columns=["status"]) - .rename(columns={"ticks": "date", "cost": "volume_notional"}) - .convert_dtypes() - ) - df["date"] = to_datetime(df.date, unit="ms", origin="unix", utc=True) - if interval == "1D": - df.date = df.date.dt.date - df["symbol"] = symbol - results.extend( - df[ - [ - "date", - "symbol", - "open", - "high", - "low", - "close", - "volume", - "volume_notional", - ] - ].to_dict(orient="records") - ) - - urls = generate_urls(symbol, start_date, end_date, new_interval) - - if len(urls) > 15: - raise OpenBBError( - "The request is too large. Break up the request into smaller chunks." - ) - - tasks = [get_one(url) for url in urls] - - await asyncio.gather(*tasks, return_exceptions=True) - - if results: - return sorted(results, key=lambda x: x["date"]) - raise EmptyDataError("No data found for the given symbol and dates.") + return sorted(str(d["instrument_name"]) for d in instruments) -async def check_ohlc_symbol(symbol: str) -> bool | str: - """ - Check if the symbol has OHLC data. +async def resolve_symbol(symbol: str) -> str: + """Return the instrument name a symbol refers to. Parameters ---------- symbol : str - The symbol to check. + An instrument name, or the shortened root of a perpetual. Returns ------- - bool - True if the symbol has OHLC data, False otherwise. + str + The instrument name the exchange knows the symbol by. + + Raises + ------ + OpenBBError + If the exchange lists no such instrument. """ - all_instruments = await get_instruments("all", None) - all_symbols = { - d.get("instrument_name", ""): d.get("creation_timestamp") - for d in all_instruments + from openbb_core.app.model.abstract.error import OpenBBError + + candidate = symbol.upper() + perpetuals = await get_perpetual_symbols() + + if candidate in perpetuals: + return perpetuals[candidate] + + instruments = await get_instruments("any", None) + listed = {str(d.get("instrument_name")) for d in instruments} + + if candidate in listed: + return candidate + + raise OpenBBError(f"Deribit lists no instrument named {symbol}.") + + +def _chart_windows( + start: int, end: int, resolution: str, window: int = CHART_WINDOW_SIZE +) -> list[tuple[int, int]]: + """Split a span into windows of at most one page of candles each.""" + from pandas import date_range + + freq = "1D" if resolution == "1D" else f"{resolution}min" + stamps = date_range(start=from_timestamp(start), end=from_timestamp(end), freq=freq) + + if len(stamps) <= 1: + return [(start, end)] + + return [ + ( + int(stamps[index].timestamp() * 1000), + int(stamps[min(index + window, len(stamps) - 1)].timestamp() * 1000), + ) + for index in range(0, len(stamps), window) + ] + + +async def get_ohlc_data( + symbol: str, + start_date: Any, + end_date: Any, + interval: str = "1d", +) -> list[dict]: + """Return the candles of one instrument over a span. + + Parameters + ---------- + symbol : str + The instrument name. + start_date : Any + The first date of the span. + end_date : Any + The last date of the span. + interval : str + One of the supported candle intervals. + + Returns + ------- + list[dict] + One record per candle, oldest first. + + Raises + ------ + OpenBBError + If the span needs more pages than the exchange will serve at once. + EmptyDataError + If the instrument published no candles over the span. + """ + from openbb_core.app.model.abstract.error import OpenBBError + from openbb_core.provider.utils.errors import EmptyDataError + + from openbb_deribit.utils.client import gather + from openbb_deribit.utils.constants import INTERVAL_MAP + + resolution = INTERVAL_MAP.get(interval, interval) + instruments = await get_instruments("any", None) + created = { + str(d.get("instrument_name")): d.get("creation_timestamp") for d in instruments } - all_perpetuals = await get_perpetual_symbols() - if symbol in all_perpetuals: - return all_perpetuals.get(symbol, False) - if symbol in all_symbols: - return symbol - return False + if symbol not in created: + raise OpenBBError(f"Deribit lists no instrument named {symbol}.") + + start = max(to_timestamp(start_date), int(created[symbol] or 0)) + end = to_timestamp(end_date) + windows = _chart_windows(start, end, resolution) + + if len(windows) > MAX_CHART_WINDOWS: + raise OpenBBError( + f"The span covers {len(windows)} pages of {interval} candles, and" + f" Deribit serves at most {MAX_CHART_WINDOWS} at a time." + " Narrow the dates, or widen the interval." + ) + + results = await gather( + [ + ( + "get_tradingview_chart_data", + { + "instrument_name": symbol, + "start_timestamp": window_start, + "end_timestamp": window_end, + "resolution": resolution, + }, + ) + for window_start, window_end in windows + ] + ) + records: dict[int, dict] = {} + + for result in results: + if not isinstance(result, dict) or not result.get("ticks"): + continue + + for index, tick in enumerate(result["ticks"]): + records[tick] = { + "date": from_timestamp(tick), + "symbol": symbol, + "open": result["open"][index], + "high": result["high"][index], + "low": result["low"][index], + "close": result["close"][index], + "volume": result["volume"][index], + "volume_notional": result["cost"][index], + } + + if not records: + raise EmptyDataError(f"Deribit published no {interval} candles for {symbol}.") + + ordered = [records[tick] for tick in sorted(records)] + + if resolution == "1D": + for record in ordered: + record["date"] = record["date"].date() + + return ordered -async def get_futures_symbols() -> list: - """ - Get futures symbols. +async def get_futures_curve_by_hours_ago(symbol: str, hours: int) -> list[dict]: + """Return the futures curve of one underlying as it stood some hours ago. + + Parameters + ---------- + symbol : str + The underlying root. + hours : int + How many hours back to read the curve. Returns ------- - list - A list of futures symbols. + list[dict] + The last price of each contract that had traded by that hour. A contract + the exchange published no candle for is left out, because a curve that + carries the current price under a past label is not a past curve. """ - instruments = await get_instruments("all", "future") - return [d["instrument_name"] for d in instruments] + from datetime import datetime, timedelta, timezone - -async def get_futures_curve_by_hours_ago(symbol, hours): - """Get futures curve N hours ago.""" - # pylint: disable=import-outside-toplevel - import asyncio # noqa - from datetime import datetime, timedelta + from openbb_deribit.utils.client import gather symbols = await get_futures_curve_symbols(symbol) - now = datetime.now().replace(microsecond=0, second=0) + now = datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0) + target = to_timestamp(now - timedelta(hours=hours)) + start = to_timestamp(now - timedelta(hours=hours + CURVE_LOOKBACK_HOURS)) + results = await gather( + [ + ( + "get_tradingview_chart_data", + { + "instrument_name": contract, + "start_timestamp": start, + "end_timestamp": target, + "resolution": "60", + }, + ) + for contract in symbols + ] + ) + curve: list[dict] = [] - if hours < 24: - start = (now - timedelta(days=1)).strftime("%Y-%m-%d") - else: - start = (now - timedelta(hours=hours)).strftime("%Y-%m-%d") - end = (now + timedelta(days=2)).strftime("%Y-%m-%d") + for contract, result in zip(symbols, results): + if not isinstance(result, dict) or not result.get("ticks"): + continue - results: list = [] + ticks = result["ticks"] + index = min( + range(len(ticks)), key=lambda position: abs(ticks[position] - target) + ) + curve.append( + { + "instrument_name": contract, + "hours_ago": hours, + "last_price": result["close"][index], + } + ) - async def get_one(s): - """Get data for one symbol.""" - data = await get_ohlc_data(s, start, end, "1h") - result = { - "instrument_name": data[-1 - hours]["symbol"], - "hours_ago": hours, - "last_price": data[-1 - hours]["close"], - } - results.append(result) - - tasks = [get_one(s) for s in symbols] - - await asyncio.gather(*tasks, return_exceptions=True) - - return results + return curve diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/__init__.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/__init__.py new file mode 100644 index 00000000000..549c31f0ede --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/__init__.py @@ -0,0 +1 @@ +"""Deribit options analysis.""" diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/chain.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/chain.py new file mode 100644 index 00000000000..c44436960fb --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/chain.py @@ -0,0 +1,337 @@ +"""Loading a Deribit option chain for analysis. + +The chain endpoint subscribes to every contract over a websocket to collect the +greeks the exchange publishes, which takes about twelve seconds for BTC. The +analysis views need quotes, a volatility and the contract specification rather +than the exchange's own greeks, and all three are reachable over REST in under +a second, so this is where they are read from. The greeks are modelled from the +quoted volatility instead. +""" + +import asyncio +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from pandas import DataFrame + +CHAIN_TTL = 120.0 + +_LOADED: dict = {} +_LOCKS: dict = {} + +_SETTLEMENT = {"BTC": "BTC", "ETH": "ETH"} + + +def settlement_of(root: str) -> str: + """Return the currency a root's options are collateralised in.""" + return _SETTLEMENT.get(root, "USDC") + + +async def _source(root: str) -> tuple: + """Read the quotes, specifications, and index level of one underlying. + + The specification is what names a contract's strike, side, size, and + settlement style, so it is read rather than parsed back out of the + instrument name, which spells a fractional strike with a letter. + """ + from openbb_deribit.utils.client import request + from openbb_deribit.utils.helpers import get_instruments + + currency = settlement_of(root) + summary, instruments = await asyncio.gather( + request( + "get_book_summary_by_currency", {"currency": currency, "kind": "option"} + ), + get_instruments(currency, "option"), + ) + specs = { + str(spec["instrument_name"]): spec + for spec in instruments + if str(spec["instrument_name"]).split("-")[0] == root + } + quotes = [row for row in summary if str(row["instrument_name"]) in specs] + index = next(iter(specs.values()), {}).get("price_index") + level = await request("get_index_price", {"index_name": index}) if index else {} + + return quotes, specs, float((level or {}).get("index_price") or 0) + + +def _rows(quotes: list, specs: dict, spot: float) -> list: + """Build one analysis row per quoted contract. + + An inverse contract is quoted in the coin, so its premium is carried to the + quote currency at the index level, which is what the chain endpoint does and + what the valuation layer expects. + """ + from datetime import datetime, timezone + + from openbb_deribit.utils.helpers import from_timestamp + from openbb_deribit.utils.options.greeks import SECONDS_PER_YEAR, greeks + + now = datetime.now(timezone.utc).timestamp() + built: list[dict] = [] + + for quote in quotes: + name = str(quote["instrument_name"]) + spec = specs[name] + option_type = str(spec["option_type"]) + strike = float(spec["strike"]) + inverse = str(spec.get("instrument_type")) == "reversed" + carry = spot if inverse else 1.0 + forward = float(quote.get("underlying_price") or 0) + volatility = quote.get("mark_iv") + years = (float(spec["expiration_timestamp"]) / 1000 - now) / SECONDS_PER_YEAR + modelled = greeks( + forward, strike, years, float(volatility or 0) / 100, option_type + ) + built.append( + { + "contract_symbol": name, + "expiration": from_timestamp(spec["expiration_timestamp"]).date(), + "dte": max(int(years * 365), 0), + "strike": strike, + "option_type": option_type, + "contract_size": float(spec.get("contract_size") or 1), + "tick_size": _carried(spec.get("tick_size"), carry), + "is_inverse": inverse, + "mark": _carried(quote.get("mark_price"), carry), + "bid": _carried(quote.get("bid_price"), carry), + "ask": _carried(quote.get("ask_price"), carry), + "last": _carried(quote.get("last"), carry), + "implied_volatility": volatility, + "open_interest": quote.get("open_interest"), + "volume": quote.get("volume"), + "volume_notional": quote.get("volume_usd"), + "underlying_price": forward, + "underlying_spot_price": spot, + **modelled, + } + ) + + return built + + +def _carried(value: Any, carry: float) -> "float | None": + """Carry one quoted price to the currency the position is measured in.""" + return None if value is None else float(value) * carry + + +async def load_chain(symbol: str, use_cache: bool = True) -> "DataFrame": + """Return the full chain of one underlying as a frame. + + Reading a chain is three REST calls, so a dashboard showing several views of + one underlying would make them once per view, and every parameter a reader + changes would make them again. The last read is held and shared instead: a + nine-widget tab costs three calls, and changing what is drawn costs none. + + Parameters + ---------- + symbol : str + The underlying root, as it appears in the instrument name. + use_cache : bool + Whether a read taken moments ago may be reused. + + Returns + ------- + DataFrame + One row per contract, with its quote, greeks, and settlement style. + + Raises + ------ + EmptyDataError + If the exchange published no option quotes for the underlying. + """ + from time import monotonic + + from openbb_core.provider.utils.errors import EmptyDataError + from pandas import DataFrame + + root = symbol.upper() + lock = _LOCKS.setdefault(root, asyncio.Lock()) + + async with lock: + held = _LOADED.get(root) + + if use_cache and held and monotonic() - held[0] < CHAIN_TTL: + return held[1].copy() + + quotes, specs, spot = await _source(root) + frame = DataFrame(_rows(quotes, specs, spot)) + + if frame.empty: + raise EmptyDataError(f"Deribit published no option quotes for {root}.") + + _LOADED[root] = (monotonic(), frame) + + return frame.copy() + + +async def load_combos(symbol: str, use_cache: bool = True) -> list: + """Return the combo books listed on an underlying's options, with their quotes. + + Parameters + ---------- + symbol : str + The underlying root, as it appears in the instrument name. + use_cache : bool + Whether a read taken moments ago may be reused. + + Returns + ------- + list + One record per combo: its name, its legs as (instrument, amount) pairs, + and its bid and ask in the currency the combo is quoted in. + """ + from time import monotonic + + from openbb_deribit.utils.client import request + + root = symbol.upper() + key = f"{root}#combos" + lock = _LOCKS.setdefault(key, asyncio.Lock()) + + async with lock: + held = _LOADED.get(key) + + if use_cache and held and monotonic() - held[0] < CHAIN_TTL: + return list(held[1]) + + currency = settlement_of(root) + listed, quoted = await asyncio.gather( + request("get_combos", {"currency": currency}), + request( + "get_book_summary_by_currency", + {"currency": currency, "kind": "option_combo"}, + ), + ) + quotes = {str(row["instrument_name"]): row for row in quoted or []} + books = [] + + for combo in listed or []: + legs = [ + (str(leg["instrument_name"]), int(leg["amount"])) + for leg in combo["legs"] + ] + + if not all( + name.split("-")[0] == root and len(name.split("-")) == 4 + for name, _ in legs + ): + continue + + quote = quotes.get(str(combo["id"]), {}) + books.append( + { + "name": str(combo["id"]), + "legs": legs, + "bid": quote.get("bid_price"), + "ask": quote.get("ask_price"), + } + ) + + _LOADED[key] = (monotonic(), books) + + return list(books) + + +def underlying_price(frame: "DataFrame") -> float: + """Return the spot price of the underlying the chain was quoted against.""" + spot = frame["underlying_spot_price"].dropna() + + return float(spot.iloc[0]) if not spot.empty else 0.0 + + +def is_inverse(frame: "DataFrame") -> bool: + """Return whether the chain's contracts settle in the underlying.""" + return bool(frame["is_inverse"].any()) + + +def settlement_currency(frame: "DataFrame", symbol: str) -> str: + """Return what a position in this chain settles in.""" + root = symbol.split("_", maxsplit=1)[0].upper() + + return root if is_inverse(frame) else "USDC" + + +def expirations(frame: "DataFrame") -> list: + """Return every expiration on the chain, nearest first.""" + return sorted(frame["expiration"].dropna().unique()) + + +def nearest_expiration(frame: "DataFrame", target: Any) -> Any: + """Return the expiration closest to a date. + + Parameters + ---------- + frame : DataFrame + The chain. + target : Any + The date the position is held to. + + Returns + ------- + Any + The listed expiration nearest the target. + """ + from pandas import to_datetime + + wanted = to_datetime(target).date() + listed = expirations(frame) + + return min(listed, key=lambda day: abs((day - wanted).days)) + + +def far_expiration(frame: "DataFrame", near: Any, gap: int = 30) -> Any: + """Return the expiration a calendar sells the near one against. + + Parameters + ---------- + frame : DataFrame + The chain. + near : Any + The expiration being traded. + gap : int + How many days past it the far expiration is sought. + + Returns + ------- + Any + The later expiration nearest the gap, or None when nothing is later. + """ + later = [day for day in expirations(frame) if day > near] + + if not later: + return None + + return min(later, key=lambda day: abs((day - near).days - gap)) + + +def quotes_at(frame: "DataFrame", expiration: Any = None) -> "DataFrame": + """Return the tradeable contracts, entered at no less than one tick. + + Parameters + ---------- + frame : DataFrame + The chain. + expiration : Any + The expiration to keep. None keeps every expiration. + + Returns + ------- + DataFrame + The contracts with a price, each with the price it is entered and left at. + """ + at = ( + frame.copy() + if expiration is None + else frame[frame["expiration"] == expiration].copy() + ) + + for column in ("bid", "ask", "mark", "tick_size"): + at[column] = at[column].astype(float) + + entry = at["ask"].where(at["ask"] > 0, at["mark"]) + at["entry"] = entry.combine(at["tick_size"].fillna(0), max).where(entry > 0, 0.0) + at["exit"] = at["bid"].where(at["bid"] > 0, at["mark"]) + + return at[at["entry"] > 0].sort_values(["option_type", "strike"]) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_payoff.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_payoff.py new file mode 100644 index 00000000000..881c364db9d --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_payoff.py @@ -0,0 +1,145 @@ +"""The payoff diagram of one Deribit option position.""" + +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from openbb_core.app.model.obbject import OBBject + + +def _mark(figure, price: float, text: str, color: str, height: float) -> None: + """Draw one labelled vertical marker at a price.""" + figure.add_vline(x=price, line_width=1, line_dash="dash", line_color=color) + figure.add_annotation( + x=price, + y=height, + yref="paper", + text=text, + showarrow=False, + font=dict(size=10, color=color), + bordercolor=color, + borderwidth=1, + borderpad=3, + bgcolor="rgba(255, 255, 255, 0.85)", + yanchor="middle", + ) + + +def create_payoff(data: dict, theme: str = "dark", **kwargs) -> "OBBject": + """Draw what one position returns across a range of underlying prices. + + Parameters + ---------- + data : dict + The sized position, the prices it was valued at, the profit at each, + the spot and target prices, and what it settles in. + theme : str + Either 'dark' or 'light'. + + Returns + ------- + OBBject + The figure, with the evaluated curve as its results. + """ + from openbb_core.app.model.obbject import OBBject + from openbb_core.app.utils import df_to_basemodel + from pandas import DataFrame + + from openbb_deribit.utils.options.theme import finalize, new_figure + + prices = data["prices"] + values = data["payoff"] + spot = float(data.get("spot") or 0) + target = float(data.get("target") or spot) + settles = str(data.get("settles") or "") + quote = str(data.get("quote") or "USD") + figure, text_color, background = new_figure(theme) + + figure.add_scatter( + x=prices, + y=[max(value, 0) for value in values], + mode="lines", + name="Profit", + line=dict(width=0), + fill="tozeroy", + fillcolor="rgba(63, 185, 80, 0.22)", + hoverinfo="skip", + ) + figure.add_scatter( + x=prices, + y=[min(value, 0) for value in values], + mode="lines", + name="Loss", + line=dict(width=0), + fill="tozeroy", + fillcolor="rgba(227, 93, 106, 0.22)", + hoverinfo="skip", + ) + figure.add_scatter( + x=prices, + y=values, + mode="lines", + name="P/L", + line=dict(color="#2f6fed", width=2), + hovertemplate="%{y:,.6f}", + ) + + low, high = min(prices), max(prices) + markers = [(spot, f"Spot {spot:,.6g}", "#8b949e")] + + if abs(target - spot) > (high - low) / 50: + markers.append((target, f"Target {target:,.6g}", "#8b949e")) + + markers += [ + (level, f"B/E {level:,.6g}", "#e35d6a") + for level in data.get("breakevens") or [] + ] + markers = sorted(marker for marker in markers if low <= marker[0] <= high) + + for index, (price, text, color) in enumerate(markers): + _mark(figure, price, text, color, 0.95 if index % 2 == 0 else 0.83) + + profit = float(data.get("expected_profit") or 0) + cost = float(data.get("cost") or 0) + coin = f"  |  settled in {settles}" if settles != quote else "" + priced = ( + f"for {cost:,.0f} {quote}" + if cost >= 0 + else f"for a {abs(cost):,.0f} {quote} credit" + ) + listed = f"  |  {data['combo']}" if data.get("combo") else "" + figure.set_title( + f"{data.get('strategy', '')} {priced}" + f"
{data.get('legs', '')}{listed}" + f"  |  expected {profit:+,.0f} {quote}" + f"  |  spot {spot:,.6g}{coin}", + x=0.5, + font=dict(size=15), + ) + figure.update_layout( + paper_bgcolor=background, + plot_bgcolor=background, + font=dict(color=text_color), + margin=dict(l=10, r=10, t=76, b=10), + hovermode="x", + showlegend=False, + xaxis=dict( + title="Underlying price", + showgrid=False, + linecolor=text_color, + hoverformat=",.6g", + autorange=False, + range=[low, high], + ), + yaxis=dict( + title=f"Profit and loss ({quote})", + side="left", + showgrid=True, + zeroline=True, + zerolinecolor=text_color, + linecolor=text_color, + ), + ) + frame = DataFrame({"underlying_price": prices, "profit_or_loss": values}) + output: Any = OBBject(results=df_to_basemodel(frame)) + + return finalize(output, figure, theme) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_smile.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_smile.py new file mode 100644 index 00000000000..f5b5c0c1226 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_smile.py @@ -0,0 +1,209 @@ +"""Deribit implied-volatility smile and skew chart.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, cast + +if TYPE_CHECKING: + from openbb_core.app.model.obbject import OBBject + from openbb_core.provider.standard_models.options_chains import OptionsChainsData + +MAX_EXPIRATIONS = 5 + +COLORS = [ + "royalblue", + "red", + "orange", + "green", + "grey", + "burlywood", + "magenta", + "cyan", + "indigo", + "yellowgreen", +] + + +def _first_priced_expiration(data: OptionsChainsData) -> str: + """Return the front expiration that still carries non-zero implied volatility. + + Parameters + ---------- + data : OptionsChainsData + The loaded Deribit options chain. + + Returns + ------- + str + The earliest expiration with usable implied volatility. + """ + df = data.dataframe + priced = df[df["implied_volatility"] > 0] + + if priced.empty: + from openbb_deribit.utils.options.data_handler import chain_expirations + + expirations = chain_expirations(data) + + return expirations[0] if expirations else data.expirations[0] + + return sorted(priced["expiration"].astype(str).unique())[0] + + +def create_smile( + data: OptionsChainsData, + expirations: str | None = None, + otm: bool = False, + skew: bool = False, + **kwargs, +) -> OBBject: + """Build the IV smile/skew across strikes for up to five expirations. + + Parameters + ---------- + data : OptionsChainsData + The loaded Deribit options chain. + expirations : str | None + Up to five comma-separated expiration dates. Defaults to the front expiry. + otm : bool + When True, restrict each curve to out-of-the-money contracts. + skew : bool + When True, plot the skew relative to ATM instead of raw implied volatility. + + Returns + ------- + OBBject + The plotted rows, with the Plotly figure attached to ``chart``. + + Raises + ------ + OpenBBError + If the chain carries no implied volatility, or more than five expirations + were requested. + """ + from openbb_core.app.model.abstract.error import OpenBBError + from openbb_core.app.model.obbject import OBBject + from openbb_core.app.utils import df_to_basemodel + from pandas import DataFrame, concat, to_datetime + + from openbb_deribit.utils.options.theme import finalize, new_figure + + if data.has_iv is False: + raise OpenBBError( + "Implied Volatility was not found in the data and is required here." + ) + + exp_list = ( + expirations.split(",") + if isinstance(expirations, str) + else expirations + if isinstance(expirations, list) + else [_first_priced_expiration(data)] + ) + exp_list = [data._get_nearest_expiration(e) for e in exp_list] + + if len(exp_list) > MAX_EXPIRATIONS: + raise OpenBBError("Too many dates! Up to five can be selected.") + + df = concat( + [data.skew(date=cast(str, to_datetime(exp).date())) for exp in exp_list] + ) + output_df = DataFrame() + symbol = data.underlying_symbol[0] + index_name = exp_list[0] if len(exp_list) <= 1 else None + target_col = "Skew" if skew is True else "IV" + title = ( + f"{symbol} {'OTM ' if otm is True else ''}" + f"{'IV Skew' if skew is True else 'Implied Volatility'}" + ) + theme = kwargs.get("theme") or "dark" + fig, text_color, background = new_figure(theme) + color = -1 + + for expiration in exp_list: + calls = ( + df.query("`Expiration` == @expiration & `Option Type` == 'call'") + .copy() + .reset_index(drop=True) + ) + puts = ( + df.query("`Expiration` == @expiration & `Option Type` == 'put'") + .copy() + .reset_index(drop=True) + ) + + if otm is True: + put_idx = puts[puts["Skew"] == 0].index.values[0] + call_idx = calls[calls["Skew"] == 0].index.values[0] + puts = puts.iloc[0 : put_idx + 1].reset_index(drop=True) + calls = calls.iloc[call_idx:-1].reset_index(drop=True) + + output_df = ( + concat([output_df, calls, puts], axis=0) + if not output_df.empty + else concat([calls, puts], axis=0) + ) + color = color + 1 + fig.add_scatter( + x=calls["Strike"].unique().tolist(), + y=calls[target_col], + mode="lines+markers", + name="Calls" if len(exp_list) <= 1 else f"Calls at {expiration}", + marker_color=COLORS[color], + hoverinfo="x+y+name", + ) + color = color + 1 + fig.add_scatter( + x=puts["Strike"].unique().tolist(), + y=puts[target_col], + mode="lines+markers", + name="Puts" if len(exp_list) <= 1 else f"Puts at {expiration}", + marker_color=COLORS[color], + hoverinfo="x+y+name", + ) + + fig.update_layout( + title=dict(text=title, x=0.5, font=dict(size=18)), + yaxis=dict( + ticklen=0, + showline=False, + linecolor=text_color, + tickfont=dict(size=14), + nticks=7, + ), + xaxis=dict( + showgrid=False, + autorange=True, + ticklen=5, + showline=False, + linecolor=text_color, + title=dict(text=index_name if index_name else "", font=dict(size=16)), + tickfont=dict(size=16), + nticks=7, + showspikes=False, + tickprefix="$", + ), + legend=dict( + font=dict(size=14), + orientation="v", + yanchor="top", + y=1, + xanchor="center", + x=0.5, + itemdoubleclick="toggleothers", + ), + font=dict(color=text_color), + paper_bgcolor=background, + plot_bgcolor=background, + hoverdistance=1, + hovermode="x unified", + dragmode="pan", + ) + output_df = ( + output_df.set_index(["Expiration", "Strike", "Option Type"]) + .sort_index() + .reset_index() + ) + output: Any = OBBject(results=df_to_basemodel(output_df)) + + return finalize(output, fig, theme) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_stats.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_stats.py new file mode 100644 index 00000000000..a22c7d5b46d --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_stats.py @@ -0,0 +1,143 @@ +"""Deribit open-interest and volume statistics chart.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Literal + +if TYPE_CHECKING: + from openbb_core.app.model.obbject import OBBject + from openbb_core.provider.standard_models.options_chains import OptionsChainsData + + +def create_stats( + data: OptionsChainsData, + by: Literal["strike", "expiration"] = "expiration", + metric: Literal["oi", "volume"] = "oi", + date: str | None = None, + unit: Literal["value", "percent", "pcr"] = "value", + **kwargs, +) -> OBBject: + """Chart open interest or volume by strike or expiration. + + Parameters + ---------- + data : OptionsChainsData + The loaded Deribit options chain. + by : Literal["strike", "expiration"] + The axis to aggregate over. + metric : Literal["oi", "volume"] + Open interest or contract volume. + date : str | None + Restrict to a single expiration, which forces the strike axis. + unit : Literal["value", "percent", "pcr"] + Raw values, share of total, or the put/call ratio. + + Returns + ------- + OBBject + The aggregated rows, with the Plotly figure attached to ``chart``. + """ + from openbb_core.app.model.obbject import OBBject + from openbb_core.app.utils import df_to_basemodel + + from openbb_deribit.utils.options.theme import finalize, new_figure + + stat = "open_interest" if metric == "oi" else metric + + if date is not None: + date = data._get_nearest_expiration(date) + stats = data.filter_data(by=by, date=date, stat=stat) + else: + stats = data.filter_data(by=by, stat=stat) + + symbol = data.underlying_symbol[-1] + stat_type = stat.replace("_", " ").title() + index_name = "Expiration" if by == "expiration" and not date else "Strike" + stats = stats.set_index(index_name) + stats.Puts = stats.Puts * (-1) + title = ( + f"{symbol} {stat_type} (%)" + if unit == "percent" + else f"{symbol} {stat_type} By {index_name}" + ) + theme = kwargs.get("theme") or "dark" + fig, text_color, background = new_figure(theme) + xtick_vals = ( + [ + round(d) if "." in str(d) and str(d).endswith(".0") else round(d, 2) + for d in stats.index.tolist() + ] + if by == "strike" + else stats.index + ) + + if unit == "percent": + stats = stats.dropna(subset=["Net Percent"]) + fig.add_bar( + x=stats.index, + y=stats["Net Percent"], + name="% of Total", + orientation="v", + hovertemplate="%{y:.4f}%", + ) + elif unit == "pcr": + title = f"{symbol} {stat_type} Put/Call Ratio" + fig.add_bar( + x=xtick_vals, + y=stats.PCR, + orientation="v", + name="Put/Call Ratio", + hovertemplate="%{y:.4f}", + ) + else: + fig.add_bar( + x=xtick_vals, + y=stats.Calls, + name="Calls", + orientation="v", + marker=dict(color="royalblue"), + hovertemplate="%{y:.0f}", + ) + fig.add_bar( + x=xtick_vals, + y=stats.Puts, + name="Puts", + orientation="v", + marker=dict(color="red"), + hovertemplate="%{y:.0f}", + ) + + fig.update_traces(width=0.95, selector=dict(type="bar")) + fig.set_title(title, x=0.5, font=dict(size=16)) + fig.update_layout( + paper_bgcolor=background, + plot_bgcolor=background, + barmode="relative", + yaxis=dict( + ticklen=0, + showgrid=True, + tickfont=dict(size=12), + automargin=True, + linecolor=text_color, + showline=True, + ), + xaxis=dict( + showgrid=False, + tickfont=dict(size=11), + ticklen=0, + type="category", + linecolor=text_color, + showline=True, + nticks=10, + tickprefix="$" if by == "strike" else "", + showspikes=False, + ), + legend=dict(orientation="v", yanchor="top", y=0.90, xanchor="right", x=-0.01), + font=dict(color=text_color), + margin=dict(l=10, r=10, t=10, b=10), + hovermode="x unified", + ) + stats.Puts = stats.Puts * (-1) + output: Any = OBBject(results=df_to_basemodel(stats)) + + return finalize(output, fig, theme) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_surface.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_surface.py new file mode 100644 index 00000000000..ba9cf04e507 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_surface.py @@ -0,0 +1,199 @@ +"""Deribit options 3-D surface chart over DTE and strike.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Literal + +if TYPE_CHECKING: + from openbb_core.app.model.obbject import OBBject + from openbb_core.provider.standard_models.options_chains import OptionsChainsData + +SurfaceMetric = Literal[ + "implied_volatility", "delta", "gamma", "theta", "vega", "rho", "dex", "gex" +] + +METRIC_TITLES = { + "implied_volatility": "IV", + "delta": "Delta", + "gamma": "Gamma", + "theta": "Theta", + "vega": "Vega", + "rho": "Rho", + "dex": "DEX", + "gex": "GEX", +} + +OPTION_TYPE_LABELS = {"calls": "Call", "puts": "Put", "otm": "OTM", "itm": "ITM"} + + +def create_surface( + data: OptionsChainsData, + option_type: Literal["otm", "itm", "puts", "calls"] = "otm", + metric: SurfaceMetric = "implied_volatility", + dte_range: list[int] | None = None, + moneyness: float | None = None, + oi: bool = False, + volume: bool = False, + **kwargs, +) -> OBBject: + """Chart an option metric as a 3-D surface over DTE and strike. + + Parameters + ---------- + data : OptionsChainsData + The loaded Deribit options chain. + option_type : Literal["otm", "itm", "puts", "calls"] + Which side of the chain to plot. + metric : SurfaceMetric + The Z-axis metric. + dte_range : list[int] | None + Inclusive lower and upper days-to-expiry bounds. + moneyness : float | None + Restrict strikes to within this percent of the underlying price. + oi : bool + When True, drop contracts with no open interest. + volume : bool + When True, drop contracts that have not traded. + + Returns + ------- + OBBject + The plotted rows, with the Plotly figure attached to ``chart``. + + Raises + ------ + OpenBBError + If the requested metric is not present in the chain. + """ + from openbb_core.app.model.abstract.error import OpenBBError + from openbb_core.app.model.charts.chart import Chart + from openbb_core.app.model.obbject import OBBject + from openbb_core.app.utils import df_to_basemodel + from pandas import concat + + from openbb_deribit import CHARTING_INSTALLED + from openbb_deribit.utils.options.theme import CHART_CONFIG + + options = data.dataframe + column_map = {c.lower(): c for c in options.columns} + target = column_map.get(metric.lower(), metric) + + if target not in options.columns: + raise OpenBBError(f"No {metric} data available in this chain.") + + last_price = float(data.underlying_price[0] or 0.0) + symbol = data.underlying_symbol[0] + calls = options.query(f"`option_type` == 'call' & `dte` >= 0 & `{target}` != 0") + puts = options.query(f"`option_type` == 'put' & `dte` >= 0 & `{target}` != 0") + + if oi: + calls = calls[calls["open_interest"] > 0] + puts = puts[puts["open_interest"] > 0] + + if volume: + calls = calls[calls["volume"] > 0] + puts = puts[puts["volume"] > 0] + + def _between(frame, col, low, high): + """Filter a frame to an inclusive range on one column.""" + return frame[(frame[col] >= low) & (frame[col] <= high)] + + if dte_range is not None and len(dte_range) > 1: + calls = _between(calls, "dte", min(dte_range), max(dte_range)) + puts = _between(puts, "dte", min(dte_range), max(dte_range)) + + if moneyness is not None and moneyness > 0: + low = (1 - (moneyness / 100)) * last_price + high = (1 + (moneyness / 100)) * last_price + calls = _between(calls, "strike", low, high) + puts = _between(puts, "strike", low, high) + + keys = ["expiration", "strike", "option_type"] + + if option_type == "otm": + df = ( + concat( + [ + calls.query("`strike` > @last_price").set_index(keys), + puts.query("`strike` < @last_price").set_index(keys), + ] + ) + .sort_index() + .reset_index() + ) + elif option_type == "itm": + df = ( + concat( + [ + calls.query("`strike` < @last_price").set_index(keys), + puts.query("`strike` > @last_price").set_index(keys), + ] + ) + .sort_index() + .reset_index() + ) + elif option_type == "calls": + df = calls + else: + df = puts + + columns = [ + "expiration", + "strike", + "option_type", + "dte", + target, + "open_interest", + "volume", + ] + df = df[[c for c in dict.fromkeys(columns) if c in df.columns]] + metric_title = METRIC_TITLES.get(metric, metric.replace("_", " ").title()) + label = f"{symbol} {OPTION_TYPE_LABELS[option_type]} {metric_title} Surface" + + if oi: + label += " With Open Interest" + + if volume: + label += " Excluding Untraded Contracts" + + theme = kwargs.get("theme") or "dark" + df.expiration = df.expiration.astype(str) + output: Any = OBBject(results=df_to_basemodel(df)) + + if not CHARTING_INSTALLED: + return output + + from openbb_charting.charts.generic_charts import surface3d + + fig = surface3d( + X=df["dte"], + Y=df["strike"], + Z=df[target], + xtitle="DTE", + ytitle="Strike", + ztitle=metric_title, + title=label, + theme=theme, + ) + text_color = "white" if theme == "dark" else "black" + grid_color = "rgba(255,255,255,0.15)" if theme == "dark" else "rgba(0,0,0,0.15)" + wall_color = "rgba(255,255,255,0.04)" if theme == "dark" else "rgba(0,0,0,0.04)" + scene_axis = dict( + color=text_color, gridcolor=grid_color, backgroundcolor=wall_color + ) + fig.update_layout( + paper_bgcolor="rgba(0,0,0,0)", + plot_bgcolor="rgba(0,0,0,0)", + font=dict(color=text_color), + title=dict(font=dict(color=text_color)), + scene=dict(xaxis=scene_axis, yaxis=scene_axis, zaxis=scene_axis), + hoverlabel=dict( + bgcolor="#0E0E0E" if theme == "dark" else "#FFFFFF", + bordercolor=text_color, + font=dict(color=text_color, size=12), + ), + ) + content = fig.show(config=CHART_CONFIG, external=True).to_plotly_json() + output.chart = Chart(fig=fig, content=content, format="plotly") + + return output diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_term_structure.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_term_structure.py new file mode 100644 index 00000000000..99e0d901fa9 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/create_term_structure.py @@ -0,0 +1,191 @@ +"""Deribit options term-structure chart across expirations.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Literal + +if TYPE_CHECKING: + from openbb_core.app.model.obbject import OBBject + from openbb_core.provider.standard_models.options_chains import OptionsChainsData + + +def create_term_structure( + data: OptionsChainsData, + strike: float | None = None, + moneyness: float | None = None, + metric: Literal["price", "iv"] = "iv", + option_type: Literal["both", "calls", "puts"] = "both", + **kwargs, +) -> OBBject: + """Chart price or implied volatility at the nearest strike across expirations. + + Parameters + ---------- + data : OptionsChainsData + The loaded Deribit options chain. + strike : float | None + Target strike. Defaults to the nearest out-of-the-money strike per expiry. + moneyness : float | None + Select strikes this percent out-of-the-money instead of a fixed strike. + metric : Literal["price", "iv"] + Contract price or implied volatility. + option_type : Literal["both", "calls", "puts"] + Which side of the chain to plot. + + Returns + ------- + OBBject + The plotted rows, with the Plotly figure attached to ``chart``. + + Raises + ------ + OpenBBError + If implied volatility was requested but is absent from the chain. + """ + from openbb_core.app.model.abstract.error import OpenBBError + from openbb_core.app.model.obbject import OBBject + from openbb_core.app.utils import df_to_basemodel + from pandas import DataFrame, concat + + from openbb_deribit.utils.options.theme import finalize, new_figure + + if metric == "iv" and not data.has_iv: + raise OpenBBError("No implied volatility data available.") + + from openbb_deribit.utils.options.data_handler import chain_expirations + + df = data.dataframe.copy() + expirations = chain_expirations(data) + symbol = data.underlying_symbol[0] + price_col = ( + "last_trade_price" + if "last_trade_price" in df.columns + else data._identify_price_col(df, "call", "ask") + ) + target_col_map = {"price": price_col, "iv": "implied_volatility"} + df.expiration = df.expiration.astype(str) + base = f"{symbol} {'Implied Volatility' if metric == 'iv' else 'Price'}" + + if strike: + title = f"{base} @ Strike Nearest To ${strike}" + elif moneyness: + title = f"{base} @ {moneyness}% Moneyness" + else: + title = f"{base} Nearest OTM Strikes" + + calls = DataFrame() + puts = DataFrame() + + for expiration in expirations: + nearest_otm = ( + data._get_nearest_otm_strikes(expiration, moneyness=moneyness) + if moneyness + else {} + ) + call_strike = ( + nearest_otm.get("call") + if nearest_otm + else data._get_nearest_strike( + option_type="call", days=expiration, strike=strike + ) + ) + put_strike = ( + nearest_otm.get("put") + if nearest_otm + else data._get_nearest_strike( + option_type="put", days=expiration, strike=strike + ) + ) + calls_filtered = df[ + (df.expiration == expiration) + & (df.option_type == "call") + & (df[price_col] > 0) + ] + + if not calls_filtered.empty: + calls = concat( + [ + calls, + calls_filtered.iloc[ + (calls_filtered.strike - call_strike).abs().argsort()[:1] + ], + ] + ) + + puts_filtered = df[ + (df.expiration == expiration) + & (df.option_type == "put") + & (df[price_col] > 0) + ] + + if not puts_filtered.empty: + puts = concat( + [ + puts, + puts_filtered.iloc[ + (puts_filtered.strike - put_strike).abs().argsort()[:1] + ], + ] + ) + + output_df = concat([calls, puts], axis=0).reset_index(drop=True) + theme = kwargs.get("theme") or "dark" + fig, text_color, background = new_figure(theme) + hovertemplate = ( + "$%{y} @ $%{customdata} Strike" + if metric == "price" + else "%{y} @ $%{customdata} Strike" + ) + + if option_type in ["calls", "both"] and not calls.empty: + fig.add_scatter( + x=calls["expiration"], + y=calls[target_col_map[metric]], + mode="lines+markers", + name="Calls", + marker_color="royalblue", + hovertemplate=hovertemplate, + customdata=calls["strike"], + ) + + if option_type in ["puts", "both"] and not puts.empty: + fig.add_scatter( + x=puts["expiration"], + y=puts[target_col_map[metric]], + mode="lines+markers", + name="Puts", + marker_color="red", + hovertemplate=hovertemplate, + customdata=puts["strike"], + ) + + fig.set_title(title, x=0.5, font=dict(size=16)) + fig.update_layout( + paper_bgcolor=background, + plot_bgcolor=background, + yaxis=dict( + ticklen=0, + showgrid=True, + tickfont=dict(size=12), + automargin=True, + linecolor=text_color, + showline=True, + ), + xaxis=dict( + showgrid=False, + tickfont=dict(size=11), + ticklen=0, + type="category", + linecolor=text_color, + showline=True, + nticks=10, + showspikes=False, + ), + legend=dict(orientation="v", yanchor="top", y=0.90, xanchor="right", x=-0.01), + hovermode="x unified", + font=dict(color=text_color), + margin=dict(l=10, r=10, t=10, b=10), + ) + output: Any = OBBject(results=df_to_basemodel(output_df)) + + return finalize(output, fig, theme) diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/data_handler.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/data_handler.py new file mode 100644 index 00000000000..13c4c120232 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/data_handler.py @@ -0,0 +1,144 @@ +"""Shared Deribit options chain, as the core model the options charts draw from.""" + +from typing import Any + +CHAIN_COLUMNS = { + "contract_symbol": "contract_symbol", + "expiration": "expiration", + "dte": "dte", + "strike": "strike", + "option_type": "option_type", + "contract_size": "contract_size", + "open_interest": "open_interest", + "volume": "volume", + "last": "last_trade_price", + "bid": "bid", + "ask": "ask", + "mark": "mark", + "implied_volatility": "implied_volatility", + "delta": "delta", + "gamma": "gamma", + "theta": "theta", + "vega": "vega", + "rho": "rho", + "underlying_price": "underlying_price", +} + + +def chain_expirations(data: Any) -> list[str]: + """Return the expirations that still have contracts in the chain. + + Parameters + ---------- + data : Any + The loaded ``OptionsChainsData``. + + Returns + ------- + list[str] + Sorted expirations that have rows, falling back to the model's own list + if the frame cannot be built. + """ + try: + frame = data.dataframe + except Exception: # noqa: BLE001 + return list(data.expirations) + + if "expiration" not in frame.columns: + return list(data.expirations) + + return sorted({str(value) for value in frame["expiration"]}) + + +def to_chain(frame: Any, symbol: str) -> Any: + """Return a loaded chain frame as the core ``OptionsChainsData``. + + Parameters + ---------- + frame : DataFrame + The chain, as ``load_chain`` returns it. + symbol : str + The underlying root, as it appears in the instrument name. + + Returns + ------- + OptionsChainsData + One entry per contract, with implied volatility as a decimal. + """ + from numpy import nan + from openbb_core.provider.standard_models.options_chains import OptionsChainsData + + columns = frame.reindex(columns=list(CHAIN_COLUMNS)).rename(columns=CHAIN_COLUMNS) + spot = frame["underlying_spot_price"].astype(float) + forward = columns["underlying_price"].astype(float) + columns["underlying_price"] = forward.where(forward > 0, spot) + columns["implied_volatility"] = columns["implied_volatility"].astype(float) / 100 + columns["underlying_symbol"] = symbol.upper() + columns = columns.sort_values(["expiration", "strike", "option_type"]) + columns = columns.astype(object).replace({nan: None}).reset_index(drop=True) + + return OptionsChainsData.model_validate(columns.to_dict(orient="list")) + + +async def load_symbol(symbol: str) -> Any: + """Return the chain of one underlying as the core ``OptionsChainsData``. + + Parameters + ---------- + symbol : str + The underlying root, as it appears in the instrument name. + + Returns + ------- + OptionsChainsData + The chain the options charts are drawn from. + + Raises + ------ + OpenBBError + If the exchange lists no options on the underlying. + """ + from openbb_core.app.model.abstract.error import OpenBBError + from openbb_core.provider.utils.errors import EmptyDataError + + from openbb_deribit.utils.options import chain + + root = symbol.upper() + + try: + frame = await chain.load_chain(root) + except (OpenBBError, EmptyDataError) as exc: + raise OpenBBError(f"No options available for {root}.") from exc + + return to_chain(frame, root) + + +def get_strikes(data: Any) -> list[dict]: + """Return the strike choices of a chain for its dropdown. + + Parameters + ---------- + data : OptionsChainsData + The loaded chain. + + Returns + ------- + list[dict] + ``[{label, value}]`` strike choices, led by the nearest out of the money. + """ + underlying = data.underlying_price[0] if data.underlying_price else None + choices: list[dict] = [{"label": "Nearest OTM", "value": None}] + + for strike in data.strikes: + extra = ( + {"rightOfDescription": f"Underlying: ${underlying}"} if underlying else {} + ) + choices.append( + { + "label": f"${int(strike) if strike.is_integer() else strike}", + "value": strike, + "extraInfo": extra, + } + ) + + return choices diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/greeks.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/greeks.py new file mode 100644 index 00000000000..6fc2335200a --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/greeks.py @@ -0,0 +1,79 @@ +"""Greeks for a Deribit option, off its forward price and quoted volatility. + +The book summary the analysis suite reads publishes a volatility but no greeks, +so they are modelled here. Measured against the greeks Deribit publishes over +all 972 BTC contracts, delta agrees to a median of 0.0001, gamma to 0.000002 +and vega to 0.03. + +Theta follows the exchange's own two rules rather than the bare derivative: it +is scaled down inside the last day, where the derivative diverges but the +contract cannot decay faster than it expires, and it is floored at the +contract's own value, because nothing decays by more than it is worth. Both +rules bind on real contracts and together bring the worst disagreement across +the chain from 193 to 0.11 per day. +""" + +from math import exp, log, pi, sqrt + +SECONDS_PER_YEAR = 365 * 86400 + + +def _pdf(x: float) -> float: + """Return the standard normal density at a point.""" + return exp(-0.5 * x * x) / sqrt(2 * pi) + + +def greeks( + forward: float, strike: float, years: float, sigma: float, option_type: str +) -> dict: + """Return the greeks of one unit of the underlying. + + Parameters + ---------- + forward : float + The forward price of the underlying at expiration. + strike : float + The strike of the contract. + years : float + The time to expiration, in years. + sigma : float + The implied volatility, as a decimal. + option_type : str + Either 'call' or 'put'. + + Returns + ------- + dict + Delta, gamma, theta, vega, and rho. Every value is None when the + contract has expired or carries no volatility to model it with. + """ + from statistics import NormalDist + + if years <= 0 or sigma <= 0 or forward <= 0 or strike <= 0: + return {name: None for name in ("delta", "gamma", "theta", "vega", "rho")} + + normal = NormalDist() + deviation = sigma * sqrt(years) + d1 = (log(forward / strike) + 0.5 * sigma**2 * years) / deviation + d2 = d1 - deviation + density = _pdf(d1) + call = option_type.startswith("c") + + value = ( + forward * normal.cdf(d1) - strike * normal.cdf(d2) + if call + else strike * normal.cdf(-d2) - forward * normal.cdf(-d1) + ) + decay = -forward * density * sigma / (2 * sqrt(years)) / 365 + + return { + "delta": normal.cdf(d1) if call else -normal.cdf(-d1), + "gamma": density / (forward * deviation), + "theta": max(decay * min(1.0, years * 365), -value), + "vega": forward * density * sqrt(years) / 100, + "rho": ( + strike * years * normal.cdf(d2) / 100 + if call + else -strike * years * normal.cdf(-d2) / 100 + ), + } diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/legs.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/legs.py new file mode 100644 index 00000000000..46b12ba6b30 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/legs.py @@ -0,0 +1,248 @@ +"""Valuing Deribit option positions. + +The chain quotes every contract in its quote currency per unit of the +underlying, so a position is valued the same way whichever settlement style a +contract has. What differs is where the profit lands: an inverse contract pays +in the coin, so its result is also reported there, which is the number the +account actually moves by. +""" + +MIN_VOL = 0.0001 +MAX_VOL = 5.0 +VOL_STEPS = 100 + + +def intrinsic(price: float, strike: float, option_type: str) -> float: + """Return the exercise value of one unit of the underlying.""" + return ( + max(0.0, price - strike) + if option_type.startswith("c") + else max(0.0, strike - price) + ) + + +def option_value( + forward: float, + strike: float, + years: float, + sigma: float, + discount: float, + option_type: str, +) -> float: + """Value one unit of the underlying off its forward price. + + Parameters + ---------- + forward : float + The forward price of the underlying at expiration. + strike : float + The strike of the contract. + years : float + The time to expiration, in years. + sigma : float + The implied volatility, as a decimal. + discount : float + The discount factor to expiration. + option_type : str + Either 'call' or 'put'. + + Returns + ------- + float + The value of one unit, in the quote currency. + """ + from math import log, sqrt + from statistics import NormalDist + + if years <= 0 or sigma <= 0 or forward <= 0 or strike <= 0: + return discount * intrinsic(forward, strike, option_type) + + normal = NormalDist() + deviation = sigma * sqrt(years) + d1 = (log(forward / strike) + 0.5 * sigma**2 * years) / deviation + d2 = d1 - deviation + + if option_type.startswith("c"): + return discount * (forward * normal.cdf(d1) - strike * normal.cdf(d2)) + + return discount * (strike * normal.cdf(-d2) - forward * normal.cdf(-d1)) + + +def implied_vol( + price: float, + forward: float, + strike: float, + years: float, + discount: float, + option_type: str, +) -> "float | None": + """Return the volatility one quoted price implies. + + A chain publishes a volatility alongside a price, and the two do not always + agree. Valuing a position at a volatility its own quote does not imply + prices the exit off a different market than the entry, which shows a + position as losing before it has moved. + + Parameters + ---------- + price : float + The quoted price of one unit. + forward : float + The forward price of the underlying at expiration. + strike : float + The strike of the contract. + years : float + The time to expiration, in years. + discount : float + The discount factor to expiration. + option_type : str + Either 'call' or 'put'. + + Returns + ------- + float or None + The volatility the price implies, or None when no volatility does. + """ + if price <= 0 or years <= 0 or forward <= 0 or strike <= 0: + return None + + if price <= discount * intrinsic(forward, strike, option_type): + return None + + low, high = MIN_VOL, MAX_VOL + + if option_value(forward, strike, years, high, discount, option_type) < price: + return None + + for _ in range(VOL_STEPS): + middle = (low + high) / 2 + + if option_value(forward, strike, years, middle, discount, option_type) < price: + low = middle + else: + high = middle + + return (low + high) / 2 + + +def leg_value(leg: dict, price: float, elapsed: int) -> float: + """Return what one unit of a leg is worth at a price. + + Parameters + ---------- + leg : dict + The contract, its volatility, and its days to expiration. + price : float + The price of the underlying at the valuation date. + elapsed : int + Days from now to the valuation date. + + Returns + ------- + float + The exercise value of a leg that has expired by the valuation date, + otherwise its modelled value, in the quote currency per unit. + """ + remaining = int(leg.get("dte") or 0) - elapsed + + if remaining <= 0 or not leg.get("implied_volatility"): + return intrinsic(price, float(leg["strike"]), str(leg["option_type"])) + + return option_value( + price, + float(leg["strike"]), + remaining / 365, + float(leg["implied_volatility"]), + 1.0, + str(leg["option_type"]), + ) + + +def net_cost(legs: "list[dict]") -> float: + """Return what a position costs to open, in the quote currency. + + A negative result is a credit. + """ + return sum( + float(leg["quantity"]) * float(leg["size"]) * float(leg["price"]) + for leg in legs + ) + + +def position_value(legs: "list[dict]", price: float, elapsed: int) -> float: + """Return what a position is worth at a price, in the quote currency.""" + return sum( + float(leg["quantity"]) * float(leg["size"]) * leg_value(leg, price, elapsed) + for leg in legs + ) + + +def payoff( + legs: "list[dict]", + prices: "list[float]", + elapsed: int = 0, + inverse: bool = False, + entry_price: "float | None" = None, + cost: "float | None" = None, +) -> "list[float]": + """Return the profit and loss of a position across a price range, in the quote currency. + + Parameters + ---------- + legs : list[dict] + Each leg's strike, type, quantity, contract size, price, days to + expiration, and implied volatility. + prices : list[float] + The underlying prices to evaluate. + elapsed : int + Days from now to the valuation date. + inverse : bool + Whether the contracts settle in the underlying. + entry_price : float or None + The price of the underlying when the position was opened, which an + inverse premium was paid at. Required when inverse. + cost : float or None + What the position was opened for, in the quote currency, when it was + traded as one combo rather than leg by leg. None prices it off its legs. + + Returns + ------- + list[float] + The profit and loss at each price, in the quote currency. + + Raises + ------ + ValueError + If an inverse position is valued without the price it was opened at. + """ + entry = net_cost(legs) if cost is None else cost + + if not inverse: + return [position_value(legs, price, elapsed) - entry for price in prices] + + if not entry_price: + raise ValueError( + "An inverse position needs the price it was opened at, because its" + " premium was paid in the coin at that price." + ) + + paid = entry / entry_price + + return [position_value(legs, price, elapsed) - paid * price for price in prices] + + +def breakevens(prices: "list[float]", values: "list[float]") -> "list[float]": + """Return every price where a payoff crosses zero.""" + crossings: list[float] = [] + + for index in range(1, len(values)): + if (values[index - 1] < 0) == (values[index] < 0): + continue + + low, high = values[index - 1], values[index] + weight = abs(low) / (abs(low) + abs(high)) if (low or high) else 0 + crossings.append( + prices[index - 1] + weight * (prices[index] - prices[index - 1]) + ) + + return crossings diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/optimizer.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/optimizer.py new file mode 100644 index 00000000000..df3d621f6a7 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/optimizer.py @@ -0,0 +1,764 @@ +"""Ranking every Deribit combo structure against a view.""" + +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from pandas import DataFrame + +PROBE_STEPS = 400 +PROBE_SPREAD = 0.6 +SCOUT_STEPS = 300 +WINDOW_PAD = 0.06 +MIN_PAD = 0.0025 +FLAT_SLOPE = 0.02 +ANCHOR_REACH = 0.35 +SHORTLIST = 40 +WING_REACH = 4 +STRIKE_WINDOW = 0.35 +STRESS_MOVE = 3.0 +STRESS_STEPS = 60 +DIAGONAL_REACH = 3 + +NAMES = { + ("C", 1): "Long Call", + ("C", -1): "Short Call", + ("P", 1): "Long Put", + ("P", -1): "Short Put", + ("CS", 1): "Bull Call Spread", + ("CS", -1): "Bear Call Spread", + ("PS", 1): "Bear Put Spread", + ("PS", -1): "Bull Put Spread", + ("STRD", 1): "Long Straddle", + ("STRD", -1): "Short Straddle", + ("STRG", 1): "Long Strangle", + ("STRG", -1): "Short Strangle", + ("RR", 1): "Bearish Risk Reversal", + ("RR", -1): "Bullish Risk Reversal", + ("REV", 1): "Synthetic Long", + ("REV", -1): "Synthetic Short", + ("CSR12", 1): "Call Ratio Spread", + ("CSR12", -1): "Call Backspread", + ("PSR12", 1): "Put Ratio Spread", + ("PSR12", -1): "Put Backspread", + ("CBUT", 1): "Long Call Butterfly", + ("CBUT", -1): "Short Call Butterfly", + ("PBUT", 1): "Long Put Butterfly", + ("PBUT", -1): "Short Put Butterfly", + ("IBUT", 1): "Iron Butterfly", + ("IBUT", -1): "Reverse Iron Butterfly", + ("CCOND", 1): "Long Call Condor", + ("CCOND", -1): "Short Call Condor", + ("PCOND", 1): "Long Put Condor", + ("PCOND", -1): "Short Put Condor", + ("ICOND", 1): "Iron Condor", + ("ICOND", -1): "Reverse Iron Condor", + ("CCAL", 1): "Long Call Calendar", + ("CCAL", -1): "Short Call Calendar", + ("PCAL", 1): "Long Put Calendar", + ("PCAL", -1): "Short Put Calendar", + ("CDIAG", 1): "Long Call Diagonal", + ("CDIAG", -1): "Short Call Diagonal", + ("PDIAG", 1): "Long Put Diagonal", + ("PDIAG", -1): "Short Put Diagonal", +} + + +def _leg(row: Any, quantity: int) -> dict: + """Describe one contract as a leg of a position, volatility as a decimal.""" + quoted = row["implied_volatility"] + + return { + "symbol": str(row["contract_symbol"]), + "expiration": str(row["expiration"]), + "strike": float(row["strike"]), + "option_type": str(row["option_type"]), + "quantity": quantity, + "size": float(row["contract_size"] or 1), + "price": float(row["entry"] if quantity > 0 else row["exit"]), + "dte": int(row["dte"] or 0), + "implied_volatility": None if quoted is None else float(quoted) / 100, + } + + +def _describe(legs: "list[dict]") -> str: + """Name the contracts a position trades, and how many of each.""" + parts: list[str] = [] + + for leg in legs: + side = "Buy" if leg["quantity"] > 0 else "Sell" + count = abs(int(leg["quantity"])) + parts.append( + f"{side} {leg['symbol']}" + if count == 1 + else f"{side} {count} {leg['symbol']}" + ) + + return " / ".join(parts) + + +def _key(legs: "list[dict]") -> frozenset: + """Return what identifies a position: each contract and how much of it.""" + return frozenset((leg["symbol"], int(leg["quantity"])) for leg in legs) + + +def _sign(value: float) -> int: + """Return the direction of a quantity.""" + return 1 if value > 0 else -1 + + +def _two_legs(first: dict, second: dict) -> tuple: + """Classify a two-legged position on one expiration.""" + kinds = {first["option_type"][0], second["option_type"][0]} + + if len(kinds) == 2: + call, put = ( + (first, second) if first["option_type"][0] == "c" else (second, first) + ) + + if abs(call["quantity"]) != 1 or abs(put["quantity"]) != 1: + return "", 1 + + together = call["quantity"] == put["quantity"] + + if put["strike"] == call["strike"]: + return ( + ("STRD", _sign(call["quantity"])) + if together + else ( + "REV", + _sign(call["quantity"]), + ) + ) + + if put["strike"] < call["strike"]: + return ( + ("STRG", _sign(call["quantity"])) + if together + else ( + "RR", + _sign(put["quantity"]), + ) + ) + + return "", 1 + + low, high = sorted((first, second), key=lambda leg: leg["strike"]) + kind = "C" if low["option_type"][0] == "c" else "P" + units = (int(low["quantity"]), int(high["quantity"])) + + if low["strike"] == high["strike"]: + return "", 1 + + if units in ((1, -1), (-1, 1)): + return f"{kind}S", _sign(units[0] if kind == "C" else units[1]) + + ratios = {"C": ((1, -2), (-1, 2)), "P": ((-2, 1), (2, -1))} + + if units in ratios[kind]: + return f"{kind}SR12", 1 if units == ratios[kind][0] else -1 + + return "", 1 + + +def classify(legs: "list[dict]") -> tuple: + """Return the Deribit combo code of a position and the side it is taken on. + + Parameters + ---------- + legs : list[dict] + The unit legs of the position. + + Returns + ------- + tuple + The code, such as 'CS' or 'ICOND', and 1 when the combo is bought or -1 + when it is sold. An unrecognised position has an empty code. + """ + terms = sorted({int(leg["dte"]) for leg in legs}) + ordered = sorted(legs, key=lambda leg: (leg["strike"], leg["option_type"])) + kinds = {leg["option_type"][0] for leg in legs} + + if len(legs) == 1: + leg = legs[0] + + return ("C" if leg["option_type"][0] == "c" else "P"), _sign(leg["quantity"]) + + if len(terms) == 2 and len(legs) == 2 and len(kinds) == 1: + near, far = sorted(legs, key=lambda leg: leg["dte"]) + kind = "C" if near["option_type"][0] == "c" else "P" + + if near["quantity"] != -far["quantity"] or abs(far["quantity"]) != 1: + return "", 1 + + shape = "CAL" if near["strike"] == far["strike"] else "DIAG" + + return f"{kind}{shape}", _sign(far["quantity"]) + + if len(terms) != 1: + return "", 1 + + if len(legs) == 2: + return _two_legs(*legs) + + units = tuple(int(leg["quantity"]) for leg in ordered) + strikes = [leg["strike"] for leg in ordered] + + if len(legs) == 3 and len(kinds) == 1 and len(set(strikes)) == 3: + kind = "C" if ordered[0]["option_type"][0] == "c" else "P" + even = strikes[1] - strikes[0] == strikes[2] - strikes[1] + + if even and units in ((1, -2, 1), (-1, 2, -1)): + return f"{kind}BUT", units[0] + + if len(legs) == 4 and len(kinds) == 1 and len(set(strikes)) == 4: + kind = "C" if ordered[0]["option_type"][0] == "c" else "P" + + if units in ((1, -1, -1, 1), (-1, 1, 1, -1)): + return f"{kind}COND", units[0] + + if len(legs) == 4 and len(kinds) == 2: + puts = sorted( + (leg for leg in legs if leg["option_type"][0] == "p"), + key=lambda leg: leg["strike"], + ) + calls = sorted( + (leg for leg in legs if leg["option_type"][0] == "c"), + key=lambda leg: leg["strike"], + ) + shape = ( + [int(leg["quantity"]) for leg in puts], + [int(leg["quantity"]) for leg in calls], + ) + + if len(puts) == 2 and puts[1]["strike"] <= calls[0]["strike"]: + body = "IBUT" if puts[1]["strike"] == calls[0]["strike"] else "ICOND" + + if shape == ([1, -1], [-1, 1]): + return body, 1 + + if shape == ([-1, 1], [1, -1]): + return body, -1 + + return "", 1 + + +def _position(pairs: "list[tuple]", book: dict) -> dict: + """Build one position from contracts and quantities, priced off a combo book.""" + legs = [_leg(row, quantity) for row, quantity in pairs] + code, side = classify(legs) + listed = book.get(_key(legs)) + + return { + "strategy": NAMES.get((code, side), "Custom"), + "code": code or None, + "combo": listed[0] if listed else None, + "premium": listed[1] * legs[0]["size"] if listed else None, + "legs": legs, + } + + +def _both(pairs: "list[tuple]", book: dict) -> "list[dict]": + """Return a position bought and the same position sold.""" + flipped = [(row, -quantity) for row, quantity in pairs] + + return [_position(pairs, book), _position(flipped, book)] + + +def combo_book(books: "list | tuple", spot: float, inverse: bool) -> dict: + """Return each listed combo's price, keyed by the legs that make it up. + + Parameters + ---------- + books : list or tuple + The listed combos, as ``load_combos`` returns them. + spot : float + The current price of the underlying, which a coin quote is carried at. + inverse : bool + Whether the combos are quoted in the coin. + + Returns + ------- + dict + The combo name and what one unit costs in the quote currency, for the + combo bought at its offer and for it sold at its bid. + """ + carry = spot if inverse else 1.0 + lookup: dict = {} + + for book in books: + bought = frozenset(book["legs"]) + sold = frozenset((name, -amount) for name, amount in book["legs"]) + + if book["ask"] is not None: + lookup[bought] = (book["name"], float(book["ask"]) * carry) + + if book["bid"] is not None: + lookup[sold] = (book["name"], -float(book["bid"]) * carry) + + return lookup + + +def _spaced(rows: list) -> "list[tuple]": + """Return every pair of strikes, lower first.""" + return [(low, high) for index, low in enumerate(rows) for high in rows[index + 1 :]] + + +def _single_expiry(calls: list, puts: list, book: dict) -> "list[dict]": + """Build every structure that trades one expiration.""" + built: list[dict] = [] + call_at = {float(row["strike"]): row for row in calls} + put_at = {float(row["strike"]): row for row in puts} + + for row in [*calls, *puts]: + built += _both([(row, 1)], book) + + for low, high in _spaced(calls): + built += _both([(low, 1), (high, -1)], book) + built += _both([(low, 1), (high, -2)], book) + + for low, high in _spaced(puts): + built += _both([(high, 1), (low, -1)], book) + built += _both([(high, 1), (low, -2)], book) + + for strike, call in call_at.items(): + if strike in put_at: + built += _both([(call, 1), (put_at[strike], 1)], book) + built += _both([(call, 1), (put_at[strike], -1)], book) + + for put in puts: + for call in calls: + if float(put["strike"]) < float(call["strike"]): + built += _both([(put, 1), (call, 1)], book) + built += _both([(put, 1), (call, -1)], book) + + for rows, at in ((calls, call_at), (puts, put_at)): + for low, middle in _spaced(rows): + width = float(middle["strike"]) - float(low["strike"]) + high = at.get(float(middle["strike"]) + width) + + if high is not None: + built += _both([(low, 1), (middle, -2), (high, 1)], book) + + for index, first in enumerate(rows): + for second in rows[index + 1 : index + 1 + WING_REACH]: + wing = float(second["strike"]) - float(first["strike"]) + + for third in rows: + fourth = at.get(float(third["strike"]) + wing) + + if float(third["strike"]) > float(second["strike"]) and fourth: + built += _both( + [(first, 1), (second, -1), (third, -1), (fourth, 1)], + book, + ) + + for index, low in enumerate(puts): + for high in puts[index + 1 : index + 1 + WING_REACH]: + wing = float(high["strike"]) - float(low["strike"]) + + for call in calls: + upper = call_at.get(float(call["strike"]) + wing) + + if float(call["strike"]) >= float(high["strike"]) and upper: + built += _both([(low, 1), (high, -1), (call, -1), (upper, 1)], book) + + return built + + +def _two_expiries(near: list, far: list, book: dict) -> "list[dict]": + """Build every calendar and diagonal between two expirations.""" + built: list[dict] = [] + + for kind in ("call", "put"): + later = [row for row in far if row["option_type"] == kind] + strikes = [float(row["strike"]) for row in later] + + for row in (row for row in near if row["option_type"] == kind): + position = min( + range(len(strikes)), + key=lambda index: abs(strikes[index] - float(row["strike"])), + default=None, + ) + + if position is None: + continue + + reach = later[ + max(position - DIAGONAL_REACH, 0) : position + DIAGONAL_REACH + 1 + ] + + for other in reach: + built += _both([(row, -1), (other, 1)], book) + + return built + + +def _window_rows(quotes: "DataFrame", spot: float) -> list: + """Return the tradeable contracts near the money, as records.""" + near = quotes[ + (quotes["strike"] >= spot * (1 - STRIKE_WINDOW)) + & (quotes["strike"] <= spot * (1 + STRIKE_WINDOW)) + ] + + return near.sort_values("strike").to_dict("records") + + +def candidates( + frame: "DataFrame", + expiration: Any, + spot: float, + books: "list | tuple" = (), + inverse: bool = False, +) -> "list[dict]": + """Build every combo structure an expiration can form, both bought and sold. + + Parameters + ---------- + frame : DataFrame + The chain. + expiration : Any + The expiration the position is held to. + spot : float + The current price of the underlying. + books : list or tuple + The listed combos, as ``load_combos`` returns them. + inverse : bool + Whether the contracts settle in the underlying. + + Returns + ------- + list[dict] + Each position, named and coded, with its legs and any listed combo that + trades it as one instrument. + """ + from openbb_deribit.utils.options.chain import far_expiration, quotes_at + + book = combo_book(books, spot, inverse) + near = _window_rows(quotes_at(frame, expiration), spot) + calls = [row for row in near if row["option_type"] == "call"] + puts = [row for row in near if row["option_type"] == "put"] + built = _single_expiry(calls, puts, book) + later = far_expiration(frame, expiration) + + if later is not None: + built += _two_expiries(near, _window_rows(quotes_at(frame, later), spot), book) + + tradeable = { + str(row["contract_symbol"]): row for row in quotes_at(frame).to_dict("records") + } + + for listed in books: + rows = [ + tradeable[name] for name, _amount in listed["legs"] if name in tradeable + ] + + if len(rows) != len(listed["legs"]) or ( + min(row["expiration"] for row in rows) != expiration + ): + continue + + built += _both( + [ + (row, amount) + for row, (_name, amount) in zip(rows, listed["legs"], strict=True) + ], + book, + ) + + unique: dict = {} + + for item in built: + unique.setdefault(_key(item["legs"]), item) + + return list(unique.values()) + + +def rebuild( + described: str, + quotes: "DataFrame", + books: "list | tuple" = (), + spot: float = 0.0, + inverse: bool = False, +) -> dict: + """Return the position a description names, priced at the current quotes. + + Parameters + ---------- + described : str + The legs, as ``_describe`` writes them: 'Buy SYMBOL / Sell 2 SYMBOL'. + quotes : DataFrame + The tradeable contracts of every expiration. + books : list or tuple + The listed combos, as ``load_combos`` returns them. + spot : float + The current price of the underlying. + inverse : bool + Whether the contracts settle in the underlying. + + Returns + ------- + dict + The position, named and coded, with its legs. + + Raises + ------ + ValueError + If the description names no contract, or one not listed. + """ + pairs: list[tuple] = [] + + for part in described.split("/"): + words = part.split() + count = words[1] if len(words) == 3 else "1" + + if ( + len(words) not in (2, 3) + or words[0].lower() not in ("buy", "sell") + or not count.isdigit() + or int(count) < 1 + ): + raise ValueError( + f"{part.strip()!r} does not name a leg. A position reads" + " 'Buy SYMBOL / Sell SYMBOL', with a count before the symbol" + " when there is more than one, which is what the optimizer" + " prints in its Legs column." + ) + + symbol = words[-1] + listed = quotes[quotes["contract_symbol"] == symbol] + + if listed.empty: + raise ValueError( + f"{symbol} is not a listed contract, so the position cannot be" + " priced. Pick a strategy from the optimizer." + ) + + quantity = int(count) if words[0].lower() == "buy" else -int(count) + pairs.append((listed.to_dict("records")[0], quantity)) + + return _position(pairs, combo_book(books, spot, inverse)) + + +def _stress(legs: "list[dict]", spot: float, elapsed: int) -> "list[float]": + """Return the prices a position's worst loss is sought over.""" + low, high = spot / STRESS_MOVE, spot * STRESS_MOVE + points = {low, spot, high, *(leg["strike"] for leg in legs)} + + if any(int(leg["dte"] or 0) > elapsed for leg in legs): + points.update(_grid(low, high, STRESS_STEPS)) + + return sorted(point for point in points if low <= point <= high) + + +def score(strategy: dict, spot: float, target: float, inverse: bool) -> "dict | None": + """Size one position to one unit of capital at risk and value it at the target. + + Parameters + ---------- + strategy : dict + The position, with its legs and any combo premium. + spot : float + The current price of the underlying. + target : float + The price the view expects. + inverse : bool + Whether the contracts settle in the underlying. + + Returns + ------- + dict or None + The position sized so its worst loss across a threefold move either way + is one unit, or None when it has nothing at risk. + """ + from openbb_deribit.utils.options.legs import net_cost, payoff + + legs = strategy["legs"] + premium = strategy.get("premium") + unit_cost = net_cost(legs) if premium is None else premium + elapsed = max(min(int(leg["dte"] or 0) for leg in legs), 0) + points = _stress(legs, spot, elapsed) + losses = payoff(legs, points, elapsed, inverse, spot, unit_cost) + + if inverse: + losses = [ + loss / price * spot for loss, price in zip(losses, points, strict=True) + ] + + risk = -min(losses) + + if risk <= 0: + return None + + lots = 1.0 / risk + sized = [{**leg, "quantity": leg["quantity"] * lots} for leg in legs] + expected = payoff(sized, [target], elapsed, inverse, spot, unit_cost * lots)[0] + + return { + **strategy, + "sized": sized, + "lots": lots, + "elapsed": elapsed, + "cost": unit_cost * lots, + "expected": expected, + } + + +def _grid(low: float, high: float, steps: int) -> "list[float]": + """Return an evenly spaced range of prices.""" + return [low + (high - low) * step / steps for step in range(steps + 1)] + + +def _moving(prices: "list[float]", curve: "list[float]") -> tuple: + """Return the price range over which a payoff is still changing.""" + slopes = [ + abs((curve[step] - curve[step - 1]) / (prices[step] - prices[step - 1])) + for step in range(1, len(prices)) + ] + steepest = max(slopes) + moving = [ + step for step, slope in enumerate(slopes) if slope > steepest * FLAT_SLOPE + ] + + if not moving: + return prices[0], prices[-1] + + return prices[moving[0]], prices[moving[-1] + 1] + + +def _window( + legs: "list[dict]", + spot: float, + target: float, + elapsed: int, + inverse: bool, + cost: "float | None" = None, +) -> tuple: + """Return the price range worth drawing for one position. + + Parameters + ---------- + legs : list[dict] + The sized position. + spot : float + The current price of the underlying. + target : float + The price the view expects. + elapsed : int + Days from now to the valuation date. + inverse : bool + Whether the contracts settle in the underlying. + cost : float or None + What the position was opened for, when traded as one combo. + + Returns + ------- + tuple + The lowest and highest price to draw. + """ + from openbb_deribit.utils.options.legs import breakevens, payoff, position_value + + scout = _grid(spot * (1 - PROBE_SPREAD), spot * (1 + PROBE_SPREAD), SCOUT_STEPS) + curve = payoff(legs, scout, elapsed, inverse, spot, cost) + low, high = _moving( + scout, [position_value(legs, price, elapsed) for price in scout] + ) + anchors = [target, *breakevens(scout, curve)] + + if low <= scout[0]: + low = min([anchor for anchor in anchors if anchor < high] or [spot]) + + if high >= scout[-1]: + high = max([anchor for anchor in anchors if anchor > low] or [spot]) + + reach = max(high - low, spot * MIN_PAD) * ANCHOR_REACH + nearby = [anchor for anchor in anchors if low - reach <= anchor <= high + reach] + low, high = min([low, *nearby]), max([high, *nearby]) + pad = max((high - low) * WINDOW_PAD, spot * MIN_PAD) + + return max(low - pad, 0.0), high + pad + + +def draw( + scored: dict, spot: float, target: float, budget: float, inverse: bool +) -> dict: + """Value one scored position across the price range worth drawing.""" + from openbb_deribit.utils.options.legs import breakevens, payoff + + legs = [{**leg, "quantity": leg["quantity"] * budget} for leg in scored["sized"]] + cost = scored["cost"] * budget + elapsed = scored["elapsed"] + low, high = _window(legs, spot, target, elapsed, inverse, cost) + probe = _grid(low, high, PROBE_STEPS) + curve = payoff(legs, probe, elapsed, inverse, spot, cost) + + return { + "strategy": scored["strategy"], + "code": scored["code"], + "combo": scored["combo"], + "legs": _describe(scored["legs"]), + "contracts": scored["lots"] * budget, + "cost": cost, + "expected_profit": scored["expected"] * budget, + "expected_return": scored["expected"], + "max_profit": max(curve), + "max_loss": min(curve), + "breakevens": breakevens(probe, curve), + "position": legs, + "elapsed": elapsed, + "prices": probe, + "payoff": curve, + } + + +def rank( + frame: "DataFrame", + expiration: Any, + spot: float, + target: float, + budget: float, + inverse: bool, + limit: int = SHORTLIST, + books: "list | tuple" = (), +) -> "list[dict]": + """Return the best position of each structure, most profitable first. + + Parameters + ---------- + frame : DataFrame + The chain. + expiration : Any + The expiration the position is held to. + spot : float + The current price of the underlying. + target : float + The price the view expects. + budget : float + The capital to put at risk, in the quote currency. + inverse : bool + Whether the contracts settle in the underlying. + limit : int + How many structures to return. + books : list or tuple + The listed combos, as ``load_combos`` returns them. + + Returns + ------- + list[dict] + The best-returning position of each structure, sized to the budget, + with its payoff. + """ + scored = [ + result + for strategy in candidates(frame, expiration, spot, books, inverse) + if (result := score(strategy, spot, target, inverse)) + ] + scored.sort(key=lambda item: item["expected"], reverse=True) + best: dict = {} + + for item in scored: + best.setdefault(item["strategy"], item) + + return [ + draw(item, spot, target, budget, inverse) + for item in list(best.values())[:limit] + ] diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/strategies.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/strategies.py new file mode 100644 index 00000000000..5ae94fd4ce4 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/strategies.py @@ -0,0 +1,162 @@ +"""Pricing the standard option structures at every expiration. + +Prices are quoted in the chain's quote currency per unit of the underlying, so +a cost is comparable across expirations of the same underlying but not across +underlyings. The cost as a share of spot is, which is why it is reported too. +""" + +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from pandas import DataFrame + + +def _closest(side: "DataFrame", target: float) -> "Any | None": + """Return the contract whose strike is nearest a price.""" + if side.empty: + return None + + return side.iloc[(side["strike"] - target).abs().argmin()] + + +def _sides(quotes: "DataFrame") -> tuple: + """Split one expiration into its calls and its puts.""" + return ( + quotes[quotes["option_type"] == "call"], + quotes[quotes["option_type"] == "put"], + ) + + +def straddles(frame: "DataFrame", spot: float) -> "list[dict]": + """Price a long straddle at the money, at every expiration.""" + from openbb_deribit.utils.options.chain import expirations, quotes_at + + priced: list[dict] = [] + + for expiration in expirations(frame): + quotes = quotes_at(frame, expiration) + calls, puts = _sides(quotes) + call = _closest(calls, spot) + put = _closest(puts, spot) + + if call is None or put is None: + continue + + if float(call["strike"]) != float(put["strike"]): + continue + + strike = float(call["strike"]) + cost = float(call["entry"]) + float(put["entry"]) + + priced.append( + { + "expiration": expiration, + "dte": int(call["dte"] or 0), + "underlying_price": spot, + "strike": strike, + "call_premium": float(call["entry"]), + "put_premium": float(put["entry"]), + "cost": cost, + "cost_percent": cost / spot if spot else None, + "breakeven_lower": strike - cost, + "breakeven_upper": strike + cost, + "max_loss": -cost, + } + ) + + return priced + + +def strangles(frame: "DataFrame", spot: float, moneyness: float) -> "list[dict]": + """Price a long strangle the given distance out of the money.""" + from openbb_deribit.utils.options.chain import expirations, quotes_at + + priced: list[dict] = [] + + for expiration in expirations(frame): + quotes = quotes_at(frame, expiration) + calls, puts = _sides(quotes) + call = _closest(calls, spot * (1 + moneyness)) + put = _closest(puts, spot * (1 - moneyness)) + + if call is None or put is None: + continue + + upper = float(call["strike"]) + lower = float(put["strike"]) + + if upper <= lower: + continue + + cost = float(call["entry"]) + float(put["entry"]) + + priced.append( + { + "expiration": expiration, + "dte": int(call["dte"] or 0), + "underlying_price": spot, + "call_strike": upper, + "put_strike": lower, + "call_premium": float(call["entry"]), + "put_premium": float(put["entry"]), + "cost": cost, + "cost_percent": cost / spot if spot else None, + "breakeven_lower": lower - cost, + "breakeven_upper": upper + cost, + "max_loss": -cost, + } + ) + + return priced + + +def verticals(frame: "DataFrame", spot: float, moneyness: float) -> "list[dict]": + """Price a bull call and a bear put spread of the given width.""" + from openbb_deribit.utils.options.chain import expirations, quotes_at + + priced: list[dict] = [] + + for expiration in expirations(frame): + quotes = quotes_at(frame, expiration) + calls, puts = _sides(quotes) + + for name, side, long_target, short_target in ( + ("Bull Call Spread", calls, spot, spot * (1 + moneyness)), + ("Bear Put Spread", puts, spot, spot * (1 - moneyness)), + ): + bought = _closest(side, long_target) + sold = _closest(side, short_target) + + if bought is None or sold is None: + continue + + width = abs(float(sold["strike"]) - float(bought["strike"])) + cost = float(bought["entry"]) - float(sold["exit"]) + + if width <= 0 or cost <= 0: + continue + + breakeven = ( + float(bought["strike"]) + cost + if name.startswith("Bull") + else float(bought["strike"]) - cost + ) + priced.append( + { + "strategy": name, + "expiration": expiration, + "dte": int(bought["dte"] or 0), + "underlying_price": spot, + "bought_strike": float(bought["strike"]), + "sold_strike": float(sold["strike"]), + "bought_premium": float(bought["entry"]), + "sold_premium": float(sold["exit"]), + "cost": cost, + "cost_percent": cost / spot if spot else None, + "breakeven": breakeven, + "max_profit": width - cost, + "max_loss": -cost, + } + ) + + return priced diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/options/theme.py b/openbb_platform/providers/deribit/openbb_deribit/utils/options/theme.py new file mode 100644 index 00000000000..9da5a1f0a13 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/options/theme.py @@ -0,0 +1,119 @@ +"""Shared Plotly theming for the Deribit options charts.""" + +from typing import Any + +CHART_CONFIG = {"scrollZoom": True, "displayModeBar": False, "responsive": True} + + +class Unplotted: + """Accept every drawing call and draw nothing.""" + + def __getattr__(self, name: str): + """Return a call that does nothing and keeps the chain going.""" + + def drawn(*args, **kwargs): + return self + + return drawn + + +def supported_layout(layout: dict) -> dict: + """Drop the layout keys the installed Plotly no longer accepts. + + Parameters + ---------- + layout : dict + The template layout to filter. + + Returns + ------- + dict + Only the keys the installed Plotly's ``Layout`` accepts. + """ + from plotly.graph_objects import Layout + + valid = getattr(Layout, "_valid_props", None) + + if not valid: + return layout + + return {key: value for key, value in layout.items() if key in valid} + + +def new_figure(theme: str) -> "tuple[Any, str, str]": + """Create a themed figure with its text and background colors. + + Parameters + ---------- + theme : str + Either 'dark' or 'light'. + + Returns + ------- + tuple[Any, str, str] + The figure, the text color, and the transparent background color. The + figure draws nothing when the charting extension is absent. + """ + from openbb_deribit import CHARTING_INSTALLED + + text_color = "white" if theme == "dark" else "black" + background = "rgba(0,0,0,0)" if theme == "dark" else "rgba(255,255,255,0)" + + if not CHARTING_INSTALLED: + return Unplotted(), text_color, background + + from openbb_charting.core.chart_style import ChartStyle + from openbb_charting.core.openbb_figure import OpenBBFigure + + figure = OpenBBFigure(create_backend=True) + figure.update_layout( + supported_layout(ChartStyle().plotly_template.get("layout", {})) + ) + + return figure, text_color, background + + +def finalize(output: Any, figure: Any, theme: str) -> Any: + """Apply the chart style and attach the chart to the result. + + Parameters + ---------- + output : Any + The OBBject carrying the tabular results. + figure : Any + The figure to attach. + theme : str + Either 'dark' or 'light'. + + Returns + ------- + Any + The same result, with a chart attached, or unchanged when there is no + charting extension to draw with. + """ + from openbb_core.app.model.charts.chart import Chart + + from openbb_deribit import CHARTING_INSTALLED + + if not CHARTING_INSTALLED: + return output + + text_color = "white" if theme == "dark" else "black" + output.charting._charting_settings.chart_style = theme + figure = output.charting._set_chart_style(figure) + figure.update_layout( + hoverlabel=dict( + bgcolor="#0E0E0E" if theme == "dark" else "#FFFFFF", + bordercolor=text_color, + font=dict(color=text_color, size=12), + ) + ) + figure = figure.show(config=CHART_CONFIG, external=True) + + for trace in figure.data: + if getattr(trace, "hoverinfo", None) == "skip": + trace.hovertemplate = None + + output.chart = Chart(fig=figure, content=figure.to_plotly_json(), format="plotly") + + return output diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/session.py b/openbb_platform/providers/deribit/openbb_deribit/utils/session.py new file mode 100644 index 00000000000..98ab98abe70 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/session.py @@ -0,0 +1,116 @@ +"""Pooled HTTP sessions for the Deribit API. + +One session is held per event loop so connections are kept alive across the +many parallel calls a chain or curve needs. The session is closed when its loop +shuts down, so nothing is left open once the work that opened it is done. +""" + +from contextlib import suppress +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from aiohttp import ClientSession + from aiohttp_client_cache.session import CachedSession + +_SESSIONS: dict = {} +_CACHED_SESSIONS: dict = {} + + +def _hook_loop_close(loop) -> None: + """Close this loop's sessions when the loop itself is closed. + + The Python interface runs every command in its own short-lived loop, so the + pool is emptied there without waiting for an API shutdown that never comes. + Loops that do not accept the hook, such as uvloop's, are left to the + shutdown handler. + """ + if getattr(loop, "_deribit_close_hooked", False): + return + + original = loop.close + + def close() -> None: + try: + if not loop.is_closed() and not loop.is_running(): + loop.run_until_complete(close_sessions(loop)) + except Exception: # noqa: BLE001, S110 + pass + + original() + + try: + loop.close = close + loop._deribit_close_hooked = True + except (AttributeError, TypeError): + pass + + +async def get_session() -> "ClientSession": + """Return the uncached session for the running loop. + + Returns + ------- + ClientSession + A session shared by every caller on this loop. + """ + import asyncio + + from aiohttp import ClientSession + + loop = asyncio.get_running_loop() + session = _SESSIONS.get(loop) + + if session is None or session.closed: + session = ClientSession() + _SESSIONS[loop] = session + _hook_loop_close(loop) + + return session + + +async def get_cached_session() -> "CachedSession": + """Return the cache-backed session for the running loop. + + Returns + ------- + CachedSession + A cache-backed session shared by every caller on this loop. + """ + import asyncio + + from aiohttp_client_cache.session import CachedSession + + from openbb_deribit.utils.cache import get_cache_backend, sweep_cache + + loop = asyncio.get_running_loop() + session = _CACHED_SESSIONS.get(loop) + + if session is None or session.closed: + backend = get_cache_backend() + session = CachedSession(cache=backend) + _CACHED_SESSIONS[loop] = session + _hook_loop_close(loop) + + with suppress(Exception): + await sweep_cache(backend) + + return session + + +async def close_sessions(loop=None) -> None: + """Close pooled sessions and forget the loops that owned them. + + Parameters + ---------- + loop : AbstractEventLoop or None + Close only this loop's sessions, or every pooled session when None. + """ + for pool in (_SESSIONS, _CACHED_SESSIONS): + keys = [loop] if loop is not None else list(pool) + + for key in keys: + session = pool.pop(key, None) + + if session is not None and not getattr(session, "closed", True): + with suppress(Exception): + await session.close() diff --git a/openbb_platform/providers/deribit/openbb_deribit/utils/websocket.py b/openbb_platform/providers/deribit/openbb_deribit/utils/websocket.py new file mode 100644 index 00000000000..0fd2afbd3d0 --- /dev/null +++ b/openbb_platform/providers/deribit/openbb_deribit/utils/websocket.py @@ -0,0 +1,85 @@ +"""Ticker subscriptions over the Deribit websocket.""" + +WS_URL = "wss://www.deribit.com/ws/api/v2" +SUBSCRIBE_TIMEOUT = 15.0 + + +async def _collect(websocket, wanted: set, received: dict, messages: set) -> None: + """Read tickers off one subscription until every symbol has arrived.""" + import json + + from websockets import ConnectionClosed + + while True: + try: + frame = await websocket.recv() + except ConnectionClosed: + return + + payload = json.loads(frame) + + if payload.get("error"): + messages.add(f"Deribit returned an error -> {payload['error']}") + return + + ticker = (payload.get("params") or {}).get("data") or {} + symbol = ticker.get("instrument_name") + + if not symbol or symbol in received: + continue + + received[symbol] = ticker + + if wanted <= set(received): + return + + +async def subscribe_tickers(symbols: list[str], messages: set) -> dict[str, dict]: + """Return the ticker of every symbol on one subscription. + + Parameters + ---------- + symbols : list[str] + The instrument names to subscribe to. + messages : set + Collects what went wrong, for the caller to warn about. + + Returns + ------- + dict[str, dict] + The ticker of each symbol that published one. + """ + import asyncio + import json + + from websockets.asyncio.client import connect + + received: dict[str, dict] = {} + request = { + "jsonrpc": "2.0", + "id": 1, + "method": "public/subscribe", + "params": {"channels": [f"ticker.{symbol}.100ms" for symbol in symbols]}, + } + + try: + async with connect(WS_URL) as websocket: + await websocket.send(json.dumps(request)) + + try: + await asyncio.wait_for( + _collect(websocket, set(symbols), received, messages), + timeout=SUBSCRIBE_TIMEOUT, + ) + except asyncio.TimeoutError: + missing = len(symbols) - len(received) + messages.add( + f"Deribit did not publish {missing} of {len(symbols)} contracts" + " before the subscription timed out." + ) + except Exception as error: # noqa: BLE001 + messages.add( + f"The Deribit subscription failed -> {error.__class__.__name__}: {error}" + ) + + return received diff --git a/openbb_platform/providers/deribit/poetry.lock b/openbb_platform/providers/deribit/poetry.lock deleted file mode 100644 index bb29aa93180..00000000000 --- a/openbb_platform/providers/deribit/poetry.lock +++ /dev/null @@ -1,1974 +0,0 @@ -# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.2" -description = "Happy Eyeballs for asyncio" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4"}, - {file = "aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64"}, -] - -[[package]] -name = "aiohttp" -version = "3.13.5" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b"}, - {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5"}, - {file = "aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670"}, - {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274"}, - {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a"}, - {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d"}, - {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796"}, - {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95"}, - {file = "aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5"}, - {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a"}, - {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73"}, - {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297"}, - {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074"}, - {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e"}, - {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7"}, - {file = "aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9"}, - {file = "aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76"}, - {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6"}, - {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d"}, - {file = "aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c"}, - {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb"}, - {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6"}, - {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13"}, - {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174"}, - {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc"}, - {file = "aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6"}, - {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49"}, - {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8"}, - {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d"}, - {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c"}, - {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac"}, - {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3"}, - {file = "aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06"}, - {file = "aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8"}, - {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9"}, - {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416"}, - {file = "aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2"}, - {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4"}, - {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9"}, - {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5"}, - {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e"}, - {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1"}, - {file = "aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286"}, - {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9"}, - {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88"}, - {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3"}, - {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b"}, - {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe"}, - {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14"}, - {file = "aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3"}, - {file = "aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1"}, - {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61"}, - {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832"}, - {file = "aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9"}, - {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090"}, - {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b"}, - {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a"}, - {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8"}, - {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665"}, - {file = "aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540"}, - {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb"}, - {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46"}, - {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8"}, - {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d"}, - {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6"}, - {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c"}, - {file = "aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc"}, - {file = "aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83"}, - {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c"}, - {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be"}, - {file = "aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25"}, - {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56"}, - {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2"}, - {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a"}, - {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be"}, - {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b"}, - {file = "aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94"}, - {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d"}, - {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7"}, - {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772"}, - {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5"}, - {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1"}, - {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b"}, - {file = "aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3"}, - {file = "aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162"}, - {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a"}, - {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254"}, - {file = "aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36"}, - {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f"}, - {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800"}, - {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf"}, - {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b"}, - {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a"}, - {file = "aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8"}, - {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be"}, - {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b"}, - {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6"}, - {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037"}, - {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500"}, - {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9"}, - {file = "aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8"}, - {file = "aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9"}, - {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf"}, - {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1"}, - {file = "aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10"}, - {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f"}, - {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b"}, - {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643"}, - {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031"}, - {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258"}, - {file = "aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a"}, - {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88"}, - {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0"}, - {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f"}, - {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8"}, - {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f"}, - {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b"}, - {file = "aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83"}, - {file = "aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67"}, - {file = "aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1"}, -] - -[package.dependencies] -aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.4.0" -async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -propcache = ">=0.2.0" -yarl = ">=1.17.0,<2.0" - -[package.extras] -speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "backports.zstd ; platform_python_implementation == \"CPython\" and python_version < \"3.14\"", "brotlicffi (>=1.2) ; platform_python_implementation != \"CPython\""] - -[[package]] -name = "aiosignal" -version = "1.4.0" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, - {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" -typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} - -[[package]] -name = "annotated-doc" -version = "0.0.4" -description = "Document parameters, class attributes, return types, and variables inline, with Annotated." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320"}, - {file = "annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4"}, -] - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.13.0" -description = "High-level concurrency and networking framework on top of asyncio or Trio" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"}, - {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"}, -] - -[package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} -idna = ">=2.8" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} - -[package.extras] -trio = ["trio (>=0.32.0)"] - -[[package]] -name = "async-lru" -version = "2.3.0" -description = "Simple LRU cache for asyncio" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315"}, - {file = "async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6"}, -] - -[package.dependencies] -typing_extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "async-timeout" -version = "5.0.1" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.8" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, - {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, -] - -[[package]] -name = "attrs" -version = "26.1.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"}, - {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"}, -] - -[[package]] -name = "certifi" -version = "2026.5.20" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897"}, - {file = "certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943"}, - {file = "charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00"}, - {file = "charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6"}, - {file = "charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110"}, - {file = "charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f"}, - {file = "charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c"}, - {file = "charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-win32.whl", hash = "sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207"}, - {file = "charset_normalizer-3.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444"}, - {file = "charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c"}, - {file = "charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d"}, - {file = "charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5"}, -] - -[[package]] -name = "click" -version = "8.4.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2"}, - {file = "click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, - {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "fastapi" -version = "0.128.8" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "fastapi-0.128.8-py3-none-any.whl", hash = "sha256:5618f492d0fe973a778f8fec97723f598aa9deee495040a8d51aaf3cf123ecf1"}, - {file = "fastapi-0.128.8.tar.gz", hash = "sha256:3171f9f328c4a218f0a8d2ba8310ac3a55d1ee12c28c949650288aee25966007"}, -] - -[package.dependencies] -annotated-doc = ">=0.0.2" -pydantic = ">=2.7.0" -starlette = ">=0.40.0,<1.0.0" -typing-extensions = ">=4.8.0" -typing-inspection = ">=0.4.2" - -[package.extras] -all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.9.3)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=5.8.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] -standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[standard-no-fastapi-cloud-cli] (>=0.0.8)", "httpx (>=0.23.0,<1.0.0)", "jinja2 (>=3.1.5)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "frozenlist" -version = "1.8.0" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, - {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, - {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, - {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, - {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, - {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, - {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, - {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, - {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, - {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, - {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, - {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, - {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, - {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, - {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, - {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, - {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, - {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, - {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, - {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, - {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, - {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, - {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, - {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, - {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, - {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, - {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, - {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, - {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, - {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, - {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, - {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, - {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, - {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, - {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, - {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, - {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, - {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, - {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, - {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, - {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, - {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, - {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, - {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, - {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, - {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, - {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, - {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, - {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, - {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, - {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, - {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, - {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, - {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, - {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, - {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, - {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, - {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, - {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, - {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, - {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, - {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, - {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, - {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, - {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, - {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, - {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, - {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, - {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, - {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, - {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, - {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, - {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, - {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, - {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, - {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, - {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, - {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, - {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, - {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, - {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, - {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, - {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, - {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, - {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, - {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, - {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, - {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, - {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, - {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, - {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, - {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, - {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, - {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, - {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, - {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, - {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, - {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, - {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, - {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, - {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, - {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, - {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, - {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, - {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, - {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, - {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, - {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, - {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, - {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, - {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, - {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, - {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, - {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, - {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, - {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, - {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, - {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, - {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, - {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, - {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, - {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, - {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, - {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, - {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, - {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, - {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, - {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, - {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, - {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, -] - -[[package]] -name = "h11" -version = "0.16.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, - {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, -] - -[[package]] -name = "html5lib" -version = "1.1" -description = "HTML parser based on the WHATWG HTML specification" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -groups = ["main"] -files = [ - {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, - {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, -] - -[package.dependencies] -six = ">=1.9" -webencodings = "*" - -[package.extras] -all = ["chardet (>=2.2)", "genshi", "lxml ; platform_python_implementation == \"CPython\""] -chardet = ["chardet (>=2.2)"] -genshi = ["genshi"] -lxml = ["lxml ; platform_python_implementation == \"CPython\""] - -[[package]] -name = "idna" -version = "3.17" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c"}, - {file = "idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f"}, -] - -[package.extras] -all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "importlib-metadata" -version = "9.0.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7"}, - {file = "importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc"}, -] - -[package.dependencies] -zipp = ">=3.20" - -[package.extras] -check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -perf = ["ipython"] -test = ["packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] - -[[package]] -name = "multidict" -version = "6.7.1" -description = "multidict implementation" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5"}, - {file = "multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8"}, - {file = "multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872"}, - {file = "multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991"}, - {file = "multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03"}, - {file = "multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981"}, - {file = "multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6"}, - {file = "multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190"}, - {file = "multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92"}, - {file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee"}, - {file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2"}, - {file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568"}, - {file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40"}, - {file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962"}, - {file = "multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505"}, - {file = "multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122"}, - {file = "multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df"}, - {file = "multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db"}, - {file = "multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d"}, - {file = "multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e"}, - {file = "multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855"}, - {file = "multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3"}, - {file = "multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e"}, - {file = "multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a"}, - {file = "multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8"}, - {file = "multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0"}, - {file = "multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144"}, - {file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49"}, - {file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71"}, - {file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3"}, - {file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c"}, - {file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0"}, - {file = "multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa"}, - {file = "multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a"}, - {file = "multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b"}, - {file = "multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6"}, - {file = "multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172"}, - {file = "multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd"}, - {file = "multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7"}, - {file = "multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53"}, - {file = "multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75"}, - {file = "multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b"}, - {file = "multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733"}, - {file = "multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a"}, - {file = "multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961"}, - {file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582"}, - {file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e"}, - {file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3"}, - {file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6"}, - {file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a"}, - {file = "multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba"}, - {file = "multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511"}, - {file = "multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19"}, - {file = "multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf"}, - {file = "multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23"}, - {file = "multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2"}, - {file = "multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445"}, - {file = "multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177"}, - {file = "multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23"}, - {file = "multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060"}, - {file = "multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d"}, - {file = "multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed"}, - {file = "multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429"}, - {file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6"}, - {file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9"}, - {file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c"}, - {file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84"}, - {file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d"}, - {file = "multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33"}, - {file = "multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3"}, - {file = "multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5"}, - {file = "multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df"}, - {file = "multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1"}, - {file = "multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963"}, - {file = "multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34"}, - {file = "multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65"}, - {file = "multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292"}, - {file = "multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43"}, - {file = "multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca"}, - {file = "multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd"}, - {file = "multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7"}, - {file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3"}, - {file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4"}, - {file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8"}, - {file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c"}, - {file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52"}, - {file = "multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108"}, - {file = "multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32"}, - {file = "multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8"}, - {file = "multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118"}, - {file = "multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee"}, - {file = "multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2"}, - {file = "multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1"}, - {file = "multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d"}, - {file = "multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31"}, - {file = "multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048"}, - {file = "multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362"}, - {file = "multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37"}, - {file = "multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709"}, - {file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0"}, - {file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb"}, - {file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd"}, - {file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601"}, - {file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1"}, - {file = "multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b"}, - {file = "multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d"}, - {file = "multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f"}, - {file = "multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5"}, - {file = "multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581"}, - {file = "multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a"}, - {file = "multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c"}, - {file = "multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262"}, - {file = "multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59"}, - {file = "multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889"}, - {file = "multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4"}, - {file = "multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d"}, - {file = "multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609"}, - {file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489"}, - {file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c"}, - {file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e"}, - {file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c"}, - {file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9"}, - {file = "multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2"}, - {file = "multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7"}, - {file = "multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5"}, - {file = "multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2"}, - {file = "multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f"}, - {file = "multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358"}, - {file = "multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5"}, - {file = "multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0"}, - {file = "multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8"}, - {file = "multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0"}, - {file = "multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f"}, - {file = "multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f"}, - {file = "multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e"}, - {file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2"}, - {file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8"}, - {file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941"}, - {file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a"}, - {file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de"}, - {file = "multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5"}, - {file = "multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0"}, - {file = "multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4"}, - {file = "multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9"}, - {file = "multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56"}, - {file = "multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "numpy" -version = "2.2.6" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"}, - {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"}, - {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"}, - {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"}, - {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"}, - {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"}, - {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"}, - {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"}, - {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"}, - {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"}, - {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"}, - {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"}, - {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"}, - {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"}, - {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"}, - {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"}, - {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"}, - {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"}, - {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"}, - {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"}, - {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"}, - {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"}, - {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"}, - {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"}, - {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"}, - {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"}, - {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"}, - {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"}, -] - -[[package]] -name = "numpy" -version = "2.4.6" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.11" -groups = ["main"] -markers = "python_version >= \"3.11\"" -files = [ - {file = "numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4"}, - {file = "numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d"}, - {file = "numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8"}, - {file = "numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538"}, - {file = "numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47"}, - {file = "numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93"}, - {file = "numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8"}, - {file = "numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6"}, - {file = "numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8"}, - {file = "numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147"}, - {file = "numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577"}, - {file = "numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1"}, - {file = "numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb"}, - {file = "numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41"}, - {file = "numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698"}, - {file = "numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f"}, - {file = "numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853"}, - {file = "numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a"}, - {file = "numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2"}, - {file = "numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45"}, - {file = "numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751"}, - {file = "numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8"}, - {file = "numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0"}, - {file = "numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb"}, - {file = "numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f"}, - {file = "numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3"}, - {file = "numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b"}, - {file = "numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089"}, - {file = "numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a"}, - {file = "numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605"}, - {file = "numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91"}, - {file = "numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359"}, - {file = "numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778"}, - {file = "numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1"}, - {file = "numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe"}, - {file = "numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997"}, - {file = "numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20"}, - {file = "numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d"}, - {file = "numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67"}, - {file = "numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd"}, - {file = "numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab"}, - {file = "numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75"}, - {file = "numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd"}, - {file = "numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079"}, - {file = "numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7"}, - {file = "numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5"}, - {file = "numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096"}, - {file = "numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b"}, - {file = "numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8"}, - {file = "numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402"}, - {file = "numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb"}, - {file = "numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1"}, - {file = "numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261"}, - {file = "numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6"}, - {file = "numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a"}, - {file = "numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e"}, - {file = "numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e"}, - {file = "numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43"}, - {file = "numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e"}, - {file = "numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895"}, - {file = "numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4"}, - {file = "numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063"}, - {file = "numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627"}, - {file = "numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66"}, - {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662"}, - {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7"}, - {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f"}, - {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c"}, - {file = "numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0"}, - {file = "numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02"}, - {file = "numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73"}, - {file = "numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda"}, -] - -[[package]] -name = "openbb-core" -version = "1.6.10" -description = "OpenBB package with core functionality." -optional = false -python-versions = "<4,>=3.10" -groups = ["main"] -files = [ - {file = "openbb_core-1.6.10-py3-none-any.whl", hash = "sha256:45b036ee816f2d5b1f25124f0a76f437c8a6304aa3b7d09b4c954339c6f0c447"}, - {file = "openbb_core-1.6.10.tar.gz", hash = "sha256:37e5aabc0a03fdac4f05f460127bfcb81c249afc6d6033997c246a97be70d0fe"}, -] - -[package.dependencies] -aiohttp = ">=3.13.3" -fastapi = ">=0.128.0,<0.129.0" -html5lib = ">=1.1,<2.0" -importlib-metadata = ">=6.8.0" -pandas = ">=1.5.3" -pydantic = ">=2.12.3,<3.0.0" -pyjwt = ">=2.12.0,<3.0.0" -python-dotenv = ">=1.0.0,<2.0.0" -python-multipart = ">=0.0.27,<0.0.28" -requests = ">=2.33.0,<3.0.0" -ruff = ">=0.15,<0.16" -uuid7 = ">=0.1.0,<0.2.0" -uvicorn = ">=0.40.0,<0.41.0" -websockets = ">=15.0" - -[[package]] -name = "pandas" -version = "2.3.3" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c"}, - {file = "pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a"}, - {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1"}, - {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838"}, - {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250"}, - {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4"}, - {file = "pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826"}, - {file = "pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523"}, - {file = "pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45"}, - {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66"}, - {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b"}, - {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791"}, - {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151"}, - {file = "pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c"}, - {file = "pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53"}, - {file = "pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35"}, - {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908"}, - {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89"}, - {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98"}, - {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084"}, - {file = "pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b"}, - {file = "pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713"}, - {file = "pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8"}, - {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d"}, - {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac"}, - {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c"}, - {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493"}, - {file = "pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee"}, - {file = "pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5"}, - {file = "pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21"}, - {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78"}, - {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110"}, - {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86"}, - {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc"}, - {file = "pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0"}, - {file = "pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593"}, - {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c"}, - {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b"}, - {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6"}, - {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3"}, - {file = "pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5"}, - {file = "pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec"}, - {file = "pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7"}, - {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450"}, - {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5"}, - {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788"}, - {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87"}, - {file = "pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2"}, - {file = "pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8"}, - {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff"}, - {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29"}, - {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73"}, - {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9"}, - {file = "pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa"}, - {file = "pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b"}, -] - -[package.dependencies] -numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""} -python-dateutil = ">=2.8.2" -pytz = ">=2020.1" -tzdata = ">=2022.7" - -[package.extras] -all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] -aws = ["s3fs (>=2022.11.0)"] -clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] -compression = ["zstandard (>=0.19.0)"] -computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] -feather = ["pyarrow (>=10.0.1)"] -fss = ["fsspec (>=2022.11.0)"] -gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] -hdf5 = ["tables (>=3.8.0)"] -html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] -mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] -parquet = ["pyarrow (>=10.0.1)"] -performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] -plot = ["matplotlib (>=3.6.3)"] -postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] -pyarrow = ["pyarrow (>=10.0.1)"] -spss = ["pyreadstat (>=1.2.0)"] -sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.9.2)"] - -[[package]] -name = "pandas" -version = "3.0.3" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.11" -groups = ["main"] -markers = "python_version >= \"3.11\"" -files = [ - {file = "pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98"}, - {file = "pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639"}, - {file = "pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2"}, - {file = "pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27"}, - {file = "pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824"}, - {file = "pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938"}, - {file = "pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea"}, - {file = "pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a"}, - {file = "pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09"}, - {file = "pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4"}, - {file = "pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c"}, - {file = "pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9"}, - {file = "pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf"}, - {file = "pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c"}, - {file = "pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc"}, - {file = "pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49"}, - {file = "pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa"}, - {file = "pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7"}, - {file = "pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8"}, - {file = "pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a"}, - {file = "pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb"}, - {file = "pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2"}, - {file = "pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44"}, - {file = "pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e"}, - {file = "pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d"}, - {file = "pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066"}, - {file = "pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd"}, - {file = "pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085"}, - {file = "pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870"}, - {file = "pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f"}, - {file = "pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13"}, - {file = "pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac"}, - {file = "pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f"}, - {file = "pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb"}, - {file = "pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a"}, - {file = "pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360"}, - {file = "pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76"}, - {file = "pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5"}, - {file = "pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977"}, - {file = "pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04"}, - {file = "pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6"}, - {file = "pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c"}, - {file = "pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028"}, - {file = "pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d"}, - {file = "pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a"}, - {file = "pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1"}, - {file = "pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1"}, - {file = "pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc"}, -] - -[package.dependencies] -numpy = [ - {version = ">=1.26.0", markers = "python_version < \"3.14\""}, - {version = ">=2.3.3", markers = "python_version >= \"3.14\""}, -] -python-dateutil = ">=2.8.2" -tzdata = {version = "*", markers = "sys_platform == \"win32\" or sys_platform == \"emscripten\""} - -[package.extras] -all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "adbc-driver-sqlite (>=1.2.0)", "beautifulsoup4 (>=4.12.3)", "bottleneck (>=1.4.2)", "fastparquet (>=2024.11.0)", "fsspec (>=2024.10.0)", "gcsfs (>=2024.10.0)", "html5lib (>=1.1)", "hypothesis (>=6.116.0)", "jinja2 (>=3.1.5)", "lxml (>=5.3.0)", "matplotlib (>=3.9.3)", "numba (>=0.60.0)", "numexpr (>=2.10.2)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.5)", "psycopg2 (>=2.9.10)", "pyarrow (>=13.0.0)", "pyiceberg (>=0.8.1)", "pymysql (>=1.1.1)", "pyreadstat (>=1.2.8)", "pytest (>=8.3.4)", "pytest-xdist (>=3.6.1)", "python-calamine (>=0.3.0)", "pytz (>=2020.1)", "pyxlsb (>=1.0.10)", "qtpy (>=2.4.2)", "s3fs (>=2024.10.0)", "scipy (>=1.14.1)", "tables (>=3.10.1)", "tabulate (>=0.9.0)", "xarray (>=2024.10.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.2.0)", "zstandard (>=0.23.0)"] -aws = ["s3fs (>=2024.10.0)"] -clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.4.2)"] -compression = ["zstandard (>=0.23.0)"] -computation = ["scipy (>=1.14.1)", "xarray (>=2024.10.0)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.5)", "python-calamine (>=0.3.0)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.2.0)"] -feather = ["pyarrow (>=13.0.0)"] -fss = ["fsspec (>=2024.10.0)"] -gcp = ["gcsfs (>=2024.10.0)"] -hdf5 = ["tables (>=3.10.1)"] -html = ["beautifulsoup4 (>=4.12.3)", "html5lib (>=1.1)", "lxml (>=5.3.0)"] -iceberg = ["pyiceberg (>=0.8.1)"] -mysql = ["SQLAlchemy (>=2.0.36)", "pymysql (>=1.1.1)"] -output-formatting = ["jinja2 (>=3.1.5)", "tabulate (>=0.9.0)"] -parquet = ["pyarrow (>=13.0.0)"] -performance = ["bottleneck (>=1.4.2)", "numba (>=0.60.0)", "numexpr (>=2.10.2)"] -plot = ["matplotlib (>=3.9.3)"] -postgresql = ["SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "psycopg2 (>=2.9.10)"] -pyarrow = ["pyarrow (>=13.0.0)"] -spss = ["pyreadstat (>=1.2.8)"] -sql-other = ["SQLAlchemy (>=2.0.36)", "adbc-driver-postgresql (>=1.2.0)", "adbc-driver-sqlite (>=1.2.0)"] -test = ["hypothesis (>=6.116.0)", "pytest (>=8.3.4)", "pytest-xdist (>=3.6.1)"] -timezone = ["pytz (>=2020.1)"] -xml = ["lxml (>=5.3.0)"] - -[[package]] -name = "propcache" -version = "0.5.2" -description = "Accelerated property cache" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b"}, - {file = "propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c"}, - {file = "propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d"}, - {file = "propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e"}, - {file = "propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274"}, - {file = "propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe"}, - {file = "propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d"}, - {file = "propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5"}, - {file = "propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78"}, - {file = "propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959"}, - {file = "propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b"}, - {file = "propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27"}, - {file = "propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f"}, - {file = "propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0"}, - {file = "propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82"}, - {file = "propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab"}, - {file = "propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba"}, - {file = "propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a"}, - {file = "propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476"}, - {file = "propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33"}, - {file = "propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a"}, - {file = "propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031"}, - {file = "propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42"}, - {file = "propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84"}, - {file = "propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a"}, - {file = "propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117"}, - {file = "propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d"}, - {file = "propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704"}, - {file = "propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4"}, - {file = "propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d"}, - {file = "propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757"}, - {file = "propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f"}, - {file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d"}, - {file = "propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa"}, - {file = "propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc"}, - {file = "propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55"}, - {file = "propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568"}, - {file = "propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191"}, - {file = "propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7"}, - {file = "propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96"}, - {file = "propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999"}, - {file = "propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e"}, - {file = "propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825"}, - {file = "propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5"}, - {file = "propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4"}, - {file = "propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0"}, - {file = "propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c"}, - {file = "propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0"}, - {file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb"}, - {file = "propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078"}, - {file = "propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335"}, - {file = "propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d"}, - {file = "propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2"}, - {file = "propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821"}, - {file = "propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370"}, - {file = "propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6"}, - {file = "propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe"}, - {file = "propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427"}, -] - -[[package]] -name = "pydantic" -version = "2.13.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba"}, - {file = "pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.46.4" -typing-extensions = ">=4.14.1" -typing-inspection = ">=0.4.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.46.4" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4"}, - {file = "pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01"}, - {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d"}, - {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4"}, - {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f"}, - {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39"}, - {file = "pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d"}, - {file = "pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf"}, - {file = "pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594"}, - {file = "pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398"}, - {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3"}, - {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848"}, - {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3"}, - {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109"}, - {file = "pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda"}, - {file = "pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33"}, - {file = "pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d"}, - {file = "pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2"}, - {file = "pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987"}, - {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b"}, - {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458"}, - {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b"}, - {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c"}, - {file = "pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894"}, - {file = "pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89"}, - {file = "pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a"}, - {file = "pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008"}, - {file = "pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262"}, - {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e"}, - {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd"}, - {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be"}, - {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d"}, - {file = "pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb"}, - {file = "pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292"}, - {file = "pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d"}, - {file = "pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb"}, - {file = "pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c"}, - {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb"}, - {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898"}, - {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e"}, - {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519"}, - {file = "pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4"}, - {file = "pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac"}, - {file = "pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5"}, - {file = "pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596"}, - {file = "pydantic_core-2.46.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae"}, - {file = "pydantic_core-2.46.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066"}, - {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6"}, - {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9"}, - {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29"}, - {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9"}, - {file = "pydantic_core-2.46.4-cp39-cp39-win32.whl", hash = "sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1"}, - {file = "pydantic_core-2.46.4-cp39-cp39-win_amd64.whl", hash = "sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b"}, - {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526"}, - {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc"}, - {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983"}, - {file = "pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1"}, -] - -[package.dependencies] -typing-extensions = ">=4.14.1" - -[[package]] -name = "pyjwt" -version = "2.13.0" -description = "JSON Web Token implementation in Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728"}, - {file = "pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423"}, -] - -[package.dependencies] -typing_extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "1.2.2" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a"}, - {file = "python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "python-multipart" -version = "0.0.27" -description = "A streaming multipart parser for Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "python_multipart-0.0.27-py3-none-any.whl", hash = "sha256:6fccfad17a27334bd0193681b369f476eda3409f17381a2d65aa7df3f7275645"}, - {file = "python_multipart-0.0.27.tar.gz", hash = "sha256:9870a6a8c5a20a5bf4f07c017bd1489006ff8836cff097b6933355ee2b49b602"}, -] - -[[package]] -name = "pytz" -version = "2026.2" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -groups = ["main"] -markers = "python_version == \"3.10\"" -files = [ - {file = "pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126"}, - {file = "pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a"}, -] - -[[package]] -name = "requests" -version = "2.34.2" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"}, - {file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"}, -] - -[package.dependencies] -certifi = ">=2023.5.7" -charset_normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.26,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] - -[[package]] -name = "ruff" -version = "0.15.15" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b"}, - {file = "ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e"}, - {file = "ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530"}, - {file = "ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4"}, - {file = "ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f"}, - {file = "ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622"}, - {file = "ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45"}, - {file = "ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627"}, - {file = "ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4"}, - {file = "ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c"}, - {file = "ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd"}, - {file = "ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f"}, - {file = "ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb"}, - {file = "ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a"}, - {file = "ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9"}, - {file = "ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4"}, - {file = "ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7"}, - {file = "ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6"}, -] - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "starlette" -version = "0.52.1" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74"}, - {file = "starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933"}, -] - -[package.dependencies] -anyio = ">=3.6.2,<5" -typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""} - -[package.extras] -full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, - {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.2" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, - {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[[package]] -name = "tzdata" -version = "2026.2" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -groups = ["main"] -markers = "sys_platform == \"win32\" or sys_platform == \"emscripten\" or python_version == \"3.10\"" -files = [ - {file = "tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7"}, - {file = "tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10"}, -] - -[[package]] -name = "urllib3" -version = "2.7.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, - {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, -] - -[package.extras] -brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] - -[[package]] -name = "uuid7" -version = "0.1.0" -description = "UUID version 7, generating time-sorted UUIDs with 200ns time resolution and 48 bits of randomness" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "uuid7-0.1.0-py2.py3-none-any.whl", hash = "sha256:5e259bb63c8cb4aded5927ff41b444a80d0c7124e8a0ced7cf44efa1f5cccf61"}, - {file = "uuid7-0.1.0.tar.gz", hash = "sha256:8c57aa32ee7456d3cc68c95c4530bc571646defac01895cfc73545449894a63c"}, -] - -[[package]] -name = "uvicorn" -version = "0.40.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee"}, - {file = "uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} - -[package.extras] -standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] - -[[package]] -name = "websockets" -version = "16.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a"}, - {file = "websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0"}, - {file = "websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957"}, - {file = "websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72"}, - {file = "websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde"}, - {file = "websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3"}, - {file = "websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3"}, - {file = "websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9"}, - {file = "websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35"}, - {file = "websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8"}, - {file = "websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad"}, - {file = "websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d"}, - {file = "websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe"}, - {file = "websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b"}, - {file = "websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5"}, - {file = "websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64"}, - {file = "websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6"}, - {file = "websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac"}, - {file = "websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00"}, - {file = "websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79"}, - {file = "websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39"}, - {file = "websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c"}, - {file = "websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f"}, - {file = "websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1"}, - {file = "websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2"}, - {file = "websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89"}, - {file = "websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea"}, - {file = "websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9"}, - {file = "websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230"}, - {file = "websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c"}, - {file = "websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5"}, - {file = "websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82"}, - {file = "websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8"}, - {file = "websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f"}, - {file = "websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a"}, - {file = "websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156"}, - {file = "websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0"}, - {file = "websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904"}, - {file = "websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4"}, - {file = "websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e"}, - {file = "websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4"}, - {file = "websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1"}, - {file = "websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3"}, - {file = "websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8"}, - {file = "websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d"}, - {file = "websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244"}, - {file = "websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e"}, - {file = "websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641"}, - {file = "websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8"}, - {file = "websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e"}, - {file = "websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944"}, - {file = "websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206"}, - {file = "websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6"}, - {file = "websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd"}, - {file = "websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d"}, - {file = "websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03"}, - {file = "websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da"}, - {file = "websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c"}, - {file = "websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767"}, - {file = "websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec"}, - {file = "websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5"}, -] - -[[package]] -name = "yarl" -version = "1.24.2" -description = "Yet another URL library" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "yarl-1.24.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12"}, - {file = "yarl-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0"}, - {file = "yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75"}, - {file = "yarl-1.24.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727"}, - {file = "yarl-1.24.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413"}, - {file = "yarl-1.24.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9"}, - {file = "yarl-1.24.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2"}, - {file = "yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90"}, - {file = "yarl-1.24.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643"}, - {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac"}, - {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f"}, - {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36"}, - {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a"}, - {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53"}, - {file = "yarl-1.24.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342"}, - {file = "yarl-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4"}, - {file = "yarl-1.24.2-cp310-cp310-win_arm64.whl", hash = "sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39"}, - {file = "yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1"}, - {file = "yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c"}, - {file = "yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d"}, - {file = "yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae"}, - {file = "yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a"}, - {file = "yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e"}, - {file = "yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50"}, - {file = "yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003"}, - {file = "yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f"}, - {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f"}, - {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294"}, - {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2"}, - {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c"}, - {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b"}, - {file = "yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5"}, - {file = "yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45"}, - {file = "yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122"}, - {file = "yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8"}, - {file = "yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2"}, - {file = "yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d"}, - {file = "yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035"}, - {file = "yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576"}, - {file = "yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8"}, - {file = "yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7"}, - {file = "yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c"}, - {file = "yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d"}, - {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db"}, - {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712"}, - {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996"}, - {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b"}, - {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c"}, - {file = "yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1"}, - {file = "yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad"}, - {file = "yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30"}, - {file = "yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536"}, - {file = "yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607"}, - {file = "yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1"}, - {file = "yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986"}, - {file = "yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488"}, - {file = "yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b"}, - {file = "yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592"}, - {file = "yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617"}, - {file = "yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92"}, - {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a"}, - {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44"}, - {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a"}, - {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf"}, - {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056"}, - {file = "yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992"}, - {file = "yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656"}, - {file = "yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461"}, - {file = "yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2"}, - {file = "yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630"}, - {file = "yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8"}, - {file = "yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14"}, - {file = "yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535"}, - {file = "yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14"}, - {file = "yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3"}, - {file = "yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208"}, - {file = "yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50"}, - {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd"}, - {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67"}, - {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1"}, - {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1"}, - {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b"}, - {file = "yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8"}, - {file = "yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0"}, - {file = "yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57"}, - {file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b"}, - {file = "yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761"}, - {file = "yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8"}, - {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed"}, - {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543"}, - {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0"}, - {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024"}, - {file = "yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf"}, - {file = "yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc"}, - {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb"}, - {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420"}, - {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f"}, - {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa"}, - {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe"}, - {file = "yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd"}, - {file = "yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215"}, - {file = "yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d"}, - {file = "yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9"}, - {file = "yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" -propcache = ">=0.2.1" - -[[package]] -name = "zipp" -version = "4.1.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f"}, - {file = "zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602"}, -] - -[package.extras] -check = ["pytest-checkdocs (>=2.14)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=3.4)"] -test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] -type = ["pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.10,<4" -content-hash = "67ec65ad184ef8f6b255fe0df7ad1091fb3f809e23e531373b68859770fdec87" diff --git a/openbb_platform/providers/deribit/pyproject.toml b/openbb_platform/providers/deribit/pyproject.toml index 01b2504ae06..9a96c064233 100644 --- a/openbb_platform/providers/deribit/pyproject.toml +++ b/openbb_platform/providers/deribit/pyproject.toml @@ -1,20 +1,96 @@ -[tool.poetry] +[project] name = "openbb-deribit" -version = "1.2.1" -description = "Deribit is a crypto-native derivatives exchange." -authors = ["OpenBB Team "] -license = "AGPL-3.0-only" +version = "2.0.0" +description = "Deribit provider extension for OpenBB Platform." +authors = [{ name = "OpenBB Team", email = "hello@openbb.co" }] +license = { text = "AGPL-3.0-only" } readme = "README.md" -packages = [{ include = "openbb_deribit" }] +requires-python = ">=3.10,<4" +dependencies = [ + "openbb-core[pandas]>=2.0.0", + "aiohttp-client-cache>=0.11", + "aiosqlite>=0.20", +] -[tool.poetry.dependencies] -python = ">=3.10,<4" -openbb-core = "^1.6.10" -async-lru = "^2.0.4" +[project.entry-points."openbb_provider_extension"] +deribit = "openbb_deribit:deribit_provider" + +[project.entry-points."openbb_core_extension"] +deribit = "openbb_deribit.deribit_router:router" + +[project.urls] +Homepage = "https://openbb.co" +Repository = "https://github.com/OpenBB-finance/OpenBB" + +[dependency-groups] +dev = [ + "openbb-devtools>=2.0.0", + "openbb-charting>=3.0.0", + "openbb-platform-api>=2.0.0", +] [build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" +requires = ["hatchling>=1.25"] +build-backend = "hatchling.build" -[tool.poetry.plugins."openbb_provider_extension"] -deribit = "openbb_deribit:deribit_provider" +[tool.hatch.build.targets.wheel] +packages = ["openbb_deribit"] + +[tool.uv.sources] +openbb-core = { path = "../../core", editable = true } +openbb-devtools = { path = "../../extensions/devtools", editable = true } +openbb-charting = { path = "../../obbject_extensions/charting", editable = true } +openbb-platform-api = { path = "../../extensions/platform_api", editable = true } + +[tool.ruff] +line-length = 88 +target-version = "py310" +fix = true + +[tool.ruff.lint] +select = ["D", "E", "W", "F", "S", "UP", "I", "PLC", "PLE", "PLR", "PLW", "SIM", "T20"] +ignore = [ + "PLR2004", "PLR0913", "PLR0915", "PLC0415", + "E402", "E501", + "D202", "D203", "D205", "D206", "D210", "D212", "D213", "D214", + "D215", "D300", "D400", "D404", "D405", "D406", "D407", "D408", + "D409", "D410", "D411", "D413", "D415", "D416", "D417", +] + +[tool.ruff.lint.per-file-ignores] +"openbb_deribit/routers/*.py" = ["ARG001", "PLC0415", "PLR0913", "PLR0917"] +"openbb_deribit/models/*.py" = ["ARG002", "PLC0415", "PLR0912"] +"openbb_deribit/utils/options/*.py" = ["PLC0415", "PLR0911", "PLR0912", "PLR0913", "PLR0914", "PLR0915", "PLR0917", "SLF001"] +"tests/**" = ["S101", "S108", "D", "SIM105", "SIM117"] +"integration/**" = ["S101", "S108", "D", "SIM105", "SIM117", "PLC0415", "ARG001"] +"**/__init__.py" = ["F401"] + +[tool.ruff.lint.isort] +combine-as-imports = true +force-wrap-aliases = true + +[tool.ruff.lint.pydocstyle] +convention = "numpy" + +[tool.ty] +[tool.ty.environment] +python-version = "3.10" + +[tool.ty.src] +include = ["openbb_deribit"] +exclude = ["tests", "integration"] + +[tool.ty.rules] +unresolved-import = "warn" + +[tool.coverage.report] +exclude_lines = ["pragma: no cover", "if TYPE_CHECKING:", "raise NotImplementedError", "@overload"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-q --strict-markers" +markers = [ + "record_http: VCR-backed HTTP cassette tests", + "integration: end-to-end tests that hit live services (excluded from the coverage gate)", + "uses_session: builds real pooled sessions instead of patching them", +] diff --git a/openbb_platform/providers/deribit/tests/conftest.py b/openbb_platform/providers/deribit/tests/conftest.py new file mode 100644 index 00000000000..215a2417a02 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/conftest.py @@ -0,0 +1,160 @@ +"""Shared fixtures for the openbb_deribit test suite.""" + +import json +from pathlib import Path + +import pytest + +DATA_DIR = Path(__file__).parent / "data" + + +def payload(name: str): + """Return one captured Deribit payload.""" + return json.loads((DATA_DIR / f"{name}.json").read_text(encoding="utf-8")) + + +@pytest.fixture +def load(): + """Return the loader for a captured Deribit payload.""" + return payload + + +@pytest.fixture(autouse=True) +def _no_network(request, monkeypatch): + """Fail loudly rather than reaching the exchange from a unit test. + + A test marked ``uses_session`` builds its own sessions and is left alone; + a session is only a connection pool until something asks it for a URL. + """ + if request.node.get_closest_marker("uses_session"): + return + + async def _forbidden(*args, **kwargs): + raise AssertionError( + "A unit test opened a session. Patch the client or the session." + ) + + monkeypatch.setattr("openbb_deribit.utils.session.get_session", _forbidden) + monkeypatch.setattr("openbb_deribit.utils.session.get_cached_session", _forbidden) + + +@pytest.fixture +def responder(monkeypatch): + """Answer client calls from a mapping of method name to payload.""" + + def install(mapping: dict): + async def _request(method, params=None, use_cache=True): + if method not in mapping: + raise AssertionError(f"No fixture registered for public/{method}.") + + answer = mapping[method] + + return answer(params) if callable(answer) else answer + + async def _gather(calls, use_cache=True): + return [await _request(method, params) for method, params in calls] + + monkeypatch.setattr("openbb_deribit.utils.client.request", _request) + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + + return mapping + + return install + + +def _chain_rows(inverse: bool, spot: float, size: float): + """Build a small, well-formed chain around a spot price.""" + from datetime import date, timedelta + + today = date.today() + rows = [] + + for days in (1, 30): + expiration = today + timedelta(days=days) + + for step in range(-4, 5): + strike = round(spot * (1 + step * 0.02), 4) + + for option_type in ("call", "put"): + moneyness = ( + max(0.0, spot - strike) + if option_type == "call" + else max(0.0, strike - spot) + ) + extrinsic = spot * 0.01 * (days / 30) ** 0.5 + mark = moneyness + extrinsic + rows.append( + { + "contract_symbol": ( + f"TEST-{days}-{strike:g}-{option_type[0].upper()}" + ), + "expiration": expiration, + "dte": days, + "strike": float(strike), + "option_type": option_type, + "contract_size": size, + "tick_size": 0.0001, + "is_inverse": inverse, + "mark": mark, + "bid": mark * 0.98, + "ask": mark * 1.02, + "last": mark, + "implied_volatility": 50.0 + abs(step) * 2, + "open_interest": 10.0 + step, + "volume": 5.0, + "underlying_price": spot, + "underlying_spot_price": spot, + "delta": 0.5, + "gamma": 0.001, + "theta": -1.0, + "vega": 10.0, + "rho": 1.0, + } + ) + + return rows + + +@pytest.fixture +def chain_frame(): + """Return a builder for a synthetic option chain.""" + from pandas import DataFrame + + def build(inverse: bool = True, spot: float = 100.0, size: float = 1.0): + return DataFrame(_chain_rows(inverse, spot, size)) + + return build + + +@pytest.fixture +def options_chain(chain_frame): + """Return a synthetic chain as the model the options charts draw from.""" + from openbb_deribit.utils.options.data_handler import to_chain + + return to_chain(chain_frame(), "BTC") + + +@pytest.fixture +def loaded_chain(monkeypatch, chain_frame): + """Serve a synthetic chain wherever a chain would be loaded.""" + + def install( + inverse: bool = True, + spot: float = 100.0, + size: float = 1.0, + books: list | None = None, + ): + frame = chain_frame(inverse, spot, size) + + async def _load(symbol, use_cache=True): + return frame.copy() + + async def _combos(symbol, use_cache=True): + return list(books or []) + + monkeypatch.setattr("openbb_deribit.utils.options.chain.load_chain", _load) + monkeypatch.setattr("openbb_deribit.utils.options.chain.load_combos", _combos) + + return frame + + return install diff --git a/openbb_platform/providers/deribit/tests/data/announcements.json b/openbb_platform/providers/deribit/tests/data/announcements.json new file mode 100644 index 00000000000..15bef6c01f9 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/announcements.json @@ -0,0 +1,26 @@ +[ + { + "id": 1789741775268, + "title": "\ud83d\udea8 Coin50 Index, Pre-IPO OpenAI, & EURC Perpetuals. Coming to Deribit", + "body": "

On Monday September 21, 2026 at 10:00 AM UTC, Deribit will launch the following USDC-settled perpetual markets:

\u2022 COIN50_USDC, offering exposure to the Coin50 Index through a perpetual contract
\u2022 OPENAI_USDC, a pre-IPO perpetual linked to OpenAI
\u2022 EURC_USDC
, The euro-backed stablecoin issued by Circle

Contract details:
\ud83d\udd39 Settlement: USDC
\ud83d\udd39 Full specifications: Deribit RWA Perpetual contract details

Read more here.

Deribit. Always Open.

Disclaimer: Deribit FZE is authorised and regulated by the Virtual Assets Regulatory Authority (VARA) in Dubai. Availability of products and services is subject to applicable regulatory requirements and investor classification. Virtual Assets are subject to extreme market volatility, involve a high degree of risk, and can lose value

", + "important": true, + "confirmation": false, + "publication_timestamp": 1789739944023 + }, + { + "id": 1789656759479, + "title": "\ud83d\udd14 Update: Spot Routing Completed", + "body": "All announced spot routing markets are now live and routed through Coinbase Exchange liquidity on Deribit:

\u2022 BTC-USDC
\u2022 ETH-USDC
\u2022 XRP-USDC
\u2022 BNB-USDC
\u2022 SOL-USDC
\u2022 PAXG-USDC

Benefit from deeper liquidity and improved execution quality. Routed pairs will follow Deribit\u2019s applicable Fee Schedule and CBE market specifications. For pair-level specifications, see the Spot Instruments page.

Deribit. Always Open.
", + "important": true, + "confirmation": false, + "publication_timestamp": 1789651389316 + }, + { + "id": 1789659935624, + "title": "\ud83d\udea8 More new perpetual futures are coming to Deribit", + "body": "We are pleased to announce that on September 18, 2026, at 10:00 AM UTC, Deribit is launching a new lineup of USDC-settled perpetuals.

MORPHO \u00b7 AERO \u00b7 BASED1 \u00b7 S \u00b7 EIGEN \u00b7 JTO \u00b7 NIGHT \u00b7 AVNT \u00b7 STRK \u00b7 COMP \u00b7 ALT \u00b7 DYDX \u00b7 OPN \u00b7 TOWNS \u00b7 PYTH \u00b7 RUNE \u00b7 RESOLV \u00b7 ATOM

Contract details:
\ud83d\udd39Settlement: USDC
\ud83d\udd39Click here for all contract details and specifications.

See here for more information.

Please note that these newly listed perpetual contracts are subject to our Auto-Deleveraging Mechanism (ADL) and are covered by the Liquidity Support Program (LSP). By trading any of the products listed above, you acknowledge and agree that the terms of ADL and LSP will apply.

For more information on how ADL works, when it may be triggered, and which Instruments are in scope, please refer to the ADL article available in our Knowledge Base. For more information about the LSP, please refer to the LSP article available in our Knowledge Base.

Deribit. Always Open.
", + "important": true, + "confirmation": false, + "publication_timestamp": 1789651341429 + } +] diff --git a/openbb_platform/providers/deribit/tests/data/apr_history.json b/openbb_platform/providers/deribit/tests/data/apr_history.json new file mode 100644 index 00000000000..202beecf1ab --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/apr_history.json @@ -0,0 +1,25 @@ +{ + "data": [ + { + "day": 20715, + "apr": 4.75 + }, + { + "day": 20714, + "apr": 4.75 + }, + { + "day": 20713, + "apr": 4.75 + }, + { + "day": 20712, + "apr": 4.75 + }, + { + "day": 20711, + "apr": 4.75 + } + ], + "continuation": 20711 +} diff --git a/openbb_platform/providers/deribit/tests/data/block_rfq_trades.json b/openbb_platform/providers/deribit/tests/data/block_rfq_trades.json new file mode 100644 index 00000000000..7b3aad25910 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/block_rfq_trades.json @@ -0,0 +1,343 @@ +{ + "continuation": "1789886834420:55537", + "block_rfqs": [ + { + "id": 55558, + "timestamp": 1789918919275, + "amount": 84.0, + "direction": "buy", + "mark_price": -0.001760294, + "trades": [ + { + "price": -0.0016, + "amount": 84.0, + "direction": "buy" + } + ], + "legs": [ + { + "price": 0.0026, + "direction": "buy", + "instrument_name": "ETH-21SEP26-2580-P", + "ratio": 1 + }, + { + "price": 0.0052, + "direction": "buy", + "instrument_name": "ETH-21SEP26-2600-P", + "ratio": 1 + }, + { + "price": 0.0094, + "direction": "sell", + "instrument_name": "ETH-21SEP26-2620-P", + "ratio": 1 + } + ], + "combo_id": null, + "index_prices": { + "eth_usd": 2602.81 + } + }, + { + "id": 55557, + "timestamp": 1789918897736, + "amount": 84.0, + "direction": "buy", + "mark_price": -0.001321293, + "trades": [ + { + "price": -0.001, + "amount": 84.0, + "direction": "buy" + } + ], + "legs": [ + { + "price": 0.0059, + "direction": "sell", + "instrument_name": "ETH-21SEP26-2600-C", + "ratio": 1 + }, + { + "price": 0.0032, + "direction": "buy", + "instrument_name": "ETH-21SEP26-2620-C", + "ratio": 1 + }, + { + "price": 0.0017, + "direction": "buy", + "instrument_name": "ETH-21SEP26-2640-C", + "ratio": 1 + } + ], + "combo_id": null, + "index_prices": { + "eth_usd": 2602.51 + } + }, + { + "id": 55555, + "timestamp": 1789908858693, + "amount": 60.0, + "direction": "buy", + "mark_price": 0.005304094, + "trades": [ + { + "price": 0.0057, + "amount": 60.0, + "direction": "buy" + } + ], + "legs": [ + { + "price": 0.0091, + "direction": "buy", + "instrument_name": "BTC-9OCT26-86000-C", + "ratio": 1 + }, + { + "price": 0.0034, + "direction": "sell", + "instrument_name": "BTC-9OCT26-90000-C", + "ratio": 1 + } + ], + "combo_id": "BTC-CS-9OCT26-86000_90000", + "index_prices": { + "btc_usd": 80415.41 + } + }, + { + "id": 55549, + "timestamp": 1789896450568, + "amount": 50.0, + "direction": "buy", + "mark_price": 0.005195826, + "trades": [ + { + "price": 0.0057, + "amount": 50.0, + "direction": "buy" + } + ], + "legs": [ + { + "price": 0.0093, + "direction": "buy", + "instrument_name": "BTC-9OCT26-86000-C", + "ratio": 1 + }, + { + "price": 0.0036, + "direction": "sell", + "instrument_name": "BTC-9OCT26-90000-C", + "ratio": 1 + } + ], + "combo_id": "BTC-CS-9OCT26-86000_90000", + "index_prices": { + "btc_usd": 80371.04 + } + }, + { + "id": 55548, + "timestamp": 1789896384838, + "amount": 100.0, + "direction": "buy", + "mark_price": 0.005186311, + "trades": [ + { + "price": 0.0056, + "amount": 100.0, + "direction": "buy" + } + ], + "legs": [ + { + "price": 0.009, + "direction": "buy", + "instrument_name": "BTC-9OCT26-86000-C", + "ratio": 1 + }, + { + "price": 0.0034, + "direction": "sell", + "instrument_name": "BTC-9OCT26-90000-C", + "ratio": 1 + } + ], + "combo_id": "BTC-CS-9OCT26-86000_90000", + "index_prices": { + "btc_usd": 80356.16 + } + }, + { + "id": 55547, + "timestamp": 1789896337667, + "amount": 100.0, + "direction": "buy", + "mark_price": 0.005164996, + "trades": [ + { + "price": 0.0056, + "amount": 100.0, + "direction": "buy" + } + ], + "legs": [ + { + "price": 0.0091, + "direction": "buy", + "instrument_name": "BTC-9OCT26-86000-C", + "ratio": 1 + }, + { + "price": 0.0035, + "direction": "sell", + "instrument_name": "BTC-9OCT26-90000-C", + "ratio": 1 + } + ], + "combo_id": "BTC-CS-9OCT26-86000_90000", + "index_prices": { + "btc_usd": 80340.78 + } + }, + { + "id": 55540, + "timestamp": 1789891560285, + "amount": 25.0, + "direction": "sell", + "mark_price": 0.030700065, + "trades": [ + { + "price": 0.0299, + "amount": 25.0, + "direction": "sell" + } + ], + "legs": [ + { + "price": 0.0175, + "direction": "buy", + "instrument_name": "BTC-25SEP26-80000-C", + "ratio": 1 + }, + { + "price": 0.0124, + "direction": "buy", + "instrument_name": "BTC-25SEP26-80000-P", + "ratio": 1 + } + ], + "combo_id": "BTC-STRD-25SEP26-80000", + "index_prices": { + "btc_usd": 80399.5 + } + }, + { + "id": 55539, + "timestamp": 1789890623307, + "amount": 12.5, + "direction": "buy", + "mark_price": -0.056176534, + "trades": [ + { + "price": -0.0549, + "amount": 12.5, + "direction": "buy" + } + ], + "legs": [ + { + "price": 0.0016, + "direction": "buy", + "instrument_name": "BTC-21SEP26-79500-P", + "ratio": 2 + }, + { + "price": 0.0053, + "direction": "sell", + "instrument_name": "BTC-25SEP26-83000-C", + "ratio": 2 + }, + { + "price": 0.0475, + "direction": "sell", + "instrument_name": "BTC-25SEP26-84000-P", + "ratio": 1 + } + ], + "combo_id": null, + "index_prices": { + "btc_usd": 80312.58 + } + }, + { + "id": 55536, + "timestamp": 1789886838687, + "amount": 20.0, + "direction": "sell", + "mark_price": 0.01431241, + "trades": [ + { + "price": 0.0136, + "amount": 20.0, + "direction": "sell" + } + ], + "legs": [ + { + "price": 0.3167, + "direction": "sell", + "instrument_name": "BTC-25SEP26-55000-C", + "ratio": 1 + }, + { + "price": 0.3303, + "direction": "buy", + "instrument_name": "BTC-25DEC26-55000-C", + "ratio": 1 + } + ], + "combo_id": "BTC-CCAL-25DEC26_25SEP26-55000", + "index_prices": { + "btc_usd": 80393.37 + } + }, + { + "id": 55537, + "timestamp": 1789886834420, + "amount": 30.0, + "direction": "sell", + "mark_price": 0.014293477, + "trades": [ + { + "price": 0.0136, + "amount": 30.0, + "direction": "sell" + } + ], + "legs": [ + { + "price": 0.3168, + "direction": "sell", + "instrument_name": "BTC-25SEP26-55000-C", + "ratio": 1 + }, + { + "price": 0.3304, + "direction": "buy", + "instrument_name": "BTC-25DEC26-55000-C", + "ratio": 1 + } + ], + "combo_id": "BTC-CCAL-25DEC26_25SEP26-55000", + "index_prices": { + "btc_usd": 80395.44 + } + } + ] +} diff --git a/openbb_platform/providers/deribit/tests/data/book_summary_future.json b/openbb_platform/providers/deribit/tests/data/book_summary_future.json new file mode 100644 index 00000000000..4c344ccaa7f --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/book_summary_future.json @@ -0,0 +1,251 @@ +[ + { + "high": 82220.0, + "low": 81012.5, + "last": 81407.5, + "instrument_name": "BTC-27NOV26", + "bid_price": 81587.5, + "ask_price": 81607.5, + "open_interest": 15846850, + "mark_price": 81599.51, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.4311, + "volume": 17.28326384, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 1407140.0, + "volume_notional": 1407140.0, + "mid_price": 81597.5 + }, + { + "high": 81625.0, + "low": 80435.0, + "last": 80835.0, + "instrument_name": "BTC-9OCT26", + "bid_price": 81017.5, + "ask_price": 81040.0, + "open_interest": 1456590, + "mark_price": 81025.68, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.3575, + "volume": 3.98398075, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 321910.0, + "volume_notional": 321910.0, + "mid_price": 81028.75 + }, + { + "high": 81895.0, + "low": 80630.0, + "last": 81132.5, + "instrument_name": "BTC-30OCT26", + "bid_price": 81275.0, + "ask_price": 81287.5, + "open_interest": 48225710, + "mark_price": 81280.79, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.4753, + "volume": 17.64207412, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 1428710.0, + "volume_notional": 1428710.0, + "mid_price": 81281.25 + }, + { + "high": 81445.0, + "low": 80145.0, + "last": 80930.0, + "instrument_name": "BTC-21SEP26", + "bid_price": 80810.0, + "ask_price": 80825.0, + "open_interest": 1047880, + "mark_price": 80815.99, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.1357, + "volume": 1.60535557, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 129670.0, + "volume_notional": 129670.0, + "mid_price": 80817.5 + }, + { + "high": 81542.5, + "low": 80362.5, + "last": 80775.0, + "instrument_name": "BTC-2OCT26", + "bid_price": 80950.0, + "ask_price": 80965.0, + "open_interest": 2466040, + "mark_price": 80957.16, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.502, + "volume": 6.13587707, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 495940.0, + "volume_notional": 495940.0, + "mid_price": 80957.5 + }, + { + "high": 84572.5, + "low": 83330.0, + "last": 83755.0, + "instrument_name": "BTC-25JUN27", + "bid_price": 83925.0, + "ask_price": 83970.0, + "open_interest": 20255740, + "mark_price": 83945.35, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.4457, + "volume": 12.61012996, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 1055470.0, + "volume_notional": 1055470.0, + "mid_price": 83947.5 + }, + { + "high": 81462.5, + "low": 80192.5, + "last": 80702.5, + "instrument_name": "BTC-22SEP26", + "bid_price": 80822.5, + "ask_price": 80825.0, + "open_interest": 950130, + "mark_price": 80831.9, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.4165, + "volume": 2.61038568, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 211330.0, + "volume_notional": 211330.0, + "mid_price": 80823.75 + }, + { + "high": 81497.5, + "low": 80149.5, + "last": 80832.5, + "instrument_name": "BTC-PERPETUAL", + "bid_price": 80836.5, + "ask_price": 80837.0, + "open_interest": 787799210, + "mark_price": 80831.57, + "creation_timestamp": 1789939036004, + "current_funding": 0.0, + "estimated_delivery_price": 80815.88, + "funding_8h": 1.583e-05, + "price_change": -0.2493, + "volume": 3099.91787897, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 250336360.0, + "volume_notional": 250336360.0, + "mid_price": 80836.75 + }, + { + "high": 83547.5, + "low": 82220.0, + "last": 82757.5, + "instrument_name": "BTC-26MAR27", + "bid_price": 82907.5, + "ask_price": 82960.0, + "open_interest": 72755930, + "mark_price": 82934.37, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.3432, + "volume": 8.74194039, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 722750.0, + "volume_notional": 722750.0, + "mid_price": 82933.75 + }, + { + "high": 81460.0, + "low": 80197.5, + "last": 80752.5, + "instrument_name": "BTC-23SEP26", + "bid_price": 80835.0, + "ask_price": 80855.0, + "open_interest": 680650, + "mark_price": 80845.52, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.3117, + "volume": 5.33023474, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 431280.0, + "volume_notional": 431280.0, + "mid_price": 80845.0 + }, + { + "high": 82587.5, + "low": 81300.0, + "last": 81780.0, + "instrument_name": "BTC-25DEC26", + "bid_price": 81932.5, + "ask_price": 81955.0, + "open_interest": 275741350, + "mark_price": 81945.21, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.4504, + "volume": 60.58913468, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 4975480.0, + "volume_notional": 4975480.0, + "mid_price": 81943.75 + }, + { + "high": 81525.0, + "low": 80197.5, + "last": 80775.0, + "instrument_name": "BTC-25SEP26", + "bid_price": 80872.5, + "ask_price": 80882.5, + "open_interest": 470803600, + "mark_price": 80875.33, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": -0.3208, + "volume": 88.95297416, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 7192670.0, + "volume_notional": 7192670.0, + "mid_price": 80877.5 + }, + { + "high": 81470.0, + "low": 80272.5, + "last": 80757.5, + "instrument_name": "BTC-24SEP26", + "bid_price": 80840.0, + "ask_price": 80875.0, + "open_interest": 6340, + "mark_price": 80858.26, + "creation_timestamp": 1789939036004, + "estimated_delivery_price": 80815.88, + "price_change": 0.4665, + "volume": 0.08452116, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 6840.0, + "volume_notional": 6840.0, + "mid_price": 80857.5 + } +] diff --git a/openbb_platform/providers/deribit/tests/data/book_summary_instrument.json b/openbb_platform/providers/deribit/tests/data/book_summary_instrument.json new file mode 100644 index 00000000000..115b37fdcd5 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/book_summary_instrument.json @@ -0,0 +1,23 @@ +[ + { + "high": 81497.5, + "low": 80149.5, + "last": 80832.5, + "instrument_name": "BTC-PERPETUAL", + "bid_price": 80836.5, + "ask_price": 80837.0, + "open_interest": 787799210, + "mark_price": 80831.54, + "creation_timestamp": 1789939037058, + "current_funding": 0.0, + "estimated_delivery_price": 80815.66, + "funding_8h": 1.583e-05, + "price_change": -0.2487, + "volume": 3100.02328212, + "base_currency": "BTC", + "quote_currency": "USD", + "volume_usd": 250344880.0, + "volume_notional": 250344880.0, + "mid_price": 80836.75 + } +] diff --git a/openbb_platform/providers/deribit/tests/data/combo_details.json b/openbb_platform/providers/deribit/tests/data/combo_details.json new file mode 100644 index 00000000000..8609410854d --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/combo_details.json @@ -0,0 +1,17 @@ +{ + "id": "BTC-STRG-30OCT26-66000_90000", + "state": "active", + "legs": [ + { + "amount": 1, + "instrument_name": "BTC-30OCT26-66000-P" + }, + { + "amount": 1, + "instrument_name": "BTC-30OCT26-90000-C" + } + ], + "instrument_id": 698294, + "creation_timestamp": 1789845798000, + "state_timestamp": 1789845798201 +} diff --git a/openbb_platform/providers/deribit/tests/data/combo_ids.json b/openbb_platform/providers/deribit/tests/data/combo_ids.json new file mode 100644 index 00000000000..188020b894f --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/combo_ids.json @@ -0,0 +1,202 @@ +[ + "BTC-BOX-25DEC26-30000_130000", + "BTC-BOX-25DEC26-35000_100000", + "BTC-BOX-25DEC26-35000_70000", + "BTC-BOX-25DEC26-40000_80000", + "BTC-BOX-25DEC26-45000_110000", + "BTC-BOX-25DEC26-50000_100000", + "BTC-BOX-25DEC26-55000_105000", + "BTC-BOX-25DEC26-56000_78000", + "BTC-BOX-25DEC26-56000_80000", + "BTC-BOX-25DEC26-60000_100000", + "BTC-BOX-25DEC26-60000_80000", + "BTC-BOX-25DEC26-60000_85000", + "BTC-BOX-25DEC26-60000_86000", + "BTC-BOX-25DEC26-60000_90000", + "BTC-BOX-25DEC26-65000_85000", + "BTC-BOX-25DEC26-70000_72000", + "BTC-BOX-25DEC26-80000_100000", + "BTC-BOX-25SEP26-160000_170000", + "BTC-BOX-25SEP26-30000_120000", + "BTC-BOX-25SEP26-40000_110000", + "BTC-BOX-25SEP26-40000_90000", + "BTC-BOX-25SEP26-50000_100000", + "BTC-BOX-25SEP26-50000_130000", + "BTC-BOX-25SEP26-50000_90000", + "BTC-BOX-25SEP26-56000_75000", + "BTC-BOX-25SEP26-60000_105000", + "BTC-BOX-25SEP26-60000_110000", + "BTC-BOX-25SEP26-60000_115000", + "BTC-BOX-25SEP26-60000_120000", + "BTC-BOX-25SEP26-60000_130000", + "BTC-BOX-25SEP26-60000_140000", + "BTC-BOX-25SEP26-60000_68000", + "BTC-BOX-25SEP26-60000_95000", + "BTC-BOX-25SEP26-70000_105000", + "BTC-BOX-25SEP26-70000_80000", + "BTC-BOX-25SEP26-80000_100000", + "BTC-BOX-25SEP26-80000_120000", + "BTC-BOX-25SEP26-80000_130000", + "BTC-BOX-25SEP26-85000_135000", + "BTC-BOX-25SEP26-85000_90000", + "BTC-BOX-25SEP26-90000_100000", + "BTC-BOX-26MAR27-50000_150000", + "BTC-CBUT-25DEC26-62000_72000_82000", + "BTC-CBUT-25DEC26-64000_74000_84000", + "BTC-CBUT-25DEC26-64000_75000_84000", + "BTC-CBUT-25DEC26-64000_76000_88000", + "BTC-CBUT-25DEC26-65000_75000_85000", + "BTC-CBUT-25DEC26-68000_70000_74000", + "BTC-CBUT-25DEC26-75000_125000_180000", + "BTC-CBUT-25JUN27-100000_125000_150000", + "BTC-CBUT-25JUN27-72000_74000_78000", + "BTC-CBUT-25JUN27-76000_80000_85000", + "BTC-CBUT-25SEP26-60000_62000_65000", + "BTC-CBUT-25SEP26-62000_75000_86000", + "BTC-CBUT-25SEP26-64000_75000_84000", + "BTC-CBUT-25SEP26-65000_70000_75000", + "BTC-CBUT-25SEP26-65000_75000_85000", + "BTC-CBUT-25SEP26-70000_75000_80000", + "BTC-CBUT-25SEP26-75000_130000_180000", + "BTC-CBUT-26MAR27-60000_80000_100000", + "BTC-CBUT-26MAR27-62000_72000_82000", + "BTC-CBUT-26MAR27-64000_74000_84000", + "BTC-CBUT-26MAR27-80000_90000_100000", + "BTC-CBUT-30OCT26-79000_81000_82000", + "BTC-CBUT111-25SEP26-125000_135000_145000", + "BTC-CBUT111-26MAR27-100000_115000_130000", + "BTC-CBUT111-26MAR27-60000_80000_100000", + "BTC-CCAL-22SEP26_21SEP26-82000", + "BTC-CCAL-25DEC26_25SEP26-140000", + "BTC-CCAL-25DEC26_25SEP26-45000", + "BTC-CCAL-25DEC26_25SEP26-55000", + "BTC-CCAL-25DEC26_25SEP26-66000", + "BTC-CCAL-25DEC26_25SEP26-72000", + "BTC-CCAL-25DEC26_25SEP26-75000", + "BTC-CCAL-25DEC26_25SEP26-80000", + "BTC-CCAL-25DEC26_25SEP26-90000", + "BTC-CCAL-25DEC26_25SEP26-92000", + "BTC-CCAL-25JUN27_25SEP26-115000", + "BTC-CCAL-25JUN27_25SEP26-60000", + "BTC-CCAL-25JUN27_26MAR27-100000", + "BTC-CCAL-25JUN27_26MAR27-70000", + "BTC-CCAL-26MAR27_25DEC26-100000", + "BTC-CCAL-26MAR27_25DEC26-140000", + "BTC-CCAL-26MAR27_25DEC26-70000", + "BTC-CCAL-26MAR27_25DEC26-90000", + "BTC-CCAL-26MAR27_25DEC26-94000", + "BTC-CCAL-26MAR27_25DEC26-95000", + "BTC-CCAL-26MAR27_25SEP26-100000", + "BTC-CCAL-26MAR27_25SEP26-130000", + "BTC-CCAL-26MAR27_25SEP26-70000", + "BTC-CCAL-2OCT26_25SEP26-85000", + "BTC-CCAL-30OCT26_25SEP26-70000", + "BTC-CCAL-30OCT26_25SEP26-78000", + "BTC-CCAL-30OCT26_25SEP26-85000", + "BTC-CCAL-30OCT26_25SEP26-86000", + "BTC-CCAL-30OCT26_25SEP26-90000", + "BTC-CCAL-30OCT26_9OCT26-85000", + "BTC-CCAL-9OCT26_2OCT26-85000", + "BTC-CCOND-25DEC26-68000_72000_75000_78000", + "BTC-CCOND-25SEP26-105000_110000_115000_120000", + "BTC-CCOND-25SEP26-75000_78000_83000_86000", + "BTC-CCOND-25SEP26-77000_79000_84000_110000", + "BTC-CCOND-25SEP26-85000_90000_94000_96000", + "BTC-CDIAG-25DEC26_25SEP26-100000_125000", + "BTC-CDIAG-25DEC26_25SEP26-100000_86000", + "BTC-CDIAG-25DEC26_25SEP26-100000_88000", + "BTC-CDIAG-25DEC26_25SEP26-120000_105000", + "BTC-CDIAG-25DEC26_25SEP26-125000_95000", + "BTC-CDIAG-25DEC26_25SEP26-130000_125000", + "BTC-CDIAG-25DEC26_25SEP26-170000_150000", + "BTC-CDIAG-25DEC26_25SEP26-45000_66000", + "BTC-CDIAG-25DEC26_25SEP26-50000_58000", + "BTC-CDIAG-25DEC26_25SEP26-50000_66000", + "BTC-CDIAG-25DEC26_25SEP26-70000_85000", + "BTC-CDIAG-25DEC26_25SEP26-80000_75000", + "BTC-CDIAG-25DEC26_25SEP26-82000_72000", + "BTC-CDIAG-25DEC26_25SEP26-82000_76000", + "BTC-CDIAG-25DEC26_25SEP26-82000_78000", + "BTC-CDIAG-25DEC26_25SEP26-90000_76000", + "BTC-CDIAG-25DEC26_25SEP26-90000_78000", + "BTC-CDIAG-25DEC26_25SEP26-90000_79000", + "BTC-CDIAG-25DEC26_25SEP26-90000_80000", + "BTC-CDIAG-25DEC26_25SEP26-90000_81000", + "BTC-CDIAG-25DEC26_25SEP26-95000_86000", + "BTC-CDIAG-25DEC26_25SEP26-98000_86000", + "BTC-CDIAG-25DEC26_25SEP26-98000_88000", + "BTC-CDIAG-25DEC26_27NOV26-84000_81000", + "BTC-CDIAG-25DEC26_27NOV26-90000_85000", + "BTC-CDIAG-25DEC26_30OCT26-105000_96000", + "BTC-CDIAG-25DEC26_30OCT26-106000_86000", + "BTC-CDIAG-25DEC26_30OCT26-108000_86000", + "BTC-CDIAG-25DEC26_30OCT26-74000_78000", + "BTC-CDIAG-25DEC26_30OCT26-76000_72000", + "BTC-CDIAG-25DEC26_30OCT26-76000_73000", + "BTC-CDIAG-25DEC26_30OCT26-78000_73000", + "BTC-CDIAG-25DEC26_30OCT26-80000_74000", + "BTC-CDIAG-25DEC26_30OCT26-80000_75000", + "BTC-CDIAG-25DEC26_30OCT26-82000_75000", + "BTC-CDIAG-25DEC26_30OCT26-82000_76000", + "BTC-CDIAG-25DEC26_30OCT26-82000_78000", + "BTC-CDIAG-25DEC26_30OCT26-84000_76000", + "BTC-CDIAG-25DEC26_30OCT26-86000_75000", + "BTC-CDIAG-25JUN27_25DEC26-96000_80000", + "BTC-CDIAG-25JUN27_25DEC26-98000_82000", + "BTC-CDIAG-25JUN27_25SEP26-100000_68000", + "BTC-CDIAG-25JUN27_26MAR27-88000_82000", + "BTC-CDIAG-25JUN27_30OCT26-110000_81000", + "BTC-CDIAG-26MAR27_25DEC26-110000_84000", + "BTC-CDIAG-26MAR27_25DEC26-110000_85000", + "BTC-CDIAG-26MAR27_25DEC26-110000_88000", + "BTC-CDIAG-26MAR27_25DEC26-130000_140000", + "BTC-CDIAG-26MAR27_25DEC26-130000_85000", + "BTC-CDIAG-26MAR27_25DEC26-130000_86000", + "BTC-CDIAG-26MAR27_25DEC26-130000_88000", + "BTC-CDIAG-26MAR27_25DEC26-130000_90000", + "BTC-CDIAG-26MAR27_25DEC26-130000_92000", + "BTC-CDIAG-26MAR27_25DEC26-50000_68000", + "BTC-CDIAG-26MAR27_25DEC26-80000_72000", + "BTC-CDIAG-26MAR27_25DEC26-80000_75000", + "BTC-CDIAG-26MAR27_25DEC26-82000_75000", + "BTC-CDIAG-26MAR27_25DEC26-84000_78000", + "BTC-CDIAG-26MAR27_25DEC26-85000_75000", + "BTC-CDIAG-26MAR27_25DEC26-85000_80000", + "BTC-CDIAG-26MAR27_25DEC26-86000_80000", + "BTC-CDIAG-26MAR27_25DEC26-88000_80000", + "BTC-CDIAG-26MAR27_25DEC26-90000_82000", + "BTC-CDIAG-26MAR27_25SEP26-125000_78000", + "BTC-CDIAG-26MAR27_25SEP26-130000_76000", + "BTC-CDIAG-26MAR27_25SEP26-35000_56000", + "BTC-CDIAG-26MAR27_25SEP26-58000_72000", + "BTC-CDIAG-26MAR27_30OCT26-58000_75000", + "BTC-CDIAG-26MAR27_30OCT26-82000_71000", + "BTC-CDIAG-26MAR27_30OCT26-82000_75000", + "BTC-CDIAG-26MAR27_30OCT26-88000_75000", + "BTC-CDIAG-27NOV26_25SEP26-74000_70000", + "BTC-CDIAG-27NOV26_25SEP26-75000_70000", + "BTC-CDIAG-27NOV26_25SEP26-76000_72000", + "BTC-CDIAG-27NOV26_25SEP26-77000_72000", + "BTC-CDIAG-27NOV26_25SEP26-78000_74000", + "BTC-CDIAG-27NOV26_25SEP26-80000_74000", + "BTC-CDIAG-27NOV26_25SEP26-81000_75000", + "BTC-CDIAG-27NOV26_25SEP26-82000_76000", + "BTC-CDIAG-27NOV26_25SEP26-83000_76000", + "BTC-CDIAG-27NOV26_30OCT26-100000_88000", + "BTC-CDIAG-27NOV26_30OCT26-72000_71000", + "BTC-CDIAG-27NOV26_30OCT26-73000_72000", + "BTC-CDIAG-27NOV26_30OCT26-74000_72000", + "BTC-CDIAG-27NOV26_30OCT26-82000_80000", + "BTC-CDIAG-27NOV26_30OCT26-89000_84000", + "BTC-CDIAG-27NOV26_30OCT26-93000_96000", + "BTC-CDIAG-27NOV26_30OCT26-94000_96000", + "BTC-CDIAG-2OCT26_21SEP26-79000_78000", + "BTC-CDIAG-2OCT26_25SEP26-74000_75000", + "BTC-CDIAG-30OCT26_25SEP26-100000_95000", + "BTC-CDIAG-30OCT26_25SEP26-71000_68000", + "BTC-CDIAG-30OCT26_25SEP26-73000_70000", + "BTC-CDIAG-30OCT26_25SEP26-74000_72000", + "BTC-CDIAG-30OCT26_25SEP26-75000_70000", + "BTC-CDIAG-30OCT26_25SEP26-75000_72000", + "BTC-CDIAG-30OCT26_25SEP26-76000_72000" +] diff --git a/openbb_platform/providers/deribit/tests/data/combos.json b/openbb_platform/providers/deribit/tests/data/combos.json new file mode 100644 index 00000000000..75238f9e050 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/combos.json @@ -0,0 +1,342 @@ +[ + { + "id": "BTC-CCAL-22SEP26_21SEP26-82000", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-21SEP26-82000-C" + }, + { + "amount": 1, + "instrument_name": "BTC-22SEP26-82000-C" + } + ], + "instrument_id": 698874, + "creation_timestamp": 1789903657000, + "state_timestamp": 1789903657878 + }, + { + "id": "BTC-CCAL-25DEC26_25SEP26-55000", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-25SEP26-55000-C" + }, + { + "amount": 1, + "instrument_name": "BTC-25DEC26-55000-C" + } + ], + "instrument_id": 698013, + "creation_timestamp": 1789804851000, + "state_timestamp": 1789886736196 + }, + { + "id": "BTC-CCAL-30OCT26_9OCT26-85000", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-9OCT26-85000-C" + }, + { + "amount": 1, + "instrument_name": "BTC-30OCT26-85000-C" + } + ], + "instrument_id": 699021, + "creation_timestamp": 1789935402000, + "state_timestamp": 1789935402766 + }, + { + "id": "BTC-CS-25SEP26-70000_81000", + "state": "active", + "legs": [ + { + "amount": 1, + "instrument_name": "BTC-25SEP26-70000-C" + }, + { + "amount": -1, + "instrument_name": "BTC-25SEP26-81000-C" + } + ], + "instrument_id": 698867, + "creation_timestamp": 1789899898000, + "state_timestamp": 1789899898697 + }, + { + "id": "BTC-CS-25SEP26-80000_85000", + "state": "active", + "legs": [ + { + "amount": 1, + "instrument_name": "BTC-25SEP26-80000-C" + }, + { + "amount": -1, + "instrument_name": "BTC-25SEP26-85000-C" + } + ], + "instrument_id": 698859, + "creation_timestamp": 1789892310000, + "state_timestamp": 1789892310502 + }, + { + "id": "BTC-CS-30OCT26-85000_95000", + "state": "active", + "legs": [ + { + "amount": 1, + "instrument_name": "BTC-30OCT26-85000-C" + }, + { + "amount": -1, + "instrument_name": "BTC-30OCT26-95000-C" + } + ], + "instrument_id": 686334, + "creation_timestamp": 1788236302000, + "state_timestamp": 1789918883563 + }, + { + "id": "BTC-CS-30OCT26-90000_96000", + "state": "active", + "legs": [ + { + "amount": 1, + "instrument_name": "BTC-30OCT26-90000-C" + }, + { + "amount": -1, + "instrument_name": "BTC-30OCT26-96000-C" + } + ], + "instrument_id": 681583, + "creation_timestamp": 1787577101000, + "state_timestamp": 1789930557812 + }, + { + "id": "BTC-FS-21SEP26_PERP", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-PERPETUAL" + }, + { + "amount": 1, + "instrument_name": "BTC-21SEP26" + } + ], + "instrument_id": 696453, + "creation_timestamp": 1789632397000, + "state_timestamp": 1789632397153 + }, + { + "id": "BTC-FS-22SEP26_21SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-21SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-22SEP26" + } + ], + "instrument_id": 697148, + "creation_timestamp": 1789718801000, + "state_timestamp": 1789718801472 + }, + { + "id": "BTC-FS-22SEP26_PERP", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-PERPETUAL" + }, + { + "amount": 1, + "instrument_name": "BTC-22SEP26" + } + ], + "instrument_id": 697145, + "creation_timestamp": 1789718797000, + "state_timestamp": 1789718797046 + }, + { + "id": "BTC-FS-23SEP26_21SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-21SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-23SEP26" + } + ], + "instrument_id": 698119, + "creation_timestamp": 1789805208000, + "state_timestamp": 1789805208337 + }, + { + "id": "BTC-FS-23SEP26_22SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-22SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-23SEP26" + } + ], + "instrument_id": 698120, + "creation_timestamp": 1789805208000, + "state_timestamp": 1789805208708 + }, + { + "id": "BTC-FS-23SEP26_PERP", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-PERPETUAL" + }, + { + "amount": 1, + "instrument_name": "BTC-23SEP26" + } + ], + "instrument_id": 698117, + "creation_timestamp": 1789805200000, + "state_timestamp": 1789805200468 + }, + { + "id": "BTC-FS-24SEP26_21SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-21SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-24SEP26" + } + ], + "instrument_id": 698832, + "creation_timestamp": 1789891599000, + "state_timestamp": 1789891599976 + }, + { + "id": "BTC-FS-24SEP26_22SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-22SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-24SEP26" + } + ], + "instrument_id": 698833, + "creation_timestamp": 1789891600000, + "state_timestamp": 1789891600277 + }, + { + "id": "BTC-FS-24SEP26_23SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-23SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-24SEP26" + } + ], + "instrument_id": 698834, + "creation_timestamp": 1789891600000, + "state_timestamp": 1789891600576 + }, + { + "id": "BTC-FS-24SEP26_PERP", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-PERPETUAL" + }, + { + "amount": 1, + "instrument_name": "BTC-24SEP26" + } + ], + "instrument_id": 698831, + "creation_timestamp": 1789891597000, + "state_timestamp": 1789891597281 + }, + { + "id": "BTC-FS-25DEC26_21SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-21SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-25DEC26" + } + ], + "instrument_id": 696466, + "creation_timestamp": 1789632402000, + "state_timestamp": 1789632402806 + }, + { + "id": "BTC-FS-25DEC26_22SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-22SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-25DEC26" + } + ], + "instrument_id": 697154, + "creation_timestamp": 1789718803000, + "state_timestamp": 1789718803121 + }, + { + "id": "BTC-FS-25DEC26_23SEP26", + "state": "active", + "legs": [ + { + "amount": -1, + "instrument_name": "BTC-23SEP26" + }, + { + "amount": 1, + "instrument_name": "BTC-25DEC26" + } + ], + "instrument_id": 698126, + "creation_timestamp": 1789805212000, + "state_timestamp": 1789805212770 + } +] diff --git a/openbb_platform/providers/deribit/tests/data/contract_size.json b/openbb_platform/providers/deribit/tests/data/contract_size.json new file mode 100644 index 00000000000..b9749a7ed96 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/contract_size.json @@ -0,0 +1,3 @@ +{ + "contract_size": 10.0 +} diff --git a/openbb_platform/providers/deribit/tests/data/currencies.json b/openbb_platform/providers/deribit/tests/data/currencies.json new file mode 100644 index 00000000000..68cc5be1659 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/currencies.json @@ -0,0 +1,162 @@ +[ + { + "decimals": 6, + "currency": "USDT", + "apr": 0.0, + "min_withdrawal_fee": 2.0, + "withdrawal_fee": 2.0, + "coin_type": "USDT", + "withdrawal_priorities": [], + "coinbase_networks": [ + { + "display_name": "Ethereum", + "resource_name": "networks/ethereum-mainnet" + } + ], + "network_fee": 3.69791145e-06, + "network_currency": "ETH", + "min_confirmations": 1, + "currency_long": "Tether", + "in_cross_collateral_pool": true, + "onchain_operations_precision": 6, + "currency_uuid": "b26327c1-9a34-51d9-b982-9b29e6012648" + }, + { + "decimals": 6, + "currency": "USDE", + "apr": 4.75, + "min_withdrawal_fee": 0.0001, + "withdrawal_fee": 0.007617, + "coin_type": "USDE", + "withdrawal_priorities": [], + "network_fee": 2.9071511e-06, + "network_currency": "ETH", + "min_confirmations": 1, + "currency_long": "Ethena USDe", + "in_cross_collateral_pool": true, + "onchain_operations_precision": 6, + "currency_uuid": "e98ecced-8122-45a8-8bce-b842323af19d" + }, + { + "decimals": 8, + "currency": "USDC", + "apr": 3.3999999999999995, + "min_withdrawal_fee": 5.0, + "withdrawal_fee": 5.0, + "coin_type": "USDC", + "withdrawal_priorities": [], + "coinbase_networks": [ + { + "display_name": "Arbitrum", + "resource_name": "networks/arbitrum-mainnet" + }, + { + "display_name": "Avalanche C-Chain", + "resource_name": "networks/avacchain-mainnet" + }, + { + "display_name": "Base", + "resource_name": "networks/base-mainnet" + }, + { + "display_name": "Ethereum", + "resource_name": "networks/ethereum-mainnet" + }, + { + "display_name": "Optimism", + "resource_name": "networks/optimism-mainnet" + }, + { + "display_name": "Polygon", + "resource_name": "networks/polygon-mainnet" + }, + { + "display_name": "Solana", + "resource_name": "networks/solana-mainnet" + } + ], + "network_fee": 3.0608777e-06, + "network_currency": "ETH", + "min_confirmations": 50, + "currency_long": "USD Coin", + "in_cross_collateral_pool": true, + "onchain_operations_precision": 6, + "currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "decimals": 6, + "currency": "STETH", + "apr": 2.251857142857143, + "min_withdrawal_fee": 0.0001, + "withdrawal_fee": 0.0001, + "coin_type": "STETH", + "withdrawal_priorities": [], + "network_fee": 2.9071511e-06, + "network_currency": "ETH", + "min_confirmations": 1, + "currency_long": "Staked ETH", + "in_cross_collateral_pool": true, + "onchain_operations_precision": 6, + "currency_uuid": "997e0978-a0a0-502d-a51b-9ae33bb54a49" + }, + { + "decimals": 6, + "currency": "EURR", + "apr": 0.0, + "min_withdrawal_fee": 0.0001, + "withdrawal_fee": 0.006177, + "coin_type": "EURR", + "withdrawal_priorities": [], + "network_fee": 2.71280495e-06, + "network_currency": "ETH", + "min_confirmations": 1, + "currency_long": "StablR Euro", + "in_cross_collateral_pool": false, + "onchain_operations_precision": 6, + "currency_uuid": "20d8642e-36ad-4f92-becf-a68b2b0d05ef" + }, + { + "decimals": 6, + "currency": "ETH", + "apr": 0.0, + "min_withdrawal_fee": 0.001, + "withdrawal_fee": 0.001, + "coin_type": "ETH", + "withdrawal_priorities": [], + "coinbase_networks": [ + { + "display_name": "Ethereum", + "resource_name": "networks/ethereum-mainnet" + } + ], + "network_fee": 1.7949723900000002e-06, + "network_currency": "ETH", + "min_confirmations": 50, + "currency_long": "Ethereum", + "in_cross_collateral_pool": true, + "onchain_operations_precision": 6, + "currency_uuid": "d85dce9b-5b73-5c3c-8978-522ce1d1c1b4" + }, + { + "decimals": 8, + "currency": "BTC", + "apr": 0.0, + "min_withdrawal_fee": 1e-05, + "withdrawal_fee": 1e-05, + "coin_type": "BTC", + "withdrawal_priorities": [], + "coinbase_networks": [ + { + "display_name": "Bitcoin", + "resource_name": "networks/bitcoin-mainnet" + } + ], + "network_fee": 3.072e-06, + "network_currency": "BTC", + "min_confirmations": 1, + "currency_long": "Bitcoin", + "in_cross_collateral_pool": true, + "onchain_operations_precision": 8, + "currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5" + } +] diff --git a/openbb_platform/providers/deribit/tests/data/delivery_prices.json b/openbb_platform/providers/deribit/tests/data/delivery_prices.json new file mode 100644 index 00000000000..629787a5f26 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/delivery_prices.json @@ -0,0 +1,25 @@ +{ + "data": [ + { + "date": "2026-09-20", + "delivery_price": 80321.21 + }, + { + "date": "2026-09-19", + "delivery_price": 81053.19 + }, + { + "date": "2026-09-18", + "delivery_price": 77720.28 + }, + { + "date": "2026-09-17", + "delivery_price": 76422.2 + }, + { + "date": "2026-09-16", + "delivery_price": 75764.23 + } + ], + "records_total": 2614 +} diff --git a/openbb_platform/providers/deribit/tests/data/expirations_flat.json b/openbb_platform/providers/deribit/tests/data/expirations_flat.json new file mode 100644 index 00000000000..f4fa5d3295d --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/expirations_flat.json @@ -0,0 +1,32 @@ +{ + "option": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "2OCT26", + "9OCT26", + "30OCT26", + "27NOV26", + "25DEC26", + "26MAR27", + "25JUN27" + ], + "future": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "30SEP26", + "2OCT26", + "9OCT26", + "30OCT26", + "27NOV26", + "25DEC26", + "26MAR27", + "25JUN27", + "PERPETUAL" + ] +} diff --git a/openbb_platform/providers/deribit/tests/data/expirations_grouped.json b/openbb_platform/providers/deribit/tests/data/expirations_grouped.json new file mode 100644 index 00000000000..f718995f145 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/expirations_grouped.json @@ -0,0 +1,280 @@ +{ + "steth": { + "option": [], + "future": [] + }, + "pepe": { + "option": [], + "future": [] + }, + "ltc": { + "option": [], + "future": [] + }, + "usyc": { + "option": [], + "future": [] + }, + "doge": { + "option": [], + "future": [] + }, + "dot": { + "option": [], + "future": [] + }, + "usdc": { + "option": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "2OCT26", + "30OCT26", + "25DEC26" + ], + "future": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "30SEP26", + "2OCT26", + "30OCT26", + "25DEC26", + "PERPETUAL" + ] + }, + "zora": { + "option": [], + "future": [] + }, + "xlm": { + "option": [], + "future": [] + }, + "ondo": { + "option": [], + "future": [] + }, + "bnb": { + "option": [], + "future": [] + }, + "jasmy": { + "option": [], + "future": [] + }, + "fil": { + "option": [], + "future": [] + }, + "eurr": { + "option": [], + "future": [] + }, + "uni": { + "option": [], + "future": [] + }, + "atom": { + "option": [], + "future": [] + }, + "arb": { + "option": [], + "future": [] + }, + "bonk": { + "option": [], + "future": [] + }, + "link": { + "option": [], + "future": [] + }, + "xrp": { + "option": [], + "future": [] + }, + "vet": { + "option": [], + "future": [] + }, + "avax": { + "option": [], + "future": [] + }, + "bch": { + "option": [], + "future": [] + }, + "pump": { + "option": [], + "future": [] + }, + "usde": { + "option": [], + "future": [] + }, + "paxg": { + "option": [], + "future": [] + }, + "op": { + "option": [], + "future": [] + }, + "sui": { + "option": [], + "future": [] + }, + "sol": { + "option": [], + "future": [] + }, + "eth": { + "option": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "2OCT26", + "9OCT26", + "30OCT26", + "27NOV26", + "25DEC26", + "26MAR27", + "25JUN27" + ], + "future": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "2OCT26", + "9OCT26", + "30OCT26", + "27NOV26", + "25DEC26", + "26MAR27", + "25JUN27", + "PERPETUAL" + ] + }, + "aero": { + "option": [], + "future": [] + }, + "hype": { + "option": [], + "future": [] + }, + "pengu": { + "option": [], + "future": [] + }, + "tao": { + "option": [], + "future": [] + }, + "buidl": { + "option": [], + "future": [] + }, + "hbar": { + "option": [], + "future": [] + }, + "usdt": { + "option": [], + "future": [] + }, + "matic": { + "option": [], + "future": [] + }, + "fartcoin": { + "option": [], + "future": [] + }, + "cbeth": { + "option": [], + "future": [] + }, + "aave": { + "option": [], + "future": [] + }, + "algo": { + "option": [], + "future": [] + }, + "shib": { + "option": [], + "future": [] + }, + "inj": { + "option": [], + "future": [] + }, + "ada": { + "option": [], + "future": [] + }, + "near": { + "option": [], + "future": [] + }, + "btc": { + "option": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "2OCT26", + "9OCT26", + "30OCT26", + "27NOV26", + "25DEC26", + "26MAR27", + "25JUN27" + ], + "future": [ + "21SEP26", + "22SEP26", + "23SEP26", + "24SEP26", + "25SEP26", + "2OCT26", + "9OCT26", + "30OCT26", + "27NOV26", + "25DEC26", + "26MAR27", + "25JUN27", + "PERPETUAL" + ] + }, + "eurc": { + "option": [], + "future": [] + }, + "apt": { + "option": [], + "future": [] + }, + "icp": { + "option": [], + "future": [] + }, + "crv": { + "option": [], + "future": [] + } +} diff --git a/openbb_platform/providers/deribit/tests/data/funding_chart.json b/openbb_platform/providers/deribit/tests/data/funding_chart.json new file mode 100644 index 00000000000..513c883ed73 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/funding_chart.json @@ -0,0 +1,306 @@ +{ + "data": [ + { + "timestamp": 1789910220000, + "index_price": 80478.71, + "interest_8h": 3.41745351036981e-05 + }, + { + "timestamp": 1789910280000, + "index_price": 80461.03, + "interest_8h": 5.499236711247856e-05 + }, + { + "timestamp": 1789910340000, + "index_price": 80497.56, + "interest_8h": 4.976063870761619e-05 + }, + { + "timestamp": 1789910400000, + "index_price": 80470.1, + "interest_8h": 3.656606615373497e-05 + }, + { + "timestamp": 1789910460000, + "index_price": 80491.32, + "interest_8h": 6.158639217231364e-05 + }, + { + "timestamp": 1789910520000, + "index_price": 80490.9, + "interest_8h": 9.115657794864267e-05 + }, + { + "timestamp": 1789910580000, + "index_price": 80502.97, + "interest_8h": 4.7504551695385744e-05 + }, + { + "timestamp": 1789910640000, + "index_price": 80526.04, + "interest_8h": 4.257616542440465e-05 + }, + { + "timestamp": 1789910700000, + "index_price": 80516.17, + "interest_8h": 1.988367678189268e-05 + }, + { + "timestamp": 1789910760000, + "index_price": 80535.28, + "interest_8h": 6.017462160677812e-05 + }, + { + "timestamp": 1789910820000, + "index_price": 80503.64, + "interest_8h": 2.5888146175830073e-05 + }, + { + "timestamp": 1789910880000, + "index_price": 80499.48, + "interest_8h": 6.896069390852815e-06 + }, + { + "timestamp": 1789910940000, + "index_price": 80480.81, + "interest_8h": 1.1157628757265574e-06 + }, + { + "timestamp": 1789911000000, + "index_price": 80480.89, + "interest_8h": 1.3640194584817102e-06 + }, + { + "timestamp": 1789911060000, + "index_price": 80477.35, + "interest_8h": 5.3513504108716075e-06 + }, + { + "timestamp": 1789911120000, + "index_price": 80475.24, + "interest_8h": 6.103616466388657e-06 + }, + { + "timestamp": 1789911180000, + "index_price": 80445.49, + "interest_8h": 0.0 + }, + { + "timestamp": 1789911240000, + "index_price": 80402.98, + "interest_8h": 0.0 + }, + { + "timestamp": 1789911300000, + "index_price": 80440.61, + "interest_8h": 0.0 + }, + { + "timestamp": 1789911360000, + "index_price": 80433.97, + "interest_8h": 2.712171859717478e-05 + }, + { + "timestamp": 1789911420000, + "index_price": 80424.68, + "interest_8h": 4.150840264485127e-06 + }, + { + "timestamp": 1789911480000, + "index_price": 80428.84, + "interest_8h": 4.9270759095953434e-05 + }, + { + "timestamp": 1789911540000, + "index_price": 80453.16, + "interest_8h": 2.9665832889596872e-05 + }, + { + "timestamp": 1789911600000, + "index_price": 80444.94, + "interest_8h": 1.4404448558281916e-05 + }, + { + "timestamp": 1789911660000, + "index_price": 80442.65, + "interest_8h": 0.0 + }, + { + "timestamp": 1789911720000, + "index_price": 80472.27, + "interest_8h": 3.568847380583005e-05 + }, + { + "timestamp": 1789911780000, + "index_price": 80505.23, + "interest_8h": 6.202941721935202e-05 + }, + { + "timestamp": 1789911840000, + "index_price": 80535.21, + "interest_8h": 7.097761960254734e-05 + }, + { + "timestamp": 1789911900000, + "index_price": 80542.03, + "interest_8h": 7.2569874387467355e-06 + }, + { + "timestamp": 1789911960000, + "index_price": 80519.08, + "interest_8h": 0.0 + }, + { + "timestamp": 1789912020000, + "index_price": 80511.51, + "interest_8h": 4.0009496780229505e-06 + }, + { + "timestamp": 1789912080000, + "index_price": 80511.82, + "interest_8h": 0.0 + }, + { + "timestamp": 1789912140000, + "index_price": 80521.37, + "interest_8h": 2.085977300184626e-05 + }, + { + "timestamp": 1789912200000, + "index_price": 80511.36, + "interest_8h": 4.026959673759505e-05 + }, + { + "timestamp": 1789912260000, + "index_price": 80512.72, + "interest_8h": 2.001944537465927e-05 + }, + { + "timestamp": 1789912320000, + "index_price": 80554.59, + "interest_8h": 8.629368605806955e-05 + }, + { + "timestamp": 1789912380000, + "index_price": 80535.97, + "interest_8h": 4.6513470937329694e-05 + }, + { + "timestamp": 1789912440000, + "index_price": 80531.07, + "interest_8h": 9.073805302708352e-05 + }, + { + "timestamp": 1789912500000, + "index_price": 80539.43, + "interest_8h": 4.823901162454312e-05 + }, + { + "timestamp": 1789912560000, + "index_price": 80526.07, + "interest_8h": 4.816430877596233e-05 + }, + { + "timestamp": 1789912620000, + "index_price": 80525.6, + "interest_8h": 5.139483592782319e-05 + }, + { + "timestamp": 1789912680000, + "index_price": 80528.09, + "interest_8h": 3.2261754873491686e-05 + }, + { + "timestamp": 1789912740000, + "index_price": 80538.33, + "interest_8h": 4.600812433032208e-05 + }, + { + "timestamp": 1789912800000, + "index_price": 80553.72, + "interest_8h": 7.773160569113602e-05 + }, + { + "timestamp": 1789912860000, + "index_price": 80608.02, + "interest_8h": 5.840603701716753e-05 + }, + { + "timestamp": 1789912920000, + "index_price": 80671.08, + "interest_8h": 8.444451220928148e-05 + }, + { + "timestamp": 1789912980000, + "index_price": 80750.82, + "interest_8h": 0.00010405213222598241 + }, + { + "timestamp": 1789913040000, + "index_price": 80686.09, + "interest_8h": 6.28172402455645e-05 + }, + { + "timestamp": 1789913100000, + "index_price": 80633.77, + "interest_8h": 5.7067373880630395e-05 + }, + { + "timestamp": 1789913160000, + "index_price": 80650.99, + "interest_8h": 9.457109577940204e-05 + }, + { + "timestamp": 1789913220000, + "index_price": 80649.47, + "interest_8h": 9.122976877596435e-05 + }, + { + "timestamp": 1789913280000, + "index_price": 80640.07, + "interest_8h": 8.730129450528856e-05 + }, + { + "timestamp": 1789913340000, + "index_price": 80627.14, + "interest_8h": 0.00012493578465013052 + }, + { + "timestamp": 1789913400000, + "index_price": 80606.03, + "interest_8h": 0.0001334700704153708 + }, + { + "timestamp": 1789913460000, + "index_price": 80585.89, + "interest_8h": 8.752807098112448e-05 + }, + { + "timestamp": 1789913520000, + "index_price": 80576.14, + "interest_8h": 7.093867986230891e-05 + }, + { + "timestamp": 1789913580000, + "index_price": 80555.73, + "interest_8h": 8.641306459522893e-05 + }, + { + "timestamp": 1789913640000, + "index_price": 80563.01, + "interest_8h": 7.322526181690768e-05 + }, + { + "timestamp": 1789913700000, + "index_price": 80515.67, + "interest_8h": 6.770213177138991e-05 + }, + { + "timestamp": 1789913760000, + "index_price": 80510.3, + "interest_8h": 8.5236609477181e-05 + } + ], + "interest_8h": 1.58883550430331e-05, + "current_interest": 0.0 +} diff --git a/openbb_platform/providers/deribit/tests/data/funding_rate_history.json b/openbb_platform/providers/deribit/tests/data/funding_rate_history.json new file mode 100644 index 00000000000..e2dcc1c51ff --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/funding_rate_history.json @@ -0,0 +1,170 @@ +[ + { + "timestamp": 1789855200000, + "index_price": 81079.79, + "interest_8h": 0.00020037536421028146, + "interest_1h": 2.517506579644087e-05, + "prev_index_price": 81134.69 + }, + { + "timestamp": 1789858800000, + "index_price": 81237.22, + "interest_8h": 0.0002110867228833884, + "interest_1h": 2.348022400972763e-05, + "prev_index_price": 81079.79 + }, + { + "timestamp": 1789862400000, + "index_price": 81241.25, + "interest_8h": 0.00020390510396112773, + "interest_1h": 1.5820572974398556e-05, + "prev_index_price": 81237.22 + }, + { + "timestamp": 1789866000000, + "index_price": 81280.55, + "interest_8h": 0.0001866800598579058, + "interest_1h": 1.168079644171095e-05, + "prev_index_price": 81241.25 + }, + { + "timestamp": 1789869600000, + "index_price": 81078.04, + "interest_8h": 0.00017209580603703834, + "interest_1h": 1.6878746070219188e-05, + "prev_index_price": 81280.55 + }, + { + "timestamp": 1789873200000, + "index_price": 80301.73, + "interest_8h": 0.00016377878201952157, + "interest_1h": 1.618723881084165e-05, + "prev_index_price": 81078.04 + }, + { + "timestamp": 1789876800000, + "index_price": 80466.11, + "interest_8h": 0.00015410304928169024, + "interest_1h": 1.6038603796340042e-05, + "prev_index_price": 80301.73 + }, + { + "timestamp": 1789880400000, + "index_price": 80406.86, + "interest_8h": 0.00013640125860698012, + "interest_1h": 1.0921269949361024e-05, + "prev_index_price": 80466.11 + }, + { + "timestamp": 1789884000000, + "index_price": 80467.53, + "interest_8h": 0.00011438115897154783, + "interest_1h": 3.2034180506026057e-06, + "prev_index_price": 80406.86 + }, + { + "timestamp": 1789887600000, + "index_price": 80284.73, + "interest_8h": 9.32111909045898e-05, + "interest_1h": 2.116139038499294e-06, + "prev_index_price": 80467.53 + }, + { + "timestamp": 1789891200000, + "index_price": 80292.92, + "interest_8h": 8.225487389838831e-05, + "interest_1h": 4.800598032729875e-06, + "prev_index_price": 80284.73 + }, + { + "timestamp": 1789894800000, + "index_price": 80236.52, + "interest_8h": 7.930890611540408e-05, + "interest_1h": 8.592850151835521e-06, + "prev_index_price": 80292.92 + }, + { + "timestamp": 1789898400000, + "index_price": 80439.57, + "interest_8h": 7.711337096560992e-05, + "interest_1h": 1.4684741829057852e-05, + "prev_index_price": 80236.52 + }, + { + "timestamp": 1789902000000, + "index_price": 80290.85, + "interest_8h": 7.764449418518562e-05, + "interest_1h": 1.731978357173989e-05, + "prev_index_price": 80439.57 + }, + { + "timestamp": 1789905600000, + "index_price": 80455.12, + "interest_8h": 7.064469331448168e-05, + "interest_1h": 8.904895798545535e-06, + "prev_index_price": 80290.85 + }, + { + "timestamp": 1789909200000, + "index_price": 80462.51, + "interest_8h": 6.356288774768103e-05, + "interest_1h": 3.8857716514232716e-06, + "prev_index_price": 80455.12 + }, + { + "timestamp": 1789912800000, + "index_price": 80538.42, + "interest_8h": 6.371065529221617e-05, + "interest_1h": 3.3054358812664526e-06, + "prev_index_price": 80462.51 + }, + { + "timestamp": 1789916400000, + "index_price": 80652.54, + "interest_8h": 6.88962968161614e-05, + "interest_1h": 7.465221177076952e-06, + "prev_index_price": 80538.42 + }, + { + "timestamp": 1789920000000, + "index_price": 80886.2, + "interest_8h": 6.66477755274478e-05, + "interest_1h": 2.559066476957397e-06, + "prev_index_price": 80652.54 + }, + { + "timestamp": 1789923600000, + "index_price": 81314.35, + "interest_8h": 6.029910188939407e-05, + "interest_1h": 2.303457349941978e-06, + "prev_index_price": 80886.2 + }, + { + "timestamp": 1789927200000, + "index_price": 81133.09, + "interest_8h": 4.613047154922585e-05, + "interest_1h": 4.0705398072082257e-07, + "prev_index_price": 81314.35 + }, + { + "timestamp": 1789930800000, + "index_price": 81022.44, + "interest_8h": 2.895317790831924e-05, + "interest_1h": 1.9579548790115389e-07, + "prev_index_price": 81133.09 + }, + { + "timestamp": 1789934400000, + "index_price": 81163.58, + "interest_8h": 2.0166039693856925e-05, + "interest_1h": 7.728119084922554e-08, + "prev_index_price": 81022.44 + }, + { + "timestamp": 1789938000000, + "index_price": 81080.82, + "interest_8h": 1.6317565552435866e-05, + "interest_1h": 3.6114071476582626e-08, + "prev_index_price": 81163.58 + } +] diff --git a/openbb_platform/providers/deribit/tests/data/historical_volatility.json b/openbb_platform/providers/deribit/tests/data/historical_volatility.json new file mode 100644 index 00000000000..21472f38b0c --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/historical_volatility.json @@ -0,0 +1,242 @@ +[ + [ + 1788566400000, + 45.882391305049836 + ], + [ + 1788566400000, + 45.882391305049836 + ], + [ + 1788570000000, + 45.82361062125136 + ], + [ + 1788573600000, + 45.59941784506282 + ], + [ + 1788577200000, + 44.612462181646315 + ], + [ + 1788580800000, + 44.374337703551326 + ], + [ + 1788584400000, + 44.37190609736692 + ], + [ + 1788588000000, + 44.24576674351413 + ], + [ + 1788591600000, + 44.15370915488827 + ], + [ + 1788595200000, + 44.14077093901336 + ], + [ + 1788598800000, + 43.922274146490885 + ], + [ + 1788602400000, + 39.712802749423844 + ], + [ + 1788606000000, + 38.79826012590313 + ], + [ + 1788609600000, + 38.79452396048651 + ], + [ + 1788613200000, + 38.16053217245645 + ], + [ + 1788616800000, + 38.0423846788947 + ], + [ + 1788620400000, + 38.04257037517399 + ], + [ + 1788624000000, + 38.04116200468372 + ], + [ + 1788627600000, + 38.02487922966197 + ], + [ + 1788631200000, + 38.01249826894891 + ], + [ + 1788634800000, + 38.008479376183935 + ], + [ + 1788638400000, + 37.97117984102993 + ], + [ + 1788642000000, + 37.996183996065994 + ], + [ + 1788645600000, + 37.862274629559266 + ], + [ + 1788649200000, + 37.34360231641064 + ], + [ + 1788652800000, + 37.34923604440181 + ], + [ + 1788656400000, + 37.34361921064971 + ], + [ + 1788660000000, + 37.26147973855525 + ], + [ + 1788663600000, + 37.260474276405915 + ], + [ + 1788667200000, + 37.05677556381301 + ], + [ + 1788670800000, + 37.06264148278253 + ], + [ + 1788674400000, + 37.07432061271084 + ], + [ + 1788678000000, + 36.42356181385526 + ], + [ + 1788681600000, + 36.440761159345996 + ], + [ + 1788685200000, + 36.42054104287318 + ], + [ + 1788688800000, + 36.41643755891511 + ], + [ + 1788692400000, + 36.40554285175305 + ], + [ + 1788696000000, + 36.40858448251462 + ], + [ + 1788699600000, + 36.39957652159652 + ], + [ + 1788703200000, + 36.38204816584679 + ], + [ + 1788706800000, + 36.38599922715135 + ], + [ + 1788710400000, + 36.38484019515988 + ], + [ + 1788714000000, + 36.399284438365605 + ], + [ + 1788717600000, + 36.34531043724365 + ], + [ + 1788721200000, + 36.34701202847672 + ], + [ + 1788724800000, + 36.368927169042905 + ], + [ + 1788728400000, + 36.361783875986184 + ], + [ + 1788732000000, + 36.358728205612444 + ], + [ + 1788735600000, + 36.23666065332862 + ], + [ + 1788739200000, + 36.23120744020156 + ], + [ + 1788742800000, + 36.273090013075176 + ], + [ + 1788746400000, + 36.264687293335214 + ], + [ + 1788750000000, + 36.28173268858742 + ], + [ + 1788753600000, + 36.27665195999784 + ], + [ + 1788757200000, + 36.30368643385408 + ], + [ + 1788760800000, + 36.25447357121739 + ], + [ + 1788764400000, + 36.08275664288014 + ], + [ + 1788768000000, + 36.087195163195176 + ], + [ + 1788771600000, + 36.116470746575025 + ], + [ + 1788775200000, + 35.97731607070047 + ] +] diff --git a/openbb_platform/providers/deribit/tests/data/index_chart_data.json b/openbb_platform/providers/deribit/tests/data/index_chart_data.json new file mode 100644 index 00000000000..18a64ec6edf --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/index_chart_data.json @@ -0,0 +1,242 @@ +[ + [ + 1789935450000, + 81110.95 + ], + [ + 1789935456000, + 81110.428333 + ], + [ + 1789935462000, + 81113.761667 + ], + [ + 1789935468000, + 81112.3 + ], + [ + 1789935474000, + 81110.728333 + ], + [ + 1789935480000, + 81110.993333 + ], + [ + 1789935486000, + 81111.161667 + ], + [ + 1789935492000, + 81110.811667 + ], + [ + 1789935498000, + 81110.6 + ], + [ + 1789935504000, + 81117.588333 + ], + [ + 1789935510000, + 81118.403333 + ], + [ + 1789935516000, + 81118.701667 + ], + [ + 1789935522000, + 81117.903333 + ], + [ + 1789935528000, + 81117.62 + ], + [ + 1789935534000, + 81117.58 + ], + [ + 1789935540000, + 81111.988333 + ], + [ + 1789935546000, + 81112.675 + ], + [ + 1789935552000, + 81131.768333 + ], + [ + 1789935558000, + 81131.501667 + ], + [ + 1789935564000, + 81129.551667 + ], + [ + 1789935570000, + 81118.418333 + ], + [ + 1789935576000, + 81120.615 + ], + [ + 1789935582000, + 81120.415 + ], + [ + 1789935588000, + 81117.451667 + ], + [ + 1789935594000, + 81115.761667 + ], + [ + 1789935600000, + 81115.99 + ], + [ + 1789935606000, + 81118.363333 + ], + [ + 1789935612000, + 81122.728333 + ], + [ + 1789935618000, + 81123.318333 + ], + [ + 1789935624000, + 81121.158333 + ], + [ + 1789935630000, + 81119.46 + ], + [ + 1789935636000, + 81119.791667 + ], + [ + 1789935642000, + 81118.191667 + ], + [ + 1789935648000, + 81114.616667 + ], + [ + 1789935654000, + 81111.5 + ], + [ + 1789935660000, + 81110.628333 + ], + [ + 1789935666000, + 81109.99 + ], + [ + 1789935672000, + 81109.06 + ], + [ + 1789935678000, + 81113.483333 + ], + [ + 1789935684000, + 81116.048333 + ], + [ + 1789935690000, + 81117.225 + ], + [ + 1789935696000, + 81115.436667 + ], + [ + 1789935702000, + 81108.545 + ], + [ + 1789935708000, + 81105.001667 + ], + [ + 1789935714000, + 81104.198333 + ], + [ + 1789935720000, + 81103.061667 + ], + [ + 1789935726000, + 81122.996667 + ], + [ + 1789935732000, + 81124.31 + ], + [ + 1789935738000, + 81124.393333 + ], + [ + 1789935744000, + 81130.29 + ], + [ + 1789935750000, + 81132.178333 + ], + [ + 1789935756000, + 81132.845 + ], + [ + 1789935762000, + 81131.555 + ], + [ + 1789935768000, + 81131.316667 + ], + [ + 1789935774000, + 81117.736667 + ], + [ + 1789935780000, + 81111.978333 + ], + [ + 1789935786000, + 81111.286667 + ], + [ + 1789935792000, + 81123.215 + ], + [ + 1789935798000, + 81127.655 + ], + [ + 1789935804000, + 81130.285 + ] +] diff --git a/openbb_platform/providers/deribit/tests/data/index_price.json b/openbb_platform/providers/deribit/tests/data/index_price.json new file mode 100644 index 00000000000..b15c8318052 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/index_price.json @@ -0,0 +1,4 @@ +{ + "estimated_delivery_price": 80814.47, + "index_price": 80814.47 +} diff --git a/openbb_platform/providers/deribit/tests/data/index_price_names.json b/openbb_platform/providers/deribit/tests/data/index_price_names.json new file mode 100644 index 00000000000..9dc6a8d93e4 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/index_price_names.json @@ -0,0 +1,202 @@ +[ + "ada_usd", + "ada_usdc", + "ada_usdt", + "algo_usd", + "algo_usdc", + "algo_usdt", + "avax_usd", + "avax_usdc", + "avax_usdt", + "bch_usd", + "bch_usdc", + "bch_usdt", + "bnb_usd", + "bnb_usdc", + "bnb_usdt", + "bonk_usd", + "btc_usd", + "btc_usdc", + "btc_usdt", + "buidl_usd", + "buidl_usdc", + "doge_usd", + "doge_usdc", + "doge_usdt", + "dot_usd", + "dot_usdc", + "dot_usdt", + "eth_usd", + "eth_usdc", + "eth_usdt", + "ethw_usd", + "link_usd", + "link_usdc", + "link_usdt", + "ltc_usd", + "ltc_usdc", + "ltc_usdt", + "matic_usd", + "matic_usdc", + "matic_usdt", + "mog_usd", + "near_usd", + "near_usdc", + "near_usdt", + "paxg_usd", + "paxg_usdc", + "paxg_usdt", + "pepe_usd", + "shib_usd", + "shib_usdc", + "shib_usdt", + "sol_usd", + "sol_usdc", + "sol_usdt", + "sol_eth", + "ton_usd", + "ton_usdc", + "ton_usdt", + "trump_usd", + "trump_usdc", + "trump_usdt", + "trx_usd", + "trx_usdc", + "trx_usdt", + "uni_usd", + "uni_usdc", + "uni_usdt", + "xrp_usd", + "xrp_usdc", + "xrp_usdt", + "usd_usdc", + "usd_usdt", + "usdc_usd", + "usdc_usdt", + "usdt_usdc", + "usde_usd", + "usde_usdc", + "usde_usdt", + "usdt_usd", + "drbfix-btc_usdc", + "drbfix-eth_usdc", + "eth_btc", + "eth_steth", + "steth_eth", + "steth_usd", + "steth_usdc", + "steth_usdt", + "btc_eurr", + "eth_eurr", + "usdc_eur", + "usdt_eur", + "eur_usdc", + "eur_usdt", + "eurr_usd", + "eurr_usdc", + "eurr_usdt", + "eth_matic", + "btc_usyc", + "eth_usyc", + "usyc_usd", + "usyc_usdc", + "paxg_btc", + "eth_paxg", + "btc_usde", + "eth_usde", + "eth_buidl", + "hype_usd", + "hype_usdc", + "hype_usdt", + "aave_usd", + "aave_usdc", + "apt_usd", + "apt_usdc", + "arb_usd", + "arb_usdc", + "ena_usd", + "ena_usdc", + "etc_usd", + "etc_usdc", + "fil_usd", + "fil_usdc", + "hbar_usd", + "hbar_usdc", + "icp_usd", + "icp_usdc", + "inj_usd", + "inj_usdc", + "jup_usd", + "jup_usdc", + "ondo_usd", + "ondo_usdc", + "op_usd", + "op_usdc", + "render_usd", + "render_usdc", + "sei_usd", + "sei_usdc", + "sui_usd", + "sui_usdc", + "tao_usd", + "tao_usdc", + "tia_usd", + "tia_usdc", + "wld_usd", + "wld_usdc", + "xlm_usd", + "xlm_usdc", + "1000bonk_usd", + "1000bonk_usdc", + "1000mog_usd", + "1000mog_usdc", + "1000pepe_usd", + "1000pepe_usdc", + "1000shib_usd", + "1000shib_usdc", + "aero_usd", + "aero_usdc", + "atom_usd", + "atom_usdc", + "axs_usd", + "axs_usdc", + "bera_usd", + "bera_usdc", + "bome_usd", + "bome_usdc", + "brett_usd", + "brett_usdc", + "cfx_usd", + "cfx_usdc", + "crv_usd", + "crv_usdc", + "dydx_usd", + "dydx_usdc", + "eigen_usd", + "eigen_usdc", + "ethfi_usd", + "ethfi_usdc", + "fet_usd", + "fet_usdc", + "jto_usd", + "jto_usdc", + "ldo_usd", + "ldo_usdc", + "morpho_usd", + "morpho_usdc", + "pendle_usd", + "pendle_usdc", + "pengu_usd", + "pengu_usdc", + "pnut_usd", + "pnut_usdc", + "pump_usd", + "pump_usdc", + "pyth_usd", + "pyth_usdc", + "rune_usd", + "rune_usdc", + "s_usd", + "s_usdc", + "strk_usd" +] diff --git a/openbb_platform/providers/deribit/tests/data/instrument.json b/openbb_platform/providers/deribit/tests/data/instrument.json new file mode 100644 index 00000000000..1a0e64d44fb --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/instrument.json @@ -0,0 +1,35 @@ +{ + "max_liquidation_commission": 0.01, + "taker_commission": 0.00035, + "counter_currency": "USD", + "index_id": 1000033, + "price_index": "btc_usd", + "block_trade_min_trade_amount": 200000, + "contract_size": 10.0, + "state": "open", + "tick_size": 0.5, + "kind": "future", + "creation_timestamp": 1534242287000, + "expiration_timestamp": 32503708800000, + "is_active": true, + "instrument_type": "reversed", + "underlying_type": "crypto", + "tick_size_steps": [], + "min_trade_amount": 10.0, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "instrument_id": 210838, + "product_group": "BTC", + "lot_size": 1, + "quote_currency": "USD", + "future_type": "reversed", + "base_currency": "BTC", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1", + "block_trade_tick_size": 0.01, + "instrument_name": "BTC-PERPETUAL", + "max_leverage": 50, + "settlement_currency": "BTC", + "max_non_default_leverage": 5.0, + "block_trade_commission": 0.00025, + "settlement_period": "perpetual", + "maker_commission": 0.00015 +} diff --git a/openbb_platform/providers/deribit/tests/data/instruments_future.json b/openbb_platform/providers/deribit/tests/data/instruments_future.json new file mode 100644 index 00000000000..dac5d2996f1 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/instruments_future.json @@ -0,0 +1,457 @@ +[ + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 50, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 32503708800000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 210838, + "creation_timestamp": 1534242287000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 0.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-PERPETUAL", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "perpetual", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1789977600000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 695928, + "creation_timestamp": 1789632015000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-21SEP26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "day", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1790064000000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 696991, + "creation_timestamp": 1789718417000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-22SEP26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "day", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1790150400000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 697962, + "creation_timestamp": 1789804818000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-23SEP26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "day", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1790236800000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 698438, + "creation_timestamp": 1789891217000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-24SEP26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "day", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1790323200000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 522657, + "creation_timestamp": 1758873621000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-25SEP26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "month", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1790928000000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 691809, + "creation_timestamp": 1789027214000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-2OCT26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "week", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1791532800000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 695930, + "creation_timestamp": 1789632015000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-9OCT26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "week", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1793347200000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 667489, + "creation_timestamp": 1785398413000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-30OCT26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "month", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1795766400000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 682891, + "creation_timestamp": 1787817624000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-27NOV26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "month", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1798185600000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 567609, + "creation_timestamp": 1766736024000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-25DEC26", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "month", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1806048000000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 607780, + "creation_timestamp": 1774598426000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-26MAR27", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "month", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + }, + { + "tick_size_steps": [], + "block_trade_tick_size": 0.01, + "quote_currency": "USD", + "price_index": "btc_usd", + "instrument_type": "reversed", + "future_type": "reversed", + "kind": "future", + "max_leverage": 25, + "underlying_type": "crypto", + "product_group": "BTC", + "state": "open", + "expiration_timestamp": 1813910400000, + "contract_size": 10.0, + "min_trade_amount": 10.0, + "instrument_id": 652925, + "creation_timestamp": 1782460823000, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "is_active": true, + "lot_size": 1, + "taker_commission": 0.00035, + "tick_size": 2.5, + "block_trade_commission": 0.00025, + "settlement_currency": "BTC", + "instrument_name": "BTC-25JUN27", + "block_trade_min_trade_amount": 200000, + "max_liquidation_commission": 0.01, + "max_non_default_leverage": 5.0, + "index_id": 1000033, + "counter_currency": "USD", + "base_currency": "BTC", + "maker_commission": 0.00015, + "settlement_period": "month", + "quote_currency_uuid": "dc311642-e9fb-4ba9-a312-c75c1aef4ae1" + } +] diff --git a/openbb_platform/providers/deribit/tests/data/instruments_option.json b/openbb_platform/providers/deribit/tests/data/instruments_option.json new file mode 100644 index 00000000000..2cc3a5e1133 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/instruments_option.json @@ -0,0 +1,2870 @@ +[ + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d25-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697879, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.25, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d25-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697880, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.25, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d28-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698375, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789873140000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.28, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d28-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698376, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789873140000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.28, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d3-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697881, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.3, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d3-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697882, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.3, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d32-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697883, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.32, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d32-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697884, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.32, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d325-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698885, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.325, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d325-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698886, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.325, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d34-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697885, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.34, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d34-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697886, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.34, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d35-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698887, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.35, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d35-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698888, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.35, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d36-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697887, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.36, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d36-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697888, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.36, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d375-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698889, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.375, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d375-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698890, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.375, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d38-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697889, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.38, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d38-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697890, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.38, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d4-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697891, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.4, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d4-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697892, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.4, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d42-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697893, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.42, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d42-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697894, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.42, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d425-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698891, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.425, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d425-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698892, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.425, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d44-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697895, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.44, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d44-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697896, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.44, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d45-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698893, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.45, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d45-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698894, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789905660000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.45, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d46-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697897, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.46, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d46-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697898, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.46, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d48-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697899, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.48, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d48-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697900, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.48, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d5-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697901, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.5, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d5-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697902, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.5, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d52-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697903, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.52, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d52-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697904, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.52, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d54-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698155, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789806060000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.54, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d54-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698156, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789806060000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.54, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d55-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697905, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.55, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d55-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697906, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.55, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d56-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698233, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789823220000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.56, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d56-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 698234, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789823220000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.56, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d6-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697907, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.6, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-21SEP26-1d6-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1789977600000, + "instrument_id": 697908, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789804817000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.6, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d2-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698796, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.2, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d2-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698797, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.2, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d25-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698798, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.25, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d25-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698799, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.25, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d26-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698860, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789894140000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.26, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d26-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698861, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789894140000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.26, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d28-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698800, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.28, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d28-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698801, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.28, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d3-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698802, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.3, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d3-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698803, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.3, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d32-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698804, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.32, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d32-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698805, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.32, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d34-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698806, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.34, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d34-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698807, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.34, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d36-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698808, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.36, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d36-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698809, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.36, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d38-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698810, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.38, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d38-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698811, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.38, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d4-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698812, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.4, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d4-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698813, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.4, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d42-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698814, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.42, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d42-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698815, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.42, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d44-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698816, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.44, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d44-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698817, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.44, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d46-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698818, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.46, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d46-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698819, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.46, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d48-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698820, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.48, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d48-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698821, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.48, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d5-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698822, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.5, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d5-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698823, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.5, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d52-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698980, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789922340000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.52, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d52-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698981, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789922340000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.52, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d55-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698824, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.55, + "counter_currency": "USDC", + "option_type": "call", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "xrp_usdc", + "kind": "option", + "instrument_name": "XRP_USDC-22SEP26-1d55-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "linear", + "expiration_timestamp": 1790064000000, + "instrument_id": 698825, + "underlying_type": "crypto", + "product_group": "TIER_2", + "creation_timestamp": 1789891218000, + "is_active": true, + "contract_size": 1000, + "tick_size": 0.0005, + "strike": 1.55, + "counter_currency": "USDC", + "option_type": "put", + "min_trade_amount": 1000, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 75000, + "block_trade_tick_size": 0.0005, + "settlement_currency": "USDC", + "settlement_period": "day", + "base_currency": "XRP", + "index_id": 12000004, + "quote_currency": "USDC", + "tick_size_steps": [], + "lot_size": 10000, + "base_currency_uuid": "e17a44c8-6ea1-564f-a02c-2a9ca1d8eec4", + "quote_currency_uuid": "2b92315d-eab7-5bef-84fa-089a131333f5" + }, + { + "state": "open", + "price_index": "btc_usd", + "kind": "option", + "instrument_name": "BTC-21SEP26-68000-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "reversed", + "expiration_timestamp": 1789977600000, + "instrument_id": 695978, + "underlying_type": "crypto", + "product_group": "BTC", + "creation_timestamp": 1789632015000, + "is_active": true, + "contract_size": 1.0, + "tick_size": 0.0001, + "strike": 68000.0, + "counter_currency": "USD", + "option_type": "call", + "min_trade_amount": 0.1, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 25, + "block_trade_tick_size": 0.0001, + "settlement_currency": "BTC", + "settlement_period": "day", + "base_currency": "BTC", + "index_id": 1000033, + "quote_currency": "BTC", + "tick_size_steps": [ + { + "tick_size": 0.0005, + "above_price": 0.005 + } + ], + "lot_size": 1, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "quote_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5" + }, + { + "state": "open", + "price_index": "btc_usd", + "kind": "option", + "instrument_name": "BTC-21SEP26-68000-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "reversed", + "expiration_timestamp": 1789977600000, + "instrument_id": 695979, + "underlying_type": "crypto", + "product_group": "BTC", + "creation_timestamp": 1789632015000, + "is_active": true, + "contract_size": 1.0, + "tick_size": 0.0001, + "strike": 68000.0, + "counter_currency": "USD", + "option_type": "put", + "min_trade_amount": 0.1, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 25, + "block_trade_tick_size": 0.0001, + "settlement_currency": "BTC", + "settlement_period": "day", + "base_currency": "BTC", + "index_id": 1000033, + "quote_currency": "BTC", + "tick_size_steps": [ + { + "tick_size": 0.0005, + "above_price": 0.005 + } + ], + "lot_size": 1, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "quote_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5" + }, + { + "state": "open", + "price_index": "btc_usd", + "kind": "option", + "instrument_name": "BTC-21SEP26-69000-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "reversed", + "expiration_timestamp": 1789977600000, + "instrument_id": 695980, + "underlying_type": "crypto", + "product_group": "BTC", + "creation_timestamp": 1789632015000, + "is_active": true, + "contract_size": 1.0, + "tick_size": 0.0001, + "strike": 69000.0, + "counter_currency": "USD", + "option_type": "call", + "min_trade_amount": 0.1, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 25, + "block_trade_tick_size": 0.0001, + "settlement_currency": "BTC", + "settlement_period": "day", + "base_currency": "BTC", + "index_id": 1000033, + "quote_currency": "BTC", + "tick_size_steps": [ + { + "tick_size": 0.0005, + "above_price": 0.005 + } + ], + "lot_size": 1, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "quote_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5" + }, + { + "state": "open", + "price_index": "btc_usd", + "kind": "option", + "instrument_name": "BTC-21SEP26-69000-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "reversed", + "expiration_timestamp": 1789977600000, + "instrument_id": 695981, + "underlying_type": "crypto", + "product_group": "BTC", + "creation_timestamp": 1789632015000, + "is_active": true, + "contract_size": 1.0, + "tick_size": 0.0001, + "strike": 69000.0, + "counter_currency": "USD", + "option_type": "put", + "min_trade_amount": 0.1, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 25, + "block_trade_tick_size": 0.0001, + "settlement_currency": "BTC", + "settlement_period": "day", + "base_currency": "BTC", + "index_id": 1000033, + "quote_currency": "BTC", + "tick_size_steps": [ + { + "tick_size": 0.0005, + "above_price": 0.005 + } + ], + "lot_size": 1, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "quote_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5" + }, + { + "state": "open", + "price_index": "btc_usd", + "kind": "option", + "instrument_name": "BTC-21SEP26-70000-C", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "reversed", + "expiration_timestamp": 1789977600000, + "instrument_id": 695982, + "underlying_type": "crypto", + "product_group": "BTC", + "creation_timestamp": 1789632015000, + "is_active": true, + "contract_size": 1.0, + "tick_size": 0.0001, + "strike": 70000.0, + "counter_currency": "USD", + "option_type": "call", + "min_trade_amount": 0.1, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 25, + "block_trade_tick_size": 0.0001, + "settlement_currency": "BTC", + "settlement_period": "day", + "base_currency": "BTC", + "index_id": 1000033, + "quote_currency": "BTC", + "tick_size_steps": [ + { + "tick_size": 0.0005, + "above_price": 0.005 + } + ], + "lot_size": 1, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "quote_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5" + }, + { + "state": "open", + "price_index": "btc_usd", + "kind": "option", + "instrument_name": "BTC-21SEP26-70000-P", + "maker_commission": 0.0003, + "taker_commission": 0.0003, + "instrument_type": "reversed", + "expiration_timestamp": 1789977600000, + "instrument_id": 695983, + "underlying_type": "crypto", + "product_group": "BTC", + "creation_timestamp": 1789632015000, + "is_active": true, + "contract_size": 1.0, + "tick_size": 0.0001, + "strike": 70000.0, + "counter_currency": "USD", + "option_type": "put", + "min_trade_amount": 0.1, + "block_trade_commission": 0.0003, + "block_trade_min_trade_amount": 25, + "block_trade_tick_size": 0.0001, + "settlement_currency": "BTC", + "settlement_period": "day", + "base_currency": "BTC", + "index_id": 1000033, + "quote_currency": "BTC", + "tick_size_steps": [ + { + "tick_size": 0.0005, + "above_price": 0.005 + } + ], + "lot_size": 1, + "base_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5", + "quote_currency_uuid": "5b71fc48-3dd3-540c-809b-f8c94d0e68b5" + } +] diff --git a/openbb_platform/providers/deribit/tests/data/option_tickers.json b/openbb_platform/providers/deribit/tests/data/option_tickers.json new file mode 100644 index 00000000000..ff62aa9f231 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/option_tickers.json @@ -0,0 +1,452 @@ +{ + "BTC-21SEP26-68000-C": { + "timestamp": 1789939517455, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0 + }, + "greeks": { + "delta": 1.0, + "gamma": 0.0, + "vega": 0.0, + "theta": 0.0, + "rho": 0.82154 + }, + "index_price": 80808.14, + "instrument_name": "BTC-21SEP26-68000-C", + "last_price": null, + "settlement_price": 0.15328061, + "min_price": 0.1285, + "max_price": 0.1865, + "open_interest": 0.0, + "mark_price": 0.1585, + "interest_rate": 0.0, + "estimated_delivery_price": 80808.14, + "best_ask_price": 0.161, + "best_bid_price": 0.1565, + "mark_iv": 61.59, + "bid_iv": 0.0, + "ask_iv": 315.62, + "underlying_price": 80809.22, + "underlying_index": "BTC-21SEP26", + "best_ask_amount": 56.0, + "best_bid_amount": 56.0 + }, + "BTC-21SEP26-68000-P": { + "timestamp": 1789939517455, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0 + }, + "greeks": { + "delta": 0.0, + "gamma": 0.0, + "vega": 0.0, + "theta": 0.0, + "rho": 0.0 + }, + "index_price": 80808.14, + "instrument_name": "BTC-21SEP26-68000-P", + "last_price": 0.0001, + "settlement_price": 0.0, + "min_price": 0.0001, + "max_price": 0.015, + "open_interest": 0.5, + "mark_price": 0.0, + "interest_rate": 0.0, + "estimated_delivery_price": 80808.14, + "best_ask_price": 0.0001, + "best_bid_price": 0.0, + "mark_iv": 61.59, + "bid_iv": 0.0, + "ask_iv": 193.39, + "underlying_price": 80809.22, + "underlying_index": "BTC-21SEP26", + "best_ask_amount": 20.0, + "best_bid_amount": 0.0 + }, + "BTC-21SEP26-69000-C": { + "timestamp": 1789939517455, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0 + }, + "greeks": { + "delta": 1.0, + "gamma": 0.0, + "vega": 0.0, + "theta": 0.0, + "rho": 0.83362 + }, + "index_price": 80808.14, + "instrument_name": "BTC-21SEP26-69000-C", + "last_price": null, + "settlement_price": 0.14082886, + "min_price": 0.1155, + "max_price": 0.175, + "open_interest": 0.0, + "mark_price": 0.1461, + "interest_rate": 0.0, + "estimated_delivery_price": 80808.14, + "best_ask_price": 0.148, + "best_bid_price": 0.144, + "mark_iv": 61.57, + "bid_iv": 0.0, + "ask_iv": 276.84, + "underlying_price": 80808.56, + "underlying_index": "BTC-21SEP26", + "best_ask_amount": 56.0, + "best_bid_amount": 56.0 + }, + "BTC-21SEP26-69000-P": { + "timestamp": 1789939517455, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0 + }, + "greeks": { + "delta": 0.0, + "gamma": 0.0, + "vega": 0.0, + "theta": 0.0, + "rho": 0.0 + }, + "index_price": 80808.14, + "instrument_name": "BTC-21SEP26-69000-P", + "last_price": null, + "settlement_price": 0.0, + "min_price": 0.0001, + "max_price": 0.015, + "open_interest": 0.0, + "mark_price": 0.0, + "interest_rate": 0.0, + "estimated_delivery_price": 80808.14, + "best_ask_price": 0.0001, + "best_bid_price": 0.0, + "mark_iv": 61.57, + "bid_iv": 0.0, + "ask_iv": 178.62, + "underlying_price": 80809.22, + "underlying_index": "BTC-21SEP26", + "best_ask_amount": 18.5, + "best_bid_amount": 0.0 + }, + "BTC-21SEP26-70000-C": { + "timestamp": 1789939517455, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0 + }, + "greeks": { + "delta": 1.0, + "gamma": 0.0, + "vega": 0.0, + "theta": 0.0, + "rho": 0.8457 + }, + "index_price": 80808.14, + "instrument_name": "BTC-21SEP26-70000-C", + "last_price": null, + "settlement_price": 0.12837712, + "min_price": 0.1025, + "max_price": 0.163, + "open_interest": 0.0, + "mark_price": 0.1338, + "interest_rate": 0.0, + "estimated_delivery_price": 80808.14, + "best_ask_price": 0.1355, + "best_bid_price": 0.1315, + "mark_iv": 61.57, + "bid_iv": 0.0, + "ask_iv": 252.4, + "underlying_price": 80809.22, + "underlying_index": "BTC-21SEP26", + "best_ask_amount": 56.0, + "best_bid_amount": 56.0 + }, + "BTC-21SEP26-70000-P": { + "timestamp": 1789939518462, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0 + }, + "greeks": { + "delta": 0.0, + "gamma": 0.0, + "vega": 0.0, + "theta": 0.0, + "rho": 0.0 + }, + "index_price": 80807.52, + "instrument_name": "BTC-21SEP26-70000-P", + "last_price": 0.0001, + "settlement_price": 2e-08, + "min_price": 0.0001, + "max_price": 0.015, + "open_interest": 22.1, + "mark_price": 0.0, + "interest_rate": 0.0, + "estimated_delivery_price": 80807.52, + "best_ask_price": 0.0001, + "best_bid_price": 0.0, + "mark_iv": 61.57, + "bid_iv": 0.0, + "ask_iv": 163.96, + "underlying_price": 80808.75, + "underlying_index": "BTC-21SEP26", + "best_ask_amount": 27.9, + "best_bid_amount": 0.0 + }, + "XRP_USDC-21SEP26-1d25-C": { + "timestamp": 1789939517605, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0, + "volume_notional": 0.0 + }, + "greeks": { + "delta": 0.99603, + "gamma": 0.19661, + "vega": 1e-05, + "theta": -0.00035, + "rho": 2e-05 + }, + "index_price": 1.3987, + "instrument_name": "XRP_USDC-21SEP26-1d25-C", + "last_price": null, + "settlement_price": 0.13044156, + "min_price": 0.0655, + "max_price": 0.238, + "open_interest": 0.0, + "mark_price": 0.1491, + "interest_rate": 0.0, + "estimated_delivery_price": 1.3987, + "best_ask_price": 0.0, + "best_bid_price": 0.0, + "mark_iv": 123.13, + "bid_iv": 0.0, + "ask_iv": 0.0, + "underlying_price": 1.3991, + "underlying_index": "XRP_USDC-21SEP26", + "best_ask_amount": 0.0, + "best_bid_amount": 0.0 + }, + "XRP_USDC-21SEP26-1d25-P": { + "timestamp": 1789939517605, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0, + "volume_notional": 0.0 + }, + "greeks": { + "delta": -0.00395, + "gamma": 0.19572, + "vega": 1e-05, + "theta": -7e-05, + "rho": 0.0 + }, + "index_price": 1.3987, + "instrument_name": "XRP_USDC-21SEP26-1d25-P", + "last_price": null, + "settlement_price": 0.00024156, + "min_price": 0.0005, + "max_price": 0.0335, + "open_interest": 0.0, + "mark_price": 0.0001, + "interest_rate": 0.0, + "estimated_delivery_price": 1.3987, + "best_ask_price": 0.0025, + "best_bid_price": 0.0, + "mark_iv": 123.13, + "bid_iv": 0.0, + "ask_iv": 209.07, + "underlying_price": 1.3992, + "underlying_index": "XRP_USDC-21SEP26", + "best_ask_amount": 25000.0, + "best_bid_amount": 0.0 + }, + "XRP_USDC-21SEP26-1d28-C": { + "timestamp": 1789939517605, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0, + "volume_notional": 0.0 + }, + "greeks": { + "delta": 0.98222, + "gamma": 0.7315, + "vega": 2e-05, + "theta": -0.00131, + "rho": 2e-05 + }, + "index_price": 1.3987, + "instrument_name": "XRP_USDC-21SEP26-1d28-C", + "last_price": null, + "settlement_price": 0.10089075, + "min_price": 0.0355, + "max_price": 0.211, + "open_interest": 0.0, + "mark_price": 0.1194, + "interest_rate": 0.0, + "estimated_delivery_price": 1.3987, + "best_ask_price": 0.0, + "best_bid_price": 0.0, + "mark_iv": 123.13, + "bid_iv": 0.0, + "ask_iv": 0.0, + "underlying_price": 1.3992, + "underlying_index": "XRP_USDC-21SEP26", + "best_ask_amount": 0.0, + "best_bid_amount": 0.0 + }, + "XRP_USDC-21SEP26-1d28-P": { + "timestamp": 1789939517605, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0, + "volume_notional": 0.0 + }, + "greeks": { + "delta": -0.01778, + "gamma": 0.7315, + "vega": 2e-05, + "theta": -0.00039, + "rho": 0.0 + }, + "index_price": 1.3987, + "instrument_name": "XRP_USDC-21SEP26-1d28-P", + "last_price": null, + "settlement_price": 0.00069075, + "min_price": 0.0005, + "max_price": 0.045, + "open_interest": 0.0, + "mark_price": 0.0004, + "interest_rate": 0.0, + "estimated_delivery_price": 1.3987, + "best_ask_price": 0.0025, + "best_bid_price": 0.0, + "mark_iv": 123.13, + "bid_iv": 0.0, + "ask_iv": 173.59, + "underlying_price": 1.3992, + "underlying_index": "XRP_USDC-21SEP26", + "best_ask_amount": 25000.0, + "best_bid_amount": 0.0 + }, + "XRP_USDC-21SEP26-1d3-C": { + "timestamp": 1789939517605, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0, + "volume_notional": 0.0 + }, + "greeks": { + "delta": 0.96884, + "gamma": 1.25842, + "vega": 3e-05, + "theta": -0.00196, + "rho": 2e-05 + }, + "index_price": 1.3987, + "instrument_name": "XRP_USDC-21SEP26-1d3-C", + "last_price": null, + "settlement_price": 0.08169251, + "min_price": 0.0155, + "max_price": 0.1915, + "open_interest": 0.0, + "mark_price": 0.0997, + "interest_rate": 0.0, + "estimated_delivery_price": 1.3987, + "best_ask_price": 0.0, + "best_bid_price": 0.0, + "mark_iv": 114.73, + "bid_iv": 0.0, + "ask_iv": 0.0, + "underlying_price": 1.3992, + "underlying_index": "XRP_USDC-21SEP26", + "best_ask_amount": 0.0, + "best_bid_amount": 0.0 + }, + "XRP_USDC-21SEP26-1d3-P": { + "timestamp": 1789939517605, + "state": "open", + "stats": { + "high": null, + "low": null, + "price_change": null, + "volume": 0.0, + "volume_usd": 0.0, + "volume_notional": 0.0 + }, + "greeks": { + "delta": -0.03116, + "gamma": 1.25842, + "vega": 3e-05, + "theta": -0.00069, + "rho": 0.0 + }, + "index_price": 1.3987, + "instrument_name": "XRP_USDC-21SEP26-1d3-P", + "last_price": null, + "settlement_price": 0.00149251, + "min_price": 0.0005, + "max_price": 0.05, + "open_interest": 0.0, + "mark_price": 0.0007, + "interest_rate": 0.0, + "estimated_delivery_price": 1.3987, + "best_ask_price": 0.001, + "best_bid_price": 0.0005, + "mark_iv": 114.73, + "bid_iv": 108.74, + "ask_iv": 122.91, + "underlying_price": 1.3992, + "underlying_index": "XRP_USDC-21SEP26", + "best_ask_amount": 25000.0, + "best_bid_amount": 25000.0 + } +} diff --git a/openbb_platform/providers/deribit/tests/data/order_book.json b/openbb_platform/providers/deribit/tests/data/order_book.json new file mode 100644 index 00000000000..bda4d81ad9c --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/order_book.json @@ -0,0 +1,73 @@ +{ + "timestamp": 1789939036574, + "state": "open", + "stats": { + "high": 81497.5, + "low": 80149.5, + "price_change": -0.2493, + "volume": 3099.91787897, + "volume_usd": 250336360.0, + "volume_notional": 250336360.0 + }, + "change_id": 174959286421, + "index_price": 80815.66, + "instrument_name": "BTC-PERPETUAL", + "bids": [ + [ + 80836.5, + 65010.0 + ], + [ + 80832.0, + 6510.0 + ], + [ + 80831.0, + 15520.0 + ], + [ + 80827.0, + 1470.0 + ], + [ + 80826.0, + 4460.0 + ] + ], + "asks": [ + [ + 80837.0, + 10810.0 + ], + [ + 80837.5, + 10000.0 + ], + [ + 80838.0, + 3350.0 + ], + [ + 80838.5, + 6000.0 + ], + [ + 80840.0, + 6000.0 + ] + ], + "last_price": 80832.5, + "settlement_price": 80317.84, + "min_price": 79618.5, + "max_price": 82044.0, + "open_interest": 787799210, + "mark_price": 80831.35, + "interest_value": 0.024991800794000794, + "current_funding": 0.0, + "estimated_delivery_price": 80815.66, + "funding_8h": 1.583e-05, + "best_ask_price": 80837.0, + "best_bid_price": 80836.5, + "best_ask_amount": 10810.0, + "best_bid_amount": 65010.0 +} diff --git a/openbb_platform/providers/deribit/tests/data/server_time.json b/openbb_platform/providers/deribit/tests/data/server_time.json new file mode 100644 index 00000000000..eb163017e89 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/server_time.json @@ -0,0 +1 @@ +1789939041264 diff --git a/openbb_platform/providers/deribit/tests/data/settlements_currency.json b/openbb_platform/providers/deribit/tests/data/settlements_currency.json new file mode 100644 index 00000000000..684643f7397 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/settlements_currency.json @@ -0,0 +1,55 @@ +{ + "continuation": "3JbvTsVzTGQ6HW7KSsh78zQ4sZ8tMD9vrhkwQNR7TJTVHbBswFK6j9yJdsz8vwZTgaYJihMaRyXn9", + "settlements": [ + { + "position": 31999610, + "timestamp": 1789891200065, + "type": "delivery", + "index_price": 80321.21, + "instrument_name": "BTC-20SEP26", + "mark_price": 80321.21, + "profit_loss": -3.345250995000001, + "session_profit_loss": 23493.117937861054 + }, + { + "position": 17.8, + "timestamp": 1789891200065, + "type": "exercise", + "index_price": 80321.21, + "instrument_name": "BTC-20SEP26-68000-P", + "mark_price": 0.0, + "profit_loss": -8.673617379884035e-19, + "session_profit_loss": 492.86261408100006 + }, + { + "position": 88.8, + "timestamp": 1789891200065, + "type": "exercise", + "index_price": 80321.21, + "instrument_name": "BTC-20SEP26-69000-P", + "mark_price": 0.0, + "profit_loss": 0.009744748999999999, + "session_profit_loss": 1642.87538027 + }, + { + "position": 65.2, + "timestamp": 1789891200065, + "type": "exercise", + "index_price": 80321.21, + "instrument_name": "BTC-20SEP26-70000-P", + "mark_price": 0.0, + "profit_loss": 0.005033333, + "session_profit_loss": 1642.87538027 + }, + { + "position": 8.599999999999998, + "timestamp": 1789891200065, + "type": "exercise", + "index_price": 80321.21, + "instrument_name": "BTC-20SEP26-71000-P", + "mark_price": 0.0, + "profit_loss": -0.004930000000000005, + "session_profit_loss": 1807.162918297 + } + ] +} diff --git a/openbb_platform/providers/deribit/tests/data/settlements_instrument.json b/openbb_platform/providers/deribit/tests/data/settlements_instrument.json new file mode 100644 index 00000000000..016ea59c239 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/settlements_instrument.json @@ -0,0 +1,55 @@ +{ + "continuation": "23nYDkVbSynLKHS9FLtdNzkdZehx1XPceCZ2K54nzc3PhhBDfRmKbxsg59v47UKDhQL7c", + "settlements": [ + { + "position": 778628500, + "timestamp": 1789891200065, + "type": "settlement", + "index_price": 80292.87, + "instrument_name": "BTC-PERPETUAL", + "mark_price": 80317.84, + "profit_loss": -2.882714252999952, + "session_profit_loss": 366525.4973382353 + }, + { + "position": 778159550, + "timestamp": 1789804800083, + "type": "settlement", + "index_price": 81078.23, + "instrument_name": "BTC-PERPETUAL", + "mark_price": 81109.92, + "profit_loss": -15.834744451000045, + "session_profit_loss": 1564451.0500825022 + }, + { + "position": 778769380, + "timestamp": 1789718400096, + "type": "settlement", + "index_price": 77738.19, + "instrument_name": "BTC-PERPETUAL", + "mark_price": 77762.07, + "profit_loss": 0.33698070499993793, + "session_profit_loss": 660354.2430167407 + }, + { + "position": 796354170, + "timestamp": 1789632000061, + "type": "settlement", + "index_price": 76391.01, + "instrument_name": "BTC-PERPETUAL", + "mark_price": 76418.56, + "profit_loss": 1.8330351599999015, + "session_profit_loss": 357003.47944851726 + }, + { + "position": 794478700, + "timestamp": 1789545600759, + "type": "settlement", + "index_price": 75684.39, + "instrument_name": "BTC-PERPETUAL", + "mark_price": 75709.26, + "profit_loss": -0.049004756999928276, + "session_profit_loss": 676031.4583834042 + } + ] +} diff --git a/openbb_platform/providers/deribit/tests/data/status.json b/openbb_platform/providers/deribit/tests/data/status.json new file mode 100644 index 00000000000..42af414fd53 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/status.json @@ -0,0 +1,3 @@ +{ + "locked": "false" +} diff --git a/openbb_platform/providers/deribit/tests/data/supported_index_names.json b/openbb_platform/providers/deribit/tests/data/supported_index_names.json new file mode 100644 index 00000000000..1dd0aa413e9 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/supported_index_names.json @@ -0,0 +1,192 @@ +[ + "aave_usdc", + "ada_usdc", + "aero_usdc", + "algo_usdc", + "apt_usdc", + "arb_usdc", + "atom_usdc", + "avax_usdc", + "bch_usdc", + "bnb_usdc", + "bonk_usdc", + "btc_eurr", + "btc_paxg", + "btc_usdc", + "btc_usde", + "btc_usdt", + "btc_usyc", + "buidl_usdc", + "cbeth_usdc", + "crv_usdc", + "doge_usdc", + "dot_usdc", + "eth_btc", + "eth_eurr", + "eth_paxg", + "eth_usdc", + "eth_usde", + "eth_usdt", + "eth_usyc", + "eurc_usdc", + "eurr_usdc", + "eurr_usdt", + "fartcoin_usdc", + "fil_usdc", + "hbar_usdc", + "icp_usdc", + "inj_usdc", + "jasmy_usdc", + "link_usdc", + "ltc_usdc", + "matic_usdc", + "near_usdc", + "ondo_usdc", + "op_usdc", + "paxg_btc", + "paxg_eth", + "paxg_usdc", + "pengu_usdc", + "pepe_usdc", + "pump_usdc", + "shib_usdc", + "sol_eth", + "sol_usdc", + "sol_usdt", + "steth_eth", + "steth_usdc", + "steth_usdt", + "sui_usdc", + "tao_usdc", + "uni_usdc", + "usdc_usdt", + "usde_usdc", + "usde_usdt", + "usdt_usdc", + "usyc_usdc", + "vet_usdc", + "xlm_usdc", + "xrp_usdc", + "zora_usdc", + "btc_usd", + "eth_usd", + "1000bonk_usdc", + "1000mog_usdc", + "1000pepe_usdc", + "1000shib_usdc", + "aapl_usdc", + "alt_usdc", + "amd_usdc", + "amzn_usdc", + "anthropic_usdc", + "ape_usdc", + "arm_usdc", + "aster_usdc", + "avnt_usdc", + "axs_usdc", + "based1_usdc", + "be_usdc", + "bera_usdc", + "bill_usdc", + "bome_usdc", + "brentoil_usdc", + "brett_usdc", + "btcdvol_usdc", + "cbrs_usdc", + "cfx_usdc", + "chip_usdc", + "coin50_usdc", + "comp_usdc", + "crcl_usdc", + "dash_usdc", + "dram_usdc", + "dydx_usdc", + "edgex_usdc", + "eigen_usdc", + "ena_usdc", + "etc_usdc", + "ethdvol_usdc", + "ethfi_usdc", + "ewy_usdc", + "fet_usdc", + "gold_usdc", + "googl_usdc", + "hood_usdc", + "hype_usdc", + "intc_usdc", + "inx_usdc", + "jto_usdc", + "jup_usdc", + "ldo_usdc", + "lit_usdc", + "lite_usdc", + "mega_usdc", + "merl_usdc", + "meta_usdc", + "mog_usdc", + "mon_usdc", + "morpho_usdc", + "mrvl_usdc", + "msft_usdc", + "mstr_usdc", + "mu_usdc", + "nbis_usdc", + "night_usdc", + "nvda_usdc", + "openai_usdc", + "opn_usdc", + "pendle_usdc", + "pnut_usdc", + "prl_usdc", + "pyth_usdc", + "qqq_usdc", + "render_usdc", + "resolv_usdc", + "robo_usdc", + "rune_usdc", + "s_usdc", + "saga_usdc", + "sei_usdc", + "silver_usdc", + "skhy_usdc", + "sndk_usdc", + "soxl_usdc", + "spcx_usdc", + "spy_usdc", + "strk_usdc", + "tia_usdc", + "ton_usdc", + "towns_usdc", + "trump_usdc", + "trx_usdc", + "tsla_usdc", + "tsm_usdc", + "virtual_usdc", + "vvv_usdc", + "w_usdc", + "wif_usdc", + "wld_usdc", + "wlfi_usdc", + "wtioil_usdc", + "xpl_usdc", + "zec_usdc", + "ada_usdt", + "algo_usdt", + "avax_usdt", + "bch_usdt", + "bnb_usdt", + "doge_usdt", + "dot_usdt", + "hype_usdt", + "link_usdt", + "ltc_usdt", + "matic_usdt", + "near_usdt", + "paxg_usdt", + "shib_usdt", + "ton_usdt", + "trump_usdt", + "trx_usdt", + "uni_usdt", + "xrp_usdt" +] diff --git a/openbb_platform/providers/deribit/tests/data/ticker_future.json b/openbb_platform/providers/deribit/tests/data/ticker_future.json new file mode 100644 index 00000000000..b9404eb4c15 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/ticker_future.json @@ -0,0 +1,28 @@ +{ + "timestamp": 1789939037426, + "state": "open", + "stats": { + "high": 81497.5, + "low": 80149.5, + "price_change": -0.2487, + "volume": 3100.02328212, + "volume_usd": 250344880.0, + "volume_notional": 250344880.0 + }, + "index_price": 80814.49, + "instrument_name": "BTC-PERPETUAL", + "last_price": 80832.5, + "settlement_price": 80317.84, + "min_price": 79617.5, + "max_price": 82043.0, + "open_interest": 787799210, + "mark_price": 80830.37, + "interest_value": 0.024991800794000794, + "current_funding": 0.0, + "estimated_delivery_price": 80814.49, + "funding_8h": 1.583e-05, + "best_ask_price": 80837.0, + "best_bid_price": 80836.5, + "best_ask_amount": 2520.0, + "best_bid_amount": 65010.0 +} diff --git a/openbb_platform/providers/deribit/tests/data/trade_volumes.json b/openbb_platform/providers/deribit/tests/data/trade_volumes.json new file mode 100644 index 00000000000..178dce51953 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/trade_volumes.json @@ -0,0 +1,313 @@ +[ + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "AAVE", + "currency_pair": "aave_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "ADA", + "currency_pair": "ada_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "AERO", + "currency_pair": "aero_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "ALGO", + "currency_pair": "algo_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "APT", + "currency_pair": "apt_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "ARB", + "currency_pair": "arb_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "ATOM", + "currency_pair": "atom_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "AVAX", + "currency_pair": "avax_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "BCH", + "currency_pair": "bch_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "BNB", + "currency_pair": "bnb_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "BONK", + "currency_pair": "bonk_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 3598.5, + "calls_volume_30d": 469702.8, + "calls_volume_7d": 126410.1, + "currency": "BTC", + "currency_pair": "btc_usd", + "futures_volume": 3324.25470988, + "futures_volume_30d": 176985.7839283, + "futures_volume_7d": 42302.8948902, + "puts_volume": 2512.5, + "puts_volume_30d": 276401.6, + "puts_volume_7d": 65783.1 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "BUIDL", + "currency_pair": "buidl_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 58799.0, + "spot_volume_7d": 16200.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "CBETH", + "currency_pair": "cbeth_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "CRV", + "currency_pair": "crv_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "DOGE", + "currency_pair": "doge_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "DOT", + "currency_pair": "dot_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 37094.0, + "calls_volume_30d": 2042905.0, + "calls_volume_7d": 697174.0, + "currency": "ETH", + "currency_pair": "eth_usd", + "futures_volume": 46811.902868, + "futures_volume_30d": 1743490.936324, + "futures_volume_7d": 524179.809436, + "puts_volume": 31091.0, + "puts_volume_30d": 1450096.0, + "puts_volume_7d": 399976.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "EURC", + "currency_pair": "eurc_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + }, + { + "calls_volume": 0.0, + "calls_volume_30d": 0.0, + "calls_volume_7d": 0.0, + "currency": "EURR", + "currency_pair": "eurr_usd", + "futures_volume": 0.0, + "futures_volume_30d": 0.0, + "futures_volume_7d": 0.0, + "puts_volume": 0.0, + "puts_volume_30d": 0.0, + "puts_volume_7d": 0.0, + "spot_volume": 0.0, + "spot_volume_30d": 0.0, + "spot_volume_7d": 0.0 + } +] diff --git a/openbb_platform/providers/deribit/tests/data/trades_currency.json b/openbb_platform/providers/deribit/tests/data/trades_currency.json new file mode 100644 index 00000000000..4abbc9a6f72 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/trades_currency.json @@ -0,0 +1,85 @@ +{ + "trades": [ + { + "timestamp": 1789939017544, + "iv": 31.75, + "price": 0.0011, + "amount": 0.1, + "direction": "sell", + "index_price": 80805.64, + "instrument_name": "BTC-21SEP26-80000-P", + "trade_seq": 134, + "mark_price": 0.00102437, + "tick_direction": 2, + "starbase_match_id": 227538549002481664, + "trade_id": "445682190", + "contracts": 0.1, + "starbase_timestamp": 1789939017544100459 + }, + { + "timestamp": 1789939015485, + "iv": 34.23, + "price": 0.0265, + "amount": 1.6, + "direction": "buy", + "index_price": 80796.05, + "instrument_name": "BTC-30OCT26-85000-C", + "trade_seq": 660, + "mark_price": 0.02644517, + "tick_direction": 2, + "starbase_match_id": 227538540366409728, + "trade_id": "445682189", + "contracts": 1.6, + "starbase_timestamp": 1789939015485340364 + }, + { + "timestamp": 1789939009041, + "iv": 56.3, + "price": 0.0001, + "amount": 0.1, + "direction": "buy", + "index_price": 80791.09, + "instrument_name": "BTC-25SEP26-95000-C", + "trade_seq": 2213, + "mark_price": 0.00012879, + "tick_direction": 3, + "starbase_match_id": 227538513338314753, + "trade_id": "445682181", + "contracts": 0.1, + "starbase_timestamp": 1789939009041727430 + }, + { + "timestamp": 1789938977723, + "iv": 34.72, + "price": 0.006, + "amount": 0.1, + "direction": "buy", + "index_price": 80784.57, + "instrument_name": "BTC-9OCT26-88000-C", + "trade_seq": 103, + "mark_price": 0.0060825, + "tick_direction": 3, + "starbase_match_id": 227538381981102081, + "trade_id": "445682157", + "contracts": 0.1, + "starbase_timestamp": 1789938977723298197 + }, + { + "timestamp": 1789938977723, + "iv": 34.72, + "price": 0.006, + "amount": 0.1, + "direction": "buy", + "index_price": 80784.57, + "instrument_name": "BTC-9OCT26-88000-C", + "trade_seq": 102, + "mark_price": 0.0060825, + "tick_direction": 3, + "starbase_match_id": 227538381981102080, + "trade_id": "445682156", + "contracts": 0.1, + "starbase_timestamp": 1789938977723298197 + } + ], + "has_more": true +} diff --git a/openbb_platform/providers/deribit/tests/data/trades_instrument.json b/openbb_platform/providers/deribit/tests/data/trades_instrument.json new file mode 100644 index 00000000000..f018178ecc9 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/trades_instrument.json @@ -0,0 +1,80 @@ +{ + "trades": [ + { + "timestamp": 1789939035856, + "price": 80832.5, + "amount": 6000.0, + "direction": "buy", + "index_price": 80815.88, + "instrument_name": "BTC-PERPETUAL", + "trade_seq": 299983429, + "mark_price": 80831.54, + "tick_direction": 1, + "starbase_match_id": 227538625808576513, + "trade_id": "445682210", + "contracts": 600.0, + "starbase_timestamp": 1789939035856973859 + }, + { + "timestamp": 1789939035856, + "price": 80832.5, + "amount": 2520.0, + "direction": "buy", + "index_price": 80815.88, + "instrument_name": "BTC-PERPETUAL", + "trade_seq": 299983428, + "mark_price": 80831.54, + "tick_direction": 0, + "starbase_match_id": 227538625808576512, + "trade_id": "445682209", + "contracts": 252.0, + "starbase_timestamp": 1789939035856973859 + }, + { + "timestamp": 1789939032782, + "price": 80832.0, + "amount": 2660.0, + "direction": "sell", + "index_price": 80815.6, + "instrument_name": "BTC-PERPETUAL", + "trade_seq": 299983427, + "mark_price": 80831.17, + "tick_direction": 0, + "starbase_match_id": 227538612915286016, + "trade_id": "445682208", + "contracts": 266.0, + "starbase_timestamp": 1789939032782921844 + }, + { + "timestamp": 1789939029560, + "price": 80829.0, + "amount": 2520.0, + "direction": "buy", + "index_price": 80813.03, + "instrument_name": "BTC-PERPETUAL", + "trade_seq": 299983426, + "mark_price": 80828.35, + "tick_direction": 0, + "starbase_match_id": 227538599401238528, + "trade_id": "445682207", + "contracts": 252.0, + "starbase_timestamp": 1789939029560823168 + }, + { + "timestamp": 1789939027344, + "price": 80824.5, + "amount": 2520.0, + "direction": "buy", + "index_price": 80815.27, + "instrument_name": "BTC-PERPETUAL", + "trade_seq": 299983425, + "mark_price": 80830.58, + "tick_direction": 1, + "starbase_match_id": 227538590106660864, + "trade_id": "445682206", + "contracts": 252.0, + "starbase_timestamp": 1789939027344529585 + } + ], + "has_more": true +} diff --git a/openbb_platform/providers/deribit/tests/data/tradingview_chart.json b/openbb_platform/providers/deribit/tests/data/tradingview_chart.json new file mode 100644 index 00000000000..efc3168501f --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/tradingview_chart.json @@ -0,0 +1,73 @@ +{ + "volume": [ + 2848.58634312, + 4441.92673055, + 7615.81261958, + 4795.16820914, + 5563.37198675, + 6828.39586782, + 4411.82468822, + 1653.03011548 + ], + "ticks": [ + 1789286400000, + 1789372800000, + 1789459200000, + 1789545600000, + 1789632000000, + 1789718400000, + 1789804800000, + 1789891200000 + ], + "status": "ok", + "open": [ + 77119.5, + 77859.5, + 76901.5, + 75720.0, + 76427.0, + 77759.5, + 81114.0, + 80319.0 + ], + "low": [ + 76361.0, + 76858.5, + 74920.0, + 75018.0, + 75974.5, + 77611.0, + 80149.5, + 80175.0 + ], + "high": [ + 77868.5, + 79596.0, + 77325.5, + 76767.5, + 77801.5, + 81750.0, + 81964.5, + 81497.5 + ], + "cost": [ + 219856350.0, + 347573480.0, + 579555810.0, + 364060340.0, + 427290780.0, + 548134770.0, + 358234380.0, + 133746620.0 + ], + "close": [ + 77859.0, + 76903.5, + 75735.5, + 76427.5, + 77759.5, + 81106.0, + 80318.5, + 80837.0 + ] +} diff --git a/openbb_platform/providers/deribit/tests/data/volatility_index.json b/openbb_platform/providers/deribit/tests/data/volatility_index.json new file mode 100644 index 00000000000..6efdfb96962 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/data/volatility_index.json @@ -0,0 +1,110 @@ +{ + "data": [ + [ + 1789300800000, + 38.62, + 38.92, + 37.79, + 38.89 + ], + [ + 1789344000000, + 38.89, + 38.89, + 37.75, + 37.97 + ], + [ + 1789387200000, + 37.97, + 38.64, + 37.49, + 38.57 + ], + [ + 1789430400000, + 39.13, + 40.17, + 38.89, + 39.76 + ], + [ + 1789473600000, + 39.76, + 41.17, + 39.0, + 39.01 + ], + [ + 1789516800000, + 39.01, + 39.01, + 38.18, + 38.68 + ], + [ + 1789560000000, + 38.68, + 38.94, + 36.19, + 36.19 + ], + [ + 1789603200000, + 36.19, + 36.52, + 33.45, + 33.68 + ], + [ + 1789646400000, + 33.68, + 35.26, + 33.09, + 33.59 + ], + [ + 1789689600000, + 33.59, + 34.6, + 33.55, + 34.12 + ], + [ + 1789732800000, + 34.12, + 35.99, + 34.02, + 35.46 + ], + [ + 1789776000000, + 35.46, + 35.7, + 34.28, + 34.93 + ], + [ + 1789819200000, + 34.93, + 36.52, + 34.64, + 36.32 + ], + [ + 1789862400000, + 36.32, + 36.33, + 34.66, + 34.85 + ], + [ + 1789905600000, + 34.85, + 35.51, + 34.78, + 35.47 + ] + ], + "continuation": null +} diff --git a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_curve_fetcher_urllib3_v2.yaml b/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_curve_fetcher_urllib3_v2.yaml deleted file mode 100644 index 2ebad1838e2..00000000000 --- a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_curve_fetcher_urllib3_v2.yaml +++ /dev/null @@ -1,528 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/get_instruments?currency=BTC&expired=false&kind=future - response: - body: - string: !!binary | - H4sIAAAAAAAAA+2Y30/bMBDH/xc/B2Q7ju30DWgnDe0H4sfThKI0vW5emx/YDoMh/vdd2rIBJRoP - yLApeUrva5/P5498l96Q766ubFOQEeG7lETEgmuXnoy+3JDGmgIyU83gCuWpL7LWzboh8wsymudL - BxFZoIzivPWtBdRKU2Xe5jPI8rJuK3TE6C6NiHFZXnhzCWTkbYsTTeXwpYTKZ1Veopnsnx7sMHq4 - 94knnaN8ATYr6rI0zpm6IqMddEQpi4jfklZK8sCpv246pxYuwTrowoarxtjc44TMmxKcz8sGw1Ox - FKmkqyciRV1h+IXPnPkJd8F7Uyw2Br6Ly0yXNRrW27yn4ViMbpo7yIrWWqiK6/W2yIPIDCZMMKGk - xOUsPBVRwrUULF1F5MD7JaxmNmBN3aX7B8ACna6z/sRWy/wqW3Y/869dzMnGYi5aM1uv9zh76tG2 - 7uu4Z9gRD/Xtc+abDN6LdysLRTe0O7s/wtnJGIWLtvawbf6d3Mx5aBxSeX4bvQ6Y6hXAVIxSHRpM - mXaD+8DUKeOMD2C+GTBj1g/mmpEXhlLHjIqwUMYpProXSrwsFWe6F8oSo/w2UBmSSq7fTfYDUimo - 4psiHraG91P5txo+UPkKVH7cOw5JZcyEDlzAY5oqmvZRybC8p5z3F/CByvBUqsOzkBU8YZQxHphK - gesmvVSmiWSSiYHKN0SlPJkchaRSa/wSD0wl9o6it4JzJbigQ1/5tqgcTw4CUinlHZSB+0o19JX/ - DpVHk+OjyenZ3odAXMY8obGiz24t6cuAyRnVcd91mcR4W3KuVS+Y+NKAb/Pl8+FM6P8E53lEWve+ - Wv/nzLhQiRSJ1Jx19s+tfySkYiWMzXyOCu+OFLNdgd9gfPsLcvm6kb0XAAA= - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '671' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:36 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-17JAN25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VRu3LDIBD8l6sVBhASlro8mqRImvQabJ1tEoQ0AjnJePzvOZSxbE/K273dPZYj - fITej8MGapCMQwYjhslFqI8QbYchmm6AWui8FFLpQgsuMyA0Iin6AT38jSEp9na3h7qqZMEZz8D1 - XzRprYo0DaPdYLPZG78j7R1tKF5kcOjd1BGQ54xXuuIiF2ewmUJLBJOl0lguqO+j7b1xF+qUgfUt - fjdzBmWupCiYUgkOcSSRj403KQYe3h/vhH65f5UFne5MiBdVudKMTgoYo8NZtFBaUj10WWf9AjIt - kTI6swQLzkUpk0eqprE+IvVJbZZyJVTO0/L4eR3IWUGua1pqTLhiNJ9Lm4m1ba8k1dwt4bajX2ib - Fp094Pjz//GLq+n6ydMVuSz5jeuZEExhTi1O4dnf/LZeVZwn/G2Kt0Ql0u9N4clut1DLNESy9Uh7 - W+MCnn4Boe+AJVwCAAA= - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '356' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-28FEB25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VSyW7DIBD9lzm7FmBjjI/pIvXUS+8WiUlCi7FlcJoqyr93IEriqNx424wenODL - D24aN9AAywlkMGk/2wDNCYLptQ+qH6GhoqgoKwUXtKIZIBo0OoZRO7hcfXTszW6PYkIqLnKegR1+ - oJGSFuk2Tmaj281euR2an0hOZFlncBjs3CNAWc7jKevqCraz7yLBBRMov8FuCGZwyi64cwbGdfrY - pik4tWaU52UZYR8mdLnQOhUHwerz+YnVb68rxnF7q3y4unB1ytKyXodgdXItOZ5XGNkbd5/DBY+r - 9ep4V1IuUkosqDUuaGwVO2W8YKIQST19L4KJqHLGMlijrFX+gZIsJiVmbbolUycGCdPje3Rtp605 - 6On3fwe3XNUPs8NNCiZTobfYKyFremlz9u/u4eFFLUkR8Y85PBKS8DISL2a7RYbjDwmY6zTqtsp6 - ff4DhydV1GcCAAA= - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '353' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-10JAN25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VRy1LDMAz8F51Tj+3YsZ0bjwsc4MI94zZua0icTOwUmE7/HSVAmoGjdrVarXSG - 19iFod9BCZxQyGBwcWwSlGdIvnUx2baHkqm8YFwoqZg2GSCaHCq63gX4LuOkOPrDEUpjaCEJzaDp - 3rFSkikiM+gHv3PV7mjDAbUbSiineQanrhlbBHRBuMmZMGYBqzHWSAitlKDTwB84dMl3wTYr7pKB - D7X7qGYXdNWcSSLEBMc0oCqkKtjJCG5f7jaMPt48cYnLNzamq0qY2Si6lBo3ixZKUk407tb6sICF - 0XO21l6djRFz+uk4lQ/J4UXxnpxTzCbo1Dy8rRyLgjCdwRa7KhvXjJ53mYmtr1eEmg0Q9y0+oq5q - 1/iTGz7/p1+m2rYbA65REOny1dBfnBEmnMAzjvEhrB/OmeKUTfjzmP4QQuQTce/3ewwoeAYJ5waH - fXvbRHf5AqMF+TpfAgAA - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '357' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-26SEP25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VRS3PCIBD+L3uODBAgicc+Dj21M+09gwaVlpBMINaO43/vEqvG6XG/F8u3R/gM - nR/6NSyBEwoZDCaMLsLyCNG2JkTd9rBkRa4YF4UsWFllgGg06Oh64+E8huTY2e0OxRRVnMgMXPed - RqkkJTSDfrBrU6932m/RvUBMMlTtOze2CHBJKlYoySp1AesxNEgUiopzxB/su2g7r92MO2VgfWMO - 9fQKLKuSM0mESHCIA7p8rL1OD8HDx+OCq/fnNy5xfadDvLgYVQXa8KVgYnRmct04pXLCygxa62+o - pGL6basPN7DkYopJFdXWR4O9YqssF0qIQtAkH77m0SInWO0KZbUOd4wsUvzErGwzZ86rImFbvEhT - N8bZvRl+/pdwzdVtN3rcJOfV1Og19kIwQo3ANsfw4ueX55xXkib8dYz3RM5klYgnu9ngSSgOEWO9 - Qd1Gu2BOv3IS2hpoAgAA - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '357' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-27JUN25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VRTVPDIBD9L5xTBjYhIT2qFz3oRc8Z2tAWJSQTSK3T6X93obapetz3sW/n7ZG8 - +96Nw5osCVBGMjJqP9lAlkcSTKd9UN1AlrzKSw5FJSou64wgGjQ6+kE7ch59dOzMdodiVpQ5UJER - 23/GMQdI4zCatW7WO+W26F4wyiVARva9nToEZEFrKYXkBb+AzeRbJGRR5Tmj7Aq7PpjeKXvDnTJi - XKsPTUohy1oCF7QoIuzDiC4XGqdiELl7vV9A9fT2DALPt8qHiwtvZ1LEJK9DsDq5Zo4LQes8I51x - MwoCkqNThxkUpUgHx4oa44LGXrFVwCY5MJHk48ev2JJKXL1CXaP8TFGM1eKHWJn21lOnDCRMhx9p - m1Zbs9fj1/8SrmtV108uXoIfTe7r3gtTQnXuc/KP7vb3ADljZcRfpvCH4DVE4sFsNshIVAXc6zTq - Nsp6ffoGy3fWDmoCAAA= - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '356' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-26DEC25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VRu3KDMBD8l6uxRuiBgDJ2ilRp0jOykW05QjBIOMl4/O854dhhklK7t7u6vQuc - Qu/HYQc1MEIhg9GEyUWoLxBtZ0LU3QB1rniRM6FkoSqZAaLRoKIfjIfbMyTF0R6OOEyrsqKEZuD6 - j/QsOWUEZcNod6bZHbU/oHpFSc6kyuDcu6lDgOekEDmOy+oONlNokRBSCDY7/sC+j7b32i24awbW - t+azmVOgrkqWSyJEgkMcUeVj43UKgqe39YoVm+c1k/h9p0O8q/DvTMqUFEyMzsyqBVdUhGXQWf8L - qkLM23X6kZ3PNsklNdRYHw3WGlOPSomK0TQ9vi+NS05KXHuLY40OSwo3TE4zs7XtUsRVCkbCdniQ - tmmNs2czfv3v4OGru37yMZV9O9HD9k5gmOFY5hRe/OLwijFOuUj46xT/EIwWidjY/T4x2FBEW29w - bq9dMNdvYSt3zWcCAAA= - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '356' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-PERPETUAL - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VSy27bMBD8F55lgcuHSPmWNDkUKJCgSM8CY9E2G4oyRNJpEeTfu2RSx06OO7Oz - OzvkC/kd57AcNmRNWEtJQxYbs09k/UKSm2xMZjqQNSjeARNKKsZ4QxBNFhXzwQbyVsai2Lvdnqx7 - rSVtaUP8/IyVYrpWh8Vt7LDZm7BD7QoxzqEhx9nnCQHGOmip6noBoP7DQ45joUAxJaGOeSfCnNwc - jL9gXxviwmj/DHVXccJAtkIUOKYFdSENwZR15Prh2+r+9uf97cOvqx94hDcxnemEbCVeZlPytso+ - KIlBITe5cAI7JXUxN5mP3b2idUYJaXAhWUwWcwWQPUNfrLYvT2eDObSdKl7feoej8RmZkhRQpXoF - nILWVHS6IY+lw8RzveiKh0o8uvHLMYi7CR9uHEbr3dEuf7/mdJpqpjmHYpcLEPRi7omCvhKbvCwl - oW0Oowu7argh79Wg8UfoFjjYlcT3yfF7uPxPWoi+4Hc5fSI6rgpx47ZbZDQendBDsNi3NT7a138K - i9jIvgIAAA== - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '406' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-28MAR25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VSyW7DIBD9lzm7CDDYxrculx6qSlXvFolJQouxZXCaKsq/d3CVxe1x3jJvZuAI - H6H347CGGjihkMFowuQi1EeItjMh6m6AmpV5wbgoZclZkQGi0aCjH4yH3zIkx85udyimTOYlkRm4 - /iuVlMq5HEa7Ns16p/0W3XeUsIqKDPa9mzoEOFMkl5ghq/KMNlNokeFMMqYooRfc99H2Xrtb8pSB - 9a05NHMQ1KpCigiR4BBHtPnYeJ2y4OH98Y5XL/dvXOIGTod4duH4OHCKCiZGZ2bXDVcyovIMOusv - OUqIebZOH65KPBdPW6cjNdZHg5fFuwpeFJVS5SwfP696qqQkafMV6hodFlQxDzQzK9sumDkZCdvh - o7RNa5zdm/H7/xEufXXXTx5HqSq66HrG8SMYgcecwrNfvH2uyqJK+OsU/xCKq0Q82c0GmYJlELGt - N6jbaBfM6QeRjc7xagIAAA== - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '358' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-31JAN25 - response: - body: - string: !!binary | - H4sIAAAAAAAAA2VRy1LDIBT9l7tOmUBICNn52OhCN+4ztKEtSkgmkKrT6b97QRszOqw4554HlzO8 - +sFN4w4aYCSHDCbtZxugOUMwvfZB9SM0VBQVZVyUglKeAaJBo2IYtYPvq4+KozkcoZFSMEHKDOzw - jreaypLkGYyT2el2d1TugNpNTnIuZAanwc49AlwQwfEUvL6C7ey7SDBeFnm0+IHdEMzglF1xlwyM - 6/RHm1JiKqMl4TzCPkyocqF1KgbB7cvdpqCPN0+sxPJW+bCoJK1Tc69DsDqJfilZEMEy6I1bQFGJ - 9LZeLck0z6uSRZO4nda4oHGluFDKRcWwbBqf3lbGFSMVzWCLY63ya0akNonYmm5NpATETY9f0bWd - tuakp8//719cVT/MDnvUIu1yMb3ilHDNcY+zf3DrH2eFrGoW8ec5/CFqwSNxb/Z7ZCROBbR1Guf2 - ynp9+QJ/d3xeYAIAAA== - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '355' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:52:37 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -version: 1 diff --git a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_historical_fetcher_urllib3_v2.yaml b/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_historical_fetcher_urllib3_v2.yaml deleted file mode 100644 index 5d21a3d3f4d..00000000000 --- a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_historical_fetcher_urllib3_v2.yaml +++ /dev/null @@ -1,916 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/get_instruments?currency=any&expired=false&kind=future - response: - body: - string: !!binary | - H4sIAAAAAAAAA+2cXXPaOBSG/4uvSUZfluzcEcL2Y7NJJiGdzux0GAeU1Bsw1DbZtJ3+95WAZAv4 - GBssV2mcq9QiRkc8fV+dc2S+O/8kkyieDpwjhxwip+XEMpmNUufo7+/ONA4Hsh9GQ/mohm/SQX+W - DPVLbr84R7fBKJEt514Nq8HbWTqLpRobh1E/jYOh7AfjySxSN8LoELWcMOkHgzR8kM5RGs/UH4ZR - on4ZyyjtR8FYXXaOe50DjN63z4irbxTcy7g/mIzHYZKEk8g5OlA3Qgi3nHRjaD7irtw0/TrVN43l - g4wTqactH6dhHKTqD/ppOJZJGoynanqCcuZzNP9pOYNJpKY/SPtJ+E0+TT4NB/fLC+RQvc3NaKIu - LML8aUy9Vs3uJkhkfzCLYxkNvi7CclZmFqoFY5gJztXbxTJrRi7xOMP+fEaJTNORnP/lVMbhRC/3 - v1Leq5suVj0j1HHw2B/pfwZ3es7u8kr4ZRYOF++3vnpiLayfx1XM8oCtjm9+zmS5gj/Nd2MVBvql - +rP7f+D66kQNfJlNUrl5+Xlx+0kqp4mi8tOP1jqYMv28G5iFqOz23tpH5QqS6JCUZVIFBTDp5zNJ - Nj5jg0z6+yKJc5FcLIIRJOvRSvELqBRqTb26tZL7+sUQl56PCSaNVlqilbZRaUgrFZNoC5O40cpi - Wjl8mIw0loPSXKLDYmSq/1InH85P+2penQPiw4guNKwEnqMwkkGcB6eHqeDZcM6nv0IgLq+YOrAs - QhWFHkwoxpTnEDpWE/28gehzsKuAYrSfaD7Jwj6iqT9ZENEOwGjHKkOnuEIui8imRzFi9Zo59dUP - jKXaYwqCvZJYNm5uys2tQtKIk8+BhDPxBZCiTiBfrpVPg8e7XX0cFYPyov3xzcLFK0WzgIvDYC4m - X9jGM8jUUWVYOFL4wYkPxcwjglVj4W5j4VVYOPH+6B7XqJcMCeIDm0ujtcucneWW2mVj4fVauFVI - GitcipxkfEvhsrHwX2XhlaK51cLzwDRm4Rj5nguiyVzfJZg2Fm6Vhf/VvqxTL/U+ruaSOkW+QGCr - B2Pu+4TAJfXGwuu2cIuQNJOFayBFPpC4sXAbLbxCNLdbeA6YRrNweHepZ4TdJgu3ysLF++s6q5Yu - RhiTmi2cqfcFd5bYdznmuCyWjYUbs3CbkDRj4RpIkg8k4o2F22fhVaK51cLzwDSahbMmC39JFs6v - uhd16qXnCVpzIZ0KShhYSCeCEYaaXrg9Fm4TkmYsXFBM6RYgm164TSp50u3UiCTnT0TW3G4ES0NN - u9E6lbQISVPtRu5bBeTLVclgGOyW5+BiItk+aS+SnIvu5UW3d90+rSXNocRFVCC4rbMG5uaWP2t4 - DU4V2yacBGOGoWolZ8Ljz2eRs+BUv0xlOgtGRdOdffXS/nQnGN1NzDJ6+ub894VUBZdFqQsXL7mr - GPbzJLShdJPSBxWe0Uco2h/aHy3EdPMRinxKsxhVkWUqKRJQPVMpqUsIapS0VOFo8HnnoiYplhN1 - 3lqJqJ7+foyqyAAZhY5xaBl1sUfgZ3waRDcRjW4M192Pz47tRHRDRnOTJTcD0bPjjJIS9z0MJkuE - uxwTv1FR28rvFcO5NY/fuhc9XN+NVlJbUuLoUagC71JGGCEeXPCE6QTT+X3bQ5bXl5Zw7qSfRQW0 - 17FTQNcUFOsJAIguxooRigWCWpeccY8wyuCTmqX189W0L1PziPZeCaIeofDBD0QYZ8SHu+svFdEe - iGgPQLRXFNHh5E7uWnIq5vEn52+6FqrofPqrEMoD2Omfh9cw1dEB2RJk9otsSexk9q92KzqcpDti - WhDSno2MbpRF86uiGXtRFVim03MONd85E77LmnpTKTzNNzttS5NKpfBF+50qSRIcaim5LnG5x3IO - wBtKkl5uz3OJpcEak5q8hcK5/+lOAE9MEdhLYoJw4lWpm5ZsP83r5k4ZUilA7UuQjACq8yPwVF2T - H5XPj1Tg9yY3nqfvzv60UEAr2HnqyIDMCCwyLdvxFRaZfv+t52j3OmhBRC2tgu7djD/NroIqQKFv - QloASmlzXqQEoDpskxJ61m1fWghoBRKqIwMIhb5GbimhZJck6dUSWuszcFZBauwxOEQJAQv1lKo8 - SqH6221EDTKamP7e4qun7yy2DNC9bf4q6zuL9UOYFDzPxARlfsan3YgoDGga73gqtGgjqXdp46HQ - ivpIKjjA6aETTU9tpObQXQlIZ1Focit6ffbORkT334mqwAA8wTbSYiPaaGgZPB/jqdHnPz5eXtjI - ZyWPf6jYshAlgoIKygSjrus32TyM6KeWM0veRfNdO8eEcpe5nAif6Ovns3RtwGe+HjgJb291x06t - fKqWO5LpkuUf/wEjI6skBm4AAA== - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '1786' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:34:14 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/get_instruments?currency=any&expired=false - response: - body: - string: !!binary | - H4sIAAAAAAAAA+zdbZPc1pmt6f+iz00GXjawgf4m0bR1Oty2QqInHDExUcGXtMVpipSLlEbnOPzf - B5lJ0iQrsasShYXnZnH1mTkzRbLFrMLFBWCvZwP//Or/ff3q5eXPT7/6z6+a+9VX//HV5e71Ly/e - fPWf//c/v/r58vnT3cXzl892v02//eTlk4tfXj97uv8zf/vHV//5t8cvXu/+46v/mX5/+t1XP795 - /url9Hs/PX958eby8bPdxeOfXv3ycvov1f/x1fPXF4+fvnn+6+6r/3xz+cv0v/X85evp/+en3cs3 - Fy8f/zT98lff/Ombi7/88LsH9/r/+vpPTXevb6p7D/b/vcf/s7u8ePrqp5+ev369/yv+s7pfVdX0 - H31z+ne6j/7rb/73z/v/+ovnL3ePL6f/3O63n59fPt5/1os3z3/avX7z+Kefp4+Y277uqlTt/+c/ - vnr66uX0LTx9c/H6+f+Z/rfr+9OvTf/F5/8zfTF9rv2Xb54//Z+3v13dnz7Mkxevpl84fuNXfu/x - 693F018uL3cvn/7v47f61Ucf8vn0E0x17uo8/d2Xu1Mfrxtz7vvjx3u9e/Pmxe7wv/nz7vL5q/3P - /9nj/z39N49H4d03/fTxixdfffzRrvy02o9//+rRa7pP/soPvpH98fpq/+Oa/uT+UFz5nX/88urN - 7sSvv/8JXbx+s/v59aTt//nXfwDAffelgRtWBffzL2/s7cbeWmjAtUJvowMuFBwy4ITgmsoBF+Yt - QQMuCb3VDrhQcMiAU4JrHHBh3jpowHVCb60DLhQcMuCU4JIDLsxbDw24Xuitc8CFgkMGnBJc74AL - 85ahAZeF3lwyxIJDBpwSnEuGOG8DNOAGoTeXDLHgkAEnBNe6ZAj01kEDrtN5c8kQC44ZcEJwLhni - vI3QK7hReEJ1yRALDhlwSnAuGQK9Qa/gRuEJ1SVDLDhmwAnBuWQI85Yr5hXc9Ll03lwyxIIjBpwU - nEuGQG/MK7jpc+m8uWSIBccMOB245JIhzlsNvYKrdSfU5JIhFhwy4JTgXDIEeoNewdXCE6pLhlhw - zIATgnPJEOcN+jSRLHy4Q3LJEAsOGXBKcC4ZAr1Br+Aa4QnVJUMsOGbACcG5ZIjzBn1cUhY+vSa5 - ZIgFhww4IbjOJUOgN+gVXKs7oXYuGWLBMQNOCM4lQ5w36PPgsvDxXJ1LhlhwyIBTgnPJEOgNegWX - hCdUlwyx4JgBJwTnkiHOG/SBl1n4/MHOJUMsOGTAKcG5ZAj0Br2C64QnVJcMseCYAacD17tkiPMG - faJvFj5gtXfJEAsOGXBKcC4Z4rxBn+ibhQ9Y7V0yxIJDBpwSnEuGOG/QJ/pm4QNWe5cMseCQAacE - 55Ihzhv0gZdZ+PzB3iVDLDhkwCnBuWQI8zZAnwc3CB/P1btkiAVHDDgluOySIc4b9HFJg/DpNdkl - Qyw4ZMApwblk+MDbm6d7b2dzO/zIbwLu0b+t7X+wpXhrZ7W1p7Rd7n7dXb7ePVvF2/1+lz7xdvQ/ - S+7tb3+q7tGDU+r6sSrcNwxj3cTE3By647dx2twpcsc/f1XcP6/8SKe/8vGTV7/uLg4Oj7/Y/Ysg - s5CDd1tm4Q5jgcx18tAwDzAHcmQOYph14U7EkUmQSY1MuczCPYsjMxTmWHEjc7xfqWEW7m0cmQSZ - zMjcQGZh1MqRGQuzIUdmo4ZZGMlyZBJkUiNTLrMwvOXIjIXZkiOzVcMsDHk5MgkyqZEpl1kYB3Nk - xsJM5MhMapiuf+AyqZEpl+n6BwuzI0dmJ4bZuP6By6RGplym6x8sTPCQ0Sgf5Whc/8BlUiNTLtP1 - DxdmR45M7ck89818/9NXfarq2pkZSxObmXKa8wXQEpoOzRVlZvJ1Zlafzd2Zw2VSM1Mu0505Fyb5 - OjPLT+bzpbmvMxE0sZkppznfmvs6M1gmeAfQKN9nUchMX2ciZFIzUy7Tg0ZcmOTrzEF+Mp+fNPJ1 - JoImNjPlNOdHjXydGSxzJF9njuqzuacz4TKpmSmX6elMLkzydeaoPpm38+OZvs5E0MRmppzm/Hym - rzNjZdYHIdDUnL7YddLTeSE0faHJoMlMzQ1oeqidK7NGh2atpumxdrpNbGrKbXqwnUsT/GCj6YtG - TdNTmnSb2NiU2/ScJpcm+OFG0xetlGauh9Ibd6q+Ghyb0Taxsam2ORbWjxbYdGyuSRP8gKPpi6Q+ - o3tWk24TG5tym57W5NIEP7Fj+qJX0/ToEd0mNjblNj18xKUJ3hg0fTGIaSYX6XSb2NiU23STjqV5 - GPLAxqa6rEwu0uk0qakpp+kenSsT3aPX6q4yuUen28Smptyme3QuTXQhVGsX3XNTKoTGpu4qx2a0 - TWxsym0WCqEFNu98bO7e/LiI5s1gPnz07TuYzYiNzOb+uGvXUDl9uzMnc2SFXs25PH4fN3d5/POf - sUtmXG7gklifm+WBZYeNy+mj7b9WukR254b5DiY0LzeASSzO7XJyCR5tb+9X4vN4hyzN7fKdS2Ze - buCSWJib5YEl9vpy/9HEp/EOWZcb5juY0LzcACaxLLfLvcsafH1Zq8/jyKLcLt+5hOal3iWxJDfL - A0vu9WWtP42770HDhOblBjBd+FBdNuDry0Z9Hnffg3YJzUu9S9c9WJbc68tGfhrv3fegYULzcgOY - LnywLnOHDczcqV268EHDpAamHqYLH6rLFnxD3opvfHoXPmiX0LzUu3Thg2XZYK8vp4+mPo278EHD - hOblBjBd+GBdclcwW/1CkRsfNExqYOphuvLBuuSuYLbyhaLsygcNkxqYepiufKguE3gFM4lXirIb - H7RLaF7qXbrwwbLkrmAm+UJRduODhgnNyw1guvLBuuSuYCb5QlF25YOGSQ1MPUxXPliX3BXMpF8o - cuWDhkkNTD1MVz5Ulx14BbMTrxQNbnzQLqF5qXfpwgfLkruC2ckXigY3PmiY0LzcAKYrH6xL7gpm - J18oGlz5oGFSA1MP05UP1iV3BbOTLxQNrnzQMKmBqYfpyofqsgevYPbqlSI3PmiX0LzUu3Thg2XJ - XcHs5QtFoxsfNExoXm4A05UP1iV3BbOXLxSNrnzQMKmBqYfpygfrkruC2csXikZXPmiY1MDUw3Tl - Q3WZwSuYWbxSNLrxQbuE5qXepQsfLEvuCmbWLxS58UHDhOblBjBd+WBdclcws3ihKB8/ggMTC5Ma - mHqYrnywLrkrmFm8UJSbVFhZH5vkwAyGSQ1MPczC0voCmA7M9VwO4BXMQbpSNJ3HXZGjXULzUu/S - DTmWJXcFcxAvFOWmK6ysj23dNc7LUJjQvNwAZmFpfQFMB+aKLrkrmIN+ocgjRWiY1MDUw/RIEdYl - dwVzkC8UlV6YMrZd68CMhUkNTD3MwlLRApgOzPVcjuAVzFG9UuQZTLRLaF7qXXoGE8uSe0M+6u97 - PIOJhgnNyw1gegYT6nIPBBqY6X4lPo/XHsFEu2Tm5QYuPYGJZYm9vtx/NPFpvPZEERomNC83gOmR - IqrLGnx9WavP4y7I0S6heal36X4cy5J7fVnrT+Pue9AwoXm5AUwXPlSXDfj6slGfx933oF1C81Lv - 0nUPliX3+rJRn8ab0o7IsW6z8zIWJjQvN4BZerTB+TAdmOu5bMHXl632PN6U7sfHeuicl7EuoXmp - d1naqXu+S8fliiy515et/DTeljbqNtPndF6GwoTm5QYwCzfkC2De/cB8/erF3uXT82FWN5L5w5// - eDF9Nw/e8ayHoRSb9azO7pTOF89f7h5fLrX5Ac3pY12RWWJ5wuT0nZ40mXKhhBxz7oMuLmdV7g/X - LMsHp1y+/fWrMCHgCnF4J8GV7mbOB7dSCH4p3sYGGXBjo/NWuktxwG0AjhhwSnCl2w8HnNpbzwy4 - XuetdFfhgNsAHDLgdOCGwn4tB5zY2zWDNlHepo+l81Z6Xo8DbgNwwICTgisNKDjg1N4SM+CSzltp - 7sABtwE4ZMAJwZVeleCAU3tD3qJOH0vnzSVDMDhkwAnBuWSI9IZsUaePpfPmkiEYHDLghOBcMgR6 - q5lrcLVwScQlQzA4YsAJwY0uGSK9IcdEpo+l8+aSIRgcMuCE4FwyRHpjlgy1bs13dMkQDA4ZcEJw - LhkivTFLhlq35ju6ZAgGhww4ITiXDJHemCVDrVvzHV0yBINDBpwQnEuGQG8Ns2RohGu+LhmCwRED - TgfucGgdcGHemCVDI1vz7SqXDMHgkAEnBOeSIdIbs2RoZGu+XfEJig64DcAhA04IziVDpDdmydDI - 1ny7yiVDMDhkwAnBuWSI9MYsGRrZmm9XuWQIBocMOCE4lwyB3lrmGlwrXBJxyRAMjhhwQnCl19s7 - 4OTemLeore6OofTaegfcFuCQAScE55Ih0FtijokkXWtfes28A24LcMSAU4JzyRDpjdmiJl2pVXox - vANuC3DIgBOCc8nwb2+/Xf681Ft1M3F//f67j8U9qzkRV31obq/oxNt7ZtG9/e1P3E3f70l3Ey4H - XTQ8TNRtCa/3mlywuw6beJ1Wnlfn4ulRM09Mz+t0sfJAXevH8BqtO6/XhcODJp4Yntftgt1hr/Ia - 8anWK3jx9KiZJ6bntbxgeZmbellqbyg8Eaev8thXjr0N8FFzT41vfnJ9CT4H37n2WmrstdoTrndM - hMODRp4YnndOBLvj3t+26rsMN7cAfNTYE+PLbm+D7XFzTyzP7W08PWrqiem5vQ2Wx13Xa7VLK212 - gwvAR809NT63uLH2QPsvPpaXtO7c4YbDg0aeGJ4b3GB33HW9pF5acZkBwEeNPTU+FxrB9ri5J5bn - OiOeHjX1tPQGlxnB8rjrekm8tDK4zgDgo+aeGp8LjVh72NgTu3OZEQ4PGnlieC4ygt1x1/U68dLK - 4DIDgI8ae2p8LjSC7XFzTyzPdUY8PWrqiem5zAiWx13X69RLK64zAPiouSfGN7rQiLUHeuPFx/J6 - rTuXGeHwoJEnhuciI9gd9kKvF59qXWXE06Nmnpiey4xYeZmaeVnrzkVGODxo4onhucQIdoe9ylMv - qLjEiKdHzTwxPZcY/5b35M3TvbwSvP338+TVSX6HNzLdQN83jx7ce/DNXx6903d4cOHFOOz/7/qA - Yk7iDMNTBi93v+4uX++eLVV45U1Ux38Gn7qavpeTrsZq/iqur8ZmbLtrXJ0+/se/7/ThP3X0j3+e - fPDz8eB3d+rgl54SO7Zp8MF/e/D3h30c79a//NJTWse2zzXt4O/e/HiLg3+zQ//w0bf3Hvzuf339 - h3v5cOwv3hKYfibVRdvl7vM58tO3cvrIF17b29d1l5tx0ZE//n03P/LHP4868t/d9SM/v1Y4Hfm+ - GZcFvvDIP3n5ZOFNxs2S/k/fHO8wjsf8Xl9v/BLvpu2HwsF+d38xfa7bvnN0+lZPohgK2zD6qu+q - 5ou6qw0At+kdLQFcIYUWgPvM72U39tZAA67ReWuK3lL1ZS3bBYBDBpwSXGlR5XxwDrhzvLXQgGuF - 3krrOA44PThkwCnBldaOHHBabwkacEnobb79csBtAQ4ZcEpw8/t2HHBqbx004Dqdt3Z+q44Dbgtw - yIBTgpvfo+OAU3vroQHXC73Nb81xwG0BDhlwSnDFVssBJ/WWoQGXhd5cMsSCQwacEpxLhkBvG2+B - uLG3TufNJUMsOGbACcG5ZIjzNkCv4AbhCdUlQyw4ZMApwblkCPQGvYIbdCfU5JIhFhwz4ITgXDLE - eRuhV3Cj7oSaXDLEgkMGnBKcS4ZAb9AruFF4QnXJEAuOGXBCcC4ZwrzlinkFN30unTeXDLHgiAEn - BeeSIdAb8wpu+lw6by4ZYsExA04IziVDnDfo00Sy8OEOnUuGWHDIgFOCc8kQ6A16BVfrTqidS4ZY - cMyAE4JzyRDnDfq4pCx8ek3nkiEWHDLglOBcMgR6g17BNcITqkuGWHDMgBOCc8kQ5w36PLgsfDxX - 55IhFhwy4JTgXDIEeoNewbW6E2rvkiEWHDPghOBcMsR5gz7wMgufP9i7ZIgFhww4JTiXDIHeoFdw - SXhCdckQC44ZcEJwLhnivEGf6JuFD1jtXTLEgkMGnBKcS4Y4b9An+mbhA1Z7lwyx4JABpwTnkiHO - G/SJvln4gNXskiEWHDLglOBcMsR5gz7wMgufP5hdMsSCQwacEpxLhjhv0OfBZeHjubJLhlhwyIBT - gnPJEOZtgD4uaRA+vSa7ZIgFRww4KTiXDHHeoE8TGYQPd8guGWLBIQNOCc4lwwfe3jzdezub2+FH - fhNwj/5tbf+DLcVbO6utPaXtcvfr7vL17tkq3u73u/SJt6P/WXJvf/tTdY8enFTXDfPXcd04pGoI - ibk5dMdv47S5U+SOf/6quH9e+ZFOf+XjJ69+3V0cHB5/sfsXQWYhB++2zPkLviUy18lDwzzAHMiR - OWhhtvX8ibobc+5jrgwt898yqZGpltnMzw0skenIXA/mWHEjc7xfqWHOzxc4MhEymZG5gcz5SQRH - ZjDMhhyZjRrm/MSCIxMhkxqZcpnzsw2OzGCYLTkyW/GK0Vi6/Rm7unFkBsukRqZcZun+53yZjswV - YSZyZCb1uXx+bMxXmQiZ1MiUyywVk77KDIXZkSOzU8Ms9ZKOTIBMamTKZc6PrDkyg2GCh4xG9ShH - 27gxh8ukRqZaZuvGHAszkyMzq2G6MYfLpEamXKYbcyxM8FzmKJ9+a92Yw2VSI1Mu0405FuZIjsxR - DdP1D1wmNTLlMl3/UGHWByDQzJy+2HVame5/6DSZobkBTRdAXJk1OjRrNU1XQHSb2NRU20wugbg0 - wZuApi8aNU3XQHSb2NiU23QRxKUJ3gg0fdGqaboKotvExqbcpssgLk3wZqDpi6Sm6TqIbhMbm3Kb - LoS4NMEbgqYvOjVNN0J0m9jYlNt0JcSlCd4UNH3Rq2m6EqLbxMam2mbnSohLEzzlPn0xqGm6EqLb - xMam3KYrISzNw2wcNjbVMx6dGyE6TWpqymm6EOLKRI8f1eoRj86FEN0mNjXlNl0IcWmie/Ra3VV2 - LoToNrGxKbfpQugsmrs3Py6ieTOYDx99+w5mW3XUyNx/tCvvPV3Ecvp+T7MckWVQNQfz+H3cHObx - z3/GMJl5qYeZKmITZJd7lzX2GrO9X+9aLUtkC2SX71xC81LvktgAmeWBJff6stafxpH9j2G+gwnN - yw1gEtsfu9y7bMDXl436PI5sfuzynUtoXupdElsfszyw5F5fNvrTOLLyMcx3MKF5uQFMYt9jl3uX - Lfj6slWfx933oF1C81Lusnbdg2XJvb5s5afx2n0PGiY0LzeA6cKH6jKBry+T+jzuvgftEpqXepeu - e7Asp/8LGpfTR1Ofxt33oGFC83IDmC58sC65N+RJf9/jwgcNkxqYepgufLAuM/cKM8tP5G580DCp - gSmH2bjyobrswCuYnXilqHHjg3YJzUu9Sxc+WJbcFcxOvlDUuPFBw4Tm5QYwXflgXXJXMDv5QlHj - ygcNkxqYepiufLAuuSuYnX6hyJUPGiY1MPUwXflQXfbgFcxevVLkxgftEpqXcpetCx8sS+4KZi9f - KGrd+KBhQvNyA5iufLAuuSuYvXyhqHXlg4ZJDUw9TFc+WJfcFcxevlDUuvJBw6QGph6mKx+qywxe - wczqlSI3PmiX0LzUu3Thg2XJXcHM+oUiNz5omNC81MNMrnywLrkrmFm+UJRc+aBhUgNTD9OVD9Yl - dwUzyxeKkisfNExqYOphuvKhuhzAK5iDeKUoufFBu4Tmpd6lCx8sS+4K5qBfKHLjg4YJzcsNYLry - wbrkrmAO+oUiVz5omNTAlMPsXPlgXXJXMAf5QlHnygcNkxqYepiufKguR/AK5iheKerc+KBdQvNS - 79KFD5YldwVzVC8U9dV849NXzdBWzstQmNC83ADmfOWzBKYDc0WX3BXMUb9Q5IocDZMamHqY7sih - LlOFvSFP9yv1jY8rcrRLZl5u4NINOZYl9vpy/9HUp3E35GiY0LzUw+zdkFNd1uDry1p8Hu9dkKNd - QvNS79L9OJYl9/qy1p/GXZCjYULzcgOYbsipLhvw9WWjPo+770G7hOal3qXrHixL7vVloz+Nu+9B - w4Tm5QYwXfhQXbbg68tWfR5334N2Cc1LucvsugfLknt92cpP49l9DxomNC83gOnCh+oyga8vk/o8 - 7r4H7RKal3qXrnvOY/n61Ys9y6fnu6xuBPOHP//xYvpuHrzTWQ+pFJr1LM7uFM4Xz1/uHl8upfmB - zOljXTmRl1ieMDl9pydNDuP8PU9f5SoPMVk5q3J/uGZZPjjl8u2vX4UJAVdIwzsJbv5mZgm4lULw - i/E2MANuUHnLw/w9Sl/1qQq6GPyCwCEDTghu/u5jCTgH3FnexgYZcGOj8zZ/V+GA2wQcMeCU4ErP - T3HAqb31zIDrdd5Kj0VxwG0ADhlwQnDz81wOOLW3awavo7xNH0vnbX5MywG3CThgwEnBzQ9gOeD0 - 3pC3qNPH0q35lrwNXd044MTgkAEnAzdWpVbrfHAOuPO8IVvU6WPJTqjFFtVXcBuAQwacEFypRfUV - nNobcg1u+lg6b25Rg8EhA04Izi1qpDfkmMj0sXTe3KIGg0MGnBCcW9RAbzWzZKh1a76jW9RgcMSA - U4JzixrpjVky1LqxpNEtajA4ZMAJwblFjfTGLBlq3c6ZYqnlgNsAHDLghOBcMkR6Y5YMtWzNd6hc - MgSDQwacEJxLhkhvzJKhlq35DpVLhmBwyIATgnPJEOitYZYMjWzNdyi+wdoBtwE4YsApwblkiPTG - LBka3U6GyiVDMDhkwAnBuWSI9MYsGRrdmm/tkiEYHDLghOBcMkR6Y5YMjW7Nt3bJEAwOGXBCcC4Z - Ir0xS4ZGt+Zbu2QIBocMOCE4lwyB3lrmGlyrWxKpXTIEgyMGnBKcS4ZIb8xb1FZ4x+CSIRgcMuCE - 4FwyBHpLzDGRpGvtG5cMweCIAacE55Ih0huzRU26UqtxyRAMDhlwQnAuGf7t7bfLn5d6q24m7q/f - f/exuGdVx8m46kN09/d/xUeujn/rLLu3v/2JvOk7Pi1vKJ9a05f1AsEgepi025Ze+Sx7Lj2H3rny - amrm1Up3/coPfnDkLYAHTTwpvOwN+dHusFd5tfRUm1feKe3IW0KPmnlieh4viZUHGjD5GF6jdbdu - 6+/IWwAPmnhieG5jg91hr/Ia7al25b04jrwl9KiZJ6bnAiNYXuamXhbbW/cOw7G3CB8199T4PFwc - a6+lxl6rdecSIxweNPK08DyLF+2Oe3/biu8yVh6TcugtwkeNPTU+FxnB9ri5J5bnHbPx9KipJ6a3 - 7l5Gh97Z8rjreq14aaX1NjMAPmruqfGtuwXIwXeuPdCms4/lJa07P/4pHB408sTw1r3JcOCd7Y67 - rpfESyutn5gCwEeNPTU+z+sF2+PmnlZe8rxePD1q6onpeV4vWB53XS+Jl1aS5/UA+Ki5p8bneb1Y - e9jYE7vzvF44PGjkaeF1ntcLdsdd1+vESyud5/UA+Kixp8bneb1ge9zcE8vzowfi6VFTT0zPZUas - PNDbLj6G10vd9X5icjg8aOKJ4a07L+XAO9sd9iqv155qe+/KiKdHzTwxPe/KiJWXqZmXpe6yd2SE - w4Mmnhiex1X+7e7Jm6d7dyV2++/nyauT+A4vJrmBvW8ePbj34Hf/6+s/3Kurg76Ltwjr/aGvLuqq - mf4/cxhnJJ5ieLn7dXf5evdsKcQrL2U5/kv41Nb0/Zy0NZaepFLXQze019g6beD4950mcErA8c/D - AHx3CsC4P/AXY74bh7/0Mru6Hsc80A7/7s2Ptzj8Nzv4Dx99e/pff9of+zZ3n9Ghn76XmX/5hYao - rvu+zYsO/fHvu/mhP/75tf7l3/rf/HA80MP+nFq6zG1nrzbaxQe7bdqmcLDfXW4M9/vdid1Qpbdx - nZcKw1CscLp3H3LjK1112Pzz1DXc4yevft1dHBwef7H7F0Fm4Tr4bsss3vufLXOdS2HDPMAcyJE5 - aGH21+2Yr2tHZqxMamTKZV6zenCmTEfmejDHihuZ4/1KDfOa8tKRGS2TGZkbyLxmeNKRGQezIUdm - o4Z5TevuyIyWSY1Mucxrpn4dmXEwW3JktuIVo7F0lZmHPnktM1gmNTLlMktXmefLdGSuCDORIzOp - z+XXDNj5KjNaJjUy5TKv2eDjq8w4mB05MjsxzHTNbKgjM1omNTLlMq95AoYjMw4meMhoVI9y9MmN - OVwmNTLlMt2YY2FmcmRmNUw35nCZ1MiUy3RjjoUJnssc5dNvyY05XCY1MuUy3ZhjYY7kyBzVMF3/ - wGVSI1Mu0/UPFeZhWzw1M6cvdieeSbOizM79D50mMzQ3oOkCiCuzRodmrabpCohuE5uacpsugbg0 - wZuApi8aNU3XQHSb2NiU23QRxKUJ3gg0fdGqaboKotvExqbcpssgLk3wZqDpi6Sm6TqIbhMbm3Kb - LoS4NMHT7dMXvZhm70aIbhMbm3KbroS4NMHjmtMXg5qmKyG6TWxsym26EsLSPAx5YGNTXVZe9x5A - p2Y4TWpqymm6EOLKRPfotbqrvO4dlk7NcJvY1JTbdCHEpYkuhGr1ovt17191bIbbxMam3KYLobNo - 3uBteidp3gzm/j16b2G2VUeNzP1Hm3uZ3nks5961NwzFm6DusK62fWRWczDXeoffZwOTmZdbwCze - Ap0Nc6VXC9vl5LLGXmO29+tdq2TZQ1+1bpdvXULzUu/ymuI85k3sZrlnyb2+rNWn8T5f05o7L2Nh - QvNyA5jXVOYOzDCXDfj6slGfx6/py52XsS6heal3eU1Z7riMY8m9vmz0p/FrmnLnZSxMaF5uAPOa - mtyBGeayBV9fturz+DUVufMy1iU0L/Uur6nHHZdxLLnXl638ND6470HDhOblBjBd+FBdJvD1ZRKf - xwf3PWiX0LzUu3Tdg2XJvb5M+tO4+x40TGhebgDThQ/WZe6wgZk7tUsXPmiY1MDUw3ThQ3XZgW/I - O/WNjwsftEtoXupduvDBsmyw15fTRxOfxkcXPmiY0LzcAKYLH6xL7gpmJ18oGt34oGFSA1MP05UP - 1iV3BbOTLxSNrnzQMKmBqYfpyofqsgevYPbilaLRjQ/aJTQv9S5d+GBZclcwe/1CkRsfNExoXsph - 5mrdpXUH5oouuSuYvXqhKFdewUTDpAamHua6K0UOzBVdclcwe/VCUa58R46GSQ1MPcx1b30cmOu5 - zOAVzKxdKcq1Z4rQLqF5qXe57o2P43JFltwVzCxfKKrdkKNhQvNyA5jr3vg4MFd0yV3BzPKFotqV - DxomNTDlMBtXPliX3BXMLF8oalz5oGFSA1MP05UP1eUAXsEcxCtFzbqzbs7LlV1C81Lv0rvIsSy5 - K5iDfKGoXXezrvNyZZjQvNwApjdFYl1yVzAH+UJRu+7eMwfmyjCpgamH6V0+WJfcFcxBvlCUPFOE - hkkNTD1MDxVRXY7gFcxRvFKUPFOEdgnNS71LjxRhWXJXMEf5QlHySBEaJjQv9TA7jxRhXXJXMEf5 - QlHnkSI0TGpg6mF6pAjqMlXYG/J0vxLf+HTeRI52yczLDVx6DzmWJfb6cv/RxKfx3oUPGiY0LzeA - 6RdTUF3W4OvLWnwe79eddHNeruwSmpd6l37KOpYl9/qy1p/GvcUHDROalxvA9B4fqssGfH3ZiM/j - 2Vt80C6heal36flLLEvu9WUjP41nD2CiYULzcgOYviGnumzB15et+jzufhztEpqXepfeEIllyb2+ - bPWncQ+so2FC83IDmF6/pLpM4OvLJD6PD54nQruE5qXepceJzmP55M3T61juv+cnr07irO7fjOc3 - jx7c++6Hd0LH/VG4GKf7oGqO6YxRFdAbkJu+hZPkxuJro8auq4dryJ22cfz7bm7j+Oev2jj3mP/t - lze/XO5OR9HxCNzscNfV4WCfPMD33v2QZ4KoW3yc08HWyeP86ZVac78r5c7NCaTcz6+2dM3Qp3qc - JfD/7Xb/M/1Hjz/1E9/qT49/u3ix//Lx3/efuXv7K8//8cvzZ4/f/ev86KeXu/nEmr7n3b10bWJV - V1LyFjCPv3wjmMVzZAnmjc+ROJWfZE9zrsmZM2EqDT4cTDbzZ8L1TY63JVkXSZ57Hj2D5JOXT/Yk - n4qu27750zcX06d5H5f3pnNG6abiTJ0vnr/cPb5cavPj9y9evdMtXbedis8/fXOSalt4k1i3Z9rX - 11HV3E3MetsfsVlwD2bE3fz0HECucL9wN8nNz3cvIrfSjcIXIu6alZMwcdPn0ombn2BwyG1Djhhy - UnLzswkOOb24BA25JBRXuhF2yG1BDhlySnLzEwUOOb24ARpyg1Dc/IKvQ24bcsiQU5IrLeY55MTi - Oujtaqe7d0iFB9J1zfT7hSLVIbcOOWLIScmV1uQWkHPInSWugYZcozutFl4w7Su5bcghQ05Jbn5A - yVdyenHQNblOuEBSeFu5Q24bcsiQU5JzuxopDjpC0gn7/MKb7x1y25BDhpySnNvVSHHQ4qETrgI3 - blejySFDTknO7WqguB5aPPTCoaXG7Wo0OWLIScm5XY0UV0ND7sqOsfWqrsLjebqm7vvrN+c45G5J - DhlySnKlK7kF5BxyZ4mDtqu9sOpq3a5Gk0OGnJKc29VIcS005FqhOLer0eSQIack53Y1Uhx0hKQX - 9vmt29VocsiQU5Jzuxoprvws2DhxJ563uZo4t6vR5JAhpyTndjVSHHROrhcOLbVuV6PJIUNOSc7t - aqS4DA25rBOXXDxEk0OGnJKci4dIcdBh4F44mZlcPESTQ4ackpyLh0hxIzTkRqE4Fw/R5JAhpyTn - 4iFQXIbueMjC8fPk4iGaHDHkpORcPESKg+54yLrx8za5eIgmhww5JTkXD5HioDsesnD8vHPxEE0O - GXJKci4eIsVBdzxk4fh58cnADrktyCFDTknOxUOkOOiOhywcP+9cPESTQ4ackpyLh0hx0B0PWTh+ - 3rl4iCaHDDklORcPkeKgOx6ycPy8c/EQTQ4ZckpyLh4ixUF3PGTh+Hnv4iGaHDLklORcPESKg+54 - yMLx897FQzQ5ZMgpybl4iBQH3fGQhePnvYuHaHLIkFOSc/EQKG6A7ngYhOPnvYuHaHLEkJOSc/EQ - KQ46DDwIJzN7Fw/R5JAhpyTn4iFSHHRObhAOLWUXD9HkkCGnJOfiIVIcdIRkEPb52cVDNDlkyCnJ - uXiIFAdtVwdh1ZVdPESTQ4ackpyLh0BxI7R4GIWrwMX3rjrktiBHDDkpORcPkeKgxcMoXAXOLh6i - ySFDTknOxUOkOOia3ChcIBlcPESTQ4ackpyLh0Bx+x8uMuWmL3atipybh3BzxJiTmnP1EEoOOkWy - /2C6U6vLh3B0yJyTonP9EGoOWrLuP5jOnAuIcHTMoFOicwURaa6GdhD7D6Yz5xIiHB0y6KToXEN8 - aO7N0725s8kdfuY3Qffoo2cZlpfn2llw7Slwl7tfd5evd8/WIJfvd7v0ibjjP4FZdG9/+1N3jx6c - ctdM/2fWXeqHcbqJCQm7OXbH7+O0ulPojn/+qrl/XvmZTn/l4yevft1dHCQef7H7F8JmIQzvts35 - JeRFNtfJRNN8txMbG5vD/UpKc/qf+WvE1I1d0zs2w20yY3MDm/OXkotsOjbXpAm+2hzEZ/T9DZRj - k26TGptym4U7IcdmNM2eHJu9lGbbNvOr4Gkc9rHq2Iy2SY1Nuc3CndASm47NNWkO5NgctGf0XBdi - s2/z6NiMt0mNTbnNQmwusenYXJPmSI7NUX1GL6wf+WqTYZMam2qbbWEByVebwTTHihubo3zZvdRW - em2TYZMZmxvYnN8Y47XNcJrX7Q8MpVlrhzyaNL9/JvXjkByb8TapsSm3Ob/NZpFNx+aaNBtybDba - M3pfeABd6qd7Id+kx9ukxqbc5vxOsUU2HZtr0mzJsdmKl90L79JJfeoaj7vH26TGptxmqRJaYNOx - uSbNRI7NpF4/Ktyke22TYZMam3KbhTshr21G0wSPu4/qkeJclTZXtu8/pGMz0CY1NuU2S5srF9h0 - bK5JEzzuPopHiqczemH9yFebDJvU2JTbLCwg+WozmmYmx2ZW0/SedLxNamzKbXpPOpgmeJfQqN6J - UZdGih2bDJvU2JTb9J50ME3wLqFRvBNjoulxd7xNamzKbXrcnUuzPhCB5ub+7SSd1qardD5OZnBu - gNNdOtkmeKPQ9EWtxulaiK8Tm5xynS6GyDjBm4WmLxo1TldDfJ3Y6JTrdDlExgneMDR90YpxJtdD - fJ3Y6JTrdEFExgneNDR9kdQ4XRHxdWKjU67TJREZJ3jj0PRFp8bploivExudcp2uicg4wZuHpi96 - NU7XRHyd2OiU63RNRMYJ3kA0fZHVOF0T8XVio1Ou0zURGSd4E9H0xSDG2bkm4uvERqdcp2siMk7w - RqLpi1GN0zURXyc2OuU6XROBcR72a2CjUz1z3Lkl4uOkJqccp0sisk30XqJajLMvvhq4HionJ0An - NjnlOktPgl+g09G5Kk70XqJavVujc7/O14mNTrlO9+tknOiB+Fo9cty5X+frxEanXKf7dTJO9EB8 - rR457t2v83Vio1Ou0/06GOfhfaPY6FTfEfWu1/k4qckpx+l2nWwTfc3ZyM/qrtf5OrHJKdfpfh2M - s0Vfc6ofS9O7I+LjpCanHKcrIrJN9DVnKz+ruyLi68Qmp1ynKyIwzoS+5lTXl9kNER8nNTnlOF0Q - kW2irzmT+KzelW+IhuM/XSdnrE5scsp1Fu+Iztd556Nz9+bHRThvRvPho2/f02wSNjab6ZTeruFy - +n5PuWyq0r1QP0z/b0xqVnMyj9/IzWUe//znLJMZmXqZpTuhJTLXSUzDPMDswZHZS2HWfVMo1Pvm - 8HgzR2aoTGpkymUWyvQlMh2ZK8LM4MjM2nN5nUtXmePepiMzViY1MtUyh9JV5gKZjswVYQ7gyByk - MKtxYA4fWeZ7mdTIlMtETh4Z5gHmCI7MUXv7k5tCZPbt2Doyo2VSI1Mus/SgrgUyHZnrwQS/GLi9 - X6nP5cxBTct8L5MZmRvIRA5pGuYRZoeNzOmj7b/WLRk1pd2+uaqcmeE0oZm5Ac1CNbmEpkNzRZk1 - +DqzFjeTpbN533Zt0HCmZb6XCc1MucyxVAAtkOnIXBMm9zqzFp/M6zyUpjm6dnRmRtOEZuYGNEsr - 7QtoOjRXlNmArzMb8apR6Wzu9UyCTGhm6mUi904a5hEm9zqzkZ/MS6+46lMX9RYh03xPE5qZG9As - XWcuoOnQXFFmC77ObNVnc+bjiC3zvUxoZuplIp9FbJhHmNzrzFZ9Mu/H4hBcN3gTUDRNaGZuQLP0 - qsoFNB2aK8rkPp6jFT8EYTqbMx/ebpnvZUIzUy8T+eB2wzzC5F5nJvHJfJLpnZNwmtDM3ICmt05y - ZXbg68xOfTb3PiC4TGhm6mV6HxAYJvc6s1MvGh0/gjMTTBOamRvQ9LARVyb32Zmt+gmFVeXeHC4T - mpl6me7NwTC515m9/mTuDghOE5qZG9B0CcSVyX3gcCt+rOsE0x0QXCY0M/UyXQGBYXKvM7P+ZO4O - CE4Tmpkb0HQJxJXJfUp7K34Wdl3V7oDgMqGZqZfpCggMk3udOchP5rU7IDhNaGZuQNMlEFcm99UW - rfoFAlXtDgguE5qZepmugMAwudeZo3xTb1165nA9dH5+ZjRNaGbqaTaFZaMlNB2a68nc/+yhoZnE - b12ZzubuzeEymZm5gUz35lyY3FdbJPULBKratTlcJjUy5TLdmnNhcp/SnsTPwq6r0u2PI5MgkxqZ - cpluzbkwuQ8cTuLHuk4wXZrDZVIjUy7TnTkXJvfZmUn8hMIJpjtzuExqZMplujPnwuQ+niPJH4LQ - uP6By6RGplym6x8uTO4OoCTfZ9G4/oHLpEamXKbrnzNh/vz4t7/vZT4V0fzu67/+4WL6dh68B9p0 - TTE661mg3SmgL56/3D2+XIPn/oNdGYLrCji7qzL33+4pmikVbs2nv7dqo0JzFuf+oM3qfHCK59tf - v+qT464QjHfV3fyN9yJ3K0XiF8QuUeMu6di1hbeS79lVfe2407tjxp3U3Xybvcid4+5cdj017nol - u/mu2nG3mTtm3EndzTfRjrtN2A3UuBuU7Mo3s467Tdwx407qrnwz67iTsxupcTfK2HV5KNxUdENX - Rb3G+8tyx4w7qbvCXcUSd467M9ldMxgTx+7E8MF6J9n5iRhf3G3GDpl2Unbz4y6+tttEXQ0Nu+mD - CW8p5odZnHabuUOmndbd/KiK424TdtQxlF45DlB4g6vjbjN3zLiTunMvG8yupcZdq2TnXhbgjhl3 - UnfuZYPZUafueuH0Uyq8N9Vxt5k7ZtxJ3bmXDWZXfup1ILsTDxZej52bCoA7ZtxJ3bmqCGZHHTLu - hcOeqfC2UsfdZu6YcSd156oimF2mxl0Wsiu8aNRxt5k7ZtxJ3bmqCGZH3VPRC2fbU+EdoY67zdwx - 407qzlVFLLuMHTLOsmnPVHi7p9NuM3bItJOyc1ERrI46dZeV00+Fd8w57TZzh0w7rTsXFcHsqGMo - WTkOUHg/nONuM3fMuJO6c1ERzI7ay2ZlP1Z4t5vjbjN3zLiTunNREcyOWlRk5YJx4b1sjrvN3DHj - TurORUUsu2veeRG4Ynz1vQKrqSs+YbEd+lQ57fTskGknZVfqxxaw+7zD7vWrFwvRVTdS98Of//gx - uhoUdR+Qmz7WlfNr6fUpJ96dMn2rp8RNpK5btPuygi6EHCXmtiJ33XqdQ04qDtTFfijuRCO2lrh8 - 3VKdQ05OjhhySnLXrdI55KTiQA3sh+JO9GCribtugc4hJydHDDkluevW5hxyUnGg7f8fijuxCXs1 - cdcNEDvk5OSIIackd93wsENOKg40XPKhuBMV/2rirpsbdsjJyRFDTknuupFhh5xW3MAMuUEkLqW+ - FHL7t6A45OTkkCEnJFcKuQXkHHLniQM9z+RDcSeeKrHaadXtajg5YsgpybldjRXXMEOukYkb3K6G - k0OGnJCc29VYcT0z5HqdOLer4eSQISck53Y1VBxom9eH4k5stllNnNvVcHLEkFOSc7saKy4xQy7p - xLldDSeHDDkhOberseKY7eqgqromcS4ewskhQ05IzsVDqLiRebs6qu4d+mLVNaSxc8jpyRFDTkiu - 1HUtIeeQO1Mcs10ddVXX6HY1nBwy5ITk3K7GimO2q6Ou6hrdroaTQ4ackJzb1UhxDfNRS43wuTfF - h3s55DYhBww5KTm3q7HikO3q9LF04tyuhpNDhpyQnNvVWHHIdnX6WDpxblfDySFDTkjO7WqouJp5 - u1qr7h1SV3x7YT/kyiEnJ0cMOSW50pXcAnIOuTPFIdvV6WOpTqtj5XY1nBwy5ITk3K7GikO2q9PH - 0olzuxpODhlyQnJuV0PFMd/x0OgeuD9WblfDyRFDTknO7WqsOGa72qiqrj6X2tWhzslrcnpyyJAT - kiusyS0h55A7UxyzXW1UVVeuS3NyYzWOfjKwnhwy5ITkCldyS8g55M4T1zJvV1vhvYPn5MLJEUNO - Sc5zcrHimO1qq6q6cpMKc3JjM2SHnJ4cMuSE5ApzckvIOeTOE8d872qjewnmWHkYOJwcMeSU5DwM - HCqO+d7VRvcSzLH2nFw4OWLIKcl5Ti5UHPO9q43uJZhj7Tm5cHLEkFOS85xcqDjmOx4a3QP3x9pz - cuHkiCGnJOc5uUhxLfNRS63uuTdjce+qQ24TcsCQk5Jzu/qBuN8uf14qrrqZub9+/90nzzB8BupX - qw/U1febT8wd/9JZdm9/+xN50zd8Sl6qHHYEepi425SeQy9aHmgPxMfyklaeW1cAPWjoiem5fY2W - B9rC/7G8XiqvcQsLoAcNPTE9t7HR8jI19LJWnltZAD1o6InpuZ2Nlgfa/fqxvEErzy0tgB409MT0 - 3NaGy+uwqdcJ7XW5L9jrct0mx94W+Ki5J8ZX6NGW4HPwnW1vpObeqD3lusEF0IOmnpieG9xwedir - vVF5wk2pL63s1V3r2NsEHzX3xPhKa3sL8Dn4zrQHnda7+miKlU+4HlwJh4dMPDk8j60Eu3tWQa/0 - JnrSk23VenCFgI+ae2J8Hl2JtldTc6/WyvPoCoAeNPXE9Dy6Ei4Pe7VXi0+4Hl4h4KPmnhifx1ei - 7WFX9rSbIVu3uAB60NQT03OLGy4Pe7XXiE+4LjQI+Ki5J8bnUiPaXkvNvVYqL7nSANCDpp6YnguN - cHnYq71We8JNrjQI+Ki5J8bnUiPaHvRxU434mT/JlQaAHjT1xPRcaITLw17tJfEJ15UGAR8198T4 - XGpE28PmnlieKw0APWjqiem50AiXx009pb2+qQpXe3no+8axtwU+au6J8RWu9pbgc/CdbQ/6VOVG - /Gjbzk0ugB409cT03OSGy8Ne7fXKE25ucuEmd5z+H8feJviouSfGV7jNXYLPwXe2Pejj5BvxM707 - D7AA6EFTT0zP4yvR8qCPk2/Ez/TuPL4CoAcNPTE9j69Ey4M+VbmRPtq2b0rvxc1jXbvO2IIeNPTE - 9EplxgJ6Dr0z5UE3pbXiZzx2ntcLh4cMPDk8z+oFu6M+daUVP/qi86wegB409MT0PKsXLQ+6Ha0V - 7wnqPbICoAcNPTE9j6x8IO/Jm6d7eafh/e2XN79cHr6fJ69m+N2/Gb9vHj249/sf7rX1Ad/FW4Rz - /GbsnYJ3uft1d/l692wxvSv3FScsTZ/+pKU8lmJsGPKYrrN0+pgf/8LTh3zmiDMPeDP8/uE3d+mA - l8Jjf8C7L/6A//fX39+lA16atvABnw54/q+/3KlIL804+IBPB7z/4eF3d+mAl0YLfMD3B/x3Dx/c - pQNeKvT3B7z/wg/42wN98d3D72fvED+fo11qlHy0p6Od79I9Wa5L6+nDfjS7xR3w3Zsfb3PAb3S0 - Hz76lnkH/uliz4ljPX32k/+4h7p8OzZU9bJjffwLP/djzbv5vtWxLt+JffHHmnbffatjXb4J+9KP - Ne6W+1bHunz/NVTNF32scXfbtzrW5VuvL/5Y0260b3WsyzdeX/ixht1j3+pAl0bnfKBxt9fLj3Wu - +8I/6sPN9cLFUuGxvsFqyu3XUd4e5ZNH996SgYqbHOQ8XQUPN1xDOTz2bX6Eor7xAks/VoWb7mGs - m/raf+9v/4Vd/VZ/evzbxYv9l4//vv/M3dtfef6PX54/O/59n/70cjc/ezF9z7t76drZi+rKwAdm - 1eeW8YNT+Un0NOeanMmlyWRhEONg8tobxhVNjrclWRdJKrPy5ZOFc2g3S8o/ffN2CC2/HULrq03n - H0s27384g9ZfXYOuC1ZPxeefvjlJtS/tNBiqNPTXUr1To48R5Lace0SQK1UiC8h95iOPG4tLFTPk - ps+lE1fYWeCQ24QcMeSU5EpbChxycnEJGnJJKK50I+yQ24IcMuSU5AoluENOLm6AhtwgFFeo4h1y - m5BDhpySXGEiwCGnFtdBb1c75b1DYS7BIbcJOWLIScmVmlSHnFpcAw25RijOxUM0OWTIKcm5eIgU - B12T65QLJC4eoskhQ05ILrt4iBQHHSHphH1+dvEQTQ4ZckpyLh4ixUGLh064CpxdPESTQ4ackpyL - h0BxPbR46IWrwNnFQzQ5YshJybl4iBQHLR564SpwdvEQTQ4ZckpyLh4ixbXQkGuF4lw8RJNDhpyQ - 3ODiIVIctF3thVXX4OIhmhwy5JTkXDxEiuugIdcJxbl4iCaHDDklORcPkeKgIyS9sM8fXDxEk0OG - nJKci4dIcRkaclkozsVDNDlkyCnJuXiIFAedk+uFQ0uDi4docsiQE5IrvfbUIScXN0JDbhSKc/EQ - TQ4ZckpyLh4CxWXoMHAWTmaWXj3rkNuEHDHkpORcPESKq6Ehd+IVnKuJc/EQTQ4ZckpyLh4ixUF3 - PGTh+Pno4iGaHDLklORcPESKg+54yMLx89HFQzQ5ZMjpyOXKxUOkOOiOh6wbP8/Flx065LYghww5 - JTkXD5HioDsesm78PFcuHqLJIUNOSc7FQ6Q46I6HrBs/z5WLh2hyyJBTknPxECkOuuMh68bPc+Xi - IZocMuSU5Fw8RIqD7njIuvHzXLl4iCaHDDkhudrFQ6Q46I6HrBs/H+rSvUM9jkPlkBOTQ4acklzp - 5mEBOYfcOeIG6I6HQTd+nmu3q9HkiCEnJed2NVIcdBh40E1m5trtajQ5ZMgpybldjRQHnZMbhENL - tdvVaHLIkFOSc7saKQ46QjII+/ziAolDbgtyyJBTknO7GikO2q4OyqrL7Wo0OWTICck1blcDxY3Q - 4mEUrgI3Lh6iyRFDTkrOxUOkOGjxMApXgRsXD9HkkCGnJOfiIVIcdE1uFC6QNC4eoskhQ05JzsVD - oLj9DxeZctMXu1ZFzs1DuDlizEnNuXoIJQedItl/MN2p1eVDODpkzinRta4fQs1BS9b9B9OZcwER - jo4ZdEp0riAizdXQDmL/wWTmcjnoxqZy0KnRIYNOiq4cdGej+8yD7s3TvbmzyR1+5jdB9+ijZxmW - l+faWXDtKXCXu193l693z9Ygl+93u/SJuOM/gVl0b3/7U3ePHpxyl1IqrAw3VY4Kuzl2x+/jtLpT - 6I5//qq5f175mU5/5eMnr37dXRwkHn+x+xfCZiEM77bNwhLyEpvrZKJpvtuJjY3N4X6lpVmX7kv2 - LHvHZrhNZmxuYLMwRLXEpmNzTZrgq81BfUav28KwlWOTYZMam3KbhbEsx2Y0zZ4cm72UZpf6Qsfc - Van3TXq8TWpsqm3mQhW9xKZjc02aAzk2B/H6Uamxbqq6cmzG26TGptxmcQHpfJuOzTVpjuTYHNVn - 9ML6UbefuXBshtukxqbcZmEBaYlNx+aKNEdwJTTql92LTbrXNgk2mbG5gc1ik+61zVia1+0PDKVZ - i2+ESutHTd2Njs14m9TYlNssLSAtsOnYXJNmQ47NRn1GL2x19dUmwyY1NuU2C3Wlrzajabbk2GzF - NFPpatOxibBJjU25zWJd6diMpZnIsZnUND3ujrdJjU25TY+7g2mCx91H+Uhx8rg73iY1NuU2Pe4O - pgkedx/FI8XT/3GTjrdJjU25TTfpYJqZHJtZTdOVEN4mNTblNl0JgWmCdwmN6p0YdedKCG+TGpty - m66EwDTBu4RG8U6MiaYrIbxNamzKbboS4tKsD0Sgubl/O0mntelOiI+TGZwb4HQpRLYJ3ig0fVGr - cboW4uvEJqdcp4shMk7wZqHpi0aN09UQXyc2OuU6XQ6RcYI3DE1ftGKcveshvk5sdMp1uiAi4wRv - Gpq+SGqcroj4OrHRKdfpkoiME7xxaPqiU+N0S8TXiY1OuU7XRGSc4M1D0xe9GqdrIr5ObHTKdbom - IuMET8JPXwxqnK6J+Dqx0SnX6ZoIjPMwOYeNTvX0R/Epxk5OBk5qcspxuiQi20SvdNbqtaTskoiv - E5uccp0uicA4G/Q1p3psrvQuLCcnBCc1OeU4XRGRbaKvORv5Wd0VEV8nNjnlOl0RgXG26GtO9bxx - dkPEx0lNTjlOF0Rkm+hrzlZ9Vh/cEPF1YpNTrtMV0Xk4d29+XITzZjQfPvr2Pc0mYWOzuZ927Rou - p+/3pMtUfpVg06aY1KzmZB6/kZvLPP75z1kmMzL1MstvEjxf5jqJaZgHmD04MnspzHaELm5a5nuZ - 1MiUy0QubBrmAeYAjsxBDZO5qmmZ72VSI1MuE7mkaZgHmCM4Mkf1jXlxMK6ufWMeLZMamXKZxbG4 - 82U6MteDCX6UXHu/Ep/LoQ2QZb6XyYzMDWQi2x/DPMCswZFZq2EyNwdZ5nuZ1MiUy0RuDDLMI8wO - G5nTR9t/Lbz/KT6wuO7aypkZTBOamRvQLF1nLqDp0FxRZgO+zmzUZ3PmVkrLfC8Tmpl6mch9lIZ5 - hMm9zmzEJ/NJpueM4DShmbkBTQ8acWW24OvMVn0296ARXCY0M/UyPWgEhsm9zmzlJ/PRtTmcJjQz - N6Dp3pwrk7ttshVvTptgujeHy4Rmpl6me3MwTO51ZtKfzN0BwWlCM3MDmi6BuDI78HVmpz6buwOC - y4Rmpl6mKyAwTO51Zqc/mbsDgtOEZuYGNF0CcWVyn2nUip8ck46fwJkJlgnNTL1MV0BgmNzrzF69 - 2aKq3AHBaUIzcwOaLoG4MjP4OjOrz+bugOAyoZmpl+kKCAyTe52ZxSfz3PQFmWN7eK+CMzOUJjQz - N6BZqCeX0HRoriiT+8DhVvxY1+ls7t4cLhOamXqZ7s3BMLnXmYP6ZN7X8x1QX7V5dGZG04Rm5gY0 - 50ugRTQdmivK5D6lvVU/C3v6GL7OZMuEZqZepkeNsDD38zbQyEziZ2GnqnCZ6chEyGRG5gYyPWrE - hcl9SnsSPws7N11h0mhsxnpwZAbLpEamXGZh0GiJTEfmijC5DxxO4se6TudyD2fCZVIjUy7Ts5lc - mNxnGiXxk2MmmJ7NhMukRqZcpmczz4T58+Pf/r6X+VRE87uv//qHi+nbefDvV6JOR6IUnfUs0O4U - 0BfPX+4eX67Bc//BrtSTXQFnd1Xm/ts9RbPPuXA2H6ruwDMiNGdx7g/arM4Hp3i+/fWrPjnuCsF4 - V90VTtZL3K0UiV8Qu0SNuyRk1xam14YqDX3tuNO7Y8ad1F1hzmKJO8fduex6atz1SnaFIQrH3Vbu - mHGndJcKMxKOuy3YDdS4G5TsCgMQjrut3DHjTuquUNY57jZgd82jz+LYnXi81Hrqikt3Trtt2CHT - TsquuHLnsNOrq6FhN30w4Tm20KE57bZyh0w7rbvCTnzH3RbsqL1sr+zHkosKgDtm3EnduagIZtdS - 465VsnNRAXDHjDulu85FRTA76hhKrxwH6FxUANwx407qzkVFMLvyA7oC2Z14BtJ67NxUANwx407q - zlVFMDvq1F2vnH7qXFUA3DHjTurOVUUwu0yNu6xk56oC4I4Zd1J3riqC2VGHjHvlsGfnqgLgjhl3 - Sne9q4pgdiM17kYdu2EoVRVdNabKcad3x4w7qbtSVbHAnePuTHbXvOYzcLj96qsU1zvJupgFsEOm - nZSde9lgddQh46wc9uzdywLcIdNO6869bDA76tRdVk4/lV5E7Ljbyh0z7qTu3MsGs6OOoWTlOEDv - Xhbgjhl3UnfuZYPZUXvZLO3H3MsC3DHjTukuu5eNZTdgi4pBtmI8FJ/s2Yxd1CvVvix2yLRTsis+ - 2HMBu8877F6/erEQXXUjdT/8+Y8fo6tBNcUH5OoTi8Wl16eceHfK9K2eFNdc28h+WUEXQo4Sc1uR - u66NdchJxYHKiQ/FnVgiXk3cdUWsQ05OjhhySnLXdbAOOak40IMAPhR3Yjv2auKuq18dcnJyxJBT - kruueXXIScWBetcPxZ1ov1YTd13p6pCTkyOGnJLcdX2rQ04qDrTp/0NxJ7ZerybuuqrVIScnRww5 - IblrW1aHnFQcaKTkQ3Eniv3VxLl4CCdHDDklORcPseISM+SSTpyLh3ByyJATknPxECtuYIbcoBPn - 4iGcHDLkhORcPISKAz2q6UNxJx6Ys4643DSF4mGsx/4Le05TCDliyCnJFYqHJeQccmeKa5gh1+hO - q25Xw8khQ05Izu1qrLieGXK9Tpzb1XByyJDTkRvcrkaKa0A7WD8QN30snTi3q+HkgCEnJed2NVYc - sl2dPpZOnNvVcHLIkBOSc7saKw7Zrk4fSyfO7Wo4OWTICcm5XQ0VVyOLh+lj6cS5eAgnRww5JTkX - D7HikMXD9LF04lw8hJNDhpyO3OjiIVQc83lyjfDhXqOLh3ByxJBTknPxECuOWTw0ulXg0cVDODlk - yAnJuXiIFccsHhrdKvDo4iGcHDLkhORcPISKa5m3q63w3sHFQzg5Ysgpybl4iBXHbFdbVdWV6+Ju - 6bHLlUNOTg4ZckJypfPqAnIOuTPFMdvVVlV15aYqVF1jXbXeoK8nhww5IblC17WEnEPuPHHMF9k0 - wreKjB4hCSdHDDkdubZ0XvXtql4c80U2je6tIm3ptOqQ24YcMeSU5DxCEiqO+SKbRvdWkbbyCEk4 - OWLIKcl5hCRUHPNFNo3srSK5LvX5YzWO577O3CF3PjliyCnJlZ7TuoCcQ+48ccwX2TS6t4q0lefk - wskRQ05JznNykeJa5vPkWt3DvdrKc3Lh5IAhJyXnOblQccy9q61uI2FbuV0NJ0cMOSG52u1qqDjm - CEkr6/NzU7p3GOsueU5OT44YckpyxXcnnU/u8w653y5/Xiquupm5v37/3SeP3H8G2vNQfaCuvt98 - Yu74l86ye/vbn8ibvuGTp9fkVxMS6GHiblN6XqOLlgd6LsnH8pJWntfqAPSgoSem5zW7aHmgjV8f - y+u18rx2B6AHDT0tPb/dK1xepoZe1srzTgkAPWjoiel5x0S0PNAT6T6WN2jleecEgB409MT0vIMi - Wt5IDb1RK89FBoAeNPTE9FxkxMqDdrdXHx62sjvXGOHwkIEnh+cSI9jds5oaebVWnksMAD1o6Gnp - +U1h8fI6bOp1WnuuMQj4qLknxuciI9oe9gZXOyHqt4gR6EFTT0zPRUa4POzVXiM+4brKIOCj5p4Y - n8uMaHstNfdarTzXGQB60NQT03OhES4Pe7XXik+4rjQI+Ki5J8V3OMoOvlB70D24jXYjZOd3WBDo - QVNPTM+FRrg87NVeEp9wXWkQ8FFzT4zPpUa0PWzuieW50gDQg6aemJ4LjXB53NQT23OlQcBHzT0x - Ppca0fagj5pqtM/76fyYeAI9aOpp6flx8fHysFd7vfSEO9SldeV6/NIeHB+Fj5p7YnylheUF+Bx8 - Z9uDPmOv0T7orKvd5ALoQVNPTM9NbrQ86DP2Gu2Dzrri6dahtxE9aOiJ6bnFjZYHfcZeo33QWVe7 - xQXQg4aemJ5b3Fh50E1prfZRZ13tBjccHjLw5PDc3ga7oz5jr9U+6GxoS7e2TddUjrwN6EFDT0yv - dGu7gJ5D72x50EdNtdrn/XS1R1YA9KChp6XXeGQlWh50D24r3gjZuLkF0IOGnpiem9toedDh5FY8 - Idq4uQXQg4aemJ6b22h50HGVVjszkIuvCqqrdnDobUAPGnpieqUiYwG9zzv0nrx5upd3Gt7ffnnz - y+Xh+3nyaobf/Zvx++bRg3u//+FeWx/wXbxFOMdvxt4peJe7X3eXr3fPFtO7UpudsDR9+lOWct2V - VunGthvb6yydPubHv/D0IZ854swD3gy/f/jNHTrgfWltbH/A0xd/wP/76+/v0gEvLUn5gE8HPP/X - X+5SpPelhSAf8OmA9z88/O4uHfDS+osP+P6A/+7hg7t0wEurHj7gP7y7Rbz47uH3s3eIn8/RLs2I - 7492hzvauzc/3uZo3+hQP3z0LfOG7NN7/xPHevrsM8e6tKq0P9b9smN9/As/92PNuxe71bEuLeP4 - WONuw251rK9bZvmyjzXuDuw2xzpft8LyZR9r3M3XrY71dYsr+cs+1rT7rlsd6+vWVb7oYw275brV - gb5uPeUzO9Bv2741DvR3P/y7fq276qKZfv6Ao31q51h98yM+FvbC9nWdpz+yzRE//nnsEe+mI74/ - 7HfhiM/faE9HfGgOJTrriBeX0AoTFtX9mx3t/dpZk46H+vBjLw32tLPTFe3iw52rri8c7nfjFcP9 - andilLYuD1icwDGz4ta3xQYlbqBxrYW845+/au6fp4ZWHj959evu4iDx+IvdvxA2C5M/d9smcuLR - NI80O3JsdmqazIfZ2OYHNqmxKbe58m5Ux+aaNAdybA5imsV1JccmwiY1NuU2fbXJpTmCb9JH+Y1Q - Xvn5dY7N9W0yY3MDm8hHjZnmkWZNjs1aTHMoTSc4NhE2qbEpt4l8hIVpHmk25Nhs1DRXvhFybK5v - kxqbcpvIJ3mb5pFmS47NVk1z5Rshx+b6NqmxqbY5rnwn5Nhck2Yix2ZS03QlhLdJjU25TVdCYJrg - AaRRPuQxegAJb5Mam3KbK7/2yLG5Js2eHJu9lmaqXAnhbVJjU27TlRCYZibHZlbTZD723DY/sEmN - TblNV0JgmuBx91E9Upwq5vtfbfMDm9TYlNv03CaY5kiOzVFMs2a+Wcw2P7BJjU25Td+kc2nWByLQ - 3Jy+2HVam75L5+NkBucGON2lk22CNwpNX9RqnG7T+TqxySnX6RVOMk7wZqHpi0aN02ucfJ3Y6JTr - 9ConGSd4w9D0RavGufKDuhydAp3Y6FTrbLxniIwTvGlo+iKpcboi4uvERqdcp0siMk7wxqHpi06L - cyi+iy01eXB0xuvERqdcZ+meaIFOR+eqOMGbh6YvevV53QU7Xyc2OuU63bCTcYIn4acvBvF5vS3d - sDft4KtOgE5sdMp1lm7YF+h0dK6J8zA5h41O9fRH49kkPk5qcspxejSJbBO90lmL15JS49Ekvk5s - csp1ejQJjLNBX3Oqx+YaTybxcVKTU42z9WAS2Sb6mrNRn9WL65xOToZObHLKdXowCYyzRV9zqueN - W5frfJzU5JTjXHkR3sG5qk30NWcrP6v79cB8ndjklOv00+DPw7l78+MinDej+fDRt+9pNj02Npv7 - /a5dw+X0/Z502UKfMlfNyTx+IzeXefzzn7NMZmRuIBN5l26YB5gDODIHNUzmLbplvpdJjUy5TOTs - u2HuYYIf8tHer9QwmdOblvleJjMyN5CJHN00zAPMGhyZtRomc2zTMt/LpEamXCZyZNMwDzAbcGQ2 - apjMeU3LfC+TGplqmcynyBnmEWaHjczpo+2/Vsp0/wOnCc3MDWi6AOLKbMHXma36bO4CCC4Tmpl6 - mS6AwDC515mt/mTuBghOE5qZG9B0BcSVmcDXmUl9NncFBJcJzUy9TFdAYJjc68ykP5m7A4LThGam - nibziR2WeZDZga8zO/HZHPq0Dst8LxOamXqZroDAMLnXmZ3+ZO4OCE4Tmpkb0HQJxJXJ3Wveynf0 - rv34GGfm2jKhmamX6QoIDJN7ndnrT+bugOA0oZm5AU2XQFyZGXydmdVnc3dAcJnQzJTLTK6AwDC5 - 15lZfDLPBwRzMsdmGJMzM5gmNDM3oFmoJ5fQdGiuKJP7ILhW/rit5N4cLhOamXqZ7s3BMLnXmYP8 - ZD4U7s3H1GZnZjRNaGbqaY6lW6AFNB2aK8ocwdeZo/ps7lkjuExoZupletQIC3O/LAKNzCR/rGvy - qBFcJjMyN5DpUSMuTO4Dh5P6sa5DcTWzGXs3QNEyqZEpl1lazVwg05G5IkzuA4eT/LGuycOZcJnU - yJTL9GwmFyb3mUZJ/uSY5NlMuExqZKpllibgHJnBMLnbJpN4c1puqsIi+1in6Q84MmNlUiNTLrOw - yr5E5t2PzCdvnl4Hc/89P3l1kmd1/2ZAv3n04N6DH76vm/dM62q/SHJR1/sjMqd1hqrK6Q3kHd8r - f1XeWM1fRvbVOP3NzXXytC++Bx76argbh76ev047HPp9If9FH/r2g0Pf3Kl/9fV8RbE/9EM/EA/9 - s19fvdgf/qenj//ffnnzy+Xutkf+d//Xn/94MX3cB/ea8XDwzzzch3PY1SP+4vnL3ePL0vEe6reT - cVeP9+Hjf3S8i9cepyXsv7FTGqabssIaV123/YH9aQ0/TR/0x+m/evzZX/lmf3r828WLvfXHf9+z - rd7+yvN//PL82eN3/1o/+unl7prrlvra65bqysXSxxDniT44ZfTtr98+nwo+62NO3Cya2npFlzdJ - oqE9nvpOJdGns6TN/e58mldZtuP0P/Msm6HPTT0fUqdZfvC9fgxz/06F28CcvundvXQ7mOdG5/GX - b6SyeBNXUnnjmzgUyU+isjkX5Mm7tAPI+YmTI8i8Jcjxth7rosdz7/HO8Pjz49/+vvQ8Xt0M5Xdf - //UPx7P4qjRvcBafh3n88Dc+jZ+Quf+uTpzCq4nf/AVdW6ehyWmdU3h390/hL58UbN56yeubP33z - kct717z/rd4Q6Mczz9XVbSJncp2+11Nau67w9Ia2Hbq+b6/VqlmJ/YLMFZZa76a5QkIuMrfSGuuX - Qq6HxlyvIpe7cX6qqc1D0xZW9R1zK5lDxpzS3PxN8zJzjrlzyF0zkRRGLinPrPNTxb6a28gcMeak - 5uZ38vpqbgNyCRpzSUhufnrIMbeROWTMKc3NzwU55jYgN0BjbhCSmy85HHMbmUPGnNJcYaHEMScn - 1zXMmJs+l45cYZ3EMbeNOWLMSc3Nz9o65jYgB60gOtlycNcV9mI55jYyh4w5pTk3raHkoDetnewG - Io9d4QZiSMP+EdaOObE5ZMwpzRXuIBaZc8ydQ+6ajcxh5Hph69W5aQ03R4w5qTk3raHkoGtzvXCd - pHPTGm4OGXNKc25aQ8lBB0p6YbnfuWkNN4eMOaU5N62h5KAVRK9cDnbTGm4OGXNKc25aQ8lBK4he - OMPUu2kNN4eMOaU5N62R5DK0gsjC5eDeFUS4OWLMSc25ggglB60gsnA5uHcFEW4OGXNKc64gQslB - K4gsXA7uXUGEm0PGnNKcK4hQctAKIguXg3tXEOHmkDGnNOcKIpQctILIwuXg7Aoi3Bwy5pTmXEFE - khugFcQgXA7OriDCzRFjTmrOFUQoOWgFMQiXg7MriHBzyJhTmnMFEUoOWkEMwuXg7Aoi3Bwy5pTm - XEGEkoNWEINsObhvhsKZtau73k8o0ZtDxpzSXOHUusicY+4sctAKYlAuB7tpDTeHjDmlOTetkeRG - 6NrcKFwnGdy0hpsjxpzUnJvWUHLQm9ZROMM0uGkNN4eMOaU5N62R5PYvhUfm3PTFrlWZc9Uaj44Y - dFJ07lpjzUHL1v2f0p1d3bbGq0MmnVSd+9ZYdNAmYv/BdOhcRcSrY0adUp3LiFB0Nfb2tRbdSeSu - Kty+5unDNZ4t0aNDJp0SXeH2dRE6B9155qC16/6Dyc6uo3vXeHXIpJOqc/Maiw5ave4/mGp4MzeF - 29e+2nt31MnVMaNOqa5w+7pInaPuLHQN9va1kRVho6dM4tEhk06JzmMmoeZabNC1OnMeM4lHxww6 - ITqPmYSaS9igSzpznjKJR8cMOiE6D5l8ZO7N0725s8kdfug3Qffo397232Ux5dpZcO0pcJe7X3eX - r3fP1iDX3O926RNyx38Cs+re/van8B49uAqvHZq6m10ebsbc9cO+HoxIuzl3x2/kNLtT6o5//iq6 - f175oU5/5eMnr37dXRwoHn+x+xcCZyEN7zbO2cWVhTjXSUXbPNjcn5Kowdner9Q2Z9dgHJwYnMzg - 3ADn7GKNgxNgE3zF2epP6rNrOg5ODE5qcMpxzi7+ODjjbSbwFWfSn9Rn14gcnBiczODcAOfsxJeD - E2ATfMWZ9Cf12bkwBycGJzU41Tj72V0BDs54mx34irOTn9R7l0N8nMzg3ACnyyGyzYYcnI3apssh - Pk5qcMpxuhwi20zk4Exqmy6H+DipwSnH6XKIbBO8xtmJl5H6obBTohpyn/vKwRmOkxqccpzzWyqW - 4XRwrmmzJwdnrz6pu1Xn46QGpxynW3WyzYEcnIPaplt1Pk5qcKpxZrfqYJuH631ocPby4jK7Vefj - ZAbnBjjdqpNtXveGq1CbtdqmW3U+TmpwynG6VSfbBI8j9fKJj+xWnY+TGpxynG7VyTZbcnC2apsu - h/g4qcEpx+lyiGwTPMfZy0flssshPk5qcKpxDi6HyDbBc5y9fDvw4HKIj5ManHKcLofINsFznL18 - VG5wOcTHSQ1OOU6XQ2SbmRycWW3T5RAfJzU45ThdDpFtggfge/mM8eByiI+TGpxynC6HyDZHcnCO - apsuh/g4qcGpxjm6HALb3N8MUYMzyzdnjC6H+DiZwbkBTpdDZJvgnUNZvjljdDnEx0kNTjlOl0Nk - m+CdQ1m+OWP+sXIOTgxOanDKcbocItsE7xzK8s0Zo8shPk5qcMpxuhwi2wTvHMryzRmjyyE+Tmpw - inE2lcshsk3wzqGs3pzRVC6H+DipwSnH6XKIbBO8cyirN2c0lcshPk5qcMpxuhwi2wTvHMrqzRld - VwrOoRtS5eAMx0kNTjnOUnAuwengXNMmeOdQVm/OaCq36nyc1OCU43SrTrYJ3jmUxZsz2nH+1dVt - VXft4b3eDs5gnNTglOMsvIJ1EU4H54o29+d0anAO6s0ZTeVxJD5OZnBugNPjSGSb4J1Dg3hzRqra - 2XGk6aQ+NJ3fqw7ASQ1ONc40O460EKeDc02b4J1Dg3pzRlN5jpOPkxqcapy15zjJNsE7hwbx5ox+ - mH8eZ1sNuW8GX3HG46QGpxznbDm0EKeDc02b4J1Dg3xzRu0BeD5OanDKcXoAnmwTvHNoEG/OyPX8 - xEdbjdOn9BonACc1OOU4Z5vLhTgdnGvaBO8cGuSbM2rvHOLjpAanHKd3DpFtgncODeLNGcMw/5Dt - tm6qsfEcJwAnNTjlOGfXkRbidHCuaRO8c2iQb86ovXOIj5ManHKc3jlEtgneOTSIN2eMzfyzYqeT - enP42Tg4o3FSg1OOc36NcxlOB+eKNg8/fmhwjvLNGbV3DvFxMoNzA5zeOUS2Cd45NIo3Z4wpz+8c - qpuxrr3GCcBJDU41zqG0AL8Ep4NzTZvgnUOjenNG15c2ZwzD6HEkAE5qcMpxloaMl+B0cK5pE7xz - aNRuzhjffoKZk3rbVbXXOAE4qcEpx1lo1RfhdHCuaRO8c2gUb85ox352GWn/5K4++4oTgJManHKc - pSfQLMHp4FzTJnjn0Ch/rUvth3zwcVKDU42z8UM+yDbBO4dG8eaMVKX5uyE/Vo6Ckxqccpyze9X9 - WDmATfAA/CieMU5NLmwHbtrGe9UJOKnBKcdZWEdahNPBuaLN4xIzNDmnL3ad9nbIj0f6DHQyo3MD - nX4+EhoneCJp+qKR6sylgqiu6jR4lpPAE5udcp6FUeNFPB2eq+oEl+vTF0mqc2wKDzTe727rfctO - 4IkNTznP8s7g83k6PFfVCS7Ypy869X2RHzH3GfDEhqecpx8yh9YJLtmnL3rtqT21pSvPvhv9Kg0C - T2x4ynmWrjyX8HR4rqoTXLRPXwxinUN5//rhdQ4Oz2ie2PBU8yw+QnYJT4fnmjoPdSY2PGv1bZEf - 0/kZ6KRmp1ynn9OJxonu2mtpmTlOB6DwMMS261p37QSe2OyU8yyMgizi6fBcVSe6a6+lZebY5cK7 - C6aL0rHziieBJzY85TzndxYt4+nwXFUnumuv5WWmH3X8GfDEhqecpx92jNaJ7tprZZmZqjTMLyq1 - 7V6v6yICT2x4ynnOryot4+nwXFUnumuvlWVmqrq+nx2jm3SO023T6PCM54kNTznP+Tm6ZTwdnmvq - PCw5Y8NTu/mtHYfSwzv73o+LR+ikZqdcZ+npnUt0OjpXxYnu2hulzsOJvXTT7utOCE9sdsp5lm7a - fd0ZrhNdFzXa9fjUFHRWTTN0HlQi8MSGp5xn6WF0S3g6PNfU2aJv2lspzmEovvG6Th7yROikZqdc - Z2HGc5FOR+eqONHXna32xD604+wjQabPmPcvjHF2xvPEZqec53yXuYynw3NNnQl93Sl+2NdY3L2R - ar8iE6GTmp1ynYVHLizS6ehcFSf6ujNJT+xjVbfzmzema55x9IQngSc2O+U8C7dFi3g6PNfU2aGv - O+U3RYUn0fmenaKTmp1ynYUH0fmWPRxnj45O6VMSx2rsCpedXd0M3tJO0EmNTrnOwlXnIp2OzjVx - DujolD4jcaJZuCVq6rEbBl91AnRSo1Ouc74oWqbT0bkizsOMAzU6G+37CPc452/YHZ0Ynczo3EDn - /A27o/Mkzt2bHxfhvBnNh4++/ehFrtDY3L/GtV0D5vT9nlpJqsca+uDjao7m8Tu5Oc3jn/+caTIz - cwOazKceW+a7BXhsaHZqmdDnzpnme5rU0JTTZD5zzjLfrR9hQ3OQymz6qnClOaS6iaqFTPM9TWpo - ymkWrjQX0XRoricT/MCPRn8PNDtg7CtNCE1maOppNpWvNLkyG3BoNmqZs+/UcGhCaFJDU05zdjbO - oRkvswWHZquWOTt65NCE0KSGppzm7NyRQzNeZgKHZlLLdHtOp0kNTTlNt+dgmdz2vJFXlNS3tpnm - e5rU0JTTdHsOltmDQ7NXy3QRRKdJDU01zdZFEFhmBodmVst0EUSnSQ1NOU0XQWCZ3DnNRjwMN8l0 - EUSnSQ1NOU0XQWCZIzg0R7VMF0F0mtTQlNN0EcSVuX9tCzQ0W/kEcesiiE6TGZob0HQRBJZZg0Oz - lsrMdT+7ptlWY8rD2w/p0IyjSQ1NOc35B78uo+nQXFEmd0dQK9920bo9p9OkhqaaZnJ7DpbJ3RHU - irdd5LH4koFmqKJe+Wua72lSQ1NOs/A26kU0HZoryuTuCGrl2y6SR47oNKmhKafpkSOwTO6OoFa+ - 7SJ55IhOkxqacpoeOQLL5O4IasXbLlKa33veTrdAca9ZM833NKmhKac5v3K0jKZDc0WZ3B1BrXbb - xVhVQ/GV6H3n0AynSQ1NOc3Zm6CFNB2aK8rk7ghqxdsu8phmh+H24y5jkxya0TSpoSmnOTvYsZCm - Q3NFmdwdQa1228U41IV7oKZLqXVohtOkhqac5nx7voymQ3M9mfsfPjQ0k3zbRfI2SjpNZmhuQNPb - KMEyuTuCknTbRarq1BeuNMe+aj2nGU6TGppymoUrzUU0HZoryuTuCEribRdjkwur7U2Te9+eh9Ok - hqac5uzI0UKaDs0VZXJ3BCXptotUdbmel7lfNOp7h2Y0TWpoymnOT8Mto+nQXFEmd0dQ0m67GKtq - LLTnbddnh2Y4TWpoymkW2vNFNB2aK8rk7ghK4m0X+xaysNqeU1cnh2YwTWpoymmWOsolNB2aK8rk - 7ghK2m0X4zD0s4+SaZvc9o2LoHCa1NCU05x/auEymg7NFWVyh9uTdII4Vam4ptm2Q+UdQeE0qaEp - p1lY01xE06G5nszpFogamp14GG66ByoNw/n2nECTGZob0Cy9VMC358EyuWuanXjhaGiLDy3Mqdrv - fnZohtKkhqacZmmH7xKaDs31ZPbcK81efDqfZJY2+Do0CTSZoSmnmar55XaHZrTMgRuag1RmqlI/ - zq+2t21TjftHRjg0Q2kyQ3MDmvMP4FpG06G5nsz9FlZqak5f7JKW5vyWIKcmxCYzNjewWWopHZvB - NMFdUH2/U9MstZSOTYRNamzKbc4/GsGxeZrmz49/+/ve5lMRzu++/usfLqZv58F7os01u4PqWaHd - KaEvnr/cPb5cw2dzYgNGV8DZXZW5/25P0kyFhc22GVM3Rj3qaBbn/qDN6nxwiufbX7/qk+OukIx3 - 0l1XWLVc5m6lSPyC2HXQuJs+2P5rkbvCmqTzbjN4yLzTwiusODrwtnB3zdBP4Hn26lzFWuzqrsSu - mVxG3RZ/We6QeSd1V1jFXuTOcXcuuwYad9MHU51mJ1WF+9nUphz2QLUvCx4y76Tw6sIN7SJ4Drxz - 3SVq4CXdfUXdFSo5X+BtBo8ZeFJ4hcLNV3ibuKOu4HW6hZScq/lNAu10+deH7ZT+suAxA08Kr3Br - sQieA+9cdz018HrlibYwPeArvM3gMQNPCm9+p6iv8LZxN1ADb1C6K1zhOfA2g8cMPCm80jCUA28L - dyM18EaZu754Z9GNXQp7YuKXBY8ZeEp4pdJiETwH3pnurnlQbOBwwNVnca52ni08IdYXeNu5Q+ad - 1F1p+NPXd1uwq6FxN30w3X1F7yE8Ajxk3mnheQov2h11Cq/XDUNN7jyUQoDHDDwpPA+lRLtrqYHX - Kt25oyXAYwaeFJ472mh31LHjXjn92bujJcBjBp4UnjvaaHfUseNeuH+7zu4sCPCYgSeF59Ii2h11 - 7LhXTn9mlxYEeMzAk8JzaRHtLlMDLyvdubQgwGMGnhSeS4tod9R9Fr1y3D27tCDAYwaeFJ5Li2h3 - 1H0WvW7cPfelK7w8dp33WWwCjxl4UniFK7xF8Bx4Z7rL2H0WWTfvnl3SEtwh807qzh1tNDvqPous - G3cfqrpwQzv0h5Os804PD5l3WniFG9pF8Bx457qj7rPIynH3wUMpBHjMwJPC81BKtDvqPousG3cf - mtJCypjG7Id/bgKPGXhSeIVbi0XwHHjnuqPus8jKcffBU3gEeMzAk8LzFF60O+o+i6wcdx88hUeA - xww8KTxP4UW7o+6zyLpx99yX7iw8lLIZPGbgSeEVSgsPpWzijjp2nHXTn0NVl17g45Z2K3jMwJPC - K73Axy3tBu4G7BTeoJuGGrzNguAOmXdSd95lEc2OuoI3SBdSPHVMgIfMOy08jx0Huxux13ej7Dzb - VaWmbEzdWDvvNnCHzDupu1JTtsSd4+5cdtTru1F4mu1Lz9huq7Gvk/NuA3jIvNPCKyykLILnwDvP - XVtBr+/a+5XqPJvHUt4NaTx+QOed2h0x77TuSrsZl7j7vOPu9asXC9VVN2L3w5//+LG6GhR2H5ib - PtaVU2xdMFdfNTd9q6fITXeyhVuKduj6/guLuhBzlKDbylzpdmKJOcfceeRAO2c/JHdi++Jq5Eo3 - Eo65bcwRY05prtDGOuY2IAfaL/shuRObFlcjVyhiHXMbmSPGnNJcoYN1zG1ADrRL9kNyJ7YqrkUu - VY65cHPEmFOaKzz/xDG3ATlQ8/ohuRP112rkCk8CcMxtZI4Yc0pzhdEmx9wG5EA7Yj8kd2Jb4mrk - XEHEmyPGnNKcK4hYcqD3jX1I7sRLn1Yj5woi3hwx5pTmXEHEkgNt9/+Q3Ik916uRcwURb44Yc0pz - riCCyXXMmOtE5HJfirk81oNjbgNzyJgTmivE3CJzjrnzyIFeofghuRPvsVvrzNq5aY03R4w5pTk3 - rcHkmFdzo+zMWpfWSXKTHHNbmEPGnNBc6Q5iiTnH3FnkGuZmr0a48abzQEm8OWDMSc15oCSYHPJq - bvpYOnIeKIk3h4w5oTkPlMSSA73++kNyJ95BvBo5D5TEmyPGnNKcB0qCyTGv5mrhmdUDJfHmkDEn - NOeBklhyzCeUNMKnRfRuWuPNEWNOac5NazA55tVcozuz9q4g4s0hY05ozhVELDnmg5ga4UNxelcQ - 8eaIMac05woimBzzaq4VnlldQcSbQ8ac0JwriFhyzOfNNcJnf/WuIOLNEWNOac4VRDA55tVc0p1Z - syuIeHPImBOacwURS475WM1G+IjD7Aoi3hwx5pTmXEEEk2NezXXCM6sriHhzyJgTmnMFEUuO+fTg - Rvgk1+wKIt4cMeaU5lxBBJNjXs31wjOrK4h4c8iYE5pzBRFLjvmQ9Eb4wOrBFUS8OWLMKc25gggm - x7yay7Izay698iZVdT845vTmkDEnNFdYm1tkzjF3HjnmuyAa4XP5Bzet8eaIMac056Y1lhzzIemN - 8IHVg5vWeHPEmFOac9MaSq5lPj24FT7JdXDTGm8OGHNSc25aY8kxH6vZCh9xOLhpjTdHjDmlOTet - seSYz5trhc/+Gt20xpsjxpzSnJvWWHLMBzG1wofijK4g4s0RY05pzhVELDnmE0pa4dMiRlcQ8eaI - Mac05woilhxz634r3EY9uoKIN0eMOaU5VxCx5Jh7Wlvh/sLRFUS8OWLMKc25goglx9zs1eo23uTS - 04NTNQzeBbGBOWLMKc0VruYWmXPMnUeOuQui1U2k15Wb1nhzxJhTmnPTGkouMceDk25Us67ctMab - A8ac1Jyb1lhyzLm5pJthmv5+x1y4OWLMKc25aY0lxxwoSbpyv67ctMabI8ac0pyb1lhyzKY16Vqv - 7pqmNTeOOb05YswpzZWb1vPNOebOI8esIJJuOTi3xefNVaOv5jYwR4w5pbni8+YWmPu8Y+63y5+X - kqtuhu6v33/3MbqaE3PVB+jqK+SOf+msure//Qm86ds9BS+NHhIGyMOE3abyfAMbDe9ZQw29Rkiv - y6Ub2dSMY3LobWEPGntie4Ub2kX2HHtn00vU2EvaM653ShDsQWNPbM87JsLpgd4c8TG9Tkevrvo0 - f6ORUtq/Rsext4E9aOyJ7c2fcpfZc+ydTa+nxl6vvNGovJMCYQ8ae2J73lERTm+gxt6gpeedFQR7 - 0NgT2/MOi3B6IzX2RiG93PeF2Mtj0/kmdxN70NgT2yvE3iJ7jr0z6UF73EY6QjAdYG8vi5eHjDy5 - PG8yi4b3DDqx1+wHP5X0PLFHsAeNPbE9z+yF08Ne60nnpqrKwysEe9DYE9vz8Eo4vZYae62UXu0W - l2APGntie25xw+lBR5Ub6bjoRM8tLsEeNPbE9tzihtODjio3ynHRPT0XGgR70NgT23OlEU4POqrc - iMdFa1caBHvQ2BPbc6URTi9TYy9r6bnSINiDxp7YniuNcHrQHRqNeEq+caVBsAeNPbE9Vxrh9KA7 - NBrplHyfS2fcbhw6P3RqE3vQ2NPaK51yF9lz7J1JDzq+0orn5Bv3uPHykJEnl+cWNxoedVS5FY+L - Nm5xCfagsSe25xY3nB50Zq8Vz001bnEJ9qCxJ7bnFjecHnRmr5XOTeU+FWIvj/Xg2NvEHjT2xPYK - sbfInmPvbHrQmb1WPDfVeHiFYA8ae2J7Hl4JpwcdXmm1AwR9eWYvN1/aO3CD7EFjT2yvGHsL7Dn2 - zqQHXdlL4i6t9cRevDxk5MnleV4vGh51XS+J90O2Hl0h2IPGntieh1eC6UFDr5Oeb/tcgtfXKXlI - eQN5yMiTyysMTS2S93lH3pM3T/fwTrv72y9vfrk8fD9PXs3ou38zfd88enDv9z/ca4b//vr7prt4 - a3DO3wy+U/Iud7/uLl/vni22d4XbCUzTpz+FqUqFTd3N0I/d2F+L6fRBP/6Np4/5zCGHHvH8X3/5 - 01064vPrsj7ixyPe//Dwu7t0xOdXQ33ED0f87ZG++O7h97OXFJ/N4S5sYToe7vylH+5m+P3Db+7K - P/A65XH+JN41w5D9D7zpf/fwwV064vMn8eMRB/4b37358TZH/EaH++Gjb5lX6Z/eEZ442NNnPx3o - hVJpH+h9df0N3+mDffwbP/uDjbtAv9XBnu9xjge7/rIPNu7a/FYHe7458cHGXZbf6kjPLxf7SBOv - yBcf7DoNhSWW/dXZULVf9sHGXYzf6mDPr65gD/YNbr5uf9v19t/0mYd3pp+5wRFOVW7eXwxfd7t1 - eEb0fCNTn3Ev1hf/tfepHq8F8Pbf19Xv9afHv1282H/5+O/7D929/ZXn//jl+bPjX/jpjy9381XO - 9E3v7qVrq5yr9xOYG8Rbhg+K5Ceh05wLci6Rcp6/XzyAvMH064ogx9t6rIsehSn58+Pf/l7otAsi - jwfsBii/+/qvfziW2qvSvLbNLsE8fviP+NUFmidk7r+rUzSrcZh/6ELqxq6prz9Zfkzz/Xf6Mcyu - ul1SvhshuE1SBtbehXGLwwE77wx+b39QqtKYTzvLs1Uk5/2PthJUuxM75kti3/72Tc7v7ThWXeEm - PQ9V2Lbltc7Qxz9/Fd0/Tw2xPH7y6tfdxYHi8Re7fyFwFiaB7jbO+WGgZTjXGQayzYPNBA7OpLc5 - f7J3cFJwMoNzA5yFwTcHZ7TNDhycnd5mYWDLwQnByQxOPc6+UFA7OKNt9uDg7PU2174bcnCuj5MZ - nBvgXPt2yMG5os0MDs6st7n23ZCDc32czODcAOfat0MOzjVtduTg7MQ2C127g5OCkxqccpxrN5cO - zhVtDuArzkF+Us++VefjZAbnBjh9q062Cb7iHPQndd+q83FSg1OO07fqZJs9OTh7sc3Bt+p8nNTg - lOP0rTrZ5kAOzkFt07fqfJzU4JTj9K062OYIXuMc5ctIg2/V+TiZwbkBTt+qk2025OBsxDbHtWeM - HZzr46QGpxynb9XJNhM5OJPapm/V+TipwSnH6Vt1sk1wqz5Ki8tUT392/iFeqa1HBycBJzU45Tjn - b9WX4XRwrmkT3KqP8uKy8O4BX3FScFKDU46z8K4jX3GG2wS36qO6uKwrjyPxcVKDU47TD/kA2zw+ - EReanNMXuxPvfF4TZ+FdCk5Oik5mdG6g06ucaJzgYn36olHr9O36Z8ATm51ynh5KQusEl+vTF0ms - s/ZY0mfAExuecp4eTELrBPdE0xe9WqdHkz4DntjwlPPsHZ5kneCuaPpiUOtcu8h0eAp4YsNTzbNZ - u8t0eK6ps0bXRbUap+/aPwOd1OyU6/QL3NA40XVRrV6Pb9a+K3J2Cnhis1PO0/OdaJ3ouqhWrsdP - 33nfFnQOYzpMGDo8o3liw1POs9C1L+Lp8FxVJ3g/5qSzU5/a/Q7Mz4AnNjzVPFuveKJ1orv2Wllm - pirlwiNq2naoGocngSc2POU85zdwLOPp8FxVJ7prr5Vl5qRzqObXPCedxyFDh2c0T2x4ynnO7+BY - xtPhuabO/YI8NzzVC/Ktu/bPQCc1O+U63bWjcaK79kapM1Vd38/XRW075rEfnZ3xPLHZKec5vyC/ - jKfDc1Wd6K690ZaZXV94D7vDE8MTG55ynvP3RQ5PgE50197Iy0xPeX4GPLHhKefpKU+yzha94tmq - cXpO6TPQSc1Otc7k53iicaKvO1vlif1QZZZWPN20Q3his1POs7Ti6aY9WmdCX3eqH/aV/BDkz0An - NTvlOv0QZDLODh2d6vWk5OXOz0AnNTrlOtd+Qrejc02cPTo61U9JTF7t/Ax0UqNTrbPzaicZZ0ZH - Z1bj9POPPwOd1OiU6/QNOxnngI5O7Za3CeA8zrZOXU6dozNeJzU65ToL+4UX6XR0rojzsOONGp2N - 9G2EB5yF3cKOTopOZnRuoLMwAOLoPIVz9+bHRThvRvPho28/WoaHxuZ+Eb5dA+b0/Z6+HcrQdr2a - o3n8Tm5O8/jnP2eazMzcgCZzE7tlvjufQ0NzfzYXy1x7DcmhuTZNZmhuQJP5djfLPMhM4NBMQpmp - bupxvlFP/di1UTPwpvmeJjU0xTSbav6ZCstoOjRXlMm9PW/090Brz8g5NNemSQ1NOU3mZnXLPMjs - waHZS0/ndWm3Zepz8pVmPE1qaMpplm6CltB0aK4ocwCH5qA+nUMH3k3zPU1qaKppDvO3577SjJbZ - cougVr7aPkCfAm+a72kyQ3MDmvNvxnJohsuswaFZa++BcmFNM/Upd41DM5omNTTlNEsrR0toOjRX - lNmAQ7NRn849p0mnSQ1NOU3PaYJltuDQbNUyPadJp0kNTTlNz2mCZXLnNFvpMNxBpkeO6DSpoSmn - 6ZEjsEzunGYrH4Yb3J7TaVJDU01zdHsOlsmd02ylw3AHmW7P6TSpoSmn6fYcLDODQzOrZboIotOk - hqacposgsEzucHsrnyAeXQTRaVJDU07TRRBY5ggOzVEt00UQnSY1NOU0XQRxZe73sEJDM8m3XRSe - cuTQZNBkhqacZukpRw7NcJncHUFJuu1ikjkUX2c1dskP7AinSQ1NOc1SEbSEpkNzRZncHUFJve2i - qdye02lSQ1NO0+05WCZ3R1CSbrtIVcrNvMzpf4aucmhG06SGppzm/GDHMpoOzRVlcncEJfW2i6by - yBGdJjU05TQ9cgSWyd0RlKTbLlLVN33hfZRdXbcOzXCa1NCU0yy8jHIRTYfmijK5O4KSettFU3lO - k06TGppymp7TBMvkDrcn9QRxU3lOk06TGppymp7T5MrcryhDQ7PTD8N5TpNOkxmaepq15zTBMrkj - R510riNVXW7nZe6fSzY6NMNpUkNTTnN+Gm4ZTYfmijK5RVCnfv5WU3tOk06TGppymp7T5Mrsubfn - vf4eyCNHdJrM0NyApkeOwDK5V5q9eK6jy0359nzwNspwmtTQlNMs356fT9OhuZ7MgXulOUhP56lK - feEdQW3bVOP+4WQOzVCazNDcgOb8yNEymg7N9WTWFTc1py92SUuzMN3u1GTYZMbmBjYL4+2OzWia - 4KmjeroNEtOcnzpybEJsUmNTbbPwniDH5mmaPz/+7e97m09FOL/7+q9/uJi+nQfviTZtV0zPelZo - d0roi+cvd48v1/C5/2D7rz/6WXYFnd1Vmvtv94TNuhqGeZupS+N+xjomN2d17o/aLM8Hp3y+/fWr - QDnwCtF4V+HNr1sug7dSKH457q558EacuxPPNliHXTX0TWFRchyrFFZ/f1nukHkndVdYcVzkznF3 - Ljvq9V2SnWb37grLic67zeAh804Lr7BW6MDbwt01AzuB59mrMxHrsSusAzrvNnOHzDulu9IGLsfd - JuwaaNxNH0y2jDIWHtOXur7d71xz3unhIfNOC29+IWUZPAfeue4SNfCS7L5ibPr5hZSJ5fTbDrwt - 4DEDTwpvfiVlGTwH3rnuqCt4nXIhpbT/3ne0m8FjBp4UXuGNHr6l3cRdTw28XuduLDwPNFVNTr6l - 3QQeM/Ck8ApXeIvgOfDOdZepgZeFSyldaQavH+Je0vplwWMGnhReaQZvCTwH3rnuBmrgDco7i8IL - 2XxLuxk8ZuBJ4c23Zb6l3cbdSA28UXiirQt3Fl3f1w68TeAxA08Kb34Kbxk8B96Z7q556VTgNNTV - 9/qsd571LguCO2TeSd15l0U0uxoad9MHk80G9O381HFq6qb3Ct4m8JB5J4WXCkvHi+A58M51Rx07 - 7mXTn/sTrbeVEeAxA08Kz9vKot211MBrhdOfXWkoZRwaD6VsAo8ZeFJ4paGUJfAceOe6o+6z6HXj - 7kPpltZXeNvBYwaeEl7hltZXeNu4o+6z6IXj7mM1P+6equn6z0/C2wQeM/Ck8OanoZbBc+Cd6466 - z6IXjrv3yRvLCPCYgSeF541l0e6o+yx62bj73p3HjgnwmIEnheex42h31H0WvXLcPXkMjwCPGXhS - eJ7Di3ZH3WfRy8bd9+48lkKAxww8KTyPpQS7y9h9Flk3755c0hLcIfNO6a7wxArH3TbsqPsssm7c - fXLnyoIAD5l3WniuLKLdUfdZZOW4e+fKggCPGXhSeK4sot1R91lk3bj75M6VBQEeM/Ck8FxZRLuj - 7rPIynH3wv4eB9528JiBJ4XnyiLaHXWfRRa++H2sC89K6freO2m3gccMPCW8Zr60WAbPgXeuO+o+ - i6wcd+9c0hLgMQNPCa93Sxvtjjp2nJXTn71bWgI8ZuBJ4bmlDXY3YKfwBt00VO+SluAOmXdSd+5o - o9lRh1IG5WxA746WAA+Zd1p47mij3VEri0H5JuTCq98deNvBYwaeFJ472mB3I/aGdhTeWLixILhD - 5p3SXXZhEc2Oen03Kk+z2YUFAR4y77TwXFjEumsr6PVde78SnmddWBDcEfNO686FRTQ76PXd/oMJ - T7MuLAjwkHmnhefCIthdjb2+q2Xn2aEpsqt7590m7pB5J3VX7MkWuPu84+7Jm6d7dafR/e2XN79c - Hr6fJ69O0zucl26g75tHD+79/oeJ3n9//X3TXbwlOGdvBt4pdZe7X3eXr3fPlro7fWL9RNP06U9g - qlMe50uIrhmGPOZrMZ0+6Me/8fQxnznk0COe/+svf7o7R3yo5pf/fcSPR7z/4eF3d+mIzy+8H4/4 - 4CP+u4cP7tIRn1/x9hF/ex4/HOmL7x5+P3sB+fkc7vmVZuzh3r358TaH+0bH+uGjb5nXbJ8c6+rE - wZ4+++mDXdhgvT/YE4aFB/v4N372Bxt3uXarg10+dQ9V+rIPNu5K7VYHu3zW9sGmXaTd6mCXz9lf - /MFmXZ/d6kjP97HHI919Zkf67erhGkf6h0ff/+7fy7m5qgDH+sql+HEl+YbHe/oPz/eg3djVbb3V - Bdrxz4OPd383jvd8D3Q43s3SBbW7cbz/8O/jPcG/SO1dOOhjPb9npK/roeuAB/0GKyy3X1t5e7N9 - 5iGeaeRucpTb6RQ73HBZpSm2cKcAnFxzaasxV7PFSV3349gcHnRWBvD2iunq9/rT498uXuy/fPz3 - /Yfu3v7K83/88vzZ43f/Nj/68eVuvsCbvundvXRtgVddqQ0xC0G3DCEUyU9ipzkX5MlEOoCcrYWP - IOvra+EVQY639VgXPQovfa8ZYyiIPB6wMycZVqR5/fxCAebxw3/Ery7QPCFzbmKhbYbCjqPpE9Xd - 9fe6H9N8/51+DHP/wqzbJOX7q4BbJGXgsENhwuZwwM47g987fJ+lsa52lmerSM6PN8hVu3T6Cm42 - TE9e4M2d36tq9gJvitO6mdDGTHetdYY+/vmr6P555Yc6/ZWPn7z6dXdxoHj8xe5fCJyF2a+7jbN0 - 8bkE5zojYLZ5sNmCg7OV26xnT/YOTgxOZnBugHO2T3VwxttM4OBMepuz9a+DE4OTGZwb4Jytqx2c - AJsdOTg7pc2UmjS78tlUdZ/DNmIZ5wc4qcEpxzm7jrQQp4NzRZsd+Iqz05/UZyeTfMWJwckMzg1w - zo5R+YoTYBN8xdlpT+p13XTza5xN1Y6p8hVnPE5qcMpxzq9xLsPp4FzRZg++4uz1J/XZWVlfcWJw - MoNzA5ylCbr/v72za27jSLP0H1ohKr+zLmWabfes162w2Bveiw0FZcGyYmTLLVMOb0z0f98qgORI - AjIBFOtUHlBnrpq0YohiPXzezDxvZmrE2ZpN4hFnxBb1kU21I/HDySpOOJxqRyJmMxGPOBO8qJeP - S5A4aeDkFOcCcKodiZlN4hFnghf18mkUEicNnKzihMOpdiRiNjPxiDPji7pSdX44OcW5AJxK1ZnZ - JB5xZnxRVzjEDyerOOFwKhwiZrMnHnH2+KKucIgfTk5xLgCnwiFmNolHnD22qA9/luVdbc64YHrt - HCKAk1WccDjL58ZOg1PinJHN7flkpOYcvlgHaFV3itXPgE5OdS5Ap3J1ajiJB53DFwFJZ9/1sXxA - uAnDR91cWCt3NsaT1p1wPMvnmU/DU/Kck05DPe406MqurqQzoJPVnXA61ZZEDSf1uNPAC3v54nCN - O2nwpHUnHM/ylSoad7ank/k4eLOy6Mqups4zoJPVnXA61dVJDSf1uNNiC3uw5VmRsyZ7L3cy4Enr - Tjie5Zx9Gp6S55x0Mt+mYVYOXdnVE38GdLK6E06nmuKp4aQedzpkYfddiKmYFTnn+tyZw9cOyp1w - PGndCcezPC2ahqfkOSedzJcRmZVHV3ZtKToDOlndCadTe4qo4aQed3poYTed8+Vxp3c+p6CcnQBP - WnfC8SyPO6fhKXnOSSfzlURmBV2MHyq7176iM6CT1Z1wOrWviBlO5rs1zCqi4VRr/BnQyapOOJ1q - jWeGk/mQeLNK0KV4n8tHdjrnsjW5kzrb08mqTjid5f3C0+iUOueEk/m0Y7PK6LquxvgzoJNVnXA6 - 1RhPDOdm0warOi38lBqv3s4zoJNTnQvQqd5OZjiZt2Na+IY3r/akM6CTVZ1wOtWexAwnc8JusRmm - 6eq3XPd9dlInAZ2s6oTTWSvsU+iUOmeE0zFP2B12SjTCWavrUicJnZzqxNNZvela6twD5/rml0lw - Hofm5dW3n5zHTarN8TRuNweYw/MWwKx0zaXhP7dK17sSmtsnOR7N7b8/ZzQ5nbkAmpWeuUlozuNM - kXnXbEwrzYAks+tNONAI3+pqIqF5jyarNOFoHuiCb3Qxkci8a/iglWZGkhmi64qJurV5bKXLkmZj - NFmlCUezuOw+EU1Jcz4yLe/03GLnQGM5P7BvSCPN5mhySnMBNA9sGtJIsyWZlliaFlrOQ98Xp+dD - OXf27kNKmu3QZJUmHM3i9HwimpLmjGR6Yml66Gp7rJVz14XNkWaSZls0WaUJR7O8jW0ampLmjGTy - BkEWv9p+YIOlpufN0WSVJhzNA7srNT1vSWYklmZEk3lga6Wk2RxNVmnC0Tywr1LSbEkmb3pusRHl - SOaBTZWSZnM0WaUJR/PAjkpJsyGZ474LUmk6eEQZ1afJjianNBdAU32axGTypucOG1GOZKrliB1N - VmnC0VTLETGZjliaDk2mgiB2NFmlCUdTQRAxmbwtRw7b1zGSqSCIHU1WacLRVBBETCZvy5GD93VE - BUHsaLJKE46mgiBiMnlbjhy8ryMpCGJHk1WacDQVBBGTmYilmdBkKghiR5NVmnA0FQQRk8nbp+ng - zXBJQRA7mqzShKOpIIiXzHEXK6k0PbwZLikIYkeTU5oLoKkgiJhM3j5ND2+GSwqC2NFklSYcTQVB - xGTythx5eF9HVhDEjiarNOFoKggiJpO35chj+zrGi1gq5TwNryZImq3RZJUmGk1XqeeT0JQ0ZyST - t+XIw/s6stJzdjRZpQlHU+k5MZm86bmHR5RZ6Tk7mqzShKOp9JyXzHGYTyrNAI8os9JzdjQ5pbkA - mkrPicnkXdMM8L1qWek5O5qs0oSjqfScl8zIO9KM8HJevlhN0iRBk1OaC6Cp9JyYTN6RZoSX815B - EDuarNKEo6kgiJfMxDvSTPhyriCIHU1OaS6ApoIgYjJ5R5oJ3gzX1cjs+9hJmq3RZJUmHM1aRjkF - TUlzPjIz70gzY8v5SGYtopQ0GdDklOYCaNYySkmzLZk9rzR7PJm1iFLSZECTU5p4NE0to5Q025I5 - qoPVmsMXa49Fs5ZRypoUbHJqcwE2aymltNkYTcOsTQNG01ZSyjT85yxtNmeTVZtwNis55SQ2pc0Z - 0bTM2rToil5r7hgq+qamS5tN2WTVJpzNWnvHFDalzRnRJN5NaVYBjeaBEF3abM8mqzbhbB5I0aXN - HTR/v/7r9cjmTyA4nz398ZsXw+Nc3CN6aNBpioSGfYS+ffPb+vr9HHzaPWvvoQJn2CVzfNpdNH3X - xfLJHc7mPibbapJehHN8aUU6L/bhefv9XT55uKuY8ZFyVyzXE7mbSYlfDnaGVncGiF2xEkt3C3LH - qTskd8VuNeluGewOXDnRELvdU/3nw67YiSbdLcgdp+6Q3BX7zKS7ZbBztLpzOOzK51xIdwtyx6k7 - JHfFBjHpbiHsAqnuhg82fg3irtj8Jd8tCB6l77DgFXsUJLxluDtwGVjDOrt739J82CmsYOCO0ndQ - 7hRWtMaOdXznoWVWaQUDeJS+w4KnuKIxdwfOw2lYZ3ePHJkPO8UVDNxR+g7KneKK1th5Ut0NHwxV - ZkMun2HjnHc+efluCfAofYcFr1xop4En4Z3KHeuENgDnFalTQMsAHqfwoOApoW3NXWQVXkRyp4SW - ATxO4UHBU0LbmrvMKryM5E4RLQN4nMKDgqeMtjF3kTayiLCl41Q+IF2+W5A7St9BuVNC2xo7S6q7 - 4YMBy6wiWgbwKH2HBU8ZbWvuWDPaiIvKulQ+l1zCWxA8TuFBwVNk0Zo71sgiIleOyyeKS3gLgscp - PCh4iixac8caWUTkynH5UFsJb0HwOIUHBU+RRWPuEm1kkXBLx+XTaOW7Bbmj9B2UO0UWrbFjjSwS - cuXYKLJgAI/Sd1jwFFm05o41skjIlWOryIIBPE7hQcFTZNGaO9ZtZQm3uyfkSrO7885Z7aNdBDxO - 4UHBKy8dTwNPwjuVO9aMNiGjMquMlgE8TuFBwVNG25o71ow24aKy1PvKSSnZpy5KeEuAxyk8KHiV - k1ImgSfhnchdps1oMy4rK99PrwHegtxR+g7KnVpSWmPHmtFmXFSWsy9HFq7vvZHvFgGP0ndY8MqR - xTTwJLxTuWONLDLyQDKrJjwG8DiFBwVPXXiNuetpJ7Q9cGKhJjwG7ih9B+VOPXitsWMd3/XIMuvU - g8cAHqXvsOCpB68td64jHd+5VYers04dKQzcMfoOy50aUlpjRzq+Gz8YsMwqoGUAj9J3WPCU0Dbm - ztCO7wywziqvYOCO0ndQ7hRXNMbO0urOArFTXMHAHafukNwprmiMnaPVncNhV2m/k+6W445Td0ju - FFY0xs7T6s4DsVNYwcAdp+6Q3CmsaIxdoNVdAGKnrIKBO07dIblTVPExdi9vfhqp2w/dzx9uPrzf - PM/Ld/vR22RKR9D31dXFk789v0PvxbPLH4rgFajbh9z79Z/r93+sX02Fbn8i9hlKw0ffJWlc9w5F - gxmTOmtcf5Ck/W98+xP3v/DC+yZ93ek//vn98LpvX/t5v3Fvu/IJN6YPcbCL+eLfeHx++eyxvPGh - IqTi0qcdJmzR9fobj19fXjySN+6H/6+V09OCzTn1me+Nr29+ecgbP+p1X159S1jBP3vR3Z43PXzw - /fU7FUeC2/rtw8Q3vf2JZ/+m6Yr35Jc9lO7yKsdt6Z76Z/1IXjZd3Z78stOYWlSrtt8ckPQlv2y2 - kj31ZY8Fu3I6zFiwc3duGr9dXpjjZT+/+uHr/17vGagneNc7o7PtUtOR7zvFyiXzsRtHaEuZfPvv - id93ehzvu9z1tHnf2fK97yOmYA+ffN0O0E58xYXV2yPecjCd2R49c8Sky1ZXbPcBUFh1GX5ueZw+ - DN1MNP4gALf1c/dZf73+68Xb8cvr1+OHDrffefOvD29eXd/9WX7y60uhvNg7PPT6iT+42NvtLDHT - TBMf6B8qJD/Tjj0VyMJcYvip1WVAE48YXs4IZP9QHk2VR+Ao6EDkVSFy+8JOS73mRPNg1lUDc/vh - P8HPVNDcQ2Yh3TJdn8urVz70wRp3Ipr3T/opmKF7mCnvRwEPMGXDYKySxm5e2GkV/MnmOWstAK6I - p0OY89ODULq13z+CK8p07wCvVN9NKM7WTe8H2bY6RnuuCr3997vQ/dfOL3X4kdcv3/25frFBcfvN - 8G8KOCt9Ao8bzmIz6EQ452kXEJsbNh2xOB2ezco4VOIkgZNTnAvAWQlcJM7WbHpicXo8m+XOLYmT - BU5OcS4AZ2W5U+JszmZgFmeAsun68sGltjPRxixxtoeTVZxoOMunm06EU+Kckc1APOIM+KJePKlI - I04aODnFuQCcxY4ajTgJ2CQecQZwUR/YLHb/SJw0cLKKEw5ncTokcbZnMxKPOCO8qEel6vxwcopz - ATiVqjOzSTzijPCiHpWq88PJKk44nErVidlMxCPOhC/qStX54eQU5wJwKlVnZpN4xJnwRV3hED+c - rOKEw6lwiJjNTDzizPiirnCIH05OcS4Ap8IhZjaJR5wZW9SDzV1x/d1a6zb91xJnazhZxQmHs7gA - PxFOiXNGNnviEWcPL+pJqTo/nJziXABOperMbBKPOHtsUc+9Kx+264wNJmnESQAnqzjhcJbvkZ4G - p8Q5I5vb88lIzTl8sQ7Yqq5+pDOgk1OdC9CphiRqOIkHncMXAUln39nyhT3OuORzq0vmhOfHeNK6 - E45n+erNaXhKnnPSaajHnQZd2dXOeQZ0sroTTqf6OanhpB53GmxhD9YUV+KdNSn2Wu1kwJPWnXA8 - y9OiaXhKnnPSyXwcvFlZdGVXN/wZ0MnqTjidaoenhpN63GmRhd2bzvnK9S/O59QfvitN7oTjSetO - OJ7lJpBpeEqec9LJfJuGWTl0ZddmojOgk9WdcDq1m4gaTupxp4MX9nIDncadNHjSuhOOZ/nKcI07 - 29PJfBmRWXlwZc/aUnQGdLK6E06n9hQxw8l8q4ZZQXPM4CqXs1prQ+zU3slAJ6s64XTW9rFPoVPq - nBNO5uPhzSqi67p2FZ0BnazqhNOpXUXMcDKfc2xWCbxroyuH7MalzmnUyUAnqzrhdFYOWphEp9Q5 - J5zMB3aaVUbXde0pOgM6WdUJp1N7ipjhZD55zqx67J6N4IrtSc5am6VOCjpZ1Qmns9ieNJFOqXNG - OLeMkKrTos+oidU9G33aHikrdTamk1OdC9BZ6zueQqfUOSeczJsxLXa7W+6drax1Wpes1MlAJ6s6 - 4XTWDpWdQqfUOSeczH2dFts5N8wGyzs2nHEhmk7qJKCTVZ1oOstdxxPplDrnhJO5r9OCO+c6V+mc - M9H3YXNspNTZmE5WdcLpLDYnTaRT6pwTTua+TovtnOu9D+W6PtDZWTUnMdDJqk44nbVzEafQKXXO - CSdzc5KFtn+Mu4T78mnc3iXbK2FnoJNVnXA6a0d7TaFT6pwRzs3hNKzqdNgMc5wSFeHUhJ2HTk51 - LkBnMSbShL09nJ5ZnR4Kpzch5fLZNCH0rtOok4FOTnUuQGe563ganY9eneubXybBeRyal1fffnLz - IKk2x3sH3RxgDs+7p6Z7YwzpqUldCc3tkxyP5vbfnzOanM5cAE3OI5NE5t2ZNLTSDGgySQ/9EJr3 - aLJKE44m54kfIvNuXzCtNDOSzBCtrRwyl13fbPeQ0LxHk1WacDQrJ8xNQlPSnI9Myzs9t/g5EOlB - H0LzHk1OaS6AJucpHyLzbkMbrTQttJyHvnZgbLaxz5JmazRZpQlHs9zzPg1NSXNGMj2xND20nLve - l6fnnYk2aXreHE1WacLRLNfzaWhKmjOSyRsEWfxqO+kd60LzHk1WacLR5LxgXWTebV+jlWbElnMb - y3OgrkvbAyAlzaZoskoTjmZ5/8U0NCXNGcnkTc8tNqIcyznprepC8x5NVmnC0eS8Ul1k3m1ZI5Wm - g0eUVs3t7GhySnMBNNXcTkwmb3rusBHlSKaa29nRZJUmHE01txOTyZueO3BEOXwK9Wmyo8kqTTia - 6tMkJpM3PXfYiDKEGIsLR9amPqdWZ2wKzXs0WaWJRjMVV44moilpzkgmb3ruwBHl8CnUcsSOJqs0 - 4Wiq5YiYTN703MEjSqv0nB1NVmnC0VR6zkvmuLOAVJoeHlE6pefsaHJKcwE0lZ4Tk8mbnnt0RBlD - rZz3PmjveXM0WaWJRjPW6vkUNCXNGcnkTc89NKL0nQmV29FdZ1LWSLM5mqzShKNZvhp9GpqS5oxk - 8qbnHr7B16lPkx1NVmnC0VSfJjGZvOm5h0aUvgvJle9hcUO5NzrlqDmarNKEo1lcOZqIpqQ5H5mB - NwgK+NV2Nbezo8kpzQXQVHM7MZm80/OAnwOpT5MdTVZpwtFUnyYvmZF3pBnx5Vx9muxockpzATTV - p0lMJu9IM8LLuVefJjuarNKEo6k+TV4yE+9IM8HLeflOAUmTBE1OaS6AptJzYjJ5R5oJXc5jqpHZ - x6jm9uZoskoTjmYto5yCpqQ5H5mZd6SZ8eVc6Tk7mpzSXABNpee8ZPa80uzxZCo9Z0eTU5oLoKn0 - nJdM0/Fac/hi7bFoKj6nZ5NTmwuwqfycGE3DrE2DRTNWWzX7TpuCCNhk1Saczdpocwqb0uaMaFpm - bVoomimm4mjTmnEG75y02ZpNVm3C2SxfLTCNTWlzRjSJL6U0KwdGMxfbNaVNFjZZtQlns9ivKW22 - R5P4NGKz8mg0iw2b0iYLm6zahLNZ7IuTNtujSXx2h1kFNJrF7iNpk4VNVm3C2Sy2H0mbBTR/v/7r - 9cjmTyA4nz398ZsXw+Nc/DeiB+4NMkVCwz5C3775bX39fh4+d69mCRU4wy6Z49Puoum73pbPynbD - f+59s073IpzjSyvSebEPz9vv7/LJw13FjI+Uu+IEfCJ3Mynxy8Gup9Vdj8Iu+66YMjrX951v1jj0 - ZXHHqTskd5XjWCdxJ92dht2hALsZdnZPH+982FWOWpXuFuOOUXdQ7kx5ViHdLYKdodWdAWJXnlRI - d8txx6k7JHfFyE66Wwa7AzcxNsRu97K7+bArpnHS3YLcceoOyV3lFjvpbhHsAqnuhg82fg3gznR9 - xXc+xO21nvIdHDxK32HBKwtvGngS3oncOdrxnQPW2cqVnBrfLcYdpe+g3BW3iWp8txB2rOM7Byuz - I3cKZxnAo/QdFjyls42587Tju9171+fDTuksA3eUvkNyZ5XOtsaOdXznkWXWKp5lAI/Sd1jwlM82 - 5u7AZQIN6+zuee3zYad8loE7St9BuVM+2xo7S6q74YPhYrKuvG7sQ3R9s+MFvyzwKH2HBa+8cDwN - PAnvVO48q/A8bF7Ru66yidHYPlkJbwnwOIUHBa88oZ0GnoR3KnesK3gBupCijhQG8DiFBwVPLSmt - uYuswou4Qrs5I6d8TEUwnYS3BHicwoOCVz0fZQJ4Et6p3GVW4WVcoc19TXim7zSlXQQ8TuFBwasJ - bwp4Et6J3EXajDYCszL1HDNwR+k7KHdqOW6NHWtGG2FR2cideo4ZwKP0HRQ8p6bj1tyxZrQRF5UN - 3KnpmAE8TuFBwVPTcWvuWCOLiFs5HrhT1zEDeJzCg4KntuPW3LFGFhG4cuydmlIYwOMUHhQ8NaU0 - 5i7RRhYJt3TsFFkwcEfpOyh3iixaY8caWSToyrEiCwbwKH0HBc8rsmjNHWtkkZArx16RBQN4nMKD - gqfIojV3rJFFQq4ce0UWDOBxCg8KniKL1tyxRhYJuXLsFVkwgMcpPCh4iiwac5dpIwvYbe8Ddoos - GLij9B2UO0UWrbFjjSwydOVYkQUDeJS+g4JXOa9CwluGO9bIIiNXjivHVUh4y4HHKTwoeIosWnPH - ehJeBh5INpBVOa7CRpslvCXA4xQeFLzy0vE08CS8U7ljzWgzMioLymgZwOMUHhQ8ZbSNuetpI4se - t3QcFNEycEfpOyh3SmhbY8c6oe2RJ2wHRbQM4FH6DgueMtq23LmOdHznVh2wziqiZeCO0XdQ7qIS - 2tbYkY7vxg+GK7NRCS0DeJS+w4KnhLYxd4Z2fGeAdVZ5BQN3lL6Dcqe4ojV2rOM7Ay2zyisYwKP0 - HRY8BRaNubO04zsLrLPKKxi4o/QdlDvFFa2xYx3fWWiZVV7BAB6l76DgJQUWjblztOM7h6uzSXkF - A3eUvoNyp7iiMXaeVnceiJ3iCgbuOHWH5E5xRWPsAq3uAhA7pRUM3HHqDsmdworG2EVa3QFve08K - Kxi449QdkjuFFY2xY72hzCFvikrKKhi449QdkLusqKIxdqynGzvkKbNZUQUDd5y6Q3KnqKIxdqwn - ozjkCRVZUQUDd5y6Q3KnqOJj7F7e/DRStx+6nz/cfHi/eZ6X7/ajt+kYOoK+r64unvzt+R16L55d - /lAEr0DdPuTer/9cv/9j/WoqdPv7nT5DafjouyQ5bztbNJjpQ/TDH9BBkva/8e1P3P/CC++b9HXH - 55fPhtd9+9rP+40nZ8szQ5u8jaM/vvg3/vXlxSN54974XDknNdicE+MbX9/88pA3ftTrvrz6ltDn - n73obs+bHj74fpuXL/G7tfnUN739iWf/pulUPvllDwO9rrjWshG57+IX/rLZLD71ZY8Ot+W0fnT4 - 4Hi+l12t2rfzj/LL7lbHve6xaF/cW/yJGc9S6l6Y8ffREbz0ncq9nZQeWby7mMt5uR/mgc5PffGn - Fu/tv59ruPbwgdqtzE98xYV5/zFveRgpuXjkAM1W5/pHAzCO18sXt42a953JBwG4Fevus/56/deL - t+OX16/HDx1uv/PmXx/evLq++/v85NeXQnmZYHjo9RN/cJmg21mcoBlSPrDqUCH5mXbsqUCWxh3G - FZcMboFMSwLZP5RHU+WxbXl8cGG8JfLJ5o+utj7vinA6BJyfniHdrf3+Ilnk9fga6tJ4jXCZWOdS - jIcXuSDL9OjS/F87v9ThR16/fPfn+sUGxe03w78p4Kws4j9uOCvTuElwzrOWLzY3bDpicTo8m8VY - XeKkgZNTnAvAWRmHSpyt2fTE4vR4NosL8RInDZyc4lwAzmIeKHG2ZzMQizPg2SzmHBInDZyc4lwA - zmIWI3ESsBmYxRnQbFZiIomTBE5WccLhLO6ClDjbsxmJR5wRXtR7hUP8cHKKcwE4FQ4xs0k84ozw - ot4rHOKHk1WccDgVDhGzmYhHnAlf1BUO8cPJKc4F4FQ4xMwm8Ygz4Yu6wiF+OFnFCYdT4RAxm5l4 - xJnxRV3hED+cnOJcAE6FQ8xsEo84M7aoZ9OVr063vU9xs/dS4mwMJ6s44XCW15GmwSlxzshmTzzi - 7NFFPXdK1fnh5BTnAnAqVWdmk3jE2YOLeu5d+QRtY23XacRJACerOOFwlo/ZnganxDkjm9uTTEjN - OXyxDtiqrn6kM6CTU50L0KmGJGo4iQedwxcBSWffmcr9GcbFzdGxcmdzPGndCcezfM/GNDwlzznp - NNTjToOu7GrnPAM6Wd0Jp1P9nNRwUo87Dbawe2/Kp1tbY11nNe4kwJPWnXA8y5dGT8NT8pyTTuZT - jc3Koiu7uuHPgE5Wd8LpVDs8NZzU406LLOy+C8mUr9NxvuuSxp0MeNK6E45neVo0DU/Jc046mQ+F - NyuHruzaTHQGdLK6E06ndhMxw8l8LLxZeTCcRl3xZ0AnqzrhdKotnhlO5oPhzQq6FJ9dn8odSp0J - KahDiYFOVnXC6Sw3KE2jU+qcE07mE47NKqLruhrjz4BOVnXC6VRjPDOczEd1mlWC1vXepfJCvLHB - ZOVEDHSyqhNOZ7k9aRqdUueccDKfOWdWGV3X1RZ/BnSyqhNOp9rimeFkPjzJrHrsdre+fEGrM0Fn - gJDQyapOOJ3l3rlpdEqdM8K5GfOzqtPCj1koN3Zq1MlDJ6c6F6BTTfHMcDLvY7fQncK+85UTapxz - eaj7GnUS0MmqTjidxeWkiXRKnXPCybwV02I3u2XflTvnXGdiylHqJKCTVZ1wOmutc1PolDrnhJO5 - Jd5im45zzuX7Bp2xxvVSJwOdrOqE01lZ65xEp9Q5J5zMfZ0W2zmX+wG+yqmI3hipk4FOVnXC6axc - ZDCJTqlzTjiZm5Mstv2j73pfOVA2dH3qpE4COlnVCaezvNY5jU6pc0Y4N+crsKrTYTPMPvhKS7y1 - Jgapk4FOTnUuQGflxM5JdEqdM8LpmdXpoXB6E1KlrzOE3nWasDPQyanOBegsr3VOo/PRq3N988sk - OI9D8/Lq23s0A601h082fvlwLofH3VPSk4+mcqJX7kzX6uSPrkTm9kmOJ3P778+YTE5j4sm0leO8 - JpE5jzEF5t19g6TOHG8bdEAyretJD5oTmvdockpzATQ5T5kTmXfHeNFKM6DJJD0nSWjeo8kqTTia - nIckicy7TW2k0rT4ck56zIfQvEeTU5oLoMl5xofIvNuTQStNiyaTdJe60LxHk1WacDQ5t6iLzLvd - GLTS9GgySe8dEpr3aLJKE44m56VDInNDJu+apsUuHOWcfWV7pemda3WAsdC8R5NVmnA0K3srJ6Ep - ac5IZiSWZsSWc98pPWdHk1WacDSVnhOTmYmlmdFkKj1nR5NVmnA0lZ7zkul403OHjih9+VRDSZME - TU5pLoCm0nNiMnnTc4eOKH2n9JwdTVZpwtFUek5MJm967rARZU4mVw6B7UP02kXZHE1WacLRLB/G - NQ1NSXNGMnnTc4feduE7tRyxo8kqTTiaajkiJpM3PXfYiDJn78sjTWM6H1rdIyQ079FklSYczfJI - cxqakuaMZPKm5w4bUebhc5RP1TS2i1nSbI4mqzThaJaP1JyGpqQ5H5meNz338IjSqE+THU1OaS6A - pvo0icnkTc89NqLss6ncBmiD73KnkWZrNFmlCUezPD2fhqakOSOZvOm5x0aUfc7lQwudTaEzSs+b - o8kqTTialXo+CU1Jc0YyedNzD48oyyNNTc9J0GSVJhxN7QgiJpM3PffQiNJ3Ppnyavvwf9lqet4c - TVZpwtEsthxNRFPSnJFM3vTcQyNK30Uby6ccuWCMUXreHE1WacLRLJ9yNA1NSXM+MsfrQEmlGfAR - pfaes6PJKc0F0NTec2Iyedc0A37hSHvP2dFklSYcTe095yUz8o40I76caxslO5qc0lwATW2jJCaT - d6QZ4cdj29rJ7X7DpaTZFk1WacLRrJ3cPgVNSXM+MhPvSDPBy7nVjiB2NDmluQCa2hFETCbvSDNh - y3mfcyy3HA1oxhQ10myNJqs04WiWW46moSlpzkdm5h1pZnw5V3M7O5qc0lwATTW385LZ80qzx5KZ - U5+LLUfOdNYHbaNsjianNBdAs7LcPglNSXM+MjcryqTWHL5YeyCafV87tdC53usO3/ZscmpzATYr - FX0Sm9LmjGgaZm0aJJp99UhNm7wJnbTZnE1WbcLZrC25T2FT2pwRTcusTQtEc9yw5iq7fH2fjdKg - 9myyahPNpi+2d0xk8/Fr8+XNT2U0f/5w8+H95plfvtsPaDe+4CMY/erq4snfnt9h+uLZ5Q9FRguA - Iujc/rSPf3Vhl7bho+9bR3c2Vzo2vI2u7w/Sth+L7U88Hovtt3exIHjdX19eDK/79rWf9Rv3xufy - zaIu2OHHMr7xau05/MaPrkB8f92fl5o9b7qQkTnXVeLb4W97KDYT3/Spymd902x/2FNf9vhnbcsr - AeOfde4C38s+QuQPV/jtaz7x9W5GH5PecIx3L/gIdduNuosjxz3jxpLXU6wCEL057PXbv6/dZ/31 - +q8Xb8cvr1+PHzrcfufNvz68ebX9gZ//+lIoDzmHh14/8QeHnLvrAzTF5sETHSIkP5OOPRXIkpFi - TwVk/1Ae9+z4ILBkZeLdrY4j8iNJPrHLh5BVOD+eetu5gp6CQl1fOZcwjL+ZVstCc0lw+++Xm3pD - 4Fx0XYgJzvKa5TQ451kXEpsbNh2xOB2czfKWNImTBk5OcS4AZ/leC4mzOZueWJwez2b5YguJkwVO - TnEuAGe5J1PibM5mIBZnwLNZ7smUOFng5BTnAnCW91lInM3ZjMTijHg2a/G/xMkBJ6c4F4CzFhlJ - nI3ZTMTiTHg2FQ7xw8kpzgXgVDjEzObyd7CcwGaAshl8rTEpdC61urhKcH4EJ6s44XBWqvokOCXO - GdnMxCPODC/qTqk6P5yc4lwATqXqzGwSjzgzuKgPbCpV54eTVZxwOJWqE7PZE484e3xRV6rODyen - OBeAU6k6M5vEI84eX9SVqvPDySpOOJxK1YnZ3O4rJTXneLhmwMKpWP0M6ORU5wJ0KlenhpN40Dl8 - EcB0Vo6Kkztp8KR1JxxPRUTMdDa4cOAEOg0aTmVEZ0AnqzvhdCokooaTetxp4IVdMdEZ4EnrTjie - CoqY6WQ+Y2483h0Mp5KiM6CT1Z1wOhUVMcPJfMqcWTk0nIqKzoBOVnXC6VRUxAwn8zlzZuXBcAYl - RWdAJ6s64XQqKGKGk/mkObNCryYFBUVnQCerOuF0KihihpP5rDmzimg4lROdAZ2s6oTTqZiIGU7m - 0+bMKqHhVEx0BnSyqhNOp2IiZjiZj00yq4yGUzHRGdDJqk44nYqJmOFkPv/DrHownFEx0RnQyapO - OJ2KiYjh3N7XTapOC98pHBUTnQGdnOpcgE7FRMxwMm/FtPDNblEx0RnQyapOOJ2KiZjhZN5NZOH7 - NaJiojOgk1WdcDoVEzHDydwSb+FNx7WbX6ROFjpZ1QmnUzERM5zMfZ0W3jmXFBOdAZ2s6oTTqZiI - GU7m5iQLb/9IionOgE5WdcLpVExEDOfmfAVWdTp4hpkUE50BnZzqXIBOxUTEcHpmdXownCHlSl0P - vZM6KejkVOcCdFYK+yQ6H7061ze/TILzODQvr7795P4XUm2Ot7+4OcAcnndvTc+WdMTZldDcPsnx - aG7//TmjyenMBdDkHG6KzLszaWilGdBkkrYkCc17NFmlCUeTsx9JZN7t0CCVpsWXc9JmJKF5jyan - NBdAk7MTSWRuyOQdaVp4OXekbUhC8x5NVmnC0eTsQRKZd00etNLMaDJJG5CE5j2arNKEo8nZfSQy - 79o7SKXp4HMgpyCIHU1OaS6ApoIgYjItsTQtmkwFQexoskoTjqaCIGIyPbE0PZpMBUHsaLJKE46m - giBiMiOxNCOYTK8giB1NVmnC0VQQREwmbxDk4KvtXkEQO5qs0oSjqSCIl8xxsxqpND18td0rCGJH - k1OaC6CpIIiYTN4gyMNX272CIHY0WaUJR1NBEDGZvEGQB6+2J1u7saV3LnSSZms0WaUJR7MyCZqE - pqQ5I5m8O4I8fNuFV3rOjiarNOFoKj3nJTPwrmkG+MJRUHrOjianNBdAU+k5MZm8I80AL+dB6Tk7 - mqzShKOp9JyXzMg70oz4cq70nB1NTmkugKbSc2IyeUeaEV/OlZ6zo8kqTTiaSs95yUy8I82EL+cK - gtjR5JTmAmgqCCImk3ekmeDlPCoIYkeTVZpwNBUE8ZKZeUeaGV7Oo4IgdjQ5pbkAmgqCeMnseaXZ - 48lUEMSOJqc0F0BTQRAvmeOVf6zWHO/881g0lQTRs8mpzQXYVBREjCb1Lb4GiqavXxPUb8mUNtuy - yapNOJu1dc0pbD5+bb68+amM5s8fbj683zzzy3f7Ae3GF3wEo19dXTz52/M7TF88u/yhyGgBUASd - 25/28a8u7NK2vXB+D21drUjnnPr+IG37sdj+xOOx2H57F4tTTXT4dR/tI753/bl49rzpoleq94nm - nDdmmfKmTxXACW/6+tX1+KZ/qr3qwt/0UW/56ddPXwwf5+LJ+H4vr/759LsTX/JGUrvv+e2b39bX - 74tv2Q06danLufQnPUeBGZ5tFwRrjDelwU/0aRi01wrM8D9+X998uB7HOrd/aJ8/8a/Xf714O2J+ - /Xo91o3b77z514c3r7Y/8vNfYQoHqpM5WJ12P/KnvJVVdFEg9OJoRN++fodl9Ltv/vF4IR0ebh+l - IRW7r2MYGO69KRcmUbpL6Z/D402itFsdVzCf/u+nPxJiuvn4n4JYpXQfo8OT7TVpl0pB72DSYG0n - k57C6MuffpmKaGePG8BffEuJ6PjxH8bo8GQFjZZaC0eNBpOtEaInIPrby8mIHqfRr77/ihPRvXOP - IqL7pqDff7WLqIt9NsUpqI0hGtvLoqcgengt5OGrIDPDeXBafHAsuncN5CSB7lsgGeRYvn0pOG+9 - tTlNoPOjB/6Uz3GZ7SF8jteYP/EP4xO4cnML5yR/HivQqwtOgX5m0M2qcgHR7X87jlCTulLnTPQx - W+/G01zn8udD+Twbf97gEb36QhDN1pWPRumsj9727tEhelVE9KqA6NWxiL5693o9dcnpuBr/9T++ - uSS06Objfwrh+km50t//588wHZ+uMFsqFfvtbClNKvZf7FD01bubiZgeCekVI6M7y6L1VdE9Y9Hh - wfZW+ljs3o4+9cFrvekkPI+IER8YILJNk06awh/bszBMklJxm2AINsTsN2wuOknqHzpHMlUygZnn - LZbANabhwxOKc88aU82cx+NpXFfMknyy0eY5vUky/MR7c9IM6SRA+SZIEEDH+VFxW5bmR6fPj4YH - /0/kwPO7v3//PwkFOsPIc3yywsyouMh0G8fPuMj0+Ieeb6evgx6JKOkq6IPD+O/2r4IOgJbaZreA - Oqd+kRMAHR8bqdDvL5/+QAjoDAodn6xAaGmv1q1C7ZRJ0hdL6O/Xf70Gz5OePf3xG0JIHz4OHR9s - l1HfOWvLR1e4YR5lxrMNH9lAFMjoH+/eYsv88398Rwnog8v88GD7JOo6V+xn8sn5fs/blkTLgN68 - n9gVemyQdPUDY1PoTDnS8HCFSl/qaLqLkdR0dwKkH357gxyK/vP7vzMi+vCR6PBgBTyLMdJ2ICqH - noLnX+9/h+7/+PGHZ4x8zrL948fNrrodRG1yRYP65F0IlW2JQvTEtuU/fn9387CJ0l3T8olgEvQp - 76Vyb6ey77ypXIvnrOmz39b1CiiHU0d0Y9sECE5tvWTA4JRetiIHe9cag4+mWECzdeNSuOXnoBDf - zcjB1ePmIA9/8cXTlzrrkt8slqA4eHBKNsa4Aws4DMYUd/uro6Ng4nBlf347CqHYYLAVgksgELa/ - 3x0Ott+eod1kLg5Yy0J96fRUCooZ6ZYC64jLQr2rY0YOKMvCfByMZaG4oW1bFhJwmPjgsvDr8LEn - DRSPg+B/Pb36O8kocZaSsHmePRj0MeQyBn03HoHHPFs4EK89fM54H6415+DEPG0/B5VIrXzktjPO - jLNKXgzqCVaFgtPyKwIGTous9jKwP7QaTVA8emlrAm4EbsahwXpzABRoZPD8ahwbbOtpaw7mmits - nmkXB+djVz590naxj6nzIBy2n+joE/mKMGDnC1saSLQwy0ixwoItXv21YSFQLyWNHEyZNBwZzA2f - hGPCMIsQbn+x++YL5fKwmS9k1OrBHPOFD3/8P+B04Z/P/89jmi2Mj7PHAynZ8g5pm3303vfEHqhH - oA9E4C7/fBwE7E88xzFisRBsx4g28gDwf//H8Gf/9982c5horIvjNr+w6c368Mc/Ptx89h9M3vyH - r9/8/PO4JXB8Y8PT/ba+uSXl3/8fS0hArCy1HQA= - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '43292' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:34:15 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/get_tradingview_chart_data?end_timestamp=1733184000000&instrument_name=BTC-PERPETUAL&resolution=720&start_timestamp=1733011200000 - response: - body: - string: '{"usOut":1736123656142468,"usIn":1736123656140693,"usDiff":1775,"testnet":false,"result":{"volume":[1765.50937228,1795.26276348,3824.28604442,7511.65710613,3391.8982902],"ticks":[1732996800000,1733040000000,1733083200000,1733126400000,1733169600000],"status":"ok","open":[97116.5,96529.5,97288.0,96077.0,95872.5],"low":[95555.0,96487.5,95843.5,94377.5,95153.5],"high":[97116.5,97627.0,98275.0,97469.0,96375.0],"cost":[170140340.0,174409490.0,371629810.0,720033750.0,324979260.0],"close":[96530.0,97288.0,96077.5,95872.5,95602.0]},"jsonrpc":"2.0"}' - headers: - Connection: - - keep-alive - Content-Length: - - '546' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 00:34:16 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -version: 1 diff --git a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_info_fetcher_urllib3_v2.yaml b/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_info_fetcher_urllib3_v2.yaml deleted file mode 100644 index 7acf8a10ad7..00000000000 --- a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_info_fetcher_urllib3_v2.yaml +++ /dev/null @@ -1,132 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/get_instruments?currency=any&kind=future - response: - body: - string: !!binary | - H4sIAAAAAAAAA+2cXVPiSBSG/0uu0eqvdCfeIbLzsa5ailNTtTVFRWidrBCYJLjOTM1/325Ad4Cc - kEA6047xStMQ+oTH9+1zTiffnX+SSRRPB86RQw6R03JimcxGqXP093dnGocD2Q+joXxUwzfpoD9L - hvolt1+co9tglMiWc6+G1eDtLJ3FUo2Nw6ifxsFQ9oPxZBapE2F0iFpOmPSDQRo+SOcojWfqjWGU - qF/GMkr7UTBWh53jXucAo/ftM+LqEwX3Mu4PJuNxmCThJHKODtSJEMItJ90Ymo+4KydNv071SWP5 - IONE6mnLx2kYB6l6Qz8NxzJJg/FUTU9QznyO5j8tZzCJ1PQHaT8Jv8mnyafh4H55gByqj7kZTdSB - RZg/janXqtndBInsD2ZxLKPB10VYzsrMQnXBGGaCc/VxscyakUs8zrA/n1Ei03Qk5++cyjic6Mv9 - r5T36qSLq54R6jh47I/0n8GdnrO7PBJ+mYXDxeetXz2xFtbP4ypmecBWxze/Z7K8gj/Nd+MqDPRL - 9Xf3/8D11Yka+DKbpHLz8PPF7SepnCaKyk8/WutgyvTzbmAWorLbe2sflStIokNSlkkVFMCkn88k - 2fiODTLp74skzkVycRGMIFmPVopfQKVQ19SrWyu5r18Mcen5mGDSaKUlWmkblYa0UjGJtjCJG60s - ppXDh8lIYzkozSU6LEam+pc6+XB+2lfz6hwQH0Z0oWEl8ByFkQziPDg9TAXPhnM+/RUCcXnF1IFl - Eaoo9GBCMaY8h9CxmujnDUSfg10FFKP9RPNJFvYRTf3Ngoh2AEY7Vhk6xRVyWUQ2PYoRq9fMqa9+ - YCzVGlMQ7JXEsnFzU25uFZJGnHwOJJyJL4AUdQL5cq18Gjze7erjqBiUF+2PbxYuXimaBVwcBnMx - +cI2nkGmjirDwpHCD058KGYeEawaC3cbC6/Cwon3R/e4Rr1kSBAfWFwarV3mrCy31C4bC6/Xwq1C - 0ljhUuQk41sKl42F/yoLrxTNrRaeB6YxC8fI91wQTeb6LsG0sXCrLPyv9mWdeqnXcTWX1CnyBQJb - PRhz3ycELqk3Fl63hVuEpJksXAMp8oHEjYXbaOEVorndwnPANJqFw6tLPSPsNlm4VRYu3l/XWbV0 - McKY1GzhTH0uuLLEvssxx2WxbCzcmIXbhKQZC9dAknwgEW8s3D4LrxLNrRaeB6bRLJw1WfhLsnB+ - 1b2oUy89T9CaC+lUUMLAQjoRjDDU9MLtsXCbkDRj4YJiSrcA2fTCbVLJk26nRiQ5fyKy5nYjWBpq - 2o3WqaRFSJpqN3LfKiBfrkoGw2C3PAcXE8n2SXuR5Fx0Ly+6vev2aS1pDiUuogLBbZ01MDeX/FnD - a3Cq2DbhJBgzDFUrORMef96LnAWn+mUq01kwKpru7KuX9qc7wehuYpbR0zfnvy+kKrgsSl24eMld - xbCfJ6ENpZuUPqjwjN5C0f7Q/mghppu3UORTmsWoiixTSZGA6plKSV1CUKOkpQpHg887FzVJsZyo - 89ZKRPX092NURQbIKLSNQ8uoiz0C3+PTILqJaHRjuO5+fHZsJ6IbMpqbLLkZiJ4dZ5SUuO9hMFki - 3OWY+I2K2lZ+rxjOrXn81rXo4fpqtJLakhJHj0IVeJcywgjx4IInTCeYzu/bHrK8vrSEcyf9LCqg - vY6dArqmoFhPAEB0MVaMUCwQ1LrkjHuEUQbv1Cytn6+mfZmaR7T3ShD1CIU3fiDCOCM+3F1/qYj2 - QER7AKK9oogOJ3dy15JTMY8/OX/TtVBF59NfhVAewE7/PLyGqY4OyJYgs19kS2Ins3+1S9HhJN0R - 04KQ9mxkdKMsml8VzViLqsAynZ5zqPnOmfBd1tSbSuFpvtlpW5pUKoUv2u9USZLgUEvJdYnLPZaz - Ad5QkvRye55LLA3WmNTkLRTO/Xd3AnhiisBeEhOEE69K3bRk+WleN3fKkEoBal+CZARQnR+Bu+qa - /Kh8fqQCvze58Dx9d/anhQJawcpTRwZkRmCRadmOr7DI9PsvPUe710ELImppFXTvZvxpdhVUAQo9 - CWkBKKXNfpESgOqwTUroWbd9aSGgFUiojgwgFHqM3FJCyS5J0qsltNZ74KyC1NhtcIgSAhbqKVV5 - lEL1t1uIGmQ0Mf3c4qunZxZbBujeNn+V9cxifRMmBfczMUGZn/FtNyIKA5rGO+4KLdpI6l3auCm0 - oj6SCg5wemhH01Mbqdl0VwLSWRSaXIpen72zEdH9V6IqMABPsI20WIg2GloGz8d4avT+j4+XFzby - WcntHyq2LESJoKCCMsGo6/pNNg8j+qnlzJJ30XzVzrFPMPMY9T3M9PHzWbo24CNPD5yEt7fOka9e - lKqrHcl0ifKP/wA0kD8VBW4AAA== - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '1786' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 19:35:48 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/ticker?instrument_name=BTC-PERPETUAL - response: - body: - string: !!binary | - H4sIAAAAAAAAA2WST2/cIBDFv8ucHQswf/fWJjlEitSoSs8WWbO7JDZeGdi2ivLdO5BNu26OzG/e - zOPBKzzHOSzHLWyAtQQaWFzMY4LNKyQ/uZjsdIQNVZ2khlFuKJMNYDU5VMxHF+D9GIvi4PcHbCZM - Ut2KBsb5J2yMMly1pIHj4reu3x5s2KO4aw0RtIHTPObJVRXlrZBMU2b0R73PcahMkI4rTsqcMwlz - 8nOw4xq/NeDD4H71dVthVHPdmlKOaUFhSH2wZSN8fby+erj9/nD7+OPLPd5jtDFd6gQrl4gupdFV - 3ZkZg2m0XDUw+fBPgBZk8TfZi+0dJ3VKiar3ITnMF9OlDPs55lT7l5fLtYq3VBa/7839yY4ZEV5O - CcGVkh0lhlCmeQNPpcHGlV7XtCt58sOKyGIFgZ/wAYd+cKM/ueX357D+zrXTnAMa1oIospr7QfBT - dKyibV6WEtMuh8GHfbXcwPnU6/I1Wsm7zl1xfKYc78L6ZykpulL/ltN/QJeccrzxux0SgckntBEc - 9u3sGN3bH+ZnaX7IAgAA - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '414' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 19:35:49 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -version: 1 diff --git a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_instruments_fetcher_urllib3_v2.yaml b/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_instruments_fetcher_urllib3_v2.yaml deleted file mode 100644 index 57834285ab9..00000000000 --- a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_deribit_futures_instruments_fetcher_urllib3_v2.yaml +++ /dev/null @@ -1,79 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/get_instruments?currency=any&kind=future - response: - body: - string: !!binary | - H4sIAAAAAAAAA+2cXXPaOBSG/4uvSUZfluzcEcL2Y7NJJiGdzux0GAeU1Bsw1DbZtJ3+95WAZAv4 - GBssV2mc3qQWGB37yfvqnCPz3fknmUTxdOAcOeQQOS0nlslslDpHf393pnE4kP0wGspHNXyTDvqz - ZKhfcvvFOboNRolsOfdqWA3eztJZLNXYOIz6aRwMZT8YT2aROhFGh6jlhEk/GKThg3SO0nim3hhG - ifplLKO0HwVjddg57nUOMHrfPiOuPlFwL+P+YDIeh0kSTiLn6ECdCCHcctKNofmIu3LS9OtUnzSW - DzJOpJ62fJyGcZCqN/TTcCyTNBhP1fQE5cznaP7TcgaTSE1/kPaT8Jt8mnwaDu6XB8ih+pib0UQd - WIT505h6rZrdTZDI/mAWxzIafF2E5azMLFQXjGEmOFcfF8usGbnE4wz78xklMk1Hcv7OqYzDib7c - /0p5r066uOoZoY6Dx/5I/ze403N2l0fCL7NwuPi89asn1sL6eVzFLA/Y6vjmfSbLK/jTfDeuwkC/ - VN+7/weur07UwJfZJJWbh58vbj9J5TRRVH760VoHU6afdwOzEJXd3lv7qFxBEh2SskyqoAAm/Xwm - ycY9Nsikvy+SOBfJxUUwgmQ9Wil+AZVCXVOvbq3kvn4xxKXnY4JJo5WWaKVtVBrSSsUk2sIkbrSy - mFYOHyYjjeWgNJfosBiZ6k/q5MP5aV/Nq3NAfBjRhYaVwHMURjKI8+D0MBU8G8759FcIxOUVUweW - Raii0IMJxZjyHELHaqKfNxB9DnYVUIz2E80nWdhHNPWdBRHtAIx2rDJ0iivksohsehQjVq+ZU1/9 - wFiqNaYg2CuJZePmptzcKiSNOPkcSDgTXwAp6gTy5Vr5NHi829XHUTEoL9of3yxcvFI0C7g4DOZi - 8oVtPINMHVWGhSOFH5z4UMw8Ilg1Fu42Fl6FhRPvj+5xjXrJkCA+sLg0WrvMWVluqV02Fl6vhVuF - pLHCpchJxrcULhsL/1UWXimaWy08D0xjFo6R77kgmsz1XYJpY+FWWfhf7cs69VKv42ouqVPkCwS2 - ejDmvk8IXFJvLLxuC7cISTNZuAZS5AOJGwu30cIrRHO7heeAaTQLh1eXekbYbbJwqyxcvL+us2rp - YoQxqdnCmfpccGWJfZdjjsti2Vi4MQu3CUkzFq6BJPlAIt5YuH0WXiWaWy08D0yjWThrsvCXZOH8 - qntRp156nqA1F9KpoISBhXQiGGGo6YXbY+E2IWnGwgXFlG4BsumF26SSJ91OjUhy/kRkze1GsDTU - tButU0mLkDTVbuS+VUC+XJUMhsFueQ4uJpLtk/YiybnoXl50e9ft01rSHEpcRAWC2zprYG4u+bOG - 1+BUsW3CSTBmGKpWciY8/rwXOQtO9ctUprNgVDTd2Vcv7U93gtHdxCyjp2/Of19IVXBZlLpw8ZK7 - imE/T0IbSjcpfVDhGX2Eov2h/dFCTDcfocinNItRFVmmkiIB1TOVkrqEoEZJSxWOBp93LmqSYjlR - 562ViOrp78eoigyQUWgbh5ZRF3sEfsanQXQT0ejGcN39+OzYTkQ3ZDQ3WXIzED07zigpcd/DYLJE - uMsx8RsVta38XjGcW/P4rWvRw/XVaCW1JSWOHoUq8C5lhBHiwQVPmE4wnd+3PWR5fWkJ5076WVRA - ex07BXRNQbGeAIDoYqwYoVggqHXJGfcIowzeqVlaP19N+zI1j2jvlSDqEQpv/ECEcUZ8uLv+UhHt - gYj2AER7RREdTu7kriWnYh5/cv6ma6GKzqe/CqE8gJ3+eXgNUx0dkC1BZr/IlsROZv9ql6LDSboj - pgUh7dnI6EZZNL8qmrEWVYFlOj3nUPOdM+G7rKk3lcLTfLPTtjSpVApftN+pkiTBoZaS6xKXeyxn - A7yhJOnl9jyXWBqsManJWyic++/uBPDEFIG9JCYIJ16VumnJ8tO8bu6UIZUC1L4EyQigOj8Cd9U1 - +VH5/EgFfm9y4Xn67uxPCwW0gpWnjgzIjMAi07IdX2GR6fdfeo52r4MWRNTSKujezfjT7CqoAhT6 - JqQFoJQ2+0VKAKrDNimhZ932pYWAViChOjKAUOhr5JYSSnZJkl4tobU+A2cVpMYeg0OUELBQT6nK - oxSqv91C1CCjienvLb56+s5iywDd2+avsr6zWD+EScH9TExQ5mfc7UZEYUDTeMddoUUbSb1LGzeF - VtRHUsEBTg/taHpqIzWb7kpAOotCk0vR67N3NiK6/0pUBQbgCbaRFgvRRkPL4PkYT40+//Hx8sJG - Pit5/EPFloUoERRUUCYYdV2/yeZhRD+1nFnyLpqv2jn21D+sLpnQt2yWnM/StQGfYj1wEt7eqmkS - rTzqckcyXbL84z9ZbTVhBm4AAA== - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '1786' - Content-Type: - - application/json - Date: - - Mon, 06 Jan 2025 16:43:31 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -version: 1 diff --git a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_get_options_symbols_urllib3_v2.yaml b/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_get_options_symbols_urllib3_v2.yaml deleted file mode 100644 index 6fb44364537..00000000000 --- a/openbb_platform/providers/deribit/tests/record/http/test_deribit_fetchers/test_get_options_symbols_urllib3_v2.yaml +++ /dev/null @@ -1,265 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://www.deribit.com/api/v2/public/get_instruments?currency=BTC&kind=option - response: - body: - string: !!binary | - H4sIAAAAAAAAA+3dXa8muXUd4P/S16OD4leRnDtbUoAYiGPE8VVgNFqtI6Qz0x/uc1qxYui/h+97 - RokUD6lqVi/WXsi6MOBSj2aOjh4tsmrvTf7bq//x9PHD509vX33/yj9sr7579fnx6cuPz6++/2// - 9urT53dvH1+/+/Dbx39tf/yb57evvzz99vaX/O5fXn3/uzc/Pj1+9+qH9sftDz9+en738UP7s3cf - np4/f3n/+OH59Yc37x/bH/3tf/3lL/Kvfv1LH39R4rZtv/jl7S97ev3m7fO737e/oP3l7e/z/s0P - j59fv/34/v27p6fb3+r77aH9xeG7V8/dP/mzf9bzHz7d/lmfH3//+Pnp8fZDPv7rp3ef39x+qtfP - 794/Pj2/ef/p1fcuh5DS/efYtu9evf344flz+0FeP737X+3f7x7av9b+nu9+aA/lIT7G9o9/9/aH - n/74/s913736zY8f27/W/o2/fXz9c3/85unx9dsvnz8/fnj7h5dfwF/+Yt6131jcYna3n+Dz48/+ - kCG4bX/5IZ8en59/fLz/Oz89fn738fb7/u2bP7S/58tv/U//4d+++fHHV3/50/3cb+3P//z9uw8/ - /X9v3n/88qH91+7TX/wD/91/jLe3v+z238f//YN/+sdftT/493+r7aH9Mv7ly8fnn/lt/J9f2+un - 58dPT3du/++vsv0kb37z8fePr+8OX/7F9Md//uN3MJn/IJk/yXTfVOanL8+COQlzp4rMHQsz+b0P - 09fqnSJzrUyayITLzN9UpiJzHmahisyChlkUmaZk0kQmXGZVZNqAWTeiyKwPGxhmGLyYKzIvkEkS - mQtkDl7MFZlLYTqqyHTgL0bbYJfZZLqoyFwrkyYy4TIHu8wJmYrMeZieKjI9ei332mWakkkTmXCZ - QbtMIzADVWQGNMyoyDQlkyYy4TKTItMITKYmo4pu5UhBFXNbMmkiEy5TFXMrMBNVZCY0TFXMbcmk - iUy4TFXMrcBk6sus8O63qIq5LZk0kQmXqYq5GZiJKjKxi3mqo8yM2Wv8ZzVNnsyE0xyE5gRNhea8 - zEy1z8zo1Vw1c1syaTITLlM1czMwqfaZGb6YDzJT+8wLaPJkJpzmIDS1z1wqk2lossJH06IajWzJ - pMlMuEw1GpmBSbXPLPDFfJCZ2mdeQJMnM+E0B6GpfeZSmZVqn1nRq7m6M23JpMlMuEx1Z5qBSbXP - rPDFfJCZ2mdeQJMnM+E0B6GpfeZKmffliSY128Njwi7n6mk3RpMkNRfQVFO7HZlEW013+01Abe4+ - DT4cpW0v2muuxskTm3icgxehCZwKzhM2mU6Daw8OvKYnzQMZs8mTm3CbmgiyQ5Nrv+nQS3odLene - ee03V+Mkyk04zsHXzQmcCs4TNpmO0mwPHr2may7ImE2e3ITb1GSQGZpMx2m2h4CmqTZ3YzZ5YhNu - U43uZmgyHanZHiKapro2jdnkiU24TfVtmqHJdKxme8B+P4q5DnaboWz6uLncJk9swm0OdpsTNhWb - J2gyHa3ZHnb0iq7WTWM2eWITblO9m2ZoMp0U1x4ylmZxgxakUF7eGxWbK23yxCbc5qAFacKmYvME - TaaDj9pDAa/ouzo3jdnkiU24TXVumqHJdI5He6hQmmkfNbyHmrNzis21NnliE25ztNv8epuKzXma - 9xWKJzbRoxi72jaN0aRJTThNdW2akcnV7O7QDcW7ujaN2eRJTbhNdW2aocnVtenAnXF5dHlqiCF6 - xeZimzyxibY5ugtwwqZi8wRNrvYjB27xyHnQfhT2vG2KzcU2eWITbnO0pH+9TcXmCZpcdXQHrlXm - Mvrovhev2Fxtkyc24TYHn90nbCo2v55meaFZTGw29xjLAZnlYceen13c6J6WUIJ6NhfLvDoz7cgc - XTrw9TIVmfMwTWw0D8PE3geYhkOV0ZddXzUXy6SJTLjM0WL+9TIVmdMwb/1wNJFZHzYwzH3w1Si6 - 27ivInOpTJLIxMvMgzLQhExF5jxMEz1Hh2F67G2AJYy+GJW06+iOxTJpIhMuc9CnOSFTkTkP00S/ - 0WGYEQ1zeNqRInO5TJrIhMscnnWkyFwI08QJcYdhgq9PLX70+hO9OjRXy6SJTLjMQTfHhExF5jxM - pop5Bdcl21o+qEtql3mBTJrIhMscHESsXeZSmCZOhjsMM4M/spcBzOjV0L5cJk1kwmUORi0mZCoy - 52EyNRlVcCtHW8tHM0DaZa6XSROZcJmjCSDtMlfCNHEi3GGYFQxzONGryFwvkyYy4TKHhw8rMtfB - vB+GQpOZ7QF8LHZUl5ExmiShuYCm2ozMyHRcoQk+qbBENRoZs8mTmnCbajUyQ5Opo709gE8qLFGV - c2M2eWITblO1czM0A1dsBjRNlYKM2eSJTbhNFYPM0GQaBmoP4ANey/B+IMXmBTZ5YhNuUwUhMzSZ - BoLaQ0LTVEXImE2e2ITbVEnIDE2moaD2AD4XuySVhIzZ5IlNuE2VhMzQZBoMag8ZTVMlIWM2eWIT - blMlITM0mYaD2gP4OoGSVBIyZpMnNuE2VRIyQ5NpQKg9VDDNXSUhYzZ5YhNuUyUhKzTvkxg8sYnu - KN5VETJGkyY14TRVEDIjk6vZ3aEbincVhIzZ5ElNuE0VhMzQ5OradOjOuF0FIWM2eWITblMFITM0 - udqPHLrFY1dByJhNntiE21RByAxNrjq6Q9cqR7f/KTavsMkTm3CbKghZoem5CkLoz0dZBSFjNGlS - E05TBaGrZdYXmcXEVjOHl/+F/BWYBXxqdqqjQzxi9psyc7HMqyPTjszB6/mETEXmNMxqYpt5EGZ9 - 2MAwRwd4KDIvkEkSmQtkDl7NFZlLYZroOjoM06NhDt7MFZkXyKSJTLjMwYu5InMpTBMdR4dhRjTM - QZ+mIvMCmTSRCZc56NJUZC6FaeKMuMMwExrmoEdTkXmBTJrIhMscXTStyFwJ00SD5mGYOxqmyj+2 - ZNJEJlymyj9WYJo4G+4wzAyGOTqsQ5F5gUyayITLVPnHCkymJqMKb+UYndShyLxAJk1kwmWq/GMF - pokz4Q7DrGiYKv/YkkkTmXCZKv8YgXk/dIsmM9sDdMiiyVT9xxhNktBcQFMFIDMyHVdoYs8qbDRV - AjJmkyc14TZVBDJDk6mjvT1gJ3pTHR3Rodi8wiZPbMJtqhBkhmbgis2ApqlSkDGbPLEJt6likBma - TMNA7QF7xGujqXKQMZs8sQm3qYKQGZpMA0HtIaFpqiJkzCZPbMJtqiRkhibTUFB7wJ6M3WiqJGTM - Jk9swm2qJGSGJtNgUHvIUJq7r4PYTN6lTbG52CZPbMJtDmJzwqZi8wRNpuGg9oC9UCDVokq6MZs8 - sQm3qUq6FZo2bj8/TBPdGldUSDdGkyY14TRVRzcjk6trE3zDdKOpOroxmzypCbepOroZmlztR+Ab - phtN1dGN2eSJTbhN1dHN0OSqo4NvmN796NjXtO1FsbnaJk9swm2OipVfb1OxeYImV0EIfMP07kfn - GKatVMXmaps8sQm3OfjsPmFTsfn1NF14sZktNLvHLe9HrpjO0DsGQi1pcMdaS9RyP/ixI/N/Pj7+ - oNj89jYvjk1DNvsVoSmbis15mvffNUtsFuidqaHG5Prv6N7lfbtvPBSbK22SxOYCm/139Cmbis0T - NC2UhI7TRF4C2Fb0fXDWpt9TiF67zdU2aWITbnOwpM/YVGyeoMn0kl7AL0JtRe+PYWi3eY1NmthE - 2/T9OQztNlfTtFBJP04TeRlgW9G967dt+tR+0Kjd5mqbNLEJt9lv25yyqdg8QdNCJf04TeTtVqGm - kPqFdO/vpz8qNhfbpIlNuM1BuXLGpmLzBE0Ld68dp4m84epWrRwMCfndh1FHsWITY5MmNuE2B7vN - GZuKzXmaN5k0sVnh1Uo/2G3q2+YlNklic4HNwW5T3zYX07RwAdtxmg67orsyKAml3XntNpfbpIlN - tM06KAnN2FRsnqBp4SiP4zQ9ekUftBRrt3mJTZrYhNscfEDSbnMxTQsXsB2nGdA0B9+PFJuX2KSJ - TbjNQU+xYnMxTaZ29wpuKW40NSVkzSZNbMJtakrIDk2mdveKbylWu7s1mzSxibYZ1O5uhyZTu3sF - txQ3mqqkW7NJE5twm6qk26Fp4QK24zQzmqZKQtZs0sQm3KZKQnZoMk0JVfAkRqOpkpA1mzSxCbep - kpAdmkxTQhU8idFoqiRkzSZNbMJtqiRkhub9ckCa3GwPwBO07zZVEzKHkyQ48TijikKGbDINCrUH - 5L2qd5wqC5nTyZOccJ0qDBnCyTQs1B6Qd6vecao0ZE4nT3TCdao4ZAgn08BQewhonCoPmdPJE51w - nSoQGcLJNDTUHpBXU99xqkRkTidPdMJ1qkhkCCfT4FB7SGicqhKZ08kTnWidSWUiQziZhofaw47G - qTKROZ080QnXqTKRIZxMA0TtIUNxpn10l4uPQXeoX6CTJzrhOgffOmd0KjrP4GQaImoPBb2uq8Ju - TidPdMJ1qsJuCCfTIFF7qECccUv74JUohJrLvdNQ0blUJ090wnX2F/YpnYrOEzjv8xo80YnuOU7q - TTKHkyY54TjVmmTIJldDvEO2HN9X9f4LkfacF+nkSU64zv66rj3ncpxcXZ0O2Td3x9lf1xWdF+nk - iU64zn7PsaJzOU6urk4H75tTQ7w5nTzRCdephng7OD3Xl070iHBSP7w5nDTJica5qx3ekE2uPadH - r+q72uHN6eRJTrhOtcPbwRm49pzosxV2tXSaw0mTnHCc6ug0ZJNrzxngq7r6kszp5ElOuE41JtnB - Gbn2nNhDaZIfHoVYcvzph1RyLsRJk5xwnKNlfQKngvPrbfrtxWY2seXci9sP0Mzg26xrGV0ZnIt7 - +SmVmyttXh2bdmwOvnHO2FRsztMsJrabB2mWhw1L09dBWT2XrSg2l9skiU24zbANquozNhWbJ2gy - 7TYLekUP23C3qdi8wCZNbMJtDnebis2lNE0cknSYJvY260ZzUFBXbF5ikyY24TYHH94Vm2tpVqaX - 9Ip/ERp8dldsXmKTJDYX2BzU0hWbi2mauC/4ME2HpjkYtlRsXmKTJjbhNgejlorNxTRNHI10mKZH - 01RJyJpNmthE23QqCdmhaeKe4MM0A5qmSkLWbNLEJtymSkJ2aJo4Te4wzYimqZKQNZs0sQm3qZKQ - HZpMDUgV3uQxuqhNsXmJTZrYhNtUScgOTRN3Ax+muaNpqiRkzSZNbMJtqiRkh6aJe4EP08xomioJ - WbNJE5tom14lITs0mdrdK7yl2KskZM0mTWzCbaokZIemifuAD9OsaJoqCVmzSRObcJsqCZmheb9u - lSY32wP0dK5mUzUhczhJgnMBThWFDNlkGhRqD9ibqhtOlYXM6eRJTrhOFYYM4WQaFmoP2KuvGk6V - hszp5IlOtM6g4pAhnEwDQ+0Be4NLw6nykDmdPNEJ16kCkSGcTEND7QF7EUHDqRKROZ080QnXqSKR - IZxMg0PtAXn5VfvfadoHX5NKqckrOpfr5IlOuM7B56QZnYrOMziZhofaw45e11VgN6eTJzrhOlVh - N4STaYCoPWTouh5z7eMMoWwxKjqX6+SJTrjOfv/HlE5F5xmcTENE7aFg1/WaB+t6W9SzXtjX6+SJ - TrjOQevcjE5F5xmcTINE7aFi1/UymA1u67p3is71OnmiE66z3/8xpVPReQLnfV6DJzrRPcdBHfHm - cNIkJxynGuIN2eSaJXJInHFLe+rjDKHmkpKSc7VOnuSE6+zX16d0KjrP4OSaJXLIaY37C1G/5Viv - 6xfp5IlOuM5+z7Fe15fj5GqId8iW49u6vo++dGrXeYlOnuiE6xx96dSuczFOroZ4h2w5vn9N0gS7 - OZ080YnWGTXBbggnV0O8Q7Yc39f10Qu7dp2X6OSJTrjO0Qu7dp1rcd6/jvBEJ/pgmqizP8zhpElO - OE4d/WHIJtfruoe/EOnoD3M6eZITrlNHf9jBGbj2nOgTvaLG183hpElOOE5NrxuyybXnDPBVXd3w - 5nTyJCdcp9rh7eCMXHtObOdHzKW/5wwhb7koOZfjpElOOM7RoTQTOBWcEzbzn92XdXVuJl/2eIjm - Brxn0JdYa3dN30JOW7zPUv88zfftB//vCs5vj/Pi3DSEs9sxN4lTwXnCpoV39eM2E9TmS0eHgtMW - TprghOPsdn0oOJfbNNGRdNSmBy/qzWa36UPBeRVOkuBcgLPb9KHgXG+Tacfp8Yt6t+VDwXkVTprg - hOPsfn9XcK63aeFk4+M2C9am79/10myWmgaf3xWcIJw0wYnGmUffkWZwKjhP2LRwrvFxmxW9qHfr - ltpxXoWTJjjhOLu9ctpxLrdpogP+qM2A/4ykqro5nCTBuQCnquqGbFo41fi4TQe26VRVN4eTJjjh - OFVVN2TTwpnGx216tE1V1c3hpAlOOE5V1Q3ZDFTBGdA2VVU3h5MmOOE4VVU3ZNPCUfDHbUa0TRWH - zOGkCU44ThWHDNlk6uMM8FY5p+KQOZw0wQnHqeKQIZsWjoE/bnMH2+zfsq7gvAonTXDCcao4ZMhm - pgrOjLap4pA5nDTBCcep4pAhm0yTQwE9nLF5FYfM4aQJTjhOFYcM2WSaHArw4Qyv4pA5nDTBCcep - 4pAdmybOMT5qM8KHM7yKQ+ZwkgTnApwqDhmyyTQ5FOHDGUHFIXM4aYITjlPFIUM2mSaHInw4I6g4 - ZA4nTXDCcao4ZMgm0+RQhA9nBBWHzOGkCU44ThWHDNlkmhyK8OGMoOKQOZw0wQnHqeKQIZtMk0MR - PpwRVBwyh5MmOOE4VRwyZJNpcijChzOiikPmcNIEJxynikOGbDJNDkX4cEZUccgcTprghONUcciQ - TabJoQgfzogqDpnDSROccJwqDhmyyTQ5FOHDGVHFIXM4aYITjlPFITs2b5/faYIzwYczoopD5nCS - BOcCnCoOGbLJNDmU4MMZScUhczhpghOOU8UhQzaZJocSfDgjqThkDidNcMJxqjhkyCbT5FCCD2ck - FYfM4aQJTjhOFYcM2WSaHErw4Yyk4pA5nDTBCcep4pAhm0yTQwk+nJFUHDKHkyY44ThVHDJkk2ly - KMGHM3YVh8zhpAlOOE4VhwzZZJocSvDhjF3FIXM4aYITjlPFIUM2mSaHEno4o5b+21B0yZV9U3Cu - xkkTnGictf86NIdTwXnCJtPkUAIPZ+yjA2ii2/f7OWcKzrU4aYITjXNwAs0cTgXnvM37KsUSnDt8 - OGNXO5I5nCTBuQCn2pEM2WSaHNrBwxn7VvvBGV2NWa/q63HSBCccZz8453AqOE/YZJoc2sHDGdmP - bOYU7sf1KDjX4qQJTjjOfpPxHE4F5wmbTJNDO3g4I4dt8I0zp5QUnOtx0gQnGqcbfOOcwqngPGGT - qQF+h/cY75ocMoeTJjjhODU5ZMgmUzvSDu34CMNzFELDGfdcFJyrcdIEJxxn9wP8JE4F57zNvBEF - Z8YXLjVyaQ4nSXAuwKmRS0M2mYpDGfr9PQwPoNGO8yqcNMEJx9n9jqQd53qbTKcjZegBNGF4AI2C - 8yqcNMEJx9l9HVJwrrfJVBzK8O/vWYd8mMNJE5xwnDrkw5BNptORMvQAmjA8uUs7zqtw0gQnGmf/ - 6C7tONfbZKqqZ3jhsn9yl4LzKpw0wQnH2T26S8G53Oa9aZYlOAu8cJl1rJw5nCTBuQCnjpUzZJOp - ql7Ahctct36r3G1ueg8KzuU4aYITjrPf8jGHU8F5wiZTVb2AC5fNZr/jQ8F5EU6a4ITj7Ld8KDiX - 22Sqqhd84VLHypnDSROccJw6Vs6QTaaqegEXLtui3m+V047zIpw0wQnH2W/50I5zuU2mqnoBFy5z - 7R9Ao+C8CidNcMJx9ls+FJyrbd5+1zTBWfGFS52OZA4nSXAuwKnTkQzZZKqqV3jh0vVb5bTjvAgn - TXDCcfaP7tKOc7lNpqp6hRYu209Qfb9w6WtM+71coeBcipMmOOE4+x/g53AqOE/YZKqqV3zhUsfK - mcNJE5xwnDpWzpBNpqp6hRYu74t6v3CpHedFOGmCE40z9CuX2nEut5mpgjMjPyNVv5VBO9Je2tK+ - KzhX46QJTjjOQTvSFE4F5wmbTO1IFdrxcV/U+x0f2nFehJMmOOE4+5VL7ThX23R3EizJ2R4eE/Q7 - UtGBnPZ0kkTnAp06kdMSTqaOpPbggTrvC3u/60O7zqt48mQnnGf/HBrtO9frZOpKag8RqtPH/k2s - IbRflt83hedynjzhCefZ/xQ/x1PheUYnU2dSe0jo9yKdzWmPJ094wnnqdE5LOpm6k9rDDl3aY66D - pT2U+/+WFZ6refKEJ5xnv84+x1PheUYnU6G9PRSszjIY2Gg6vY8Kz/U8ecITznMYnhM8FZ4ndN7L - mTzh6dCvRTqm055OmuyE69Q5nZZwctXaHbaYmfb+vFtb2GvJ9zvAlZ1refJkJ5pnv41ukqfC84xO - rnKRg3+P16Fz9njyhCecp46dM6TTc720I1f2O06dAmJPJ012wnXqGBBLOLn2nR69sFcNF9njyZOd - cJ6aLjKkM3DtOwMWZ639Hrrokt9UaL9AJ012wnX2S5lzOhWdZ3By7TsDdmHft9T/oBRddknZeQFP - nuyE8+x/UprjqfA8oTNy7TuRg2/3lyKNFtnTSZOdcJ2aLDKEM3FFJ3JdD5vfQrfM7rb2f1Uj7Rfo - pIlOuM7uS9GkTkXnCZw7V3Qih97u67pa4+3ppIlOuE61xhvCWbiiEznydsep5k57OmmiE65TvZ12 - cN6b52ii04OPl8116zeAxFpCVZ3oAp0k0blAZ/9L/JxORecJnFRlIo/9EF9L8n2cNYWY96LoXK6T - JjrhOvtlojmdis55nIFq1xmg63rYXO1Hp9tcTCXr2Pj1Okmic4HObnRO6lR0fj3O4P7ub/7ep1+Y - GCgq4eV08L++rCMvvQ7Fu9R9X/c1p73cj/RRci7FeXFwGsLZXdYncSo4522aGCY6avO2qINtdhd1 - BedVOEmCcwHObu+HgnO9TaYdZ8Av6t3ODwXnVThpghOOs9v5oeBcbtNEfeiozYhf1Lt9HwrOq3CS - BOcCnN0BTAXneptMO86IX9S7J3opOK/CSROcaJx790AvBedymyamL4/aTPBFfVdxyBxOkuBcgFPF - IUM2LdytcdymR9tUccgcTprghONUcciQzUgVnBFtU8UhczhpghOOU8UhQzaZvnEm8GekvfTPUwhb - yXvWQUnrcdIEJxxnNzgncSo4T9jcqYJzRy/qqqqbw0kTnHCcqqobslmogrOgbaqqbg4nTXCicWZV - 1e3YNHEw51GbO7xwmVVVN4eTJDgX4FRV3ZBNRxWcDm1TVXVzOGmCE45TVXVDNpnakXZ4x0dWVd0c - TprghONUVd2QzUAVnAFtU8UhczhpghOOU8UhQzaZ+jh3eKtcVnHIHE6a4ETjLCoOGbLJ1Me5w8eB - i4pD5nDSBCccp4pDhmwy9XHu8Fa5ouKQOZw0wQnHqeKQIZuZKjgz2qaKQ+Zw0gQnHKeKQ4ZsMjXA - 7/Ae46LikDmcNMEJx6nikCGblSo4K9qmikPmcNIEJxpnVXHIjs3byxBNcGb4cEZVccgcTpLgXIBT - xSFDNpkmhzJ8OKOqOGQOJ01wwnGqOGTIJtPkUIYPZ/SPlVNwXoWTJjjhOFUcMmSTaXIow4czqopD - 5nDSBCccp4pDhmwyTQ5l+HBGVXHIHE6a4ATj9JuKQ4ZsMk0OZfRwht9UHDKHkyY44ThVHDJkk2ly - KKOHM/ym4pA5nDTBCcep4pAhm0yTQxk9nJHSKDhLKnFTcK7GSROccJyj4JzBqeA8YZNpciijhzP8 - pqq6OZw0wQnHqaq6IZtMk0MZPJwRav/q6rC5FO4XfSs41+KkCU44zsEVrFM4FZzzNm9rOk1wFvRw - ht/UjmQOJ0lwLsCpdiRDNpkmhwp4OCNuoduO1Bb14pPuVV+PkyY40Thjtx1pEqeC84RNpsmhgh7O - 8Jv6OM3hpAlONE6nPk5DNpkmhwp4OGMv/fM4w1by7ot2nMtx0gQnHGe3ODSJU8F5wibT5FCBD2c4 - NcCbw0kTnHCcaoA3ZJNpcqiAhzOy63d8hK22n1LfONfjpAlOOM5u5XISp4LzhE2myaECH85wmhwy - h5MmOOE4NTlkyCbT5FABD2eU0j9kOzi/Va8+zvU4aYITjrP7HWkSp4LzhE2myaECH85wmhwyh5Mm - OOE4NTlkyCbT5FABD2dU3z8rti3q/v7LUnAuxkkTnHCc/W+cczgVnPM2779tluCs8OEMp8khczhJ - gnMBTk0OGbLJNDlUwcMZNeb+5JDz1Tl941yPkyY40TjL6AP8DE4F5wmbTJNDFT2ckfbRcEYpVe1I - 63HSBCcc56jJeAangvOETabJoYodzqg//QSdRT2kzekb53qcNMEJxzmoqk/hVHCesMk0OVTBwxmh - 7t3PSLeTu/asHed6nDTBCcc5OoFmBqeC84RNpsmhCr/WxemQD3M4aYITjdPrkA9DNpkmhyp4OCNu - sf82pGPlLsJJE5xwnN1ZdR0rt94mUwN8BfcYR58H48A+eM2qX4CTJjjhOAffkaZwKjjnbb58UWZJ - zvbwmLCvQzoeyZ5OkuhcoFPnI1nCydSR1B48VGceFYjc5mJRL+cFPHmyE85z0Go8xVPheUYnU3G9 - PUSozuoHBxrfptt2vbJfwJMnPOE8x5PBX89T4XlGJ1OBvT0k9HuRjpizx5MnPOE8dcicJZ1MRfb2 - sGOX9hhGO889VV2lcQFPnvCE8xztPGd4KjzP6GQqtLeHAtZZxvPr9/sdFJ6LefKEJ5rn8AjZGZ4K - zxM67+VMnvB06NciHdNpTydNdsJ16pxOSzi5au0OWsys7Tc9OAwxpBRUa7+AJ092wnkOWkGmeCo8 - z+jkqrU7aDGzpjy4u6BtSmvSF88LePKEJ5xnf7JojqfC84xOrlq7gxczddSxPZ484QnnqcOOLenk - qrU7ZDEzbrH0PyqFcNOrctEFPHnCE86z/1VpjqfC84xOrlq7QxYz45b2vdtG13TW9tpUFZ7LefKE - J5xnv49ujqfC84TO+xdmnvDEDr+FWkaHd+67jou/QidNdsJ1jk7vnNGp6DyDk6vW7pE67wv76KVd - +85rePJkJ5zn6KVd+87VOrnKRR77PT76gc7N+5LUqHQBT57whPMcHUY3w1PheUJn4HppD1CcpQxv - vHZRTZ5X6KTJTrjOQY/nlE5F5xmcXPvOgF3YS6jdI0Haz5hvF8YoO5fz5MlOOM9+LXOOp8LzhM7I - te8EH/ZVh9Mb0emKzCt00mQnXOfgyIUpnYrOMzi59p0RurDXzYX+8EbbBNWqDs8LePJkJ5zn4LVo - iqfC84TOxLXvhL8UDU6i0zv7RTppshOuc3AQnV7ZV+PcuaITekpi3WoabDuT80Uj7RfopIlOuM7B - rnNKp6LzBM7CFZ3QMxIbzcErkXc1laJd53qdNNEJ19kvFM3pVHTO47y3NNBEp8feR3jD2X9hV3Re - pZMkOhfo7L+wKzpX4fTlP/z6bxtOC62dccv+ZZbsr9gMzSbwEuxat8H7us9lK5rHXI/z2uC0hHOw - 55zCqeCct2mhN+mwzYi3OdhyKjivwUkSnAtw9gc2FJyrbVoorh+2mfA2+21zCs6LcJIEJx7nPjiR - W8G52KaF0vphmzve5rd+G1JwnsZJEpwLcH7r1yEF57zNzBScGW/zW78NKThP4yQJzgU4v/XrkILz - hE0DQ0RfYTOBbeZNwWkNJ01wwnF+68qlgnPepoVWzsM2C3xRz3pVN4eTJDgX4NSruiGbTDvOgl/U - 9apuDidNcMJx6lXdkE0DN7p9hc0dbLPoVd0cTprghOPUq7ohmwZuc/sKmwVtU6/q5nDSBCccp17V - 7disTN84K/wzUtGrujmcJMG5AKde1Q3ZNHCP21fY9GCb9Vv3GCs4T+OkCU44Tr2qG7IZqYIzom3q - Vd0cTprghOPUq7ohm0zFoQr//j64v03BeRFOmuCE4+xf8qLgXG6TqThU0d/f3aaqujmcNMEJx6lZ - dTs2X257YklOBz7zsOEcHGas5LxIJ0l0LtCpl3VLOJnqQ+3Bo3Xqdd0eT57shPNUbd2STqYaUXuA - 3sradDpV1+3x5AlPOE/V1y3pZKoTtQfs/VhNpyrs9njyhCec5+AiDYXncp1MtaL2gL0hq+n81oVM - hed5njzhiebpv3UtU+F5QqfjKhc5NE69tdvTSZOdcJ26h8gSTq5ykUN/j/ff+q1I2XmeJ092wnmq - v9OSTq5ykUN+j2+/ij0MdJYa7y2HCs/FPHnCE85zUGuf4qnwPKOT6bDOpjOhl3Zd5WaPJ094onkG - ffG0pJOr1u6Qxcy4xTw4aSGEsnmF5wU8ecITzrM/wDHHU+F5RidXrd0hi5lNZ9n63zybzpeuQ4Xn - Yp484Qnn2Z/gmOOp8Dyh8/ZBnig80R/kg2rt9nTSZCdcp2rtlnBy1do9Umfc0r73y0Uh1Fz3quxc - zpMnO+E8+x/k53gqPM/o5Kq1e2wxM+2D64QVnlfx5AlPOM/+e5HCc71Orlq7hxcz1eVpjydPeMJ5 - qsvTkM7A9cUzoHGqT8meTprsROuMOsfTEk6ufWdALuz3Uuboi6cq7dfw5MlOOM/RF09V2hfrjFz7 - TvRhX1GHINvTSZOdcJ06BNkQzsQVnejvSVGfO+3ppIlOuM5vfUK3ovMEzp0rOtGnJEZ97bSnkyY6 - 0TqTvnYawpm5ojOjcer8Y3s6aaITrlMv7IZwFq7oxI68NYB9nMHFlGNSdC7XSROdcJ2DeeEpnYrO - eZz3iTea6PTQ2wjvOAfTworOi3SSROcCnYMGEEXnMpz/6W/+y59wXp2c7b/2Ug7aBF6BvdWt35zk - 3O58cUrO5TgvDk5DOLvf4SdxKjjnbZroiD9qM8Btuu4opoLzKpwkwbkAZ7dEpOBcbtNES+dRmxFv - s3tqp4LzKpwkwbkAZ7ffWMG53qaFQaLjNhPSZoy+3zLnN7fnXaX19ThpghOOs/sdaRKngnPepolO - +KM2E35R7/Ykacd5FU6S4FyAs9v1oR3neptMO86EXdTd7YSerk2/hRo37TiX46QJTjjO/jfOOZwK - znmbJgaIjtrc8Yt6t1lOO86rcJIE5wKc3e9I2nGut8m049yxi/rNptqRzOGkCU44TrUj2bFpYu7y - qM0MX9S92pHM4SQJzgU41Y5kyCbTjjPDF3WvdiRzOGmCE45T7Uh2bJoYVz9qs+AXdVXVzeEkCc4F - OFVVN2STacdZ8Iu6ikPmcNIEJxynikN2bFamHWfFL+oqDpnDSRKcC3CqOGTIJtOOs2IX9fa/0/5U - W3AhuarJofU4aYITjrN/YuwcTgXnvE23MW05HfTortuqHlRWt6eTJDoX6FRd3RJOpk1ne8Be8bLV - vX+esUvtR427snM5T57shPPsH2g8x1PheUKn49p3OvTKrq4kezppshOuU21JlnBy7TsdfGHvHwav - fedVPHmyE86zfxq89p3LdVIdB+8ePHplV1OnPZ002QnXqa5OSzi59p0eu7An338rCt6VGJWdF/Dk - yU44z8HNrFM8FZ4ndFLdpuEeAnplV0+8PZ002QnXqaZ4Szi59p0BubDHLe25WysKIdSyuajsXM6T - JzvhPPuvRXM8FZ4ndFJdRuQeInpl10iRPZ002QnXqZkiQzipbtVwD9DvSQ1nVGu8PZ000QnXqdZ4 - Qzipjod3Dzsap7o77emkiU64TnV3GsJJdc6xe8jQr0mx9E+dCyEU78qm6FyukyY64Tr7I29zOhWd - J3BSHdjpHgp6XVdvpz2dNNEJ16neTjs4733HNNHp4QctRLUn2dNJEp0LdKo9yRBOqokiD5/ZiKqw - 29NJE51wnaqwG8JJVWH32Bqm28YXtdZagqJzvU6a6ITrHC3sMzoVnfM4A9ULe8C+Et1wjtZ1Rec1 - OkmiE69zeFmronMRzvx3//T3f/qadHFyJre5l8MeDnxMAh7HHW+n9XRt1piT11nx63FeG5yWcPa7 - OudwKjjnbVoYYT9sM+Bt9ps6FZwX4SQJzgU4+z2dCs7VNi3MYB62GfE2+31JCs6LcJIE5wKc/bYk - BedymwZO/vgKm9Cbs2Ko/SsH/eZ2v6sXfj1OmuBE4+zfnDWJU8E5b9NCYf2wzYRf1PvtnNpxXoST - JDgX4Ox3fWjHudwm044zgRf1ZrPf86HgvAgnTXDCcfZ7PhScq21aOPHjsM0dvqjvqqqbw0kSnAtw - qqpuyCbTjnOHL+q7qurmcNIEJxynqup2bFo4KOmwzYxf1FVVN4eTJDgX4FRV3ZBNph1nxi/qKg6Z - w0kTnHCcKg7ZsWnhfLnDNgt+UVdxyBxOkuBcgFPFIUM2mXacBbuoJ1+27vd37324N2QrOBfjpAlO - OM7uB/hJnArOeZuVacdZ4Yt6VlXdHE6S4FyAU1V1QzaZdpwVu6iXGnL/ngLn08utDgrOtThpghOO - s/sBfhKngnPeptuYtpwOe3TXbVVXP5I9nSTRuUCnGpIs4WTadLYH6ImxdfO+m53BhRyLV3au58mT - nXCe3fCc5KnwPKHTce07HXplVzunPZ002QnXqX5OSzi59p0Ou7An77pf4oN3ea/62nkBT57shPPs - vxbN8VR4ntBJdRy8w16QdVvZ1Q1vTydNdsJ1qh3eEE6qA+HdQ0DjVD+8PZ000QnXqYZ4QzipjoR3 - DxGMs6ix055OmuiE61RnpyGcVGcbO+y1rCkMrsjy3qd9U5H9Ap000QnXOZommtGp6DyBk+qQTvew - o9d19Xba00kTnXCd6u00hJPqtDn3kMG9c1u3PenWO7cF7Tov0EkTnXCdg3G3KZ2KzhM4qY5Ncg8F - va6rs9OeTprohOtUZ6chnFTnf7iHiu2cS6FbYQ/e+6LovEInTXTCdXYr7JM6FZ3zOF9IsESnR08K - 78POuZpfDvZSdK7VSRKdC3SOWudmdCo6T+Ckaon32KbjUoMffOv0IXtF5wU6aaITrnN0tNeMTkXn - CZxUfZ0e2znXXg/7TcfBhbS7TdG5XidNdKJ19ruOJ3UqOk/gpOrr9ODOuS0MOufcHmu6n+aj6Fyr - kyY64Tq7zUmTOhWdJ3BS9XV6bOdcjTH11/Wmc/NqTrpAJ010wnWOTqeZ0anonMcZNqboDNgP8bd1 - vfutU7vOy3SSROcCnd1vndp1rsO5/+Ov/+FPNcyrk7OUHPZDyzr0+pfsc/9jks8h5P3+OUnJuRTn - xcFpCGd/SHgOp4Jz3qaJY5OO2gx4m/0PnQrOi3CSBOcCnP3vnArO1TZNFNeP2ox4m/13dQXnRThJ - gnMBzv6ruoJztU0TpfWjNhPeZveMYwXnVThJgnMBzu6ghoJzvU0LN2sctwm9qfVmszumoeC8CidN - cMJxdnuNFZzLbZroSDpqc4cv6lXFIXM4SYJzAU4VhwzZZNpx7vBFvao4ZA4nTXDCcao4ZMemiQM6 - j9rM+EVdxSFzOEmCcwFOFYcM2WTacWb8oq7ikDmcNMEJx6nikB2bJs41Pmqz4Bd1FYfM4SQJzgU4 - VRwyZJNpx1mwi3px297/jFRj3l9OKFdwLsVJE5xwnP3vSHM4FZzzNk0cB3/UZkUv6mVTVd0cTpLg - XIBTVXVDNpl2nBW8qJca+kfQuNv5M9pxrsdJE5xwnN2Wj0mcCs55my9noLIkp8OeQHNb1dWPZE8n - SXQu0KmGJEs4mTad7QF6ZmzdBtetBxf2tOsejQt48mQnnGc3PCd5KjxP6HRc+06HXtnVzmlPJ012 - wnWqn9MSTq59p8Mu7DG6btNc8M6HTcfBX8CTJzvhPAeXWk/xVHie0El1qrHD3i54W9nVDW9PJ012 - wnWqHd4STq59p0cu7HFL2XWzM9wutM7ad17Akyc74Tz7r0VzPBWeJ3RSHQrvHgJ6ZdcwkT2dNNkJ - 16lpIkM4qY6Fd9hbrRtOp654ezppohOuU23xhnBSHQzvsLdal1Bzv0NpcykndShdoJMmOuE6+w1K - czoVnSdwUp1w7LC3Wt/WdTXG29NJE51wnWqMN4ST6qhO95Ch63oNuf8h3vnkiupEF+ikiU64zn57 - 0pxORecJnFRnzrmHgl7X1RZvTydNdMJ1qi3eEE6qw5PcQ8WOu9X+Ba3BJZ0Bco1OmuiE6+z3zs3p - VHTO47xv8Wmi08OPWeg3dmrXeZlOkuhcoFNN8YZwUs2xe+ikcNzi4ISaEEJp6752net10kQnXGf3 - c9KkTkXnCZxUo5geO+xW4tbvnAub23PZFZ3rddJEJ1znqHVuRqei8wROqpZ4j206LqX07xsMzrtQ - FZ0X6KSJTrjOwbfOKZ2KzhM4qfo6PbZzrtSGb3AqYnRO0XmBTprohOscXGQwpVPReQInVXOSx7Z/ - 1K3GwYGyaat5U3Su10kTnXCd/W+dczoVnfM47+cr0ERnwNYwa4qDlnjv3Z4UnRfoJInOBToHJ3ZO - 6fz/JDr/+btXX57+44eXeloKWwypti36rYn7y9N/br+Dv/wDv6fbH/zq3e9+d/uP136m5/ab/vD4 - /BPjP/5vfg/xlq3/CAA= - headers: - Access-Control-Allow-Headers: - - Authorization,User-Agent,Range,X-Requested-With,Content-Type,Partner - Access-Control-Allow-Methods: - - GET, POST, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - no-store - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '12383' - Content-Type: - - application/json - Date: - - Sat, 07 Dec 2024 00:13:55 GMT - Server: - - nginx - Strict-Transport-Security: - - max-age=15768000 - Vary: - - Origin,Authorization,Partner - - accept-encoding - X-Frame-Options: - - SAMEORIGIN - status: - code: 200 - message: OK -version: 1 diff --git a/openbb_platform/providers/deribit/tests/test_cache.py b/openbb_platform/providers/deribit/tests/test_cache.py new file mode 100644 index 00000000000..ae8b9f59882 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_cache.py @@ -0,0 +1,191 @@ +"""Tests for the Deribit response cache.""" + +import pytest + +from openbb_deribit.utils import cache + + +class _Store: + """A stand-in for the backend's response store.""" + + def __init__(self, keys): + self._keys = list(keys) + self.deleted: list = [] + + async def keys(self): + """Yield every stored key.""" + for key in self._keys: + yield key + + async def size(self): + """Return how many responses are stored.""" + return len(self._keys) + + async def bulk_delete(self, keys): + """Forget the named responses.""" + self.deleted.append(set(keys)) + self._keys = [key for key in self._keys if key not in keys] + + +class _Backend: + """A stand-in for the SQLite backend.""" + + def __init__(self, keys=()): + self.responses = _Store(keys) + self.expired = 0 + + async def delete_expired_responses(self): + """Mark the expired responses for deletion.""" + self.expired += 1 + + +@pytest.fixture(autouse=True) +def _reset_sweep(): + """Let each test sweep once, as the first caller in a process would.""" + cache._swept = False + yield + cache._swept = False + + +class TestConfiguration: + """The cache is bounded by a TTL per endpoint and a size cap.""" + + def test_path_lives_under_the_user_cache(self): + """The cache file is kept with the other OpenBB HTTP caches.""" + assert cache.cache_path().endswith("/http/deribit") + + def test_backend_carries_the_ttl_map(self, tmp_path, monkeypatch): + """The backend is built with the per-endpoint expiries.""" + monkeypatch.setattr(cache, "cache_path", lambda: str(tmp_path / "deribit")) + backend = cache.get_cache_backend() + + assert backend.expire_after == cache.CACHE_TTL_DEFAULT + assert backend.urls_expire_after + + def test_live_endpoints_are_never_cached(self): + """A quote or a book is read fresh every time.""" + for pattern in ( + "*/public/get_order_book*", + "*/public/ticker*", + "*/public/get_time*", + "*/public/status*", + ): + assert cache.URL_CACHE_TTL[pattern] == 0 + + def test_reference_endpoints_are_held_longest(self): + """A listing that barely changes is held for a day.""" + assert cache.URL_CACHE_TTL["*/public/get_currencies*"] == cache.DAY + + +class TestSweep: + """The sweep drops what expired, then trims what is still too big.""" + + @pytest.mark.asyncio + async def test_deletes_expired_and_vacuums(self, monkeypatch, tmp_path): + """A file under the cap is swept once and left alone.""" + stored = tmp_path / "deribit.sqlite" + stored.write_bytes(b"x") + monkeypatch.setattr(cache, "cache_path", lambda: str(tmp_path / "deribit")) + vacuums: list = [] + + async def _vacuum(backend): + vacuums.append(backend) + + monkeypatch.setattr(cache, "_vacuum", _vacuum) + backend = _Backend(["a", "b"]) + await cache.sweep_cache(backend) + + assert backend.expired == 1 + assert len(vacuums) == 1 + assert backend.responses.deleted == [] + + @pytest.mark.asyncio + async def test_trims_by_the_share_that_overshoots(self, monkeypatch, tmp_path): + """A file over the cap loses the oldest share of its rows.""" + stored = tmp_path / "deribit.sqlite" + stored.write_bytes(b"x" * 400) + monkeypatch.setattr(cache, "cache_path", lambda: str(tmp_path / "deribit")) + monkeypatch.setattr(cache, "CACHE_MAX_BYTES", 100) + backend = _Backend([f"key-{index}" for index in range(8)]) + + async def _vacuum(_): + if backend.responses.deleted: + stored.write_bytes(b"x" * 50) + + monkeypatch.setattr(cache, "_vacuum", _vacuum) + await cache.sweep_cache(backend) + + assert backend.responses.deleted + assert len(backend.responses.deleted[0]) == 6 + + @pytest.mark.asyncio + async def test_stops_when_nothing_is_left_to_drop(self, monkeypatch, tmp_path): + """A file still too big with nothing stored gives up.""" + stored = tmp_path / "deribit.sqlite" + stored.write_bytes(b"x" * 400) + monkeypatch.setattr(cache, "cache_path", lambda: str(tmp_path / "deribit")) + monkeypatch.setattr(cache, "CACHE_MAX_BYTES", 100) + + async def _vacuum(backend): + return None + + monkeypatch.setattr(cache, "_vacuum", _vacuum) + backend = _Backend([]) + await cache.sweep_cache(backend) + + assert backend.responses.deleted == [] + + @pytest.mark.asyncio + async def test_missing_file(self, monkeypatch, tmp_path): + """A cache that was never written is swept without error.""" + monkeypatch.setattr(cache, "cache_path", lambda: str(tmp_path / "deribit")) + + async def _vacuum(backend): + return None + + monkeypatch.setattr(cache, "_vacuum", _vacuum) + backend = _Backend([]) + await cache.sweep_cache(backend) + + assert backend.expired == 1 + + @pytest.mark.asyncio + async def test_sweeps_once_per_process(self, monkeypatch, tmp_path): + """A second caller does not sweep again.""" + monkeypatch.setattr(cache, "cache_path", lambda: str(tmp_path / "deribit")) + + async def _vacuum(backend): + return None + + monkeypatch.setattr(cache, "_vacuum", _vacuum) + backend = _Backend([]) + await cache.sweep_cache(backend) + await cache.sweep_cache(backend) + + assert backend.expired == 1 + + @pytest.mark.asyncio + async def test_oldest_stops_at_the_count(self): + """The trim reads only as many keys as it means to drop.""" + backend = _Backend([f"key-{index}" for index in range(10)]) + + assert len(await cache._oldest(backend, 3)) == 3 + + +class TestVacuum: + """Vacuuming gives back the pages the deleted responses held.""" + + @pytest.mark.asyncio + async def test_skips_a_foreign_backend(self): + """A store that is not SQLite is left alone.""" + assert await cache._vacuum(_Backend([])) is None + + @pytest.mark.asyncio + async def test_runs_against_sqlite(self, tmp_path, monkeypatch): + """A real SQLite store is vacuumed.""" + monkeypatch.setattr(cache, "cache_path", lambda: str(tmp_path / "deribit")) + backend = cache.get_cache_backend() + await backend.responses.write("key", b"value") + await cache._vacuum(backend) + + assert (tmp_path / "deribit.sqlite").exists() diff --git a/openbb_platform/providers/deribit/tests/test_choices.py b/openbb_platform/providers/deribit/tests/test_choices.py new file mode 100644 index 00000000000..23c7edaae17 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_choices.py @@ -0,0 +1,106 @@ +"""Tests for the Deribit choice feeds.""" + +import pytest + +from openbb_deribit.utils import choices + + +class TestPairing: + """Choices are paired as a label and a value.""" + + def test_defaults_the_label_to_the_value(self): + """A value with no label is its own label.""" + assert choices._choices(["BTC"]) == [{"label": "BTC", "value": "BTC"}] + + def test_uses_a_given_label(self): + """A named value carries its name.""" + assert choices._choices(["BTC"], {"BTC": "Bitcoin"}) == [ + {"label": "Bitcoin", "value": "BTC"} + ] + + +class TestFeeds: + """Each feed reads what the exchange lists right now.""" + + @pytest.mark.asyncio + async def test_currencies(self, responder, load): + """Currencies are labelled by their long name.""" + responder({"get_currencies": load("currencies")}) + feed = await choices.currency_choices() + + assert {"label": "Bitcoin", "value": "BTC"} in feed + assert [row["value"] for row in feed] == sorted(row["value"] for row in feed) + + @pytest.mark.asyncio + async def test_index_names(self, responder, load): + """Index names come back sorted.""" + responder({"get_index_price_names": load("index_price_names")}) + feed = await choices.index_choices() + + assert {"label": "btc_usd", "value": "btc_usd"} in feed + + @pytest.mark.asyncio + async def test_extended_index_names(self, responder): + """The extended listing names each index under its ``name`` member.""" + responder({"get_index_price_names": [{"name": "btc_usd"}]}) + feed = await choices.index_choices(extended=True) + + assert feed == [{"label": "btc_usd", "value": "btc_usd"}] + + @pytest.mark.asyncio + async def test_instruments(self, responder, load): + """Instruments are listed by their name.""" + responder({"get_instruments": load("instruments_future")}) + feed = await choices.instrument_choices("future") + + assert {"label": "BTC-PERPETUAL", "value": "BTC-PERPETUAL"} in feed + + @pytest.mark.asyncio + async def test_perpetuals(self, responder, load): + """Perpetuals are listed by their full instrument name.""" + responder({"get_instruments": load("instruments_future")}) + feed = await choices.perpetual_choices() + + assert feed == [{"label": "BTC-PERPETUAL", "value": "BTC-PERPETUAL"}] + + @pytest.mark.asyncio + async def test_options_roots(self, responder, load): + """Only underlyings with listed options are offered.""" + responder({"get_instruments": load("instruments_option")}) + + assert await choices.options_root_choices() == [ + {"label": "BTC", "value": "BTC"}, + {"label": "XRP_USDC", "value": "XRP_USDC"}, + ] + + @pytest.mark.asyncio + async def test_futures_roots(self, responder, load): + """Only underlyings with a curve are offered.""" + responder({"get_instruments": load("instruments_future")}) + + assert await choices.futures_root_choices() == [ + {"label": "BTC", "value": "BTC"} + ] + + @pytest.mark.asyncio + async def test_combos(self, responder, load): + """Combo identifiers come back sorted.""" + responder({"get_combo_ids": load("combo_ids")}) + feed = await choices.combo_choices("BTC") + + assert [row["value"] for row in feed] == sorted(row["value"] for row in feed) + + @pytest.mark.asyncio + async def test_combos_normalize_the_wildcard(self, responder): + """The wildcard currency reaches the exchange in lower case.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return [] + + responder({"get_combo_ids": capture}) + await choices.combo_choices("all", "active") + + assert seen == {"currency": "any", "state": "active"} diff --git a/openbb_platform/providers/deribit/tests/test_client.py b/openbb_platform/providers/deribit/tests/test_client.py new file mode 100644 index 00000000000..05193b93d59 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_client.py @@ -0,0 +1,225 @@ +"""Tests for the Deribit JSON-RPC transport.""" + +import pytest +from openbb_core.app.model.abstract.error import OpenBBError + +from openbb_deribit.utils import client + + +class _Response: + """A stand-in for one aiohttp response.""" + + def __init__(self, payload, status=200): + self.payload = payload + self.status = status + + async def json(self, content_type=None): + """Return the decoded payload, or raise what a bad body would.""" + if isinstance(self.payload, Exception): + raise self.payload + + return self.payload + + +class _Session: + """A stand-in for one aiohttp session.""" + + def __init__(self, response): + self.response = response + self.urls: list = [] + + async def get(self, url, timeout=None): + """Record the URL and return the canned response.""" + self.urls.append(url) + + if isinstance(self.response, Exception): + raise self.response + + return self.response + + +@pytest.fixture +def transport(monkeypatch): + """Install a canned session and return it.""" + + def install(response, cached=True): + session = _Session(response) + + async def _get_session(): + return session + + monkeypatch.setattr("openbb_deribit.utils.session.get_session", _get_session) + monkeypatch.setattr( + "openbb_deribit.utils.session.get_cached_session", _get_session + ) + + return session + + return install + + +class TestBuildUrl: + """``build_url`` writes the query the exchange expects.""" + + def test_no_params(self): + """A method with no parameters carries no query string.""" + assert client.build_url("get_currencies") == ( + "https://www.deribit.com/api/v2/public/get_currencies" + ) + + def test_drops_none(self): + """A parameter left unset is not sent.""" + url = client.build_url("get_instruments", {"currency": "BTC", "kind": None}) + + assert url.endswith("get_instruments?currency=BTC") + + def test_lowercases_booleans(self): + """A boolean is sent as the lower-case literal the exchange reads.""" + url = client.build_url("get_instruments", {"expired": True}) + + assert url.endswith("get_instruments?expired=true") + + def test_encodes_values(self): + """A value needing escaping is percent-encoded.""" + url = client.build_url("ticker", {"instrument_name": "BTC-25DEC26-90000-C"}) + + assert "instrument_name=BTC-25DEC26-90000-C" in url + + def test_empty_params_mapping(self): + """An empty mapping is the same as none at all.""" + assert client.build_url("status", {}) == client.build_url("status") + + +class TestRequest: + """``request`` unwraps the JSON-RPC envelope and reports failures.""" + + @pytest.mark.asyncio + async def test_returns_result(self, transport): + """A successful call returns the result member.""" + session = transport(_Response({"jsonrpc": "2.0", "result": [1, 2]})) + + assert await client.request("get_currencies") == [1, 2] + assert session.urls == ["https://www.deribit.com/api/v2/public/get_currencies"] + + @pytest.mark.asyncio + async def test_uncached_path(self, transport): + """Passing ``use_cache=False`` still returns the result.""" + transport(_Response({"result": {"locked": "false"}})) + + assert await client.request("status", use_cache=False) == {"locked": "false"} + + @pytest.mark.asyncio + async def test_missing_result(self, transport): + """A payload with no result member returns None rather than raising.""" + transport(_Response({"jsonrpc": "2.0"})) + + assert await client.request("get_time") is None + + @pytest.mark.asyncio + async def test_json_rpc_error(self, transport): + """A JSON-RPC error is raised with its message and reason.""" + transport( + _Response( + { + "error": { + "message": "Invalid params", + "data": {"reason": "invalid currency"}, + } + } + ) + ) + + with pytest.raises(OpenBBError, match="invalid currency"): + await client.request("get_instruments", {"currency": "XYZ"}) + + @pytest.mark.asyncio + async def test_json_rpc_error_without_data(self, transport): + """An error carrying no data still names its message.""" + transport(_Response({"error": {"message": "not_supported"}})) + + with pytest.raises(OpenBBError, match="not_supported"): + await client.request("get_last_trades_by_currency") + + @pytest.mark.asyncio + async def test_json_rpc_error_as_string(self, transport): + """An error the exchange sends as a bare string is reported as-is.""" + transport(_Response({"error": "rate_limited"})) + + with pytest.raises(OpenBBError, match="rate_limited"): + await client.request("ticker") + + @pytest.mark.asyncio + async def test_non_dict_payload(self, transport): + """A body that is not an object is rejected.""" + transport(_Response([1, 2, 3])) + + with pytest.raises(OpenBBError, match="Unexpected Deribit response"): + await client.request("get_time") + + @pytest.mark.asyncio + async def test_rate_limited(self, transport): + """A 429 names the status and says what to do about it.""" + transport(_Response(None, status=429)) + + with pytest.raises(OpenBBError, match="HTTP 429"): + await client.request("get_order_book") + + @pytest.mark.asyncio + async def test_other_bad_status(self, transport): + """Any other non-200 names its status.""" + transport(_Response(None, status=503)) + + with pytest.raises(OpenBBError, match="HTTP 503"): + await client.request("status") + + @pytest.mark.asyncio + async def test_transport_failure(self, transport): + """A transport failure is wrapped, naming the method.""" + transport(TimeoutError("timed out")) + + with pytest.raises(OpenBBError, match="public/ticker"): + await client.request("ticker") + + @pytest.mark.asyncio + async def test_undecodable_body(self, transport): + """A body that cannot be decoded is wrapped.""" + transport(_Response(ValueError("not json"))) + + with pytest.raises(OpenBBError, match="ValueError"): + await client.request("get_time") + + +class TestGather: + """``gather`` runs several calls and keeps the failures in place.""" + + @pytest.mark.asyncio + async def test_returns_in_order(self, monkeypatch): + """Results come back in the order the calls were given.""" + + async def _request(method, params=None, use_cache=True): + return params["n"] + + monkeypatch.setattr(client, "request", _request) + results = await client.gather( + [("ticker", {"n": 1}), ("ticker", {"n": 2}), ("ticker", {"n": 3})] + ) + + assert results == [1, 2, 3] + + @pytest.mark.asyncio + async def test_keeps_exceptions(self, monkeypatch): + """A call that fails leaves its exception in place of a result.""" + + async def _request(method, params=None, use_cache=True): + if params["n"] == 2: + raise OpenBBError("no") + + return params["n"] + + monkeypatch.setattr(client, "request", _request) + results = await client.gather( + [("ticker", {"n": 1}), ("ticker", {"n": 2})], use_cache=False + ) + + assert results[0] == 1 + assert isinstance(results[1], OpenBBError) diff --git a/openbb_platform/providers/deribit/tests/test_deribit_fetchers.py b/openbb_platform/providers/deribit/tests/test_deribit_fetchers.py deleted file mode 100644 index 00e4b3f34c5..00000000000 --- a/openbb_platform/providers/deribit/tests/test_deribit_fetchers.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Deribit Fetcher Tests.""" - -from datetime import date -from unittest.mock import MagicMock, patch - -import pytest -from openbb_core.app.service.user_service import UserService -from openbb_core.provider.utils.helpers import run_async -from openbb_deribit.models.futures_curve import DeribitFuturesCurveFetcher -from openbb_deribit.models.futures_historical import DeribitFuturesHistoricalFetcher -from openbb_deribit.models.futures_info import DeribitFuturesInfoFetcher -from openbb_deribit.models.futures_instruments import DeribitFuturesInstrumentsFetcher -from openbb_deribit.models.options_chains import ( - DeribitOptionsChainsData, - DeribitOptionsChainsFetcher, -) -from openbb_deribit.utils.helpers import get_instruments - -test_credentials = UserService().default_user_settings.credentials.model_dump( - mode="json" -) - -MOCK_OPTIONS_DATA = DeribitOptionsChainsData.model_validate( - { - "expiration": ["2024-12-07", "2024-12-07"], - "strike": [84000.0, 84000.0], - "option_type": ["call", "put"], - "underlying_symbol": ["SYN.BTC-7DEC24", "SYN.BTC-7DEC24"], - "underlying_price": [100671.2825, 100671.2825], - "contract_symbol": ["BTC-7DEC24-84000-C", "BTC-7DEC24-84000-P"], - "dte": [1, 1], - "contract_size": [1, 1], - "open_interest": [1.0, 124.8], - "volume": [0.1, 151.9], - "last_trade_price": [None, 20.13], - "bid": [10.06, 0.05], - "bid_size": [0.3, 0.0], - "ask": [0.06, 10.06], - "ask_size": [0.0, 0.2], - "mark": [16666.69, 0.0], - "high": [None, 442.83], - "low": [None, 10.06], - "change_percent": [0, -0.6], - "implied_volatility": [1.3618999999999999, 1.3618999999999999], - "delta": [0.99997, -3e-05], - "gamma": [0.0, 0.0], - "theta": [-0.24909, -0.0263], - "vega": [0.00366, 0.00366], - "rho": [0.91572, -3e-05], - "underlying_spot_price": [100644.24, 100644.24], - "settlement_price": [14590.44, 14.35], - "timestamp": [ - "2024-12-06 17:26:36.234000-0500", - "2024-12-06 17:26:36.234000-0500", - ], - "min_price": [13184.4, 10.06], - "max_price": [20078.53, 1509.66], - "interest_rate": [0.0, 0.0], - "bid_iv": [0.0, 0.0], - "ask_iv": [0.0, 2.1254], - "volume_notional": [0.0, 7083.24], - } -) - - -@pytest.fixture(autouse=True) -def _disable_alru_cache_loop_check(monkeypatch): - """Disable the alru_cache event loop safety check during tests. - - async-lru >= 2.2.0 tracks the event loop and raises RuntimeError - when the cache is reused across different event loops. During tests, - multiple event loops are created within a single test (e.g. validators - calling run_async inside transform_query, then fetcher.test calling - run_async for extract_data), so we patch out the loop check entirely. - """ - from async_lru import _LRUCacheWrapper - - monkeypatch.setattr(_LRUCacheWrapper, "_check_loop", lambda self, loop: None) - get_instruments.cache_clear() - - -@pytest.fixture(scope="module") -def vcr_config(): - """VCR configuration.""" - return { - "filter_headers": [("User-Agent", None)], - "filter_query_parameters": [ - ("expired", None), - ], - } - - -@pytest.mark.record_http -def test_get_options_symbols(): - """Test getting the list of options symbols.""" - # pylint: disable=import-outside-toplevel - from openbb_deribit.utils.helpers import get_options_symbols - - params = {"symbol": "BTC"} - - result = run_async(get_options_symbols, **params) - assert result is not None - assert isinstance(result, dict) - assert len(result) > 0 - for key, value in result.items(): - assert isinstance(value, list) - assert key.startswith("2") - - -@pytest.mark.asyncio -async def test_deribit_options_chains_fetcher(credentials=test_credentials): - """Test Deribit Options Chains Fetcher.""" - params = {"symbol": "BTC"} - fetcher = DeribitOptionsChainsFetcher() - - with patch( - "openbb_deribit.models.options_chains.DeribitOptionsChainsFetcher.fetch_data", - return_value=MagicMock(MOCK_OPTIONS_DATA), - ): - result = await fetcher.fetch_data(params, {}) - assert isinstance(result, DeribitOptionsChainsData) - - -@pytest.mark.record_http -def test_deribit_futures_curve_fetcher(credentials=test_credentials): - """Test Deribit Futures Curve Fetcher.""" - params = {"symbol": "BTC"} - - fetcher = DeribitFuturesCurveFetcher() - result = fetcher.test(params, credentials) - assert result is None - - -@pytest.mark.record_http -def test_deribit_futures_historical_fetcher(credentials=test_credentials): - """Test Deribit Futures Historical Fetcher.""" - params = { - "symbol": "BTC-PERPETUAL", - "start_date": date(2024, 12, 1), - "end_date": date(2024, 12, 3), - "interval": "12h", - } - - fetcher = DeribitFuturesHistoricalFetcher() - result = fetcher.test(params, credentials) - assert result is None - - -@pytest.mark.record_http -def test_deribit_futures_instruments_fetcher(credentials=test_credentials): - """Test Deribit Futures Instruments Fetcher.""" - params = {} - - fetcher = DeribitFuturesInstrumentsFetcher() - result = fetcher.test(params, credentials) - assert result is None - - -@pytest.mark.record_http -def test_deribit_futures_info_fetcher(credentials=test_credentials): - """Test Deribit Futures Info Fetcher.""" - params = {"symbol": "BTC-PERPETUAL"} - - fetcher = DeribitFuturesInfoFetcher() - result = fetcher.test(params, credentials) - assert result is None diff --git a/openbb_platform/providers/deribit/tests/test_helpers.py b/openbb_platform/providers/deribit/tests/test_helpers.py new file mode 100644 index 00000000000..c54ba89de1f --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_helpers.py @@ -0,0 +1,476 @@ +"""Tests for the Deribit shared accessors.""" + +from datetime import date, datetime, timezone + +import pytest +from openbb_core.app.model.abstract.error import OpenBBError +from openbb_core.provider.utils.errors import EmptyDataError + +from openbb_deribit.utils import helpers + + +class TestTimestamps: + """Dates cross the exchange boundary as milliseconds.""" + + def test_to_timestamp_from_string(self): + """An ISO date reads as midnight UTC.""" + assert helpers.to_timestamp("2026-01-01") == 1767225600000 + + def test_to_timestamp_from_date(self): + """A date object reads the same as its ISO spelling.""" + assert helpers.to_timestamp(date(2026, 1, 1)) == helpers.to_timestamp( + "2026-01-01" + ) + + def test_to_timestamp_keeps_zone(self): + """A datetime that carries a zone is not shifted twice.""" + aware = datetime(2026, 1, 1, tzinfo=timezone.utc) + + assert helpers.to_timestamp(aware) == 1767225600000 + + def test_from_timestamp(self): + """Milliseconds read back as a UTC datetime.""" + stamp = helpers.from_timestamp(1767225600000) + + assert stamp.year == 2026 + assert stamp.tzinfo is not None + + def test_to_timestamp_from_a_pandas_stamp(self): + """A pandas timestamp is used as it stands.""" + from pandas import Timestamp + + assert helpers.to_timestamp(Timestamp("2026-01-01")) == 1767225600000 + assert helpers.to_timestamp(Timestamp("2026-01-01", tz="UTC")) == ( + 1767225600000 + ) + + def test_from_timestamp_none(self): + """No timestamp reads as no datetime.""" + assert helpers.from_timestamp(None) is None + + def test_from_day_number(self): + """A count of whole days reads as a date.""" + assert helpers.from_day_number(20714) == date(2026, 9, 18) + + def test_from_day_number_none(self): + """No day number reads as no date.""" + assert helpers.from_day_number(None) is None + + +class TestNormalizeCurrency: + """The wildcard is spelled the way the exchange spells it.""" + + @pytest.mark.parametrize("given", ["any", "ANY", "all", "All"]) + def test_wildcards(self, given): + """Either wildcard resolves to the one the exchange accepts.""" + assert helpers.normalize_currency(given) == "any" + + def test_real_currency(self): + """A real currency is upper-cased.""" + assert helpers.normalize_currency("btc") == "BTC" + + +class TestFlattenTicker: + """A ticker's nested members are lifted to the top level.""" + + def test_lifts_stats_and_greeks(self, load): + """Statistics and greeks end up beside the quote.""" + flat = helpers.flatten_ticker(load("ticker_future")) + + assert "stats" not in flat + assert "greeks" not in flat + assert flat["instrument_name"] == "BTC-PERPETUAL" + assert "volume" in flat + + def test_tolerates_missing_members(self): + """A ticker with neither member is returned unchanged.""" + assert helpers.flatten_ticker({"a": 1}) == {"a": 1} + + def test_tolerates_null_members(self): + """A null member is dropped rather than unpacked.""" + assert helpers.flatten_ticker({"a": 1, "stats": None}) == {"a": 1} + + +class TestAccessors: + """The thin accessors pass their arguments through to the transport.""" + + @pytest.mark.asyncio + async def test_get_currencies(self, responder, load): + """Currencies come back as the exchange sends them.""" + responder({"get_currencies": load("currencies")}) + + assert len(await helpers.get_currencies()) == 7 + + @pytest.mark.asyncio + async def test_get_instruments_normalizes(self, responder, load): + """The wildcard currency reaches the exchange in lower case.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return load("instruments_future") + + responder({"get_instruments": capture}) + await helpers.get_instruments("all", "future", True) + + assert seen == {"currency": "any", "kind": "future", "expired": True} + + @pytest.mark.asyncio + async def test_get_instrument(self, responder, load): + """One instrument is read by name.""" + responder({"get_instrument": load("instrument")}) + instrument = await helpers.get_instrument("BTC-PERPETUAL") + + assert instrument["instrument_name"] == "BTC-PERPETUAL" + + @pytest.mark.asyncio + async def test_get_ticker(self, responder, load): + """A ticker is flattened on the way out.""" + responder({"ticker": load("ticker_future")}) + ticker = await helpers.get_ticker("BTC-PERPETUAL") + + assert "stats" not in ticker + + @pytest.mark.asyncio + async def test_get_tickers_skips_failures(self, monkeypatch, load): + """A symbol whose quote failed is left out rather than raising.""" + + async def _gather(calls, use_cache=True): + return [load("ticker_future"), OpenBBError("no"), {}] + + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + tickers = await helpers.get_tickers(["A", "B", "C"]) + + assert len(tickers) == 1 + + @pytest.mark.asyncio + async def test_get_index_names(self, responder, load): + """Index names come back as the exchange sends them.""" + responder({"get_index_price_names": load("index_price_names")}) + + assert "btc_usd" in await helpers.get_index_names() + + @pytest.mark.asyncio + async def test_get_expirations(self, responder, load): + """Expirations come back keyed the way the query asked for.""" + responder({"get_expirations": load("expirations_grouped")}) + expirations = await helpers.get_expirations("grouped", "any") + + assert "btc" in expirations + + +class TestRoots: + """Underlyings are derived from what the exchange lists right now.""" + + @pytest.mark.asyncio + async def test_options_roots(self, responder, load): + """Every root with a listed option is returned, sorted.""" + responder({"get_instruments": load("instruments_option")}) + + assert await helpers.get_options_roots() == ["BTC", "XRP_USDC"] + + @pytest.mark.asyncio + async def test_futures_roots_need_a_curve(self, responder, load): + """A root with one dated contract is not a curve.""" + rows = load("instruments_future") + responder({"get_instruments": rows}) + + assert await helpers.get_futures_roots() == ["BTC"] + + @pytest.mark.asyncio + async def test_futures_roots_skip_lone_contracts(self, responder): + """A root with a perpetual and one dated contract is left out.""" + responder( + { + "get_instruments": [ + { + "instrument_name": "SOL_USDC-PERPETUAL", + "settlement_period": "perpetual", + }, + { + "instrument_name": "SOL_USDC-25DEC26", + "settlement_period": "month", + }, + ] + } + ) + + assert await helpers.get_futures_roots() == [] + + @pytest.mark.asyncio + async def test_roots_ignore_unnamed_rows(self, responder): + """A row with no instrument name is skipped.""" + responder({"get_instruments": [{"instrument_name": ""}, {}]}) + + assert await helpers.get_options_roots() == [] + + +class TestOptionsSymbols: + """Option symbols are grouped by the expiration they carry.""" + + @pytest.mark.asyncio + async def test_grouped_by_expiration(self, responder, load): + """Every contract lands under its own expiration date.""" + responder({"get_instruments": load("instruments_option")}) + grouped = await helpers.get_options_symbols("XRP_USDC") + + contracts = [symbol for symbols in grouped.values() for symbol in symbols] + + assert len(grouped) == 2 + assert len(contracts) == 80 + assert all(symbol.startswith("XRP_USDC-") for symbol in contracts) + assert list(grouped) == sorted(grouped) + assert all(symbol.endswith(("-C", "-P")) for symbol in contracts) + + @pytest.mark.asyncio + async def test_unknown_underlying(self, responder, load): + """An underlying with no options names the ones that have them.""" + responder({"get_instruments": load("instruments_option")}) + + with pytest.raises(OpenBBError, match="XRP_USDC"): + await helpers.get_options_symbols("DOGE") + + +class TestFuturesCurveSymbols: + """A curve is the dated contracts of one root, nearest first.""" + + @pytest.mark.asyncio + async def test_sorted_by_expiration(self, responder, load): + """The perpetual is left off and the rest run nearest first.""" + responder({"get_instruments": load("instruments_future")}) + symbols = await helpers.get_futures_curve_symbols("BTC") + + assert "BTC-PERPETUAL" not in symbols + assert len(symbols) == 12 + + @pytest.mark.asyncio + async def test_unknown_root(self, responder, load): + """A root with no curve names the roots that have one.""" + responder({"get_instruments": load("instruments_future")}) + + with pytest.raises(OpenBBError, match="BTC"): + await helpers.get_futures_curve_symbols("DOGE") + + +class TestPerpetuals: + """Perpetuals are addressable by their shortened root.""" + + @pytest.mark.asyncio + async def test_keyed_by_short_root(self, responder): + """The underscore is dropped from the key, not the value.""" + responder( + { + "get_instruments": [ + { + "instrument_name": "SOL_USDC-PERPETUAL", + "settlement_period": "perpetual", + }, + { + "instrument_name": "BTC-25DEC26", + "settlement_period": "month", + }, + ] + } + ) + perpetuals = await helpers.get_perpetual_symbols() + + assert perpetuals == {"SOLUSDC": "SOL_USDC-PERPETUAL"} + + @pytest.mark.asyncio + async def test_futures_symbols(self, responder, load): + """Every listed future is returned, sorted.""" + responder({"get_instruments": load("instruments_future")}) + symbols = await helpers.get_futures_symbols() + + assert symbols == sorted(symbols) + assert "BTC-PERPETUAL" in symbols + + +class TestResolveSymbol: + """A symbol resolves to the name the exchange knows it by.""" + + @pytest.mark.asyncio + async def test_perpetual_root(self, responder, load): + """A shortened root resolves to the full perpetual name.""" + responder({"get_instruments": load("instruments_future")}) + + assert await helpers.resolve_symbol("btc") == "BTC-PERPETUAL" + + @pytest.mark.asyncio + async def test_full_name(self, responder, load): + """A full instrument name resolves to itself.""" + responder({"get_instruments": load("instruments_future")}) + + assert await helpers.resolve_symbol("BTC-25DEC26") == "BTC-25DEC26" + + @pytest.mark.asyncio + async def test_unknown(self, responder, load): + """An unlisted symbol is rejected.""" + responder({"get_instruments": load("instruments_future")}) + + with pytest.raises(OpenBBError, match="no instrument named"): + await helpers.resolve_symbol("NOPE") + + +class TestChartWindows: + """A long span is split into pages the exchange will serve.""" + + def test_single_window(self): + """A span shorter than one page is one window.""" + start = helpers.to_timestamp("2026-01-01") + end = helpers.to_timestamp("2026-01-05") + + assert helpers._chart_windows(start, end, "1D") == [(start, end)] + + def test_splits_long_span(self): + """A span longer than one page is split.""" + start = helpers.to_timestamp("2026-01-01") + end = helpers.to_timestamp("2026-01-11") + windows = helpers._chart_windows(start, end, "1440", window=3) + + assert len(windows) == 4 + assert windows[0][0] == start + + def test_degenerate_span(self): + """A span that yields no stamps falls back to itself.""" + start = helpers.to_timestamp("2026-01-01") + + assert helpers._chart_windows(start, start, "1D") == [(start, start)] + + +class TestOhlc: + """Candles come back as records, oldest first.""" + + @pytest.mark.asyncio + async def test_columnar_to_records(self, monkeypatch, load): + """The columnar response is turned into one record per candle.""" + chart = load("tradingview_chart") + + async def _request(method, params=None, use_cache=True): + return load("instruments_future") + + async def _gather(calls, use_cache=True): + return [chart for _ in calls] + + monkeypatch.setattr("openbb_deribit.utils.client.request", _request) + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + rows = await helpers.get_ohlc_data( + "BTC-PERPETUAL", "2026-09-13", "2026-09-20", "1d" + ) + + assert len(rows) == len(chart["ticks"]) + assert rows == sorted(rows, key=lambda row: row["date"]) + assert isinstance(rows[0]["date"], date) + assert set(rows[0]) == { + "date", + "symbol", + "open", + "high", + "low", + "close", + "volume", + "volume_notional", + } + + @pytest.mark.asyncio + async def test_intraday_keeps_time(self, monkeypatch, load): + """An intraday interval keeps the time of day on the candle.""" + chart = load("tradingview_chart") + + async def _request(method, params=None, use_cache=True): + return load("instruments_future") + + async def _gather(calls, use_cache=True): + return [chart] + + monkeypatch.setattr("openbb_deribit.utils.client.request", _request) + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + rows = await helpers.get_ohlc_data( + "BTC-PERPETUAL", "2026-09-20", "2026-09-20", "1h" + ) + + assert isinstance(rows[0]["date"], datetime) + + @pytest.mark.asyncio + async def test_unknown_symbol(self, responder, load): + """An unlisted symbol is rejected before any candle is asked for.""" + responder({"get_instruments": load("instruments_future")}) + + with pytest.raises(OpenBBError, match="no instrument named"): + await helpers.get_ohlc_data("NOPE", "2026-01-01", "2026-01-02", "1d") + + @pytest.mark.asyncio + async def test_span_too_wide(self, responder, load): + """A span needing more pages than the exchange serves is rejected.""" + responder({"get_instruments": load("instruments_future")}) + + with pytest.raises(OpenBBError, match="at most"): + await helpers.get_ohlc_data( + "BTC-PERPETUAL", "2019-01-01", "2026-01-01", "1m" + ) + + @pytest.mark.asyncio + async def test_no_candles(self, monkeypatch, load): + """A symbol that published nothing raises rather than returning empty.""" + + async def _request(method, params=None, use_cache=True): + return load("instruments_future") + + async def _gather(calls, use_cache=True): + return [{"status": "no_data"}] + + monkeypatch.setattr("openbb_deribit.utils.client.request", _request) + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + + with pytest.raises(EmptyDataError): + await helpers.get_ohlc_data( + "BTC-PERPETUAL", "2026-09-19", "2026-09-20", "1d" + ) + + +class TestCurveByHoursAgo: + """A past curve is read off traded closes.""" + + @pytest.mark.asyncio + async def test_picks_the_nearest_candle(self, monkeypatch, load): + """The close nearest the target hour is the one reported.""" + target = helpers.to_timestamp( + datetime.now(timezone.utc).replace(microsecond=0, second=0, minute=0) + ) + hour = 3600000 + + async def _request(method, params=None, use_cache=True): + return load("instruments_future") + + async def _gather(calls, use_cache=True): + return [ + { + "ticks": [target - 25 * hour, target - 24 * hour, target - hour], + "close": [1.0, 2.0, 3.0], + } + for _ in calls + ] + + monkeypatch.setattr("openbb_deribit.utils.client.request", _request) + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + curve = await helpers.get_futures_curve_by_hours_ago("BTC", 24) + + assert curve + assert all(row["last_price"] == 2.0 for row in curve) + assert all(row["hours_ago"] == 24 for row in curve) + + @pytest.mark.asyncio + async def test_skips_contracts_with_no_candles(self, monkeypatch, load): + """A contract the exchange published no candle for is left out.""" + + async def _request(method, params=None, use_cache=True): + return load("instruments_future") + + async def _gather(calls, use_cache=True): + return [{"status": "no_data"} for _ in calls] + + monkeypatch.setattr("openbb_deribit.utils.client.request", _request) + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + + assert await helpers.get_futures_curve_by_hours_ago("BTC", 24) == [] diff --git a/openbb_platform/providers/deribit/tests/test_models.py b/openbb_platform/providers/deribit/tests/test_models.py new file mode 100644 index 00000000000..4d315b7f864 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_models.py @@ -0,0 +1,1406 @@ +"""Tests for the Deribit models.""" + +from datetime import date, datetime + +import pytest +from openbb_core.app.model.abstract.error import OpenBBError +from openbb_core.provider.utils.errors import EmptyDataError + +from openbb_deribit.models.announcements import DeribitAnnouncementsFetcher +from openbb_deribit.models.apr_history import DeribitAprHistoryFetcher +from openbb_deribit.models.block_rfq_trades import DeribitBlockRfqTradesFetcher +from openbb_deribit.models.book_summary import DeribitBookSummaryFetcher +from openbb_deribit.models.combos import DeribitCombosFetcher +from openbb_deribit.models.currencies import DeribitCurrenciesFetcher +from openbb_deribit.models.delivery_prices import DeribitDeliveryPricesFetcher +from openbb_deribit.models.expirations import DeribitExpirationsFetcher +from openbb_deribit.models.funding_chart import DeribitFundingChartFetcher +from openbb_deribit.models.funding_rate_history import ( + DeribitFundingRateHistoryFetcher, +) +from openbb_deribit.models.futures_curve import DeribitFuturesCurveFetcher +from openbb_deribit.models.futures_historical import DeribitFuturesHistoricalFetcher +from openbb_deribit.models.futures_info import DeribitFuturesInfoFetcher +from openbb_deribit.models.futures_instruments import ( + DeribitFuturesInstrumentsFetcher, +) +from openbb_deribit.models.historical_volatility import ( + DeribitHistoricalVolatilityFetcher, +) +from openbb_deribit.models.index_historical import DeribitIndexHistoricalFetcher +from openbb_deribit.models.index_price import DeribitIndexPriceFetcher +from openbb_deribit.models.instruments import DeribitInstrumentsFetcher +from openbb_deribit.models.order_book import DeribitOrderBookFetcher +from openbb_deribit.models.settlements import DeribitSettlementsFetcher +from openbb_deribit.models.ticker import DeribitTickerFetcher +from openbb_deribit.models.trade_volumes import DeribitTradeVolumesFetcher +from openbb_deribit.models.trades import DeribitTradesFetcher +from openbb_deribit.models.volatility_index import DeribitVolatilityIndexFetcher + + +async def run(fetcher, params, responder, mapping): + """Run one fetcher against canned payloads.""" + responder(mapping) + + return await fetcher.fetch_data(params, {}) + + +class TestCurrencies: + """Currencies are returned sorted and typed.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Every currency is returned, in alphabetical order.""" + rows = await run( + DeribitCurrenciesFetcher, + {}, + responder, + {"get_currencies": load("currencies")}, + ) + + assert [row.currency for row in rows] == sorted(row.currency for row in rows) + assert rows[0].currency_long + assert rows[0].decimals is not None + + @pytest.mark.asyncio + async def test_empty(self, responder): + """An empty listing is an error, not an empty table.""" + with pytest.raises(EmptyDataError): + await run(DeribitCurrenciesFetcher, {}, responder, {"get_currencies": []}) + + +class TestInstruments: + """Instruments are returned with the perpetual sentinel dropped.""" + + @pytest.mark.asyncio + async def test_by_currency(self, responder, load): + """A currency and kind reads the whole listing.""" + rows = await run( + DeribitInstrumentsFetcher, + {"currency": "BTC", "kind": "future"}, + responder, + {"get_instruments": load("instruments_future")}, + ) + + assert len(rows) == 13 + assert all(row.kind == "future" for row in rows) + + @pytest.mark.asyncio + async def test_perpetual_has_no_expiration(self, responder, load): + """The year-3000 sentinel reads as no expiration at all.""" + rows = await run( + DeribitInstrumentsFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + {"get_instrument": load("instrument")}, + ) + + assert rows[0].symbol == "BTC-PERPETUAL" + assert rows[0].expiration_timestamp is None + assert isinstance(rows[0].creation_timestamp, datetime) + + @pytest.mark.asyncio + async def test_options_carry_strikes(self, responder, load): + """An option listing carries its strike and type.""" + rows = await run( + DeribitInstrumentsFetcher, + {"currency": "BTC", "kind": "option"}, + responder, + {"get_instruments": load("instruments_option")}, + ) + + assert all(row.strike for row in rows) + assert {row.option_type for row in rows} == {"call", "put"} + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No matching instrument is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitInstrumentsFetcher, + {"currency": "BTC"}, + responder, + {"get_instruments": []}, + ) + + +class TestExpirations: + """Expirations are flattened to one row per currency, kind, and date.""" + + @pytest.mark.asyncio + async def test_grouped_shape(self, responder, load): + """The grouped response yields one row per listed expiration.""" + rows = await run( + DeribitExpirationsFetcher, + {"currency": "grouped", "kind": "any"}, + responder, + {"get_expirations": load("expirations_grouped")}, + ) + + assert rows + assert {row.currency for row in rows} == {"BTC", "ETH", "USDC"} + assert {row.kind for row in rows} == {"future", "option"} + + @pytest.mark.asyncio + async def test_flat_shape(self, responder, load): + """The pooled response is read under the queried currency.""" + rows = await run( + DeribitExpirationsFetcher, + {"currency": "any", "kind": "any"}, + responder, + {"get_expirations": load("expirations_flat")}, + ) + + assert {row.currency for row in rows} == {"ANY"} + + @pytest.mark.asyncio + async def test_perpetual_has_no_date(self, responder): + """A perpetual code carries a code but no expiration date.""" + rows = await run( + DeribitExpirationsFetcher, + {"currency": "BTC", "kind": "future"}, + responder, + {"get_expirations": {"btc": {"future": ["PERPETUAL", "25DEC26"]}}}, + ) + + perpetual = next(row for row in rows if row.code == "PERPETUAL") + + assert perpetual.expiration is None + assert rows[0].expiration == date(2026, 12, 25) + + @pytest.mark.asyncio + async def test_empty_response(self, responder): + """An empty response is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitExpirationsFetcher, + {"currency": "BTC", "kind": "future"}, + responder, + {"get_expirations": {}}, + ) + + @pytest.mark.asyncio + async def test_all_currencies_empty(self, responder): + """A response whose currencies list nothing is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitExpirationsFetcher, + {"currency": "BTC", "kind": "future"}, + responder, + {"get_expirations": {"btc": {"future": []}}}, + ) + + +class TestCombos: + """Combos are flattened to one row per leg.""" + + @pytest.mark.asyncio + async def test_by_currency(self, responder, load): + """Every leg of every combo is a row.""" + payload = load("combos") + rows = await run( + DeribitCombosFetcher, + {"currency": "BTC"}, + responder, + {"get_combos": payload}, + ) + + assert len(rows) == sum(len(combo["legs"]) for combo in payload) + assert rows[0].leg + assert isinstance(rows[0].creation_timestamp, datetime) + + @pytest.mark.asyncio + async def test_by_id(self, responder, load): + """A named combo reads its own legs.""" + rows = await run( + DeribitCombosFetcher, + {"combo_id": "BTC-STRG-30OCT26-66000_90000"}, + responder, + {"get_combo_details": load("combo_details")}, + ) + + assert {row.combo_id for row in rows} == {"BTC-STRG-30OCT26-66000_90000"} + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No matching combo is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitCombosFetcher, + {"currency": "BTC"}, + responder, + {"get_combos": []}, + ) + + +class TestAnnouncements: + """Announcements are returned newest first.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each announcement carries a publication date and a title.""" + rows = await run( + DeribitAnnouncementsFetcher, + {"limit": 3}, + responder, + {"get_announcements": load("announcements")}, + ) + + assert [row.date for row in rows] == sorted( + (row.date for row in rows), reverse=True + ) + assert all(row.title for row in rows) + + @pytest.mark.asyncio + async def test_start_date_is_sent(self, responder, load): + """A start date reaches the exchange as a timestamp.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return load("announcements") + + await run( + DeribitAnnouncementsFetcher, + {"start_date": "2026-01-01", "limit": 3}, + responder, + {"get_announcements": capture}, + ) + + assert seen["start_timestamp"] == 1767225600000 + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No announcements over the span is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitAnnouncementsFetcher, + {}, + responder, + {"get_announcements": []}, + ) + + +class TestBookSummary: + """Book summaries normalize the percent the exchange publishes.""" + + @pytest.mark.asyncio + async def test_by_currency(self, responder, load): + """A currency reads every instrument's summary.""" + rows = await run( + DeribitBookSummaryFetcher, + {"currency": "BTC", "kind": "future"}, + responder, + {"get_book_summary_by_currency": load("book_summary_future")}, + ) + + assert len(rows) == 13 + assert [row.symbol for row in rows] == sorted(row.symbol for row in rows) + + @pytest.mark.asyncio + async def test_by_instrument(self, responder, load): + """A named instrument reads only its own summary.""" + rows = await run( + DeribitBookSummaryFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + { + "get_book_summary_by_instrument": load("book_summary_instrument"), + "get_instruments": load("instruments_future"), + }, + ) + + assert len(rows) == 1 + assert rows[0].symbol == "BTC-PERPETUAL" + + @pytest.mark.asyncio + async def test_percent_is_left_in_percent_units(self, responder, load): + """A price change is kept as the exchange publishes it.""" + payload = load("book_summary_future") + payload[0]["price_change"] = 1.25 + rows = await run( + DeribitBookSummaryFetcher, + {"currency": "BTC"}, + responder, + {"get_book_summary_by_currency": payload}, + ) + changed = next( + row for row in rows if row.symbol == payload[0]["instrument_name"] + ) + + assert changed.change_percent == 1.25 + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No summary is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitBookSummaryFetcher, + {"currency": "BTC"}, + responder, + {"get_book_summary_by_currency": []}, + ) + + +class TestOrderBook: + """The book is returned one row per resting level.""" + + @pytest.mark.asyncio + async def test_levels(self, responder, load): + """Both sides are flattened, numbered from the top of the book.""" + payload = load("order_book") + rows = await run( + DeribitOrderBookFetcher, + {"symbol": "BTC-PERPETUAL", "depth": 5}, + responder, + { + "get_order_book": payload, + "get_instruments": load("instruments_future"), + }, + ) + + assert len(rows) == len(payload["bids"]) + len(payload["asks"]) + assert {row.side for row in rows} == {"bid", "ask"} + assert [row.level for row in rows if row.side == "bid"] == list( + range(1, len(payload["bids"]) + 1) + ) + + @pytest.mark.asyncio + async def test_by_instrument_id(self, responder, load): + """A numeric identifier reads the same book.""" + rows = await run( + DeribitOrderBookFetcher, + {"instrument_id": 210838, "depth": 5}, + responder, + {"get_order_book_by_instrument_id": load("order_book")}, + ) + + assert rows[0].symbol == "BTC-PERPETUAL" + + @pytest.mark.asyncio + async def test_needs_a_selector(self): + """A query naming neither a symbol nor an identifier is rejected.""" + with pytest.raises(OpenBBError, match="symbol or an instrument ID"): + DeribitOrderBookFetcher.transform_query({}) + + @pytest.mark.asyncio + async def test_no_book(self, responder): + """No book at all is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitOrderBookFetcher, + {"instrument_id": 1}, + responder, + {"get_order_book_by_instrument_id": {}}, + ) + + @pytest.mark.asyncio + async def test_empty_book(self, responder): + """A book with no resting orders is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitOrderBookFetcher, + {"instrument_id": 1}, + responder, + { + "get_order_book_by_instrument_id": { + "instrument_name": "X", + "bids": [], + "asks": [], + } + }, + ) + + +class TestTicker: + """The ticker is flattened and its percents normalized.""" + + @pytest.mark.asyncio + async def test_row(self, responder, load): + """The quote, statistics, and greeks land on one row.""" + rows = await run( + DeribitTickerFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + { + "ticker": load("ticker_future"), + "get_instruments": load("instruments_future"), + }, + ) + + assert rows[0].symbol == "BTC-PERPETUAL" + assert rows[0].volume is not None + assert isinstance(rows[0].timestamp, datetime) + + @pytest.mark.asyncio + async def test_short_root_resolves(self, responder, load): + """A shortened perpetual root reaches the exchange in full.""" + seen: list = [] + + def capture(params): + seen.append(params["instrument_name"]) + + return load("ticker_future") + + await run( + DeribitTickerFetcher, + {"symbol": "btc"}, + responder, + {"ticker": capture, "get_instruments": load("instruments_future")}, + ) + + assert seen == ["BTC-PERPETUAL"] + + @pytest.mark.asyncio + async def test_empty(self, responder, load): + """No quote at all is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitTickerFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + {"ticker": {}, "get_instruments": load("instruments_future")}, + ) + + +class TestTrades: + """Trades are returned newest first, with the option volatility decimalized.""" + + @pytest.mark.asyncio + async def test_by_instrument(self, responder, load): + """A named instrument reads its own prints.""" + rows = await run( + DeribitTradesFetcher, + {"symbol": "BTC-PERPETUAL", "limit": 5}, + responder, + { + "get_last_trades_by_instrument": load("trades_instrument"), + "get_instruments": load("instruments_future"), + }, + ) + + assert rows + assert [row.timestamp for row in rows] == sorted( + (row.timestamp for row in rows), reverse=True + ) + + @pytest.mark.asyncio + async def test_by_currency_needs_a_kind(self, responder, load): + """A currency query sends the kind the exchange insists on.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return load("trades_currency") + + await run( + DeribitTradesFetcher, + {"currency": "BTC", "kind": "option", "limit": 5}, + responder, + {"get_last_trades_by_currency": capture}, + ) + + assert seen["kind"] == "option" + + @pytest.mark.asyncio + async def test_timed_variant(self, responder, load): + """Naming both ends of a span switches to the timed endpoint.""" + rows = await run( + DeribitTradesFetcher, + { + "currency": "BTC", + "kind": "option", + "start_date": "2026-09-01", + "end_date": "2026-09-02", + }, + responder, + {"get_last_trades_by_currency_and_time": load("trades_currency")}, + ) + + assert rows + + @pytest.mark.asyncio + async def test_timed_variant_by_instrument(self, responder, load): + """A named instrument over a span uses the timed endpoint too.""" + rows = await run( + DeribitTradesFetcher, + { + "symbol": "BTC-PERPETUAL", + "start_date": "2026-09-01", + "end_date": "2026-09-02", + }, + responder, + { + "get_last_trades_by_instrument_and_time": load("trades_instrument"), + "get_instruments": load("instruments_future"), + }, + ) + + assert rows + + @pytest.mark.asyncio + async def test_ascending(self, responder, load): + """Asking for ascending order returns the oldest print first.""" + rows = await run( + DeribitTradesFetcher, + {"currency": "BTC", "kind": "option", "sorting": "asc"}, + responder, + {"get_last_trades_by_currency": load("trades_currency")}, + ) + + assert [row.timestamp for row in rows] == sorted(row.timestamp for row in rows) + + @pytest.mark.asyncio + async def test_option_volatility_is_left_in_percent_units(self, responder, load): + """An option print's volatility is kept as the exchange publishes it.""" + payload = load("trades_currency") + payload["trades"][0]["iv"] = 42.0 + rows = await run( + DeribitTradesFetcher, + {"currency": "BTC", "kind": "option"}, + responder, + {"get_last_trades_by_currency": payload}, + ) + + assert 42.0 in {row.iv for row in rows} + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No prints is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitTradesFetcher, + {"currency": "BTC"}, + responder, + {"get_last_trades_by_currency": {"trades": []}}, + ) + + @pytest.mark.asyncio + async def test_routed_spot_by_currency_is_explained(self, responder): + """The exchange's routed-spot refusal is rewritten as an explanation.""" + + def refuse(params): + raise OpenBBError( + "Deribit returned an error for public/get_last_trades_by_currency" + " -> not_supported_for_coinbase_routed_spot" + ) + + with pytest.raises(OpenBBError, match="routes to Coinbase"): + await run( + DeribitTradesFetcher, + {"currency": "BTC"}, + responder, + {"get_last_trades_by_currency": refuse}, + ) + + @pytest.mark.asyncio + async def test_other_errors_by_currency_are_not_rewritten(self, responder): + """Any other failure is passed through untouched.""" + + def refuse(params): + raise OpenBBError("some other problem entirely") + + with pytest.raises(OpenBBError, match="some other problem"): + await run( + DeribitTradesFetcher, + {"currency": "BTC"}, + responder, + {"get_last_trades_by_currency": refuse}, + ) + + @pytest.mark.asyncio + async def test_routed_spot_by_symbol_is_explained( + self, monkeypatch, responder, load + ): + """A routed spot pair named by symbol is explained, not left as a code.""" + responder({"get_instruments": load("instruments_future")}) + + async def _gather(calls, use_cache=True): + return [ + OpenBBError( + "Deribit returned an error for" + " public/get_last_trades_by_instrument ->" + " not_supported_for_coinbase_routed_spot" + ) + ] + + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + + with pytest.raises(OpenBBError, match="routes to Coinbase"): + await DeribitTradesFetcher.fetch_data({"symbol": "BTC_USDC"}, {}) + + @pytest.mark.asyncio + async def test_other_symbol_failures_fall_through_to_empty( + self, monkeypatch, responder, load + ): + """A failure that is not the routed-spot refusal reports no data.""" + responder({"get_instruments": load("instruments_future")}) + + async def _gather(calls, use_cache=True): + return [OpenBBError("something else")] + + monkeypatch.setattr("openbb_deribit.utils.client.gather", _gather) + + with pytest.raises(EmptyDataError): + await DeribitTradesFetcher.fetch_data({"symbol": "BTC-PERPETUAL"}, {}) + + +class TestTradeVolumes: + """Traded volume is returned per currency.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each currency carries its one, seven, and thirty day volumes.""" + rows = await run( + DeribitTradeVolumesFetcher, + {}, + responder, + {"get_trade_volumes": load("trade_volumes")}, + ) + + assert rows + assert rows[0].futures_volume_30d is not None + + @pytest.mark.asyncio + async def test_the_busiest_currencies_come_first(self, responder, load): + """Currencies are ranked by what traded, not by name.""" + rows = await run( + DeribitTradeVolumesFetcher, + {}, + responder, + {"get_trade_volumes": load("trade_volumes")}, + ) + traded = [ + (row.futures_volume or 0) + + (row.calls_volume or 0) + + (row.puts_volume or 0) + + (row.spot_volume or 0) + for row in rows + ] + + assert traded == sorted(traded, reverse=True) + + @pytest.mark.asyncio + async def test_currencies_that_traded_nothing_sort_by_name(self, responder): + """A tie is broken by name, so the order never wanders between calls.""" + rows = await run( + DeribitTradeVolumesFetcher, + {}, + responder, + { + "get_trade_volumes": [ + {"currency": "ZZZ", "futures_volume": 0.0}, + {"currency": "AAA", "futures_volume": 0.0}, + {"currency": "MMM", "spot_volume": 5.0}, + ] + }, + ) + + assert [row.currency for row in rows] == ["MMM", "AAA", "ZZZ"] + + @pytest.mark.asyncio + async def test_every_product_counts_towards_the_ranking(self, responder): + """A currency that trades only options outranks one that trades nothing.""" + rows = await run( + DeribitTradeVolumesFetcher, + {}, + responder, + { + "get_trade_volumes": [ + {"currency": "AAA", "futures_volume": 1.0}, + {"currency": "BBB", "calls_volume": 2.0, "puts_volume": 3.0}, + ] + }, + ) + + assert [row.currency for row in rows] == ["BBB", "AAA"] + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No volumes is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitTradeVolumesFetcher, {}, responder, {"get_trade_volumes": []} + ) + + +class TestBlockRfqTrades: + """Block requests are flattened to one row per leg.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Every leg of every request is a row, newest first.""" + payload = load("block_rfq_trades") + rows = await run( + DeribitBlockRfqTradesFetcher, + {"limit": 10}, + responder, + {"get_block_rfq_trades": payload}, + ) + + assert len(rows) == sum(len(rfq["legs"]) for rfq in payload["block_rfqs"]) + assert [row.timestamp for row in rows] == sorted( + (row.timestamp for row in rows), reverse=True + ) + + @pytest.mark.asyncio + async def test_legless_request(self, responder): + """A request carrying no legs still produces one row.""" + rows = await run( + DeribitBlockRfqTradesFetcher, + {"limit": 10}, + responder, + { + "get_block_rfq_trades": { + "block_rfqs": [{"id": 1, "timestamp": 1789938000000}] + } + }, + ) + + assert len(rows) == 1 + assert rows[0].leg is None + + @pytest.mark.asyncio + async def test_limit_is_bounded(self): + """A count outside the range the exchange accepts is rejected.""" + with pytest.raises(ValueError): + DeribitBlockRfqTradesFetcher.transform_query({"limit": 1}) + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No traded request is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitBlockRfqTradesFetcher, + {}, + responder, + {"get_block_rfq_trades": {"block_rfqs": []}}, + ) + + +class TestSettlements: + """Settlements are returned newest first.""" + + @pytest.mark.asyncio + async def test_by_currency(self, responder, load): + """A currency reads every settlement it processed.""" + rows = await run( + DeribitSettlementsFetcher, + {"currency": "BTC", "limit": 5}, + responder, + {"get_last_settlements_by_currency": load("settlements_currency")}, + ) + + assert rows + assert rows[0].settlement_type + + @pytest.mark.asyncio + async def test_by_instrument(self, responder, load): + """A named instrument reads only its own settlements.""" + rows = await run( + DeribitSettlementsFetcher, + {"symbol": "BTC-PERPETUAL", "limit": 5}, + responder, + { + "get_last_settlements_by_instrument": load("settlements_instrument"), + "get_instruments": load("instruments_future"), + }, + ) + + assert {row.symbol for row in rows} == {"BTC-PERPETUAL"} + + @pytest.mark.asyncio + async def test_start_date_is_sent(self, responder, load): + """A start date reaches the exchange as a search bound.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return load("settlements_currency") + + await run( + DeribitSettlementsFetcher, + {"currency": "BTC", "start_date": "2026-01-01"}, + responder, + {"get_last_settlements_by_currency": capture}, + ) + + assert seen["search_start_timestamp"] == 1767225600000 + + @pytest.mark.asyncio + async def test_empty(self, responder): + """Nothing settled is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitSettlementsFetcher, + {"currency": "BTC"}, + responder, + {"get_last_settlements_by_currency": {"settlements": []}}, + ) + + +class TestDeliveryPrices: + """Delivery prices are returned oldest first.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each session the index delivered on is a row.""" + rows = await run( + DeribitDeliveryPricesFetcher, + {"index_name": "btc_usd", "limit": 5}, + responder, + {"get_delivery_prices": load("delivery_prices")}, + ) + + assert [row.date for row in rows] == sorted(row.date for row in rows) + assert {row.index_name for row in rows} == {"btc_usd"} + + @pytest.mark.asyncio + async def test_empty(self, responder): + """An index that has not delivered is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitDeliveryPricesFetcher, + {"index_name": "btc_usd"}, + responder, + {"get_delivery_prices": {"data": []}}, + ) + + +class TestIndexPrice: + """Index levels are returned one row per index.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each named index carries its level.""" + rows = await run( + DeribitIndexPriceFetcher, + {"index_name": "btc_usd,eth_usd"}, + responder, + {"get_index_price": load("index_price")}, + ) + + assert [row.index_name for row in rows] == ["btc_usd", "eth_usd"] + assert all(row.index_price for row in rows) + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No level at all is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitIndexPriceFetcher, + {"index_name": "btc_usd"}, + responder, + {"get_index_price": {}}, + ) + + +class TestIndexHistorical: + """An index series is returned oldest first.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each published level is a row.""" + payload = load("index_chart_data") + rows = await run( + DeribitIndexHistoricalFetcher, + {"index_name": "btc_usd", "span": "1h"}, + responder, + {"get_index_chart_data": payload}, + ) + + assert len(rows) == len(payload) + assert [row.date for row in rows] == sorted(row.date for row in rows) + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No series is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitIndexHistoricalFetcher, + {"index_name": "btc_usd"}, + responder, + {"get_index_chart_data": []}, + ) + + +class TestFundingRateHistory: + """Funding history is returned oldest first.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each hour of funding is a row, with no raw timestamp left over.""" + payload = load("funding_rate_history") + rows = await run( + DeribitFundingRateHistoryFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + { + "get_funding_rate_history": payload, + "get_instruments": load("instruments_future"), + }, + ) + + assert len(rows) == len(payload) + assert "timestamp" not in rows[0].model_dump() + assert [row.date for row in rows] == sorted(row.date for row in rows) + + @pytest.mark.asyncio + async def test_empty(self, responder, load): + """No funding over the span is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitFundingRateHistoryFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + { + "get_funding_rate_history": [], + "get_instruments": load("instruments_future"), + }, + ) + + +class TestFundingChart: + """The funding chart carries the running interest alongside each point.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each point is a row and the running interest is repeated.""" + payload = load("funding_chart") + rows = await run( + DeribitFundingChartFetcher, + {"symbol": "BTC-PERPETUAL", "length": "8h"}, + responder, + { + "get_funding_chart_data": payload, + "get_instruments": load("instruments_future"), + }, + ) + + assert len(rows) == len(payload["data"]) + assert "timestamp" not in rows[0].model_dump() + + @pytest.mark.asyncio + async def test_empty(self, responder, load): + """No funding over the span is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitFundingChartFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + { + "get_funding_chart_data": {"data": []}, + "get_instruments": load("instruments_future"), + }, + ) + + +class TestAprHistory: + """Yield history reads the day number as a date.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each day is a row, with the rate as a decimal.""" + payload = load("apr_history") + rows = await run( + DeribitAprHistoryFetcher, + {"currency": "usde", "limit": 5}, + responder, + {"get_apr_history": payload}, + ) + + assert len(rows) == len(payload["data"]) + assert isinstance(rows[0].date, date) + assert "day" not in rows[0].model_dump() + assert rows[0].apr > 1 + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No yield history is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitAprHistoryFetcher, + {"currency": "usde"}, + responder, + {"get_apr_history": {"data": []}}, + ) + + +class TestHistoricalVolatility: + """Realized volatility is returned oldest first, as a decimal.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each reading is a row.""" + payload = load("historical_volatility") + rows = await run( + DeribitHistoricalVolatilityFetcher, + {"currency": "BTC"}, + responder, + {"get_historical_volatility": payload}, + ) + + assert len(rows) == len(payload) + assert rows[0].volatility > 5 + assert [row.date for row in rows] == sorted(row.date for row in rows) + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No readings is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitHistoricalVolatilityFetcher, + {"currency": "BTC"}, + responder, + {"get_historical_volatility": []}, + ) + + +class TestVolatilityIndex: + """The volatility index is returned as candles, oldest first.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Each candle carries four decimal levels.""" + payload = load("volatility_index") + rows = await run( + DeribitVolatilityIndexFetcher, + {"currency": "BTC", "interval": "12h"}, + responder, + {"get_volatility_index_data": payload}, + ) + + assert len(rows) == len(payload["data"]) + assert rows[0].high >= rows[0].low + assert rows[0].close > 5 + + @pytest.mark.asyncio + async def test_resolution_is_mapped(self, responder, load): + """The interval reaches the exchange as the resolution it names.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return load("volatility_index") + + await run( + DeribitVolatilityIndexFetcher, + {"currency": "BTC", "interval": "1d"}, + responder, + {"get_volatility_index_data": capture}, + ) + + assert seen["resolution"] == "1D" + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No candles over the span is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitVolatilityIndexFetcher, + {"currency": "BTC"}, + responder, + {"get_volatility_index_data": {"data": []}}, + ) + + +class TestFuturesInstruments: + """The standard futures listing keeps the extended Deribit fields.""" + + @pytest.mark.asyncio + async def test_rows(self, responder, load): + """Every future is a row, carrying the live-only fields.""" + rows = await run( + DeribitFuturesInstrumentsFetcher, + {}, + responder, + {"get_instruments": load("instruments_future")}, + ) + + assert len(rows) == 13 + assert rows[0].index_id + assert rows[0].product_group + assert rows[0].underlying_type + + @pytest.mark.asyncio + async def test_perpetual_has_no_expiration(self, responder, load): + """The year-3000 sentinel reads as no expiration.""" + rows = await run( + DeribitFuturesInstrumentsFetcher, + {}, + responder, + {"get_instruments": load("instruments_future")}, + ) + perpetual = next(row for row in rows if row.symbol == "BTC-PERPETUAL") + + assert perpetual.expiration_timestamp is None + + @pytest.mark.asyncio + async def test_empty(self, responder): + """No futures is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitFuturesInstrumentsFetcher, + {}, + responder, + {"get_instruments": []}, + ) + + +class TestFuturesInfo: + """The standard futures quote is the flattened ticker.""" + + @pytest.mark.asyncio + async def test_row(self, responder, load): + """The quote carries the statistics the ticker nests.""" + rows = await run( + DeribitFuturesInfoFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + { + "ticker": load("ticker_future"), + "get_instruments": load("instruments_future"), + }, + ) + + assert rows[0].symbol == "BTC-PERPETUAL" + assert rows[0].volume is not None + + @pytest.mark.asyncio + async def test_empty(self, responder, load): + """No quote is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitFuturesInfoFetcher, + {"symbol": "BTC-PERPETUAL"}, + responder, + {"ticker": {}, "get_instruments": load("instruments_future")}, + ) + + +class TestFuturesCurve: + """The current curve is priced off the mark.""" + + @pytest.mark.asyncio + async def test_prefers_the_mark(self, responder, load): + """A stale last trade does not set the level of the curve.""" + payload = dict(load("ticker_future")) + payload["last_price"] = 1.0 + payload["mark_price"] = 2.0 + payload["instrument_name"] = "BTC-25DEC26" + rows = await run( + DeribitFuturesCurveFetcher, + {"symbol": "BTC"}, + responder, + { + "ticker": payload, + "get_instruments": load("instruments_future"), + }, + ) + + assert rows[0].price == 2.0 + assert rows[0].last_price == 1.0 + + @pytest.mark.asyncio + async def test_hours_ago_is_appended(self, monkeypatch, responder, load): + """A past curve is added alongside the current one.""" + + async def _past(symbol, hours): + return [ + { + "instrument_name": "BTC-25DEC26", + "hours_ago": hours, + "last_price": 3.0, + } + ] + + monkeypatch.setattr( + "openbb_deribit.utils.helpers.get_futures_curve_by_hours_ago", _past + ) + payload = dict(load("ticker_future")) + payload["instrument_name"] = "BTC-25DEC26" + rows = await run( + DeribitFuturesCurveFetcher, + {"symbol": "BTC", "hours_ago": 24}, + responder, + { + "ticker": payload, + "get_instruments": load("instruments_future"), + }, + ) + + assert 24 in {row.hours_ago for row in rows} + + def test_rejects_a_date(self): + """A date is rejected, because the exchange serves no curve for one.""" + with pytest.raises(ValueError, match="hours_ago"): + DeribitFuturesCurveFetcher.transform_query( + {"symbol": "BTC", "date": "2026-01-01"} + ) + + def test_hours_ago_accepts_a_list(self): + """Several hour counts are carried as a comma-separated string.""" + query = DeribitFuturesCurveFetcher.transform_query( + {"symbol": "BTC", "hours_ago": [1, 24]} + ) + + assert query.hours_ago == "1,24" + + def test_hours_ago_defaults_to_nothing(self): + """No hour count is carried as nothing at all.""" + assert ( + DeribitFuturesCurveFetcher.transform_query( + {"symbol": "BTC", "hours_ago": None} + ).hours_ago + is None + ) + + def test_a_non_mapping_query_passes_through(self): + """A query the validator cannot read is left to pydantic.""" + from openbb_deribit.models.futures_curve import ( + DeribitFuturesCurveQueryParams, + ) + + assert DeribitFuturesCurveQueryParams.validate_model("BTC") == "BTC" + + def test_hours_ago_accepts_an_integer(self): + """One hour count is carried as its own string.""" + query = DeribitFuturesCurveFetcher.transform_query( + {"symbol": "BTC", "hours_ago": 24} + ) + + assert query.hours_ago == "24" + + @pytest.mark.asyncio + async def test_no_quotes(self, responder, load): + """A curve with no quote at all is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitFuturesCurveFetcher, + {"symbol": "BTC"}, + responder, + {"ticker": {}, "get_instruments": load("instruments_future")}, + ) + + @pytest.mark.asyncio + async def test_no_prices(self, responder, load): + """A curve whose contracts are all unpriced is an error.""" + with pytest.raises(EmptyDataError): + await run( + DeribitFuturesCurveFetcher, + {"symbol": "BTC"}, + responder, + { + "ticker": {"instrument_name": "BTC-25DEC26"}, + "get_instruments": load("instruments_future"), + }, + ) + + +class TestFuturesHistorical: + """Candles are returned oldest first.""" + + @pytest.mark.asyncio + async def test_single_symbol_drops_the_column(self, monkeypatch, responder, load): + """One symbol needs no symbol column.""" + + async def _ohlc(symbol, start_date, end_date, interval): + return [ + { + "date": date(2026, 9, 19), + "symbol": symbol, + "open": 1.0, + "high": 2.0, + "low": 0.5, + "close": 1.5, + "volume": 10.0, + "volume_notional": 15.0, + } + ] + + monkeypatch.setattr("openbb_deribit.utils.helpers.get_ohlc_data", _ohlc) + responder({"get_instruments": load("instruments_future")}) + rows = await DeribitFuturesHistoricalFetcher.fetch_data( + {"symbol": "BTC-PERPETUAL"}, {} + ) + + assert rows[0].symbol is None + assert rows[0].volume_notional == 15.0 + + @pytest.mark.asyncio + async def test_several_symbols_keep_the_column(self, monkeypatch, responder, load): + """Several symbols are told apart by the symbol column.""" + + async def _ohlc(symbol, start_date, end_date, interval): + return [ + { + "date": date(2026, 9, 19), + "symbol": symbol, + "open": 1.0, + "high": 2.0, + "low": 0.5, + "close": 1.5, + "volume": 10.0, + "volume_notional": 15.0, + } + ] + + monkeypatch.setattr("openbb_deribit.utils.helpers.get_ohlc_data", _ohlc) + responder({"get_instruments": load("instruments_future")}) + rows = await DeribitFuturesHistoricalFetcher.fetch_data( + {"symbol": "BTC-PERPETUAL,BTC-25DEC26"}, {} + ) + + assert {row.symbol for row in rows} == {"BTC-PERPETUAL", "BTC-25DEC26"} + + def test_span_defaults_narrow_with_the_interval(self): + """A one-minute interval defaults to a much shorter span than a daily.""" + minute = DeribitFuturesHistoricalFetcher.transform_query( + {"symbol": "BTC-PERPETUAL", "interval": "1m"} + ) + daily = DeribitFuturesHistoricalFetcher.transform_query( + {"symbol": "BTC-PERPETUAL", "interval": "1d"} + ) + + assert minute.start_date > daily.start_date + assert minute.end_date == daily.end_date + + def test_a_non_mapping_query_passes_through(self): + """A query the validator cannot read is left to pydantic.""" + from openbb_deribit.models.futures_historical import ( + DeribitFuturesHistoricalQueryParams, + ) + + assert DeribitFuturesHistoricalQueryParams.validate_model("BTC") == "BTC" + + def test_given_span_is_kept(self): + """A span the caller named is not overridden.""" + query = DeribitFuturesHistoricalFetcher.transform_query( + { + "symbol": "BTC-PERPETUAL", + "start_date": "2026-01-01", + "end_date": "2026-02-01", + } + ) + + assert query.start_date == date(2026, 1, 1) + assert query.end_date == date(2026, 2, 1) + + @pytest.mark.asyncio + async def test_empty(self, monkeypatch, responder, load): + """No candles at all is an error.""" + + async def _ohlc(symbol, start_date, end_date, interval): + return [] + + monkeypatch.setattr("openbb_deribit.utils.helpers.get_ohlc_data", _ohlc) + responder({"get_instruments": load("instruments_future")}) + + with pytest.raises(EmptyDataError): + await DeribitFuturesHistoricalFetcher.fetch_data( + {"symbol": "BTC-PERPETUAL"}, {} + ) diff --git a/openbb_platform/providers/deribit/tests/test_options_analysis.py b/openbb_platform/providers/deribit/tests/test_options_analysis.py new file mode 100644 index 00000000000..0d8907dec4b --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_options_analysis.py @@ -0,0 +1,790 @@ +"""Tests for the Deribit options valuation and ranking.""" + +import pytest + +from openbb_deribit.utils.options import chain, legs, optimizer, strategies + + +class TestValuation: + """The valuation core prices a contract and inverts its own prices.""" + + def test_intrinsic(self): + """A contract is worth what exercising it would pay.""" + assert legs.intrinsic(110, 100, "call") == 10 + assert legs.intrinsic(90, 100, "call") == 0 + assert legs.intrinsic(90, 100, "put") == 10 + assert legs.intrinsic(110, 100, "put") == 0 + + def test_put_call_parity(self): + """A call less a put is the forward less the strike.""" + call = legs.option_value(100, 95, 0.5, 0.6, 1.0, "call") + put = legs.option_value(100, 95, 0.5, 0.6, 1.0, "put") + + assert call - put == pytest.approx(100 - 95) + + def test_value_without_time_is_intrinsic(self): + """A contract at expiration is worth exactly its exercise value.""" + assert legs.option_value(110, 100, 0, 0.6, 1.0, "call") == 10 + + def test_implied_volatility_round_trips(self): + """A price made at one volatility implies that volatility back.""" + price = legs.option_value(100, 100, 0.25, 0.42, 1.0, "call") + + assert legs.implied_vol(price, 100, 100, 0.25, 1.0, "call") == pytest.approx( + 0.42, abs=1e-4 + ) + + @pytest.mark.parametrize( + "price,forward,years", + [(0, 100, 0.25), (5, 100, 0), (5, 0, 0.25)], + ) + def test_implied_volatility_needs_a_real_quote(self, price, forward, years): + """A quote that cannot come from any volatility implies none.""" + assert legs.implied_vol(price, forward, 100, years, 1.0, "call") is None + + def test_a_price_below_intrinsic_implies_nothing(self): + """No volatility prices a contract under its exercise value.""" + assert legs.implied_vol(1.0, 120, 100, 0.25, 1.0, "call") is None + + def test_a_price_above_the_search_implies_nothing(self): + """A quote beyond the highest volatility searched implies none.""" + assert legs.implied_vol(99.0, 100, 100, 0.25, 1.0, "call") is None + + +class TestPayoff: + """A position's profit is measured in what it settles in.""" + + @staticmethod + def _call(price=5.0, dte=0): + """Return one long call leg.""" + return [ + { + "strike": 100.0, + "option_type": "call", + "quantity": 1, + "size": 1, + "price": price, + "dte": dte, + "implied_volatility": None, + } + ] + + def test_linear_payoff(self): + """A linear contract pays the exercise value less the premium.""" + assert legs.payoff(self._call(), [90, 100, 110]) == [-5.0, -5.0, 5.0] + + def test_an_inverse_premium_costs_what_the_coin_is_worth(self): + """The premium is coin, so holding through a rise costs more. + + Below the price it was opened at the coin paid is worth less, above it + more, which is why an inverse loss is not flat. + """ + below, entry, above = legs.payoff( + self._call(), [50, 100, 200], inverse=True, entry_price=100 + ) + + assert entry == pytest.approx(-5.0) + assert below == pytest.approx(-2.5) + assert above == pytest.approx(100 - 10.0) + + def test_an_inverse_payout_is_already_quoted(self): + """Only the premium is carried; what the contract pays is not.""" + value = legs.payoff(self._call(), [150], inverse=True, entry_price=100)[0] + + assert value == pytest.approx(50 - 5.0 * 1.5) + + def test_an_inverse_position_needs_its_entry_price(self): + """Valuing an inverse position without its entry is refused.""" + with pytest.raises(ValueError, match="paid in the coin"): + legs.payoff(self._call(), [100], inverse=True) + + def test_a_worthless_underlying_costs_nothing_to_have_paid(self): + """The coin the premium was paid in is itself worth nothing there.""" + assert legs.payoff(self._call(), [0], inverse=True, entry_price=100) == [0.0] + + def test_time_left_is_valued_not_exercised(self): + """A leg outliving the valuation date keeps its time value.""" + held = self._call(dte=30) + held[0]["implied_volatility"] = 0.6 + + assert legs.payoff(held, [100], elapsed=0)[0] > -5.0 + + def test_net_cost_of_a_credit_is_negative(self): + """A position taken in is a credit.""" + sold = self._call() + sold[0]["quantity"] = -1 + + assert legs.net_cost(sold) == -5.0 + + def test_breakevens(self): + """Every crossing of zero is reported.""" + crossings = legs.breakevens([0, 1, 2, 3], [-1.0, 1.0, 1.0, -1.0]) + + assert crossings == pytest.approx([0.5, 2.5]) + + def test_no_crossing_is_no_breakeven(self): + """A payoff that never crosses zero has no breakeven.""" + assert legs.breakevens([0, 1], [1.0, 2.0]) == [] + + +class TestGreeks: + """A contract with nothing left to model carries no greeks.""" + + def test_a_live_contract_carries_every_greek(self): + """A contract with time and volatility is fully described.""" + from openbb_deribit.utils.options.greeks import greeks + + modelled = greeks(100.0, 100.0, 0.25, 0.5, "call") + + assert set(modelled) == {"delta", "gamma", "theta", "vega", "rho"} + assert all(value is not None for value in modelled.values()) + assert 0 < modelled["delta"] < 1 + assert modelled["theta"] < 0 + assert modelled["vega"] > 0 + + def test_a_put_is_the_mirror_of_its_call(self): + """A call and a put on one strike differ by exactly one in delta.""" + from openbb_deribit.utils.options.greeks import greeks + + call = greeks(100.0, 95.0, 0.5, 0.4, "call") + put = greeks(100.0, 95.0, 0.5, 0.4, "put") + + assert call["delta"] - put["delta"] == pytest.approx(1.0) + assert call["gamma"] == pytest.approx(put["gamma"]) + assert call["vega"] == pytest.approx(put["vega"]) + + @pytest.mark.parametrize( + "forward,strike,years,sigma", + [(100.0, 100.0, 0, 0.5), (100.0, 100.0, 0.25, 0), (0, 100.0, 0.25, 0.5)], + ) + def test_nothing_to_model_carries_nothing(self, forward, strike, years, sigma): + """An expired or unquoted contract reports no greeks rather than zeros.""" + from openbb_deribit.utils.options.greeks import greeks + + modelled = greeks(forward, strike, years, sigma, "call") + + assert all(value is None for value in modelled.values()) + + +class TestChain: + """The chain exposes what the analysis needs from it.""" + + def test_reads_its_own_shape(self, chain_frame): + """Spot, settlement, and expirations come off the frame.""" + frame = chain_frame(inverse=True, spot=100.0) + + assert chain.underlying_price(frame) == 100.0 + assert chain.is_inverse(frame) is True + assert chain.settlement_currency(frame, "BTC") == "BTC" + assert len(chain.expirations(frame)) == 2 + + def test_a_linear_chain_settles_in_the_quote(self, chain_frame): + """A linear contract pays out in what it is quoted in.""" + frame = chain_frame(inverse=False, spot=1.5, size=1000) + + assert chain.is_inverse(frame) is False + assert chain.settlement_currency(frame, "XRP_USDC") == "USDC" + + def test_an_empty_spot_reads_as_zero(self, chain_frame): + """A chain with no published spot reports none rather than guessing.""" + frame = chain_frame() + frame["underlying_spot_price"] = None + + assert chain.underlying_price(frame) == 0.0 + + def test_nearest_expiration(self, chain_frame): + """The listed expiration closest to a date is the one chosen.""" + frame = chain_frame() + listed = chain.expirations(frame) + + assert chain.nearest_expiration(frame, listed[0]) == listed[0] + assert chain.nearest_expiration(frame, "2099-01-01") == listed[-1] + + def test_quotes_carry_an_entry_and_an_exit(self, chain_frame): + """Each contract is entered at the offer and left at the bid.""" + frame = chain_frame() + quotes = chain.quotes_at(frame, chain.expirations(frame)[0]) + + assert (quotes["entry"] >= quotes["exit"]).all() + assert (quotes["entry"] > 0).all() + + def test_unquoted_contracts_are_left_out(self, chain_frame): + """A contract nobody priced cannot be entered.""" + frame = chain_frame() + frame.loc[frame.index[0], ["ask", "mark"]] = 0 + quotes = chain.quotes_at(frame, chain.expirations(frame)[0]) + + assert ( + len(quotes) + == len(frame[frame["expiration"] == chain.expirations(frame)[0]]) - 1 + ) + + +def _leg(kind, strike, quantity, dte=30, price=1.0): + """Return one unit leg of a position.""" + return { + "symbol": f"TEST-{dte}-{strike:g}-{kind[0].upper()}", + "expiration": str(dte), + "strike": float(strike), + "option_type": "call" if kind == "c" else "put", + "quantity": quantity, + "size": 1.0, + "price": price, + "dte": dte, + "implied_volatility": 0.5, + } + + +class TestClassify: + """A position reads back as the Deribit combo it is, and the side taken.""" + + @pytest.mark.parametrize( + "legs,expected", + [ + ([("c", 100, 1)], ("C", 1)), + ([("p", 100, -1)], ("P", -1)), + ([("c", 100, 1), ("c", 104, -1)], ("CS", 1)), + ([("c", 100, -1), ("c", 104, 1)], ("CS", -1)), + ([("p", 104, 1), ("p", 100, -1)], ("PS", 1)), + ([("p", 104, -1), ("p", 100, 1)], ("PS", -1)), + ([("c", 100, 1), ("c", 104, -2)], ("CSR12", 1)), + ([("c", 100, -1), ("c", 104, 2)], ("CSR12", -1)), + ([("p", 104, 1), ("p", 100, -2)], ("PSR12", 1)), + ([("p", 104, -1), ("p", 100, 2)], ("PSR12", -1)), + ([("c", 100, 1), ("c", 104, 1)], ("", 1)), + ([("c", 100, 1), ("c", 100, -1)], ("", 1)), + ([("c", 100, 1), ("c", 104, -3)], ("", 1)), + ([("c", 100, 1), ("p", 100, 1)], ("STRD", 1)), + ([("c", 100, -1), ("p", 100, -1)], ("STRD", -1)), + ([("c", 100, 1), ("p", 100, -1)], ("REV", 1)), + ([("c", 100, -1), ("p", 100, 1)], ("REV", -1)), + ([("p", 96, 1), ("c", 104, 1)], ("STRG", 1)), + ([("p", 96, -1), ("c", 104, -1)], ("STRG", -1)), + ([("p", 96, 1), ("c", 104, -1)], ("RR", 1)), + ([("p", 96, -1), ("c", 104, 1)], ("RR", -1)), + ([("p", 104, 1), ("c", 96, 1)], ("", 1)), + ([("c", 100, 2), ("p", 100, 1)], ("", 1)), + ([("c", 96, 1), ("c", 100, -2), ("c", 104, 1)], ("CBUT", 1)), + ([("c", 96, -1), ("c", 100, 2), ("c", 104, -1)], ("CBUT", -1)), + ([("p", 96, 1), ("p", 100, -2), ("p", 104, 1)], ("PBUT", 1)), + ([("c", 96, 1), ("c", 100, -2), ("c", 106, 1)], ("", 1)), + ([("c", 96, 1), ("p", 100, -2), ("c", 104, 1)], ("", 1)), + ( + [("c", 96, 1), ("c", 100, -1), ("c", 104, -1), ("c", 108, 1)], + ("CCOND", 1), + ), + ( + [("p", 96, -1), ("p", 100, 1), ("p", 104, 1), ("p", 108, -1)], + ("PCOND", -1), + ), + ( + [("c", 96, 1), ("c", 100, -1), ("c", 104, 1), ("c", 108, -1)], + ("", 1), + ), + ( + [("p", 96, 1), ("p", 100, -1), ("c", 104, -1), ("c", 108, 1)], + ("ICOND", 1), + ), + ( + [("p", 96, -1), ("p", 100, 1), ("c", 104, 1), ("c", 108, -1)], + ("ICOND", -1), + ), + ( + [("p", 96, 1), ("p", 100, -1), ("c", 100, -1), ("c", 104, 1)], + ("IBUT", 1), + ), + ( + [("p", 96, 1), ("p", 100, 1), ("c", 104, -1), ("c", 108, 1)], + ("", 1), + ), + ( + [("p", 96, 1), ("p", 104, -1), ("c", 100, -1), ("c", 108, 1)], + ("", 1), + ), + ( + [("p", 92, 1), ("p", 96, 1), ("p", 100, -1), ("c", 108, 1)], + ("", 1), + ), + ( + [ + ("c", 92, 1), + ("c", 96, 1), + ("c", 100, -1), + ("c", 104, 1), + ("c", 108, 1), + ], + ("", 1), + ), + ], + ) + def test_one_expiration(self, legs, expected): + """Every single-expiration structure is named by its combo code.""" + assert optimizer.classify([_leg(*leg) for leg in legs]) == expected + + @pytest.mark.parametrize( + "legs,expected", + [ + ([("c", 100, -1, 30), ("c", 100, 1, 60)], ("CCAL", 1)), + ([("c", 100, 1, 30), ("c", 100, -1, 60)], ("CCAL", -1)), + ([("p", 100, -1, 30), ("p", 104, 1, 60)], ("PDIAG", 1)), + ([("p", 100, -1, 30), ("p", 104, 2, 60)], ("", 1)), + ([("c", 100, -1, 30), ("p", 100, 1, 60)], ("", 1)), + ([("c", 96, 1, 30), ("c", 100, -1, 60), ("c", 104, 1, 60)], ("", 1)), + ], + ) + def test_two_expirations(self, legs, expected): + """A calendar shares a strike across terms; a diagonal does not.""" + assert optimizer.classify([_leg(*leg) for leg in legs]) == expected + + +class TestOptimizer: + """Every combo structure is built, sized to what it risks, and ranked.""" + + @staticmethod + def _near(frame): + """Return the nearer of the two expirations.""" + return chain.expirations(frame)[0] + + def test_builds_every_structure(self, chain_frame): + """Every Deribit combo structure is offered, bought and sold.""" + frame = chain_frame() + built = optimizer.candidates(frame, self._near(frame), 100.0, (), True) + + assert {item["strategy"] for item in built} == set(optimizer.NAMES.values()) + assert all(item["code"] for item in built) + + def test_a_position_is_offered_once(self, chain_frame): + """No two candidates trade the same contracts in the same amounts.""" + frame = chain_frame() + built = optimizer.candidates(frame, self._near(frame), 100.0, (), True) + keys = [optimizer._key(item["legs"]) for item in built] + + assert len(keys) == len(set(keys)) + + def test_the_last_expiration_has_no_calendar(self, chain_frame): + """With nothing later to trade against, no calendar is built.""" + frame = chain_frame() + last = chain.expirations(frame)[-1] + built = optimizer.candidates(frame, last, 100.0, (), True) + + assert not {item["code"] for item in built} & {"CCAL", "PCAL", "CDIAG", "PDIAG"} + + def test_a_far_expiration_missing_one_side_builds_no_calendar_on_it( + self, chain_frame + ): + """A far expiration without puts offers no put calendar.""" + frame = chain_frame() + far = chain.expirations(frame)[-1] + frame = frame[~((frame["expiration"] == far) & (frame["option_type"] == "put"))] + built = optimizer.candidates(frame, self._near(frame), 100.0, (), True) + codes = {item["code"] for item in built} + + assert "CCAL" in codes + assert not codes & {"PCAL", "PDIAG"} + + def test_ranks_the_best_of_each_structure(self, chain_frame): + """Each structure appears once, the most profitable first.""" + frame = chain_frame() + ranked = optimizer.rank(frame, self._near(frame), 100.0, 104.0, 1.0, True) + names = [item["strategy"] for item in ranked] + profits = [item["expected_profit"] for item in ranked] + + assert len(names) == len(set(names)) == len(optimizer.NAMES) + assert profits == sorted(profits, reverse=True) + + def test_the_limit_caps_the_structures(self, chain_frame): + """Asking for fewer structures returns the best of them.""" + frame = chain_frame() + ranked = optimizer.rank(frame, self._near(frame), 100.0, 104.0, 1.0, True, 3) + + assert len(ranked) == 3 + + @pytest.mark.parametrize("inverse", [True, False]) + def test_every_position_risks_the_budget(self, chain_frame, inverse): + """The worst loss across a threefold move either way is the budget.""" + from openbb_deribit.utils.options.legs import payoff + + frame = chain_frame(inverse=inverse) + + for item in optimizer.rank( + frame, self._near(frame), 100.0, 104.0, 5000.0, inverse + ): + points = optimizer._stress(item["position"], 100.0, item["elapsed"]) + losses = payoff( + item["position"], points, item["elapsed"], inverse, 100.0, item["cost"] + ) + + if inverse: + losses = [ + loss / price * 100.0 + for loss, price in zip(losses, points, strict=True) + ] + + assert min(losses) == pytest.approx(-5000.0), item["strategy"] + + def test_a_credit_is_sized_by_what_it_risks(self): + """A position taken in for a credit is ranked, not dropped.""" + sold = {"strategy": "Short Call", "legs": [_leg("c", 100, -1, 1, 5.0)]} + scored = optimizer.score(sold, 100.0, 90.0, False) + + assert scored["cost"] < 0 + assert scored["expected"] > 0 + + def test_a_position_with_nothing_at_risk_is_not_ranked(self): + """A position that cannot lose has no capital at risk to size against.""" + free = {"strategy": "Long Call", "legs": [_leg("c", 100, 1, 1, 0.0)]} + + assert optimizer.score(free, 100.0, 120.0, False) is None + + def test_a_calendar_is_stressed_across_its_remaining_life(self): + """A leg still alive at the valuation date is sought across a grid.""" + legs = [_leg("c", 100, -1, 30), _leg("c", 100, 1, 60)] + + assert len(optimizer._stress(legs, 100.0, 30)) > optimizer.STRESS_STEPS + assert optimizer._stress(legs[:1], 100.0, 30) == [100.0 / 3, 100.0, 300.0] + + def test_the_budget_scales_the_result(self, chain_frame): + """Doubling the budget doubles the profit and the contracts.""" + frame = chain_frame() + one = optimizer.rank(frame, self._near(frame), 100.0, 104.0, 1000.0, True, 1) + two = optimizer.rank(frame, self._near(frame), 100.0, 104.0, 2000.0, True, 1) + + assert two[0]["expected_profit"] == pytest.approx(one[0]["expected_profit"] * 2) + assert two[0]["contracts"] == pytest.approx(one[0]["contracts"] * 2) + + def test_a_linear_chain_is_ranked_in_its_quote(self, chain_frame): + """A linear chain ranks without the coin conversion.""" + frame = chain_frame(inverse=False, spot=1.5, size=1000) + + assert optimizer.rank(frame, self._near(frame), 1.5, 2.0, 1000.0, False, 3) + + def test_strikes_far_from_the_money_are_left_out(self, chain_frame): + """A strike well outside the window is not offered.""" + frame = chain_frame() + built = optimizer.candidates(frame, self._near(frame), 100.0, (), True) + offered = {leg["strike"] for item in built for leg in item["legs"]} + + assert max(offered) <= 100.0 * (1 + optimizer.STRIKE_WINDOW) + + +class TestComboBooks: + """A position Deribit lists as a combo is priced from that combo's book.""" + + @staticmethod + def _book(frame, bid=0.4, ask=0.5): + """List a call spread on the far expiration of the synthetic chain.""" + far = chain.expirations(frame)[-1] + calls = frame[(frame["expiration"] == far) & (frame["option_type"] == "call")] + low, high = sorted( + calls["contract_symbol"], key=lambda name: float(name.split("-")[2]) + )[3:5] + + return far, { + "name": "TEST-CS-FAR", + "legs": [(low, 1), (high, -1)], + "bid": bid, + "ask": ask, + } + + def test_buying_pays_the_offer_and_selling_takes_the_bid(self, chain_frame): + """The combo is bought at its ask and sold at its bid, carried to USD.""" + frame = chain_frame() + far, book = self._book(frame) + built = optimizer.candidates(frame, far, 100.0, [book], True) + listed = { + item["strategy"]: item for item in built if item["combo"] == "TEST-CS-FAR" + } + + assert listed["Bull Call Spread"]["premium"] == pytest.approx(50.0) + assert listed["Bear Call Spread"]["premium"] == pytest.approx(-40.0) + + def test_a_linear_combo_is_not_carried(self): + """A combo quoted in the quote currency is taken as quoted.""" + book = {"name": "X", "legs": [("A", 1), ("B", -1)], "bid": 0.4, "ask": 0.5} + + assert optimizer.combo_book([book], 100.0, False)[ + frozenset([("A", 1), ("B", -1)]) + ] == ("X", 0.5) + + def test_an_unquoted_side_prices_nothing(self): + """A combo with no bid or offer leaves that side to its legs.""" + book = {"name": "X", "legs": [("A", 1), ("B", -1)], "bid": None, "ask": None} + + assert optimizer.combo_book([book], 100.0, True) == {} + + def test_a_listed_combo_outside_the_window_is_still_offered(self, chain_frame): + """A combo someone listed is ranked even where the builder does not reach.""" + frame = chain_frame() + far, book = self._book(frame) + built = optimizer.candidates(frame, far, 1000.0, [book], True) + + assert {item["combo"] for item in built} == {"TEST-CS-FAR"} + + def test_a_combo_on_another_expiration_is_left_out(self, chain_frame): + """Only combos held to the expiration being ranked are offered.""" + frame = chain_frame() + _far, book = self._book(frame) + built = optimizer.candidates(frame, self._near(frame), 1000.0, [book], True) + + assert not any(item["combo"] for item in built) + + def test_a_combo_on_an_unquoted_contract_is_left_out(self, chain_frame): + """A combo naming a contract nobody quotes cannot be priced leg by leg.""" + frame = chain_frame() + far, book = self._book(frame) + book["legs"] = [("NOPE-1-1-C", 1), *book["legs"][1:]] + built = optimizer.candidates(frame, far, 1000.0, [book], True) + + assert built == [] + + @staticmethod + def _near(frame): + """Return the nearer of the two expirations.""" + return chain.expirations(frame)[0] + + +class TestRebuild: + """A description of legs reads back as the position it names.""" + + def test_counts_are_read(self, chain_frame): + """'Sell 2 SYMBOL' sells two contracts.""" + frame = chain_frame() + quotes = chain.quotes_at(frame) + names = sorted( + quotes[(quotes["dte"] == 30) & (quotes["option_type"] == "call")][ + "contract_symbol" + ], + key=lambda name: float(name.split("-")[2]), + ) + built = optimizer.rebuild(f"Buy {names[3]} / Sell 2 {names[5]}", quotes) + + assert built["code"] == "CSR12" + assert [leg["quantity"] for leg in built["legs"]] == [1, -2] + assert optimizer._describe(built["legs"]) == ( + f"Buy {names[3]} / Sell 2 {names[5]}" + ) + + @pytest.mark.parametrize( + "written", ["garbage", "Buy", "Hold X", "Buy two X", "Sell 0 X", "Buy 1 2 X"] + ) + def test_something_that_is_not_a_leg_says_so(self, chain_frame, written): + """Text that names no leg is refused with what the format is.""" + with pytest.raises(ValueError, match="Buy SYMBOL / Sell SYMBOL"): + optimizer.rebuild(written, chain.quotes_at(chain_frame())) + + def test_an_unlisted_contract_says_so(self, chain_frame): + """A contract the chain does not list cannot be priced.""" + with pytest.raises(ValueError, match="not a listed contract"): + optimizer.rebuild("Buy NOPE-1-1-C", chain.quotes_at(chain_frame())) + + def test_an_unrecognised_shape_is_custom(self, chain_frame): + """Contracts that make no combo are still priced, as a custom position.""" + quotes = chain.quotes_at(chain_frame()) + calls = quotes[(quotes["dte"] == 30) & (quotes["option_type"] == "call")] + low, high = list(calls["contract_symbol"])[:2] + built = optimizer.rebuild(f"Buy {low} / Buy {high}", quotes) + + assert (built["strategy"], built["code"]) == ("Custom", None) + + +class TestStrategies: + """The standard structures are priced at every expiration.""" + + def test_straddles(self, chain_frame): + """A straddle costs both legs and breaks even either side of them.""" + frame = chain_frame() + priced = strategies.straddles(frame, 100.0) + + assert len(priced) == 2 + + for row in priced: + assert row["cost"] == pytest.approx( + row["call_premium"] + row["put_premium"] + ) + assert row["breakeven_lower"] < row["strike"] < row["breakeven_upper"] + assert row["max_loss"] == -row["cost"] + + def test_strangles_straddle_the_money(self, chain_frame): + """A strangle's call sits above its put.""" + frame = chain_frame() + + for row in strategies.strangles(frame, 100.0, 0.04): + assert row["put_strike"] < row["call_strike"] + assert row["cost"] > 0 + + def test_verticals_are_capped_both_ways(self, chain_frame): + """A vertical's profit and loss are both bounded.""" + frame = chain_frame() + priced = strategies.verticals(frame, 100.0, 0.04) + + assert priced + + for row in priced: + assert row["max_profit"] > 0 + assert row["max_loss"] == -row["cost"] + assert row["strategy"] in ("Bull Call Spread", "Bear Put Spread") + + def test_a_chain_with_no_pair_prices_nothing(self, chain_frame): + """An expiration missing one side prices no two-legged structure.""" + frame = chain_frame() + frame = frame[frame["option_type"] == "call"] + + assert strategies.straddles(frame, 100.0) == [] + assert strategies.strangles(frame, 100.0, 0.04) == [] + assert all( + row["strategy"] == "Bull Call Spread" + for row in strategies.verticals(frame, 100.0, 0.04) + ) + + def test_a_straddle_needs_both_legs_on_one_strike(self, chain_frame): + """Sides that share no strike make no straddle.""" + frame = chain_frame() + frame = frame[ + ((frame["option_type"] == "call") & (frame["strike"] < 100.0)) + | ((frame["option_type"] == "put") & (frame["strike"] > 100.0)) + ] + + assert strategies.straddles(frame, 100.0) == [] + + def test_a_strangle_needs_its_legs_apart(self, chain_frame): + """Both legs landing on one strike makes no strangle.""" + frame = chain_frame() + frame = frame[frame["strike"] == 100.0] + + assert strategies.strangles(frame, 100.0, 0.04) == [] + + def test_a_zero_width_spread_is_skipped(self, chain_frame): + """A spread whose legs share a strike is not priced.""" + frame = chain_frame() + + assert strategies.verticals(frame, 100.0, 0.0) == [] + + +class TestPayoffWindow: + """A payoff is drawn over the prices that matter to the position. + + Drawn over a fixed span either side of spot, the strikes and the profitable + side collapse into a sliver and most of the width shows a flat line. + """ + + @staticmethod + def _drawn(chain_frame, target, inverse=True, spot=100.0): + """Rank one strategy and return the range its payoff was drawn over.""" + frame = chain_frame(inverse=inverse, spot=spot) + item = optimizer.rank( + frame, chain.expirations(frame)[0], spot, target, 5000.0, inverse, 1 + )[0] + + return item, min(item["prices"]), max(item["prices"]) + + def test_the_window_holds_the_position(self, chain_frame): + """Every strike and every breakeven is inside the drawn range.""" + item, low, high = self._drawn(chain_frame, 120.0) + strikes = [leg["strike"] for leg in item["position"]] + + for price in [*strikes, *item["breakevens"]]: + assert low <= price <= high, f"{price} falls outside {low}-{high}" + + @staticmethod + def _spread(): + """Return a capped position: one call bought, a higher one sold.""" + return [ + { + "strike": strike, + "option_type": "call", + "quantity": quantity, + "size": 1, + "price": price, + "dte": 0, + "implied_volatility": None, + } + for strike, quantity, price in ((100.0, 1, 5.0), (110.0, -1, 1.0)) + ] + + def test_a_distant_view_does_not_stretch_a_capped_position(self): + """A target far past where a position stops paying is not drawn to. + + A capped structure earns nothing more above its own ceiling, so drawing + out to a remote target would add only a straight line. + """ + _low, high = optimizer._window(self._spread(), 100.0, 1000.0, 0, False) + + assert high < 200.0 + + def test_a_position_that_never_pays_is_drawn_around_spot(self): + """A payout flat across the whole scan has nowhere in particular to look.""" + unreachable = [ + { + "strike": 1e6, + "option_type": "call", + "quantity": 1, + "size": 1, + "price": 1.0, + "dte": 0, + "implied_volatility": None, + } + ] + low, high = optimizer._window(unreachable, 100.0, 100.0, 0, False) + + assert low < 100.0 < high + assert high - low == pytest.approx(2 * 100.0 * optimizer.MIN_PAD) + + def test_an_uncapped_view_is_drawn_out_to_the_target(self): + """A position that keeps paying has no ceiling, so the view is the edge.""" + bought = [self._spread()[0]] + _low, high = optimizer._window(bought, 100.0, 200.0, 0, False) + + assert high >= 200.0 + + def test_the_window_is_far_tighter_than_the_scan(self, chain_frame): + """The drawn range is a fraction of the range that was searched.""" + _item, low, high = self._drawn(chain_frame, 120.0) + searched = 100.0 * optimizer.PROBE_SPREAD * 2 + + assert high - low < searched / 2 + + def test_a_far_crossing_does_not_widen_the_window(self, chain_frame): + """An inverse position crosses zero far above its strikes. + + That crossing is real but it is nowhere near the trade, so letting it + set the range would flatten everything worth looking at. + """ + from openbb_deribit.utils.options.legs import breakevens, payoff + from openbb_deribit.utils.options.optimizer import _grid, _window + + def leg(strike, quantity, price): + return { + "strike": strike, + "option_type": "call", + "quantity": quantity, + "size": 1, + "price": price, + "dte": 0, + "implied_volatility": None, + } + + legs = [leg(100.0, 1, 8.0), leg(110.0, -1, 1.34)] + low, high = _window(legs, 100.0, 100.0, 0, True) + scanned = _grid( + 100.0 * (1 - optimizer.PROBE_SPREAD), + 100.0 * (1 + optimizer.PROBE_SPREAD), + optimizer.SCOUT_STEPS, + ) + crossings = breakevens(scanned, payoff(legs, scanned, 0, True, 100.0)) + + assert len(crossings) > 1, "this position should cross zero twice" + assert high < max(crossings), "the far crossing set the range" + assert low <= 100.0 <= high + + def test_the_window_never_starts_below_nothing(self, chain_frame): + """A position around a low price is not drawn at a negative one.""" + _item, low, _high = self._drawn(chain_frame, 1.5, inverse=False, spot=1.5) + + assert low >= 0.0 + + def test_the_profitable_side_is_a_real_share_of_the_width(self, chain_frame): + """The reader can see where the position makes money.""" + item, _low, _high = self._drawn(chain_frame, 120.0) + profitable = sum(1 for value in item["payoff"] if value > 0) + + assert profitable / len(item["payoff"]) > 0.1 diff --git a/openbb_platform/providers/deribit/tests/test_options_chains.py b/openbb_platform/providers/deribit/tests/test_options_chains.py new file mode 100644 index 00000000000..993d0ba8761 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_options_chains.py @@ -0,0 +1,193 @@ +"""Tests for the Deribit options chain.""" + +import pytest +from openbb_core.app.model.abstract.error import OpenBBError +from openbb_core.provider.utils.errors import EmptyDataError + +from openbb_deribit.models.options_chains import DeribitOptionsChainsFetcher + + +@pytest.fixture +def chain(monkeypatch, responder, load): + """Answer the instrument listing and the subscription from fixtures.""" + + def install(root, messages=(), tickers=None): + responder({"get_instruments": load("instruments_option")}) + stored = load("option_tickers") if tickers is None else tickers + wanted = { + name: ticker + for name, ticker in stored.items() + if name.startswith(f"{root}-") + } + + async def _subscribe(symbols, collected): + collected.update(messages) + + return {name: wanted[name] for name in symbols if name in wanted} + + monkeypatch.setattr( + "openbb_deribit.utils.websocket.subscribe_tickers", _subscribe + ) + + return wanted + + return install + + +class TestChain: + """A chain carries one row per contract, with greeks.""" + + @pytest.mark.asyncio + async def test_rows(self, chain): + """Each contract quoted lands on the chain, sorted.""" + quoted = chain("XRP_USDC") + result = await DeribitOptionsChainsFetcher.fetch_data( + {"symbol": "XRP_USDC"}, {} + ) + rows = result.model_dump() + + assert len(rows) == len(quoted) + assert [ + (row["expiration"], row["strike"], row["option_type"]) for row in rows + ] == sorted( + (row["expiration"], row["strike"], row["option_type"]) for row in rows + ) + assert all(row["delta"] is not None for row in rows) + + @pytest.mark.asyncio + async def test_percentages_stay_in_percent_units(self, chain): + """A volatility is kept as the exchange publishes it.""" + chain("XRP_USDC") + result = await DeribitOptionsChainsFetcher.fetch_data( + {"symbol": "XRP_USDC"}, {} + ) + quoted = [ + row["implied_volatility"] + for row in result.model_dump() + if row["implied_volatility"] + ] + + assert quoted + assert all(value > 1 for value in quoted) + + @pytest.mark.asyncio + async def test_linear_prices_are_left_alone(self, chain): + """A USDC-quoted contract keeps the price the exchange published.""" + quoted = chain("XRP_USDC") + result = await DeribitOptionsChainsFetcher.fetch_data( + {"symbol": "XRP_USDC"}, {} + ) + rows = {row["contract_symbol"]: row for row in result.model_dump()} + name = next(iter(quoted)) + + assert rows[name]["mark"] == quoted[name]["mark_price"] + + @pytest.mark.asyncio + async def test_inverse_prices_are_carried_to_usd(self, chain): + """A BTC-quoted contract's premium is carried at the index level.""" + quoted = chain("BTC") + result = await DeribitOptionsChainsFetcher.fetch_data({"symbol": "BTC"}, {}) + rows = {row["contract_symbol"]: row for row in result.model_dump()} + name = next(iter(quoted)) + published = quoted[name] + + assert rows[name]["mark"] == ( + published["mark_price"] * published["index_price"] + ) + + @pytest.mark.asyncio + async def test_no_rounding(self, chain): + """The carried premium keeps every digit the arithmetic produced.""" + quoted = chain("BTC") + result = await DeribitOptionsChainsFetcher.fetch_data({"symbol": "BTC"}, {}) + rows = {row["contract_symbol"]: row for row in result.model_dump()} + carried = [ + rows[name]["mark"] for name in quoted if rows[name]["mark"] is not None + ] + + assert any(round(value, 2) != value for value in carried) + + @pytest.mark.asyncio + async def test_decimal_strikes(self, chain): + """A strike the exchange spells with a ``d`` reads as a decimal.""" + chain("XRP_USDC") + result = await DeribitOptionsChainsFetcher.fetch_data( + {"symbol": "XRP_USDC"}, {} + ) + + assert any(row["strike"] % 1 for row in result.model_dump()) + + @pytest.mark.asyncio + async def test_warns_but_returns(self, chain): + """A partial subscription warns and returns what did arrive.""" + chain("XRP_USDC", messages=("half the chain timed out",)) + + with pytest.warns(UserWarning, match="timed out"): + result = await DeribitOptionsChainsFetcher.fetch_data( + {"symbol": "XRP_USDC"}, {} + ) + + assert result.model_dump() + + @pytest.mark.asyncio + async def test_raises_when_everything_failed(self, chain): + """A subscription that returned nothing at all raises its reason.""" + chain("XRP_USDC", messages=("the subscription failed",), tickers={}) + + with pytest.raises(OpenBBError, match="subscription failed"): + await DeribitOptionsChainsFetcher.fetch_data({"symbol": "XRP_USDC"}, {}) + + @pytest.mark.asyncio + async def test_empty_without_a_reason(self, chain): + """A silent empty subscription is an empty-data error.""" + chain("XRP_USDC", tickers={}) + + with pytest.raises(EmptyDataError): + await DeribitOptionsChainsFetcher.fetch_data({"symbol": "XRP_USDC"}, {}) + + @pytest.mark.asyncio + async def test_a_failed_expiration_is_skipped(self, monkeypatch, responder, load): + """An expiration whose subscription raised is left out, not fatal.""" + responder({"get_instruments": load("instruments_option")}) + stored = load("option_tickers") + calls: list = [] + + async def _subscribe(symbols, collected): + calls.append(symbols) + + if not any(name in stored for name in symbols): + raise RuntimeError("the socket went away") + + return {name: stored[name] for name in symbols if name in stored} + + monkeypatch.setattr( + "openbb_deribit.utils.websocket.subscribe_tickers", _subscribe + ) + result = await DeribitOptionsChainsFetcher.fetch_data( + {"symbol": "XRP_USDC"}, {} + ) + + assert result.model_dump() + + @pytest.mark.asyncio + async def test_unlisted_underlying(self, responder, load): + """An underlying with no options names the ones that have them.""" + responder({"get_instruments": load("instruments_option")}) + + with pytest.raises(OpenBBError, match="XRP_USDC"): + await DeribitOptionsChainsFetcher.fetch_data({"symbol": "DOGE"}, {}) + + @pytest.mark.asyncio + async def test_index_underlying_is_named(self, chain, load): + """A contract priced off the index names the index, not a placeholder.""" + stored = load("option_tickers") + + for ticker in stored.values(): + ticker["underlying_index"] = "index_price" + + chain("XRP_USDC", tickers=stored) + result = await DeribitOptionsChainsFetcher.fetch_data( + {"symbol": "XRP_USDC"}, {} + ) + + assert {row["underlying_symbol"] for row in result.model_dump()} == {"XRP-USDC"} diff --git a/openbb_platform/providers/deribit/tests/test_options_charts.py b/openbb_platform/providers/deribit/tests/test_options_charts.py new file mode 100644 index 00000000000..b537b4828e0 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_options_charts.py @@ -0,0 +1,378 @@ +"""Tests for the Deribit options chart builders and the chain they draw from.""" + +import pytest +from openbb_core.app.model.abstract.error import OpenBBError + +from openbb_deribit.utils.options import data_handler +from openbb_deribit.utils.options.create_smile import ( + _first_priced_expiration, + create_smile, +) +from openbb_deribit.utils.options.create_stats import create_stats +from openbb_deribit.utils.options.create_surface import create_surface +from openbb_deribit.utils.options.create_term_structure import create_term_structure + + +class TestDataHandler: + """The REST chain becomes the core model the charts are drawn from.""" + + def test_volatility_is_a_decimal(self, chain_frame, options_chain): + """Deribit quotes volatility in percent; the model carries a decimal.""" + frame = chain_frame() + + assert options_chain.implied_volatility == pytest.approx( + list( + frame.sort_values(["expiration", "strike", "option_type"])[ + "implied_volatility" + ] + / 100 + ) + ) + + def test_every_contract_names_its_underlying(self, options_chain): + """The underlying is the root the chain was loaded for.""" + assert set(options_chain.underlying_symbol) == {"BTC"} + + def test_the_forward_is_the_underlying_price(self, chain_frame): + """Each contract is priced against the forward of its expiration.""" + frame = chain_frame() + frame["underlying_price"] = 101.0 + + assert set(data_handler.to_chain(frame, "BTC").underlying_price) == {101.0} + + def test_a_missing_forward_falls_back_to_spot(self, chain_frame): + """A contract with no forward is priced against the index.""" + frame = chain_frame().drop(columns=["underlying_price"]) + + assert set(data_handler.to_chain(frame, "BTC").underlying_price) == {100.0} + + def test_an_untraded_contract_has_no_last_price(self, chain_frame): + """A contract that never traded carries no last price.""" + frame = chain_frame() + frame["last"] = None + chain = data_handler.to_chain(frame, "BTC") + + assert set(chain.last_trade_price) == {None} + + @pytest.mark.asyncio + async def test_load_symbol_reads_the_chain(self, loaded_chain): + """The chain is read through the shared REST loader.""" + loaded_chain() + chain = await data_handler.load_symbol("btc") + + assert set(chain.underlying_symbol) == {"BTC"} + assert len(chain.expirations) == 2 + + @pytest.mark.asyncio + async def test_load_symbol_wraps_fetch_errors(self, monkeypatch): + """An empty chain surfaces as a symbol-specific OpenBBError.""" + from openbb_core.provider.utils.errors import EmptyDataError + + async def _empty(symbol, use_cache=True): + raise EmptyDataError("nothing listed") + + monkeypatch.setattr("openbb_deribit.utils.options.chain.load_chain", _empty) + + with pytest.raises(OpenBBError, match="No options available for NOPE"): + await data_handler.load_symbol("nope") + + def test_strike_choices(self, options_chain): + """Strikes are offered after the nearest out-of-the-money default.""" + strikes = data_handler.get_strikes(options_chain) + + assert strikes[0] == {"label": "Nearest OTM", "value": None} + assert [choice["value"] for choice in strikes[1:]] == options_chain.strikes + assert strikes[1]["label"] == "$92" + assert "Underlying" in strikes[1]["extraInfo"]["rightOfDescription"] + + def test_a_fractional_strike_is_labelled_intact(self, chain_frame): + """A strike below one keeps its decimals in the label.""" + chain = data_handler.to_chain(chain_frame(spot=1.05), "XRP_USDC") + labels = [choice["label"] for choice in data_handler.get_strikes(chain)] + + assert "$1.05" in labels + + def test_no_underlying_price_describes_nothing(self): + """Without an underlying price a strike carries no description.""" + stub = type("Stub", (), {"underlying_price": [], "strikes": [100.0]})() + + assert data_handler.get_strikes(stub)[1]["extraInfo"] == {} + + +class TestChainExpirations: + """Expirations offered must be expirations that resolve to rows.""" + + def test_the_frame_names_the_expirations(self, options_chain): + """Every expiration offered has contracts in the frame.""" + offered = data_handler.chain_expirations(options_chain) + + assert offered == sorted( + {str(v) for v in options_chain.dataframe["expiration"]} + ) + + def test_falls_back_when_there_is_no_frame(self): + """A chain whose frame cannot be built still reports its own expirations.""" + + class _NoFrame: + expirations = ["2030-01-18"] + + @property + def dataframe(self): + raise ValueError("no validated data") + + assert data_handler.chain_expirations(_NoFrame()) == ["2030-01-18"] + + def test_falls_back_when_the_frame_has_no_expirations(self): + """A frame without an expiration column defers to the model's list.""" + from pandas import DataFrame + + stub = type( + "Stub", + (), + {"expirations": ["2030-01-18"], "dataframe": DataFrame({"strike": [1.0]})}, + )() + + assert data_handler.chain_expirations(stub) == ["2030-01-18"] + + +class TestSmile: + """Implied-volatility smile builder.""" + + def test_default_expiration_skips_zero_iv(self, options_chain): + """The default expiration is the front one that still carries IV.""" + assert _first_priced_expiration(options_chain) in options_chain.expirations + + def test_no_priced_expiration_falls_back(self, options_chain): + """With no priced contracts the front expiration is used.""" + stub = type( + "Stub", + (), + { + "dataframe": options_chain.dataframe.assign(implied_volatility=0.0), + "expirations": options_chain.expirations, + }, + )() + + assert ( + _first_priced_expiration(stub) + == data_handler.chain_expirations(options_chain)[0] + ) + + def test_builds_chart(self, options_chain): + """The default smile renders two traces and returns the plotted rows.""" + output = create_smile(options_chain) + + assert output.chart.format == "plotly" + assert [trace["name"] for trace in output.chart.content["data"]] == [ + "Calls", + "Puts", + ] + assert output.results + + def test_otm_and_skew(self, options_chain): + """The OTM and skew variants both render.""" + output = create_smile(options_chain, otm=True, skew=True, theme="light") + + assert len(output.chart.content["data"]) == 2 + assert "OTM IV Skew" in output.chart.content["layout"]["title"]["text"] + + def test_comma_separated_expirations(self, options_chain): + """A comma-separated list plots one pair of traces per date.""" + output = create_smile( + options_chain, expirations=",".join(options_chain.expirations) + ) + + assert len(output.chart.content["data"]) == 2 * len(options_chain.expirations) + + def test_list_of_expirations(self, options_chain): + """A list of expirations plots one pair of traces per date.""" + output = create_smile(options_chain, expirations=options_chain.expirations) + + assert len(output.chart.content["data"]) == 2 * len(options_chain.expirations) + + def test_too_many_expirations(self, options_chain): + """More than five expirations is rejected.""" + with pytest.raises(OpenBBError, match="Too many dates"): + create_smile(options_chain, expirations=options_chain.expirations * 3) + + def test_requires_iv(self): + """A chain without implied volatility is rejected.""" + stub = type("Stub", (), {"has_iv": False})() + + with pytest.raises(OpenBBError, match="Implied Volatility"): + create_smile(stub) + + +class TestStats: + """Open interest and volume statistics builder.""" + + @pytest.mark.parametrize( + "unit,traces", + [ + ("value", ["Calls", "Puts"]), + ("percent", ["% of Total"]), + ("pcr", ["Put/Call Ratio"]), + ], + ) + def test_units(self, options_chain, unit, traces): + """Every unit mode renders its own bars.""" + output = create_stats(options_chain, unit=unit) + + assert [trace["name"] for trace in output.chart.content["data"]] == traces + + def test_puts_are_drawn_below_the_axis(self, options_chain): + """Puts hang below zero while the returned rows stay positive.""" + output = create_stats(options_chain) + puts = output.chart.fig.data[1] + + assert all(value <= 0 for value in puts.y) + assert all(row.Puts >= 0 for row in output.results) + + def test_by_strike_for_one_expiration(self, options_chain): + """Passing a date switches the axis to strikes.""" + output = create_stats( + options_chain, + by="strike", + metric="volume", + date=options_chain.expirations[1], + theme="light", + ) + + assert output.chart.content["layout"]["title"]["text"] == ( + "BTC Volume By Strike" + ) + assert output.chart.content["layout"]["xaxis"]["tickprefix"] == "$" + + +class TestSurface: + """3-D surface builder.""" + + @pytest.mark.parametrize("option_type", ["otm", "itm", "calls", "puts"]) + def test_option_types(self, options_chain, option_type): + """Every side of the chain renders a surface.""" + output = create_surface(options_chain, option_type=option_type) + + assert output.chart.content["data"] + + @pytest.mark.parametrize("metric", ["delta", "gamma", "theta", "vega", "rho"]) + def test_greeks(self, options_chain, metric): + """The modelled greeks are plottable as the Z axis.""" + output = create_surface(options_chain, metric=metric) + + assert output.chart.content["data"] + + @pytest.mark.parametrize("metric", ["dex", "gex"]) + def test_exposures(self, options_chain, metric): + """The upper-cased DEX/GEX columns resolve from lowercase metric names.""" + output = create_surface(options_chain, metric=metric, theme="light") + + assert output.chart.content["data"] + + def test_filters(self, options_chain): + """DTE, moneyness, open-interest, and volume filters all apply.""" + output = create_surface( + options_chain, + dte_range=[0, 400], + moneyness=25, + oi=True, + volume=True, + ) + title = output.chart.content["layout"]["title"]["text"] + + assert "With Open Interest" in title + assert "Excluding Untraded Contracts" in title + + def test_unknown_metric(self, options_chain): + """An absent metric is rejected.""" + with pytest.raises(OpenBBError, match="No nope data"): + create_surface(options_chain, metric="nope") + + +class TestTermStructure: + """Term-structure builder.""" + + def test_default(self, options_chain): + """The default plots calls and puts at the nearest OTM strikes.""" + output = create_term_structure(options_chain) + + assert [trace["name"] for trace in output.chart.content["data"]] == [ + "Calls", + "Puts", + ] + + def test_by_strike(self, options_chain): + """A fixed strike is honored.""" + strike = options_chain.strikes[len(options_chain.strikes) // 2] + output = create_term_structure( + options_chain, strike=strike, metric="price", option_type="calls" + ) + + assert len(output.chart.content["data"]) == 1 + assert f"${strike}" in output.chart.content["layout"]["title"]["text"] + + def test_by_moneyness(self, options_chain): + """A moneyness target is honored.""" + output = create_term_structure( + options_chain, moneyness=4, option_type="puts", theme="light" + ) + + assert len(output.chart.content["data"]) == 1 + + def test_untraded_contracts_are_not_drawn(self, chain_frame): + """Only contracts with a last trade are read, as on Cboe.""" + frame = chain_frame() + frame["last"] = None + output = create_term_structure(data_handler.to_chain(frame, "BTC")) + + assert output.chart.content["data"] == [] + + def test_requires_iv(self): + """Requesting IV on a chain without it is rejected.""" + stub = type("Stub", (), {"has_iv": False})() + + with pytest.raises(OpenBBError, match="No implied volatility"): + create_term_structure(stub, metric="iv") + + +class TestOptionalCharting: + """Without the charting extension a view returns the rows it would draw.""" + + @pytest.fixture + def unplotted(self, monkeypatch): + """Run as though openbb-charting were not installed.""" + import openbb_deribit + + monkeypatch.setattr(openbb_deribit, "CHARTING_INSTALLED", False) + + @pytest.mark.parametrize( + "builder", + [create_smile, create_stats, create_surface, create_term_structure], + ids=lambda builder: builder.__name__, + ) + def test_every_view_returns_its_rows(self, unplotted, options_chain, builder): + """Each chart still answers with the rows behind it.""" + output = builder(options_chain, theme="dark") + + assert output.chart is None + assert output.results + + +class TestSupportedLayout: + """The chart template is trimmed to what the installed Plotly accepts.""" + + def test_unknown_keys_are_dropped(self): + """A key Plotly no longer knows is removed rather than raising.""" + from openbb_deribit.utils.options.theme import supported_layout + + assert supported_layout({"title": "x", "no_such_key": 1}) == {"title": "x"} + + def test_a_plotly_without_a_key_list_keeps_everything(self, monkeypatch): + """Where Plotly publishes no key list the layout passes through.""" + from plotly.graph_objects import Layout + + from openbb_deribit.utils.options.theme import supported_layout + + monkeypatch.setattr(Layout, "_valid_props", set()) + layout = {"title": "x", "no_such_key": 1} + + assert supported_layout(layout) is layout diff --git a/openbb_platform/providers/deribit/tests/test_options_views.py b/openbb_platform/providers/deribit/tests/test_options_views.py new file mode 100644 index 00000000000..3fe6c16d2f8 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_options_views.py @@ -0,0 +1,971 @@ +"""Tests for the Deribit options charts and the commands that serve them.""" + +import pytest + +from openbb_deribit.routers import options as router +from openbb_deribit.utils.options import chain + + +class TestLoading: + """A chain is read over REST, once, and shared while it is current.""" + + @pytest.fixture(autouse=True) + def _clean(self): + """Start each test with nothing held.""" + chain._LOADED.clear() + chain._LOCKS.clear() + + yield + + chain._LOADED.clear() + chain._LOCKS.clear() + + @pytest.fixture + def exchange(self, responder): + """Publish one synthetic underlying over the three REST calls.""" + from datetime import datetime, timedelta, timezone + + from openbb_deribit.utils.helpers import to_timestamp + + def install(root="BTC", inverse=True, size=1.0, strikes=(0.31, 100.0)): + now = datetime.now(timezone.utc) + expiry = to_timestamp(now + timedelta(days=30)) + names = [ + f"{root}-30OCT26-{strike:g}-{side}" + for strike in strikes + for side in ("C", "P") + ] + specs = [ + { + "instrument_name": name, + "strike": strike, + "option_type": "call" if name.endswith("C") else "put", + "contract_size": size, + "expiration_timestamp": expiry, + "instrument_type": "reversed" if inverse else "linear", + "price_index": f"{root.lower()}_usd", + } + for name, strike in zip( + names, [s for s in strikes for _ in range(2)], strict=True + ) + ] + quotes = [ + { + "instrument_name": spec["instrument_name"], + "mark_price": 0.01, + "bid_price": 0.009, + "ask_price": 0.011, + "last": 0.01, + "mark_iv": 50.0, + "open_interest": 10.0, + "volume": 5.0, + "volume_usd": 100.0, + "underlying_price": 100.0, + } + for spec in specs + ] + calls: list = [] + + def summary(params): + calls.append(("summary", params["currency"])) + + return quotes + + def instruments(params): + calls.append(("instruments", params.get("currency"))) + + return specs + + def index(params): + calls.append(("index", params["index_name"])) + + return {"index_price": 100.0} + + responder( + { + "get_book_summary_by_currency": summary, + "get_instruments": instruments, + "get_index_price": index, + } + ) + + return calls + + return install + + @pytest.mark.asyncio + async def test_reads_the_exchange_once(self, exchange): + """One read makes one call to each of the three feeds.""" + calls = exchange() + frame = await chain.load_chain("btc") + + assert len(frame) == 4 + assert [name for name, _ in calls] == ["summary", "instruments", "index"] + + @pytest.mark.asyncio + async def test_a_second_read_reuses_the_first(self, exchange): + """Reading the same chain twice queries the exchange once.""" + calls = exchange() + first = await chain.load_chain("BTC") + second = await chain.load_chain("BTC") + + assert len(calls) == 3 + assert first.equals(second) + + @pytest.mark.asyncio + async def test_the_caller_cannot_alter_what_is_held(self, exchange): + """A frame handed out is a copy, so editing it changes nothing.""" + exchange() + first = await chain.load_chain("BTC") + first["strike"] = 0.0 + + assert (await chain.load_chain("BTC"))["strike"].max() > 0 + + @pytest.mark.asyncio + async def test_a_fresh_read_queries_again(self, exchange): + """Asking for current data goes back to the exchange.""" + calls = exchange() + await chain.load_chain("BTC") + await chain.load_chain("BTC", use_cache=False) + + assert len(calls) == 6 + + @pytest.mark.asyncio + async def test_a_stale_read_is_not_reused(self, exchange, monkeypatch): + """A read older than its lifetime is taken again.""" + calls = exchange() + monkeypatch.setattr(chain, "CHAIN_TTL", 0.0) + await chain.load_chain("BTC") + await chain.load_chain("BTC") + + assert len(calls) == 6 + + @pytest.mark.asyncio + async def test_a_fractional_strike_survives(self, exchange): + """A strike the instrument name spells with a letter is read intact.""" + exchange(root="TRX_USDC", inverse=False, size=10000.0, strikes=(0.31,)) + frame = await chain.load_chain("TRX_USDC") + + assert sorted(frame["strike"].unique()) == [0.31] + assert sorted(frame["contract_size"].unique()) == [10000.0] + + @pytest.mark.asyncio + async def test_an_inverse_premium_is_carried_to_the_quote(self, exchange): + """A coin-quoted premium is carried at the index level.""" + exchange(inverse=True) + frame = await chain.load_chain("BTC") + + assert frame["mark"].iloc[0] == pytest.approx(0.01 * 100.0) + assert chain.is_inverse(frame) is True + + @pytest.mark.asyncio + async def test_a_linear_premium_is_left_alone(self, exchange): + """A premium already in the quote currency is not carried.""" + exchange(root="SOL_USDC", inverse=False, size=10.0) + frame = await chain.load_chain("SOL_USDC") + + assert frame["mark"].iloc[0] == pytest.approx(0.01) + assert chain.is_inverse(frame) is False + + @pytest.mark.asyncio + async def test_greeks_are_modelled_from_the_quoted_volatility(self, exchange): + """The exchange publishes no greeks here, so they are computed.""" + exchange() + frame = await chain.load_chain("BTC") + + for greek in ("delta", "gamma", "theta", "vega", "rho"): + assert frame[greek].notna().all() + + calls = frame[frame["option_type"] == "call"] + puts = frame[frame["option_type"] == "put"] + + assert (calls["delta"] >= 0).all() + assert (puts["delta"] <= 0).all() + assert (frame["gamma"] >= 0).all() + + money = frame[frame["strike"] == 100.0] + + assert float(money[money["option_type"] == "call"]["delta"].iloc[0]) > 0.4 + assert float(money[money["option_type"] == "put"]["delta"].iloc[0]) < -0.4 + + @pytest.mark.asyncio + async def test_only_the_asked_for_root_is_kept(self, exchange, responder): + """One settlement currency serves several roots, so the root is matched.""" + from datetime import datetime, timedelta, timezone + + from openbb_deribit.utils.helpers import to_timestamp + + expiry = to_timestamp(datetime.now(timezone.utc) + timedelta(days=30)) + specs = [ + { + "instrument_name": f"{root}-30OCT26-100-C", + "strike": 100.0, + "option_type": "call", + "contract_size": 1.0, + "expiration_timestamp": expiry, + "instrument_type": "linear", + "price_index": f"{root.lower()}", + } + for root in ("BTC_USDC", "XRP_USDC") + ] + responder( + { + "get_instruments": specs, + "get_book_summary_by_currency": [ + { + "instrument_name": spec["instrument_name"], + "mark_price": 1.0, + "bid_price": 1.0, + "ask_price": 1.0, + "last": 1.0, + "mark_iv": 50.0, + "open_interest": 1.0, + "volume": 1.0, + "volume_usd": 1.0, + "underlying_price": 100.0, + } + for spec in specs + ], + "get_index_price": {"index_price": 100.0}, + } + ) + frame = await chain.load_chain("BTC_USDC") + + assert list(frame["contract_symbol"]) == ["BTC_USDC-30OCT26-100-C"] + + @pytest.mark.asyncio + async def test_an_unlisted_underlying_says_so(self, responder): + """An underlying nobody quotes raises rather than returning nothing.""" + from openbb_core.provider.utils.errors import EmptyDataError + + responder( + { + "get_instruments": [], + "get_book_summary_by_currency": [], + "get_index_price": {"index_price": 0.0}, + } + ) + + with pytest.raises(EmptyDataError, match="no option quotes for NOPE"): + await chain.load_chain("NOPE") + + +class TestComboLoading: + """The combo books listed on an underlying are read with their quotes.""" + + @pytest.fixture(autouse=True) + def _clean(self): + """Start each test with nothing held.""" + chain._LOADED.clear() + chain._LOCKS.clear() + + yield + + chain._LOADED.clear() + chain._LOCKS.clear() + + @pytest.fixture + def exchange(self, responder): + """List option, other-root, and future combos on BTC.""" + calls: list = [] + + def combos(params): + calls.append(("combos", params["currency"])) + + return [ + { + "id": "BTC-CS-30OCT26-100000_110000", + "legs": [ + {"instrument_name": "BTC-30OCT26-100000-C", "amount": 1}, + {"instrument_name": "BTC-30OCT26-110000-C", "amount": -1}, + ], + }, + { + "id": "BTC-STRD-30OCT26-100000", + "legs": [ + {"instrument_name": "BTC-30OCT26-100000-C", "amount": 1}, + {"instrument_name": "BTC-30OCT26-100000-P", "amount": 1}, + ], + }, + { + "id": "BTC-FS-30OCT26_PERP", + "legs": [ + {"instrument_name": "BTC-PERPETUAL", "amount": -1}, + {"instrument_name": "BTC-30OCT26", "amount": 1}, + ], + }, + { + "id": "ETH-CS-30OCT26-3000_3100", + "legs": [ + {"instrument_name": "ETH-30OCT26-3000-C", "amount": 1}, + {"instrument_name": "ETH-30OCT26-3100-C", "amount": -1}, + ], + }, + ] + + def quotes(params): + calls.append(("quotes", params["kind"])) + + return [ + { + "instrument_name": "BTC-CS-30OCT26-100000_110000", + "bid_price": 0.01, + "ask_price": 0.02, + } + ] + + responder({"get_combos": combos, "get_book_summary_by_currency": quotes}) + + return calls + + @pytest.mark.asyncio + async def test_only_the_underlyings_option_combos_are_kept(self, exchange): + """Future spreads and other roots are left out.""" + books = await chain.load_combos("btc") + + assert [book["name"] for book in books] == [ + "BTC-CS-30OCT26-100000_110000", + "BTC-STRD-30OCT26-100000", + ] + assert books[0]["legs"] == [ + ("BTC-30OCT26-100000-C", 1), + ("BTC-30OCT26-110000-C", -1), + ] + + @pytest.mark.asyncio + async def test_each_combo_carries_its_quote(self, exchange): + """A quoted combo has its bid and offer; an unquoted one has neither.""" + quoted, unquoted = await chain.load_combos("BTC") + + assert (quoted["bid"], quoted["ask"]) == (0.01, 0.02) + assert (unquoted["bid"], unquoted["ask"]) == (None, None) + + @pytest.mark.asyncio + async def test_the_settlement_currency_is_read(self, exchange): + """The combos of a coin-settled root are read in the coin.""" + await chain.load_combos("BTC") + + assert exchange == [("combos", "BTC"), ("quotes", "option_combo")] + + @pytest.mark.asyncio + async def test_a_second_read_reuses_the_first(self, exchange): + """Reading the combos twice queries the exchange once.""" + first = await chain.load_combos("BTC") + first.clear() + second = await chain.load_combos("BTC") + + assert len(exchange) == 2 + assert len(second) == 2 + + @pytest.mark.asyncio + async def test_a_fresh_read_queries_again(self, exchange): + """Asking for current combos goes back to the exchange.""" + await chain.load_combos("BTC") + await chain.load_combos("BTC", use_cache=False) + + assert len(exchange) == 4 + + +class TestSettlement: + """A root names the currency it settles in and the index that prices it.""" + + @pytest.mark.parametrize( + "root,currency", + [ + ("BTC", "BTC"), + ("ETH", "ETH"), + ("BTC_USDC", "USDC"), + ("XRP_USDC", "USDC"), + ("TRX_USDC", "USDC"), + ], + ) + def test_settlement_currency(self, root, currency): + """Coin-margined roots settle in the coin, the rest in USDC.""" + assert chain.settlement_of(root) == currency + + +class TestCharts: + """Every view draws, and every view still answers without a chart.""" + + @staticmethod + def _payoff(frame): + """Build the sized position a payoff is drawn from.""" + from openbb_deribit.utils.options.optimizer import rank + + expiration = chain.expirations(frame)[0] + + return rank(frame, expiration, 100.0, 120.0, 1.0, True, 1)[0] + + @pytest.fixture + def drawings(self, chain_frame): + """Return every view, already reduced to the rows it draws.""" + from openbb_deribit.utils.options.create_payoff import create_payoff + from openbb_deribit.utils.options.create_smile import create_smile + from openbb_deribit.utils.options.create_stats import create_stats + from openbb_deribit.utils.options.create_surface import create_surface + from openbb_deribit.utils.options.create_term_structure import ( + create_term_structure, + ) + from openbb_deribit.utils.options.data_handler import to_chain + + frame = chain_frame() + options_chain = to_chain(frame, "BTC") + + return { + "payoff": ( + create_payoff, + { + **self._payoff(frame), + "spot": 100.0, + "target": 120.0, + "settles": "BTC", + }, + ), + "smile": (create_smile, options_chain), + "term_structure": (create_term_structure, options_chain), + "surface": (create_surface, options_chain), + "stats": (create_stats, options_chain), + } + + @pytest.mark.parametrize( + "view", ["payoff", "smile", "term_structure", "surface", "stats"] + ) + def test_draws_a_figure_and_its_rows(self, drawings, view): + """A view returns both the picture and the numbers behind it.""" + build, data = drawings[view] + output = build(data, theme="light") + + assert output.results + assert output.chart is not None + assert output.chart.format == "plotly" + + @pytest.mark.parametrize( + "view", ["payoff", "smile", "term_structure", "surface", "stats"] + ) + def test_answers_without_the_charting_extension(self, drawings, view, monkeypatch): + """With nothing to draw with, a view still returns its rows.""" + import openbb_deribit + + monkeypatch.setattr(openbb_deribit, "CHARTING_INSTALLED", False) + build, data = drawings[view] + output = build(data, theme="dark") + + assert output.results + assert output.chart is None + + def test_a_credit_is_titled_as_one(self, drawings): + """A position opened for a credit says so rather than a negative cost.""" + from openbb_deribit.utils.options.create_payoff import create_payoff + + _build, data = drawings["payoff"] + output = create_payoff({**data, "cost": -250.0}, theme="dark") + + assert "for a 250 USD credit" in output.chart.fig.layout.title.text + + def test_a_listed_combo_is_named_in_the_title(self, drawings): + """A position traded as a listed combo names the combo.""" + from openbb_deribit.utils.options.create_payoff import create_payoff + + _build, data = drawings["payoff"] + output = create_payoff({**data, "combo": "BTC-CS-X"}, theme="dark") + + assert "BTC-CS-X" in output.chart.fig.layout.title.text + + def test_a_target_at_spot_is_not_marked_twice(self, drawings): + """A target the underlying is already at needs no second marker.""" + from openbb_deribit.utils.options.create_payoff import create_payoff + + _build, data = drawings["payoff"] + output = create_payoff({**data, "target": 100.0}, theme="dark") + marked = [note.text for note in output.chart.fig.layout.annotations] + + assert sum(text.startswith("Target") for text in marked) == 0 + + def test_a_marker_outside_the_drawn_range_is_left_off(self, drawings): + """Nothing is marked where the chart does not reach. + + Drawing one would stretch the axis back out to it, which is the whole + reason the range was narrowed. + """ + from openbb_deribit.utils.options.create_payoff import create_payoff + + _build, data = drawings["payoff"] + output = create_payoff({**data, "spot": 1e9}, theme="dark") + marked = [note.text for note in output.chart.fig.layout.annotations] + + assert not any(text.startswith("Spot") for text in marked) + assert output.chart.fig.layout.xaxis.autorange is False + + +class TestUnplotted: + """Without the charting extension the drawing calls go nowhere.""" + + def test_every_call_is_accepted(self, monkeypatch): + """A figure that cannot draw still answers every drawing call.""" + import openbb_deribit + from openbb_deribit.utils.options.theme import Unplotted, new_figure + + monkeypatch.setattr(openbb_deribit, "CHARTING_INSTALLED", False) + figure, text_color, background = new_figure("light") + + assert isinstance(figure, Unplotted) + assert figure.add_scatter(x=[1], y=[2]).update_layout() is figure + assert (text_color, background) == ("black", "rgba(255,255,255,0)") + + def test_a_dark_figure_is_drawn_on_nothing(self): + """The dark theme writes in white on a transparent ground.""" + from openbb_deribit.utils.options.theme import new_figure + + _figure, text_color, background = new_figure("dark") + + assert (text_color, background) == ("white", "rgba(0,0,0,0)") + + +class TestCommands: + """Each command reads the chain once and returns what it was asked for.""" + + @pytest.mark.asyncio + async def test_underlying_choices(self, responder, load): + """The underlyings on offer are the ones with listed options.""" + responder({"get_instruments": load("instruments_option")}) + + assert await router.underlying_choices() == [ + {"label": "BTC", "value": "BTC"}, + {"label": "XRP_USDC", "value": "XRP_USDC"}, + ] + + @pytest.mark.asyncio + async def test_a_listed_combo_prices_the_drawn_position(self, loaded_chain): + """Drawing a position Deribit lists as a combo prices it off that book.""" + frame = loaded_chain() + quotes = chain.quotes_at(frame, chain.expirations(frame)[0]) + calls = sorted( + quotes[quotes["option_type"] == "call"]["contract_symbol"], + key=lambda name: float(name.split("-")[2]), + ) + books = [ + { + "name": "BTC-LISTED", + "legs": [(calls[3], 1), (calls[5], -1)], + "bid": 0.01, + "ask": 0.02, + } + ] + loaded_chain(books=books) + drawn = await router.payoff_chart( + symbol="BTC", legs=f"Buy {calls[3]} / Sell {calls[5]}" + ) + + assert "BTC-LISTED" in drawn["layout"]["title"]["text"] + assert "Bull Call Spread" in drawn["layout"]["title"]["text"] + + @pytest.mark.asyncio + async def test_optimizer(self, loaded_chain): + """The ranking comes back with what it was measured against.""" + loaded_chain() + output = await router.optimizer(symbol="BTC", target_price=120.0, limit=5) + + assert 0 < len(output.results) <= 5 + assert output.extra["results_metadata"] == { + "symbol": "BTC", + "underlying_price": 100.0, + "target_price": 120.0, + "expiration": str(chain.expirations(loaded_chain())[0]), + "quote_currency": "USD", + "settlement_currency": "BTC", + } + + @pytest.mark.asyncio + async def test_the_optimizer_holds_back_the_drawing(self, loaded_chain): + """A ranked row carries its numbers, not the curve behind them.""" + loaded_chain() + output = await router.optimizer(symbol="BTC", target_price=120.0) + + for row in output.results: + assert not {"position", "prices", "payoff"} & set(row) + assert row["strategy"] and row["legs"] and row["code"] + + @pytest.mark.asyncio + async def test_no_target_ranks_against_spot(self, loaded_chain): + """With no view expressed, the underlying is expected to sit still.""" + loaded_chain() + output = await router.optimizer(symbol="BTC") + + assert output.extra["results_metadata"]["target_price"] == 100.0 + + @pytest.mark.asyncio + async def test_payoff(self, loaded_chain): + """The drawn position is the one the ranking put first.""" + loaded_chain() + ranked = await router.optimizer(symbol="BTC", target_price=120.0) + drawn = await router.payoff_chart(symbol="BTC", target_price=120.0) + + assert set(drawn) >= {"data", "layout", "config"} + assert ranked.results[0]["strategy"] in drawn["layout"]["title"]["text"] + + @pytest.mark.asyncio + async def test_a_named_position_is_the_one_drawn(self, loaded_chain): + """Picking a strategy out of the ranking draws exactly those contracts.""" + loaded_chain() + ranked = await router.optimizer(symbol="BTC", target_price=120.0) + second = ranked.results[1] + drawn = await router.payoff_chart( + symbol="BTC", target_price=120.0, legs=second["legs"] + ) + + assert second["strategy"] in drawn["layout"]["title"]["text"] + + assert second["legs"] in drawn["layout"]["title"]["text"] + + @pytest.mark.asyncio + async def test_a_named_position_is_not_ranked_again( + self, loaded_chain, monkeypatch + ): + """Drawing a chosen position does not re-run the ranking.""" + from openbb_deribit.utils.options import optimizer as engine + + loaded_chain() + ranked = await router.optimizer(symbol="BTC", target_price=120.0) + + def _refuse(*args, **kwargs): + raise AssertionError("the ranking was run to draw a chosen position") + + monkeypatch.setattr(engine, "rank", _refuse) + + assert await router.payoff_chart( + symbol="BTC", target_price=120.0, legs=ranked.results[0]["legs"] + ) + + @pytest.mark.asyncio + async def test_an_unlisted_position_says_so(self, loaded_chain): + """A position naming a contract the expiration lacks is refused.""" + from fastapi import HTTPException + + loaded_chain() + + with pytest.raises(HTTPException) as raised: + await router.payoff_chart(symbol="BTC", legs="Buy NOPE-1-1-C") + + assert raised.value.status_code == 404 + assert "not a listed contract" in raised.value.detail + + @pytest.mark.asyncio + async def test_every_ranked_strategy_can_be_drawn_back(self, loaded_chain): + """Every set of legs the optimizer prints prices back as it was.""" + loaded_chain() + ranked = await router.optimizer(symbol="BTC", target_price=120.0) + + for row in ranked.results: + drawn = await router.payoff_chart( + symbol="BTC", target_price=120.0, legs=row["legs"] + ) + + assert row["strategy"] in drawn["layout"]["title"]["text"] + + @pytest.mark.asyncio + @pytest.mark.parametrize("view", ["smile_chart", "straddle"]) + async def test_an_unlisted_underlying_is_a_404(self, monkeypatch, view): + """A underlying the exchange does not list answers 404, not 500.""" + from fastapi import HTTPException + from openbb_core.provider.utils.errors import EmptyDataError + + async def _empty(symbol, use_cache=True): + raise EmptyDataError("nothing listed") + + monkeypatch.setattr("openbb_deribit.utils.options.chain.load_chain", _empty) + + with pytest.raises(HTTPException) as raised: + await getattr(router, view)(symbol="NOPE") + + assert raised.value.status_code == 404 + assert "NOPE" in raised.value.detail + + @pytest.mark.asyncio + async def test_straddle(self, loaded_chain): + """A straddle is priced at every expiration.""" + loaded_chain() + output = await router.straddle(symbol="BTC") + + assert len(output.results) == 2 + assert all(row["cost"] > 0 for row in output.results) + + @pytest.mark.asyncio + async def test_strangle(self, loaded_chain): + """The moneyness is read as a percentage either side of spot.""" + loaded_chain() + output = await router.strangle(symbol="BTC", moneyness=4.0) + + assert output.results + assert all( + row["put_strike"] < 100.0 < row["call_strike"] for row in output.results + ) + + @pytest.mark.asyncio + async def test_spreads(self, loaded_chain): + """Both verticals are priced at every expiration.""" + loaded_chain() + output = await router.spreads(symbol="BTC", moneyness=4.0) + + assert {row["strategy"] for row in output.results} == { + "Bull Call Spread", + "Bear Put Spread", + } + + @pytest.mark.asyncio + @pytest.mark.parametrize("otm", [True, False]) + @pytest.mark.parametrize("skew", [True, False]) + async def test_smile(self, loaded_chain, otm, skew): + """The smile draws calls and puts for the front expiration.""" + loaded_chain() + drawn = await router.smile_chart( + symbol="BTC", otm=otm, skew=skew, theme="light" + ) + + assert [trace["name"] for trace in drawn["data"]] == ["Calls", "Puts"] + + @pytest.mark.asyncio + async def test_the_smile_draws_only_what_was_asked_for(self, loaded_chain): + """A chain lists more expirations than are drawn unless named.""" + frame = loaded_chain() + listed = chain.expirations(frame) + drawn = await router.smile_chart( + symbol="BTC", expirations=",".join(str(day) for day in listed) + ) + + assert len(drawn["data"]) == 2 * len(listed) > 2 + + @pytest.mark.asyncio + async def test_the_smile_offers_the_expirations_it_lists(self, loaded_chain): + """The dropdown behind the smile names every expiration with its term.""" + frame = loaded_chain() + offered = await router.expiration_choices(symbol="BTC") + + assert [item["value"] for item in offered] == [ + str(day) for day in chain.expirations(frame) + ] + assert all("d)" in item["label"] for item in offered) + + @pytest.mark.asyncio + async def test_the_raw_toggle_returns_the_rows(self, loaded_chain): + """With the raw toggle on, a chart route answers with its numbers.""" + loaded_chain() + rows = await router.smile_chart(symbol="BTC", raw=True) + + assert isinstance(rows, list) + assert rows and "IV" in rows[0] + + @pytest.mark.asyncio + async def test_term_structure(self, loaded_chain): + """The term structure draws calls and puts, one point per expiration.""" + loaded_chain() + drawn = await router.term_structure_chart(symbol="BTC") + + assert [trace["name"] for trace in drawn["data"]] == ["Calls", "Puts"] + assert all(len(trace["x"]) == 2 for trace in drawn["data"]) + + @pytest.mark.asyncio + async def test_term_structure_at_a_strike(self, loaded_chain): + """A chosen strike and metric reach the chart.""" + loaded_chain() + drawn = await router.term_structure_chart( + symbol="BTC", strike=100.0, metric="price", option_type="calls" + ) + + assert [trace["name"] for trace in drawn["data"]] == ["Calls"] + assert "$100.0" in drawn["layout"]["title"]["text"] + + @pytest.mark.asyncio + @pytest.mark.parametrize("metric", ["implied_volatility", "vega", "gex"]) + async def test_surface(self, loaded_chain, metric): + """Any published metric can be raised over the chain.""" + loaded_chain() + drawn = await router.surface_chart(symbol="BTC", metric=metric, dte_max=60) + + assert drawn["data"][0]["type"] == "mesh3d" + assert drawn["config"]["responsive"] is True + + @pytest.mark.asyncio + async def test_surface_without_a_window(self, loaded_chain): + """With no DTE bounds every expiration is raised.""" + loaded_chain() + drawn = await router.surface_chart( + symbol="BTC", option_type="calls", oi=True, volume=True + ) + + assert drawn["data"][0]["type"] == "mesh3d" + + @pytest.mark.asyncio + @pytest.mark.parametrize("by", ["strike", "expiration"]) + async def test_stats(self, loaded_chain, by): + """Totals are grouped by whichever axis was asked for.""" + loaded_chain() + drawn = await router.stats_chart(symbol="BTC", by=by, theme="light") + + assert [trace["name"] for trace in drawn["data"]] == ["Calls", "Puts"] + + @pytest.mark.asyncio + @pytest.mark.parametrize( + "unit,name", [("percent", "% of Total"), ("pcr", "Put/Call Ratio")] + ) + async def test_stats_units(self, loaded_chain, unit, name): + """The unit chooses what the bars measure.""" + loaded_chain() + drawn = await router.stats_chart(symbol="BTC", metric="volume", unit=unit) + + assert [trace["name"] for trace in drawn["data"]] == [name] + + @pytest.mark.asyncio + async def test_stats_for_one_expiration(self, loaded_chain): + """Naming an expiration draws its strikes.""" + frame = loaded_chain() + drawn = await router.stats_chart( + symbol="BTC", date=str(chain.expirations(frame)[1]) + ) + + assert drawn["layout"]["title"]["text"] == "BTC Open Interest By Strike" + + @pytest.mark.asyncio + async def test_strike_choices(self, loaded_chain): + """The strike dropdown leads with the nearest out-of-the-money default.""" + frame = loaded_chain() + offered = await router.strike_choices(symbol="BTC") + + assert offered[0] == {"label": "Nearest OTM", "value": None} + assert [item["value"] for item in offered[1:]] == sorted( + frame["strike"].unique() + ) + + @pytest.mark.asyncio + async def test_strike_choices_for_an_unlisted_underlying(self, monkeypatch): + """An underlying with no options offers no strikes.""" + from openbb_core.provider.utils.errors import EmptyDataError + + async def _empty(symbol, use_cache=True): + raise EmptyDataError("nothing listed") + + monkeypatch.setattr("openbb_deribit.utils.options.chain.load_chain", _empty) + + assert await router.strike_choices(symbol="NOPE") == [] + + @pytest.mark.asyncio + async def test_a_linear_underlying_settles_in_its_quote(self, loaded_chain): + """A USDC-quoted position is measured in USDC.""" + loaded_chain(inverse=False, spot=1.5, size=1000) + output = await router.optimizer(symbol="XRP_USDC", target_price=2.0, budget=100) + + assert output.extra["results_metadata"]["settlement_currency"] == "USDC" + assert output.results + + +class TestPayoffRefusals: + """A position a budget cannot size is refused rather than drawn wrong.""" + + @pytest.mark.asyncio + async def test_a_credit_position_is_drawn(self, loaded_chain): + """A position taken in for a credit is sized by what it risks.""" + frame = loaded_chain() + quotes = chain.quotes_at(frame, chain.expirations(frame)[0]) + sold = quotes[quotes["option_type"] == "call"].iloc[0]["contract_symbol"] + drawn = await router.payoff_chart(symbol="BTC", legs=f"Sell {sold}") + + assert "Short Call" in drawn["layout"]["title"]["text"] + assert "credit" in drawn["layout"]["title"]["text"] + + @pytest.mark.asyncio + async def test_a_position_with_nothing_at_risk_is_refused( + self, loaded_chain, monkeypatch + ): + """A position that cannot lose has no capital to size against.""" + from fastapi import HTTPException + + from openbb_deribit.utils.options import optimizer as engine + + frame = loaded_chain() + quotes = chain.quotes_at(frame, chain.expirations(frame)[0]) + bought = quotes[quotes["option_type"] == "call"].iloc[0]["contract_symbol"] + monkeypatch.setattr(engine, "score", lambda *args, **kwargs: None) + + with pytest.raises(HTTPException) as raised: + await router.payoff_chart(symbol="BTC", legs=f"Buy {bought}") + + assert raised.value.status_code == 404 + assert "nothing at risk" in raised.value.detail + + @pytest.mark.asyncio + async def test_an_expiration_with_nothing_to_size_is_refused( + self, loaded_chain, monkeypatch + ): + """An expiration forming no affordable strategy says so.""" + from fastapi import HTTPException + + from openbb_deribit.utils.options import optimizer as engine + + loaded_chain() + monkeypatch.setattr(engine, "rank", lambda *args, **kwargs: []) + + with pytest.raises(HTTPException) as raised: + await router.payoff_chart(symbol="BTC") + + assert raised.value.status_code == 404 + assert "can be sized" in raised.value.detail + + +class TestStrategyChoices: + """The optimizer's ranking is also the payoff's dropdown. + + The value a reader picks is the contracts, so clicking the ranking and + picking from the list put the same thing in the same parameter. + """ + + @pytest.mark.asyncio + async def test_the_dropdown_is_the_ranking(self, loaded_chain): + """Every entry offered is a strategy the optimizer ranked.""" + loaded_chain() + view = {"symbol": "BTC", "target_price": 120.0} + offered = await router.strategy_choices(**view) + ranked = await router.optimizer(**view) + + assert offered + assert [item["value"] for item in offered] == [ + row["legs"] for row in ranked.results + ] + + for item, row in zip(offered, ranked.results, strict=True): + assert item["label"].startswith(row["strategy"]) + assert row["legs"] in item["label"] + + @pytest.mark.asyncio + async def test_what_is_offered_can_be_drawn(self, loaded_chain): + """Each offered value draws the contracts it names.""" + loaded_chain() + offered = await router.strategy_choices(symbol="BTC", target_price=120.0) + + for item in offered: + drawn = await router.payoff_chart( + symbol="BTC", target_price=120.0, legs=item["value"] + ) + + assert item["value"] in drawn["layout"]["title"]["text"] + + @pytest.mark.asyncio + async def test_no_view_ranks_against_spot(self, loaded_chain): + """With no target the ranking is against where the underlying is.""" + loaded_chain() + + assert await router.strategy_choices(symbol="BTC") + + @pytest.mark.asyncio + @pytest.mark.parametrize( + "written", ["garbage", "Buy", "Hold BTC-1-100-C", "Buy A B C"] + ) + async def test_something_that_is_not_a_leg_says_so(self, loaded_chain, written): + """Text that names no contract is refused with what the format is.""" + from fastapi import HTTPException + + loaded_chain() + + with pytest.raises(HTTPException) as raised: + await router.payoff_chart(symbol="BTC", legs=written) + + assert raised.value.status_code == 404 + assert "Buy SYMBOL / Sell SYMBOL" in raised.value.detail diff --git a/openbb_platform/providers/deribit/tests/test_registration.py b/openbb_platform/providers/deribit/tests/test_registration.py new file mode 100644 index 00000000000..94152a3a8f7 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_registration.py @@ -0,0 +1,116 @@ +"""Tests for the conditional provider registration.""" + +from importlib import reload +from unittest.mock import patch + +import openbb_deribit + +STANDARD_KEYS = { + "FuturesCurve", + "FuturesHistorical", + "FuturesInfo", + "FuturesInstruments", + "OptionsChains", +} +ALIAS_KEYS = { + "DeribitFuturesCurve", + "DeribitFuturesHistorical", + "DeribitFuturesInfo", + "DeribitFuturesInstruments", + "DeribitOptionsChains", +} +PROVIDER_KEYS = { + "DeribitAnnouncements", + "DeribitAprHistory", + "DeribitBlockRfqTrades", + "DeribitBookSummary", + "DeribitCombos", + "DeribitCurrencies", + "DeribitDeliveryPrices", + "DeribitExpirations", + "DeribitFundingChart", + "DeribitFundingRateHistory", + "DeribitHistoricalVolatility", + "DeribitIndexHistorical", + "DeribitIndexPrice", + "DeribitInstruments", + "DeribitOrderBook", + "DeribitSettlements", + "DeribitTicker", + "DeribitTradeVolumes", + "DeribitTrades", + "DeribitVolatilityIndex", +} + + +def _reload_with(installed: set): + """Reload the package with a controlled set of installed extensions.""" + with patch( + "importlib.util.find_spec", + side_effect=lambda name: object() if name in installed else None, + ): + return reload(openbb_deribit) + + +class TestFetcherKeys: + """The fetcher_dict keys track which OpenBB extensions are installed.""" + + def test_standalone_aliases_when_nothing_installed(self): + """With no standard extensions, the shared models take a Deribit alias.""" + module = _reload_with(set()) + + try: + keys = set(module.deribit_provider.fetcher_dict) + + assert keys >= ALIAS_KEYS + assert not STANDARD_KEYS & keys + assert module.DERIVATIVES_INSTALLED is False + assert module.CHARTING_INSTALLED is False + finally: + reload(openbb_deribit) + + def test_standard_keys_when_derivatives_installed(self): + """With openbb-derivatives present, the standard keys are used.""" + module = _reload_with({"openbb_derivatives", "openbb_charting"}) + + try: + keys = set(module.deribit_provider.fetcher_dict) + + assert keys >= STANDARD_KEYS + assert not ALIAS_KEYS & keys + assert module.DERIVATIVES_INSTALLED is True + assert module.CHARTING_INSTALLED is True + finally: + reload(openbb_deribit) + + def test_provider_models_never_alias(self): + """The Deribit-only models keep their key whatever is installed.""" + for installed in (set(), {"openbb_derivatives"}): + module = _reload_with(installed) + + try: + assert set(module.deribit_provider.fetcher_dict) >= PROVIDER_KEYS + finally: + reload(openbb_deribit) + + def test_key_helper(self): + """``_key`` picks the standard name only when the owner is installed.""" + assert openbb_deribit._key("FuturesCurve", "DeribitFuturesCurve", True) == ( + "FuturesCurve" + ) + assert openbb_deribit._key("FuturesCurve", "DeribitFuturesCurve", False) == ( + "DeribitFuturesCurve" + ) + + +class TestProvider: + """The provider advertises itself as public and credential-free.""" + + def test_metadata(self): + """The provider needs no credentials and names itself Deribit.""" + provider = openbb_deribit.deribit_provider + + assert provider.name == "deribit" + assert provider.credentials == [] + assert "deribit.com" in provider.website + assert len(provider.fetcher_dict) == 25 diff --git a/openbb_platform/providers/deribit/tests/test_router.py b/openbb_platform/providers/deribit/tests/test_router.py new file mode 100644 index 00000000000..57cf47e40da --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_router.py @@ -0,0 +1,263 @@ +"""Tests for the Deribit router and its bundled dashboard.""" + +import asyncio +import json +from importlib import reload +from pathlib import Path +from unittest.mock import AsyncMock, patch + +import pytest + +import openbb_deribit +from openbb_deribit import deribit_router +from openbb_deribit.routers import ( + futures, + index, + market, + options, + rates, + reference, + volatility, +) + +APPS_JSON = Path(openbb_deribit.__file__).parent / "assets" / "apps.json" + +SUBROUTERS = { + "reference", + "market", + "index", + "rates", + "volatility", + "futures", + "options", +} + + +def _paths(router): + """Return every route the router and its sub-routers register.""" + found = set() + + for sub in router.routers.values(): + for route in sub.api_router.routes: + found.add(route.path) + + return found + + +def _widget_ids(router): + """Return the widget identifier of every registered route. + + A command backed by a provider model is published under the provider's + name; one that computes its own answer is published as a custom widget. + """ + found = set() + + for sub in router.routers.values(): + for route in sub.api_router.routes: + extra = getattr(route, "openapi_extra", None) or {} + suffix = "deribit" if extra.get("model") else "custom" + found.add(f"deribit{route.path}".replace("/", "_") + f"_{suffix}_obb") + + return found + + +MODEL_COMMANDS = [ + reference.currencies, + reference.instruments, + reference.expirations, + reference.combos, + reference.announcements, + market.book_summary, + market.order_book, + market.ticker, + market.trades, + market.trade_volumes, + market.block_rfq_trades, + market.settlements, + index.price, + index.historical, + index.delivery_prices, + rates.funding_history, + rates.funding_chart, + rates.apr_history, + volatility.realized, + volatility.index, + futures.curve, + futures.historical, + futures.info, + futures.instruments, + options.chains, +] + + +class TestModelCommands: + """Model-backed commands hand off to the query pipeline.""" + + @pytest.mark.parametrize("command", MODEL_COMMANDS) + def test_delegates_to_from_query(self, command): + """Each command hands off to ``OBBject.from_query``.""" + module = __import__(command.__module__, fromlist=["OBBject"]) + sentinel = object() + + with ( + patch.object(module, "OBBject") as mock_obbject, + patch.object(module, "OBBQuery") as mock_query, + ): + mock_obbject.from_query = AsyncMock(return_value=sentinel) + result = asyncio.run( + command( + cc=None, + provider_choices=None, + standard_params=None, + extra_params=None, + ) + ) + + assert result is sentinel + assert mock_query.called + mock_obbject.from_query.assert_awaited_once() + + +class TestAssembly: + """The router gathers every sub-router under the provider prefix.""" + + def test_subrouters(self): + """Each sub-router is mounted once, under its own prefix.""" + assert set(deribit_router.router.routers) == SUBROUTERS + + def test_every_model_is_reachable(self): + """Every Deribit-only model has a route.""" + paths = _paths(deribit_router.router) + + assert "/reference/currencies" in paths + assert "/market/order_book" in paths + assert "/index/delivery_prices" in paths + assert "/rates/apr_history" in paths + assert "/volatility/index" in paths + + def test_choice_feeds_are_hidden(self): + """A choice feed is served but kept out of the widget listing.""" + for router, name in ( + (reference.router, "currency_choices"), + (futures.router, "curve_choices"), + (options.router, "underlying_choices"), + ): + route = next(r for r in router.api_router.routes if r.path.endswith(name)) + + assert route.openapi_extra["widget_config"]["exclude"] is True + + +class TestStandaloneRegistration: + """The shared commands are served only when nothing else owns them.""" + + def test_served_without_derivatives(self): + """With no derivatives extension, the shared commands are ours.""" + paths = _paths(deribit_router.router) + + assert openbb_deribit.DERIVATIVES_INSTALLED is False + assert {"/futures/curve", "/options/chains"} <= paths + + def test_withheld_when_derivatives_is_installed(self): + """With the derivatives extension, the shared commands are not ours.""" + with patch( + "importlib.util.find_spec", + side_effect=lambda name: object() if name == "openbb_derivatives" else None, + ): + reload(openbb_deribit) + reloaded = reload(futures) + + try: + paths = {route.path for route in reloaded.router.api_router.routes} + + assert "/futures/curve" not in paths + assert "/futures/curve_choices" in paths + finally: + reload(openbb_deribit) + reload(futures) + reload(options) + reload(rates) + reload(reference) + reload(deribit_router) + + +class TestWidgetIds: + """The dashboard names widgets the install actually serves.""" + + def test_mapping_targets_exist(self): + """Every remapped identifier resolves to a route we serve.""" + served = _widget_ids(deribit_router.router) + + for standalone, _ in deribit_router._STANDALONE_WIDGET_IDS.values(): + assert standalone in served + + def test_resolve_passes_unknown_ids_through(self): + """An identifier the mapping does not know is left alone.""" + assert deribit_router._resolve_widget_id("something_else") == ("something_else") + + def test_resolve_swaps_when_the_owner_is_missing(self): + """A shared identifier is swapped for ours when nothing owns it.""" + assert ( + deribit_router._resolve_widget_id("derivatives_futures_curve_deribit_obb") + == "deribit_futures_curve_deribit_obb" + ) + + def test_resolve_keeps_the_shared_id_when_the_owner_is_present(self, monkeypatch): + """A shared identifier stays put when its extension is installed.""" + monkeypatch.setitem( + deribit_router._STANDALONE_WIDGET_IDS, + "derivatives_futures_curve_deribit_obb", + ("deribit_futures_curve_deribit_obb", True), + ) + + assert ( + deribit_router._resolve_widget_id("derivatives_futures_curve_deribit_obb") + == "derivatives_futures_curve_deribit_obb" + ) + + +class TestApps: + """The bundled dashboard is served with its identifiers resolved.""" + + @pytest.mark.asyncio + async def test_served_ids_resolve(self): + """Every widget the dashboard names is one this install serves.""" + served = _widget_ids(deribit_router.router) + apps = await deribit_router.deribit_apps() + + for app in apps: + for tab in app["tabs"].values(): + for widget in tab["layout"]: + assert widget["i"] in served + + @pytest.mark.asyncio + async def test_tabs(self): + """The dashboard covers each area of the surface.""" + apps = await deribit_router.deribit_apps() + + assert set(apps[0]["tabs"]) == { + "markets", + "futures", + "options", + "analysis", + "volatility", + "reference", + } + + @pytest.mark.asyncio + async def test_the_file_is_not_rewritten(self): + """Resolving the identifiers does not touch what is on disk.""" + before = APPS_JSON.read_text(encoding="utf-8") + await deribit_router.deribit_apps() + + assert APPS_JSON.read_text(encoding="utf-8") == before + + def test_layout_is_well_formed(self): + """Every widget names a position and a size within the grid.""" + apps = json.loads(APPS_JSON.read_text(encoding="utf-8")) + + for app in apps: + for tab in app["tabs"].values(): + for widget in tab["layout"]: + assert set(widget) >= {"i", "x", "y", "w", "h"} + assert widget["x"] + widget["w"] <= 40 + assert widget["h"] > 0 diff --git a/openbb_platform/providers/deribit/tests/test_routes.py b/openbb_platform/providers/deribit/tests/test_routes.py new file mode 100644 index 00000000000..c78a8046e8a --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_routes.py @@ -0,0 +1,159 @@ +"""Tests for the Deribit routes that answer without a model.""" + +import pytest + +from openbb_deribit.routers import futures, options, rates, reference + + +class TestReference: + """The reference routes answer the small scalar endpoints.""" + + @pytest.mark.asyncio + async def test_contract_size(self, responder, load): + """The contract size is unwrapped from its envelope.""" + responder({"get_contract_size": load("contract_size")}) + + assert await reference.contract_size("BTC-PERPETUAL") == 10.0 + + @pytest.mark.asyncio + async def test_contract_size_of_an_unlisted_instrument(self, responder): + """An instrument the exchange answered nothing for reads as nothing.""" + responder({"get_contract_size": None}) + + assert await reference.contract_size("NOPE") is None + + @pytest.mark.asyncio + async def test_status(self, responder, load): + """The platform status is returned as the exchange sends it.""" + responder({"status": load("status")}) + + assert await reference.status() == {"locked": "false"} + + @pytest.mark.asyncio + async def test_server_time(self, responder, load): + """The server time is returned as milliseconds.""" + responder({"get_time": load("server_time")}) + + assert await reference.server_time() > 0 + + @pytest.mark.asyncio + async def test_currency_choices(self, responder, load): + """Currency choices come from the live listing.""" + responder({"get_currencies": load("currencies")}) + + assert {"label": "Bitcoin", "value": "BTC"} in ( + await reference.currency_choices() + ) + + @pytest.mark.asyncio + async def test_index_choices(self, responder, load): + """Index choices come from the published names.""" + responder({"get_index_price_names": load("index_price_names")}) + feed = await reference.index_choices() + + assert {"label": "btc_usd", "value": "btc_usd"} in feed + + @pytest.mark.asyncio + async def test_supported_index_choices(self, responder, load): + """Asking for the supported names reads the other endpoint.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return load("supported_index_names") + + responder({"get_supported_index_names": capture}) + feed = await reference.index_choices(supported=True, kind="spot") + + assert seen == {"type": "spot"} + assert feed + + @pytest.mark.asyncio + async def test_instrument_choices(self, responder, load): + """Instrument choices come from the live listing.""" + responder({"get_instruments": load("instruments_future")}) + feed = await reference.instrument_choices("future") + + assert {"label": "BTC-PERPETUAL", "value": "BTC-PERPETUAL"} in feed + + @pytest.mark.asyncio + async def test_combo_choices(self, responder, load): + """Combo choices come from the live listing.""" + responder({"get_combo_ids": load("combo_ids")}) + + assert await reference.combo_choices("BTC") + + +class TestRates: + """The rates router serves the single funding figure.""" + + @pytest.mark.asyncio + async def test_funding_value_defaults_to_a_week(self, responder, load): + """With no span, the last week's funding is read.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return 0.00098 + + responder( + { + "get_funding_rate_value": capture, + "get_instruments": load("instruments_future"), + } + ) + value = await rates.funding_value("BTC-PERPETUAL") + + assert value == 0.00098 + assert seen["end_timestamp"] - seen["start_timestamp"] == 7 * 86400000 + + @pytest.mark.asyncio + async def test_funding_value_resolves_a_short_root(self, responder, load): + """A shortened perpetual root reaches the exchange in full.""" + seen: dict = {} + + def capture(params): + seen.update(params) + + return 0.0 + + responder( + { + "get_funding_rate_value": capture, + "get_instruments": load("instruments_future"), + } + ) + await rates.funding_value("btc", "2026-01-01", "2026-01-08") + + assert seen["instrument_name"] == "BTC-PERPETUAL" + assert seen["start_timestamp"] == 1767225600000 + + +class TestDerivativeChoices: + """The futures and options routers offer what the exchange lists.""" + + @pytest.mark.asyncio + async def test_curve_choices(self, responder, load): + """Only underlyings with a curve are offered.""" + responder({"get_instruments": load("instruments_future")}) + + assert await futures.curve_choices() == [{"label": "BTC", "value": "BTC"}] + + @pytest.mark.asyncio + async def test_perpetual_choices(self, responder, load): + """Every perpetual is offered by its full name.""" + responder({"get_instruments": load("instruments_future")}) + + assert await futures.perpetual_choices() == [ + {"label": "BTC-PERPETUAL", "value": "BTC-PERPETUAL"} + ] + + @pytest.mark.asyncio + async def test_underlying_choices(self, responder, load): + """Only underlyings with listed options are offered.""" + responder({"get_instruments": load("instruments_option")}) + feed = await options.underlying_choices() + + assert {"label": "XRP_USDC", "value": "XRP_USDC"} in feed diff --git a/openbb_platform/providers/deribit/tests/test_session.py b/openbb_platform/providers/deribit/tests/test_session.py new file mode 100644 index 00000000000..cdd062309d6 --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_session.py @@ -0,0 +1,211 @@ +"""Tests for the pooled Deribit HTTP sessions.""" + +import asyncio + +import pytest + +from openbb_deribit.utils import session as sessions + +pytestmark = pytest.mark.uses_session + + +@pytest.fixture(autouse=True) +def _empty_pools(): + """Start and finish each test with empty pools.""" + sessions._SESSIONS.clear() + sessions._CACHED_SESSIONS.clear() + yield + sessions._SESSIONS.clear() + sessions._CACHED_SESSIONS.clear() + + +class TestGetSession: + """One uncached session is held per running loop.""" + + @pytest.mark.asyncio + async def test_shared_within_a_loop(self): + """Two callers on one loop share a session.""" + first = await sessions.get_session() + second = await sessions.get_session() + + try: + assert first is second + assert len(sessions._SESSIONS) == 1 + finally: + await sessions.close_sessions() + + @pytest.mark.asyncio + async def test_rebuilt_when_closed(self): + """A session someone closed is replaced rather than reused.""" + first = await sessions.get_session() + await first.close() + second = await sessions.get_session() + + try: + assert first is not second + finally: + await sessions.close_sessions() + + +class TestGetCachedSession: + """One cache-backed session is held per running loop, swept once.""" + + @pytest.mark.asyncio + async def test_shared_and_swept_once(self, monkeypatch, tmp_path): + """The cache is swept the first time a session is built.""" + monkeypatch.setattr( + "openbb_deribit.utils.cache.cache_path", lambda: str(tmp_path / "deribit") + ) + swept: list = [] + + async def _sweep(backend): + swept.append(backend) + + monkeypatch.setattr("openbb_deribit.utils.cache.sweep_cache", _sweep) + first = await sessions.get_cached_session() + second = await sessions.get_cached_session() + + try: + assert first is second + assert len(swept) == 1 + finally: + await sessions.close_sessions() + + @pytest.mark.asyncio + async def test_a_failing_sweep_does_not_break_the_session( + self, monkeypatch, tmp_path + ): + """A sweep that raises leaves a usable session behind.""" + monkeypatch.setattr( + "openbb_deribit.utils.cache.cache_path", lambda: str(tmp_path / "deribit") + ) + + async def _sweep(backend): + raise RuntimeError("disk is gone") + + monkeypatch.setattr("openbb_deribit.utils.cache.sweep_cache", _sweep) + session = await sessions.get_cached_session() + + try: + assert session.closed is False + finally: + await sessions.close_sessions() + + +class TestCloseSessions: + """Closing empties the pools and closes what was open.""" + + @pytest.mark.asyncio + async def test_closes_everything(self, monkeypatch, tmp_path): + """Both pools are emptied and both sessions closed.""" + monkeypatch.setattr( + "openbb_deribit.utils.cache.cache_path", lambda: str(tmp_path / "deribit") + ) + + async def _sweep(backend): + return None + + monkeypatch.setattr("openbb_deribit.utils.cache.sweep_cache", _sweep) + plain = await sessions.get_session() + cached = await sessions.get_cached_session() + await sessions.close_sessions() + + assert plain.closed is True + assert cached.closed is True + assert not sessions._SESSIONS + assert not sessions._CACHED_SESSIONS + + @pytest.mark.asyncio + async def test_closes_only_the_named_loop(self): + """Closing one loop leaves another loop's session alone.""" + mine = await sessions.get_session() + sessions._SESSIONS["other"] = object() + await sessions.close_sessions(asyncio.get_running_loop()) + + try: + assert mine.closed is True + assert "other" in sessions._SESSIONS + finally: + sessions._SESSIONS.clear() + + @pytest.mark.asyncio + async def test_tolerates_a_session_that_will_not_close(self): + """A session whose close raises does not stop the rest.""" + + class _Stubborn: + closed = False + + async def close(self): + raise RuntimeError("no") + + sessions._SESSIONS["stubborn"] = _Stubborn() + await sessions.close_sessions() + + assert not sessions._SESSIONS + + @pytest.mark.asyncio + async def test_empty_pools(self): + """Closing empty pools does nothing at all.""" + await sessions.close_sessions() + + assert not sessions._SESSIONS + + +class TestLoopHook: + """A loop closes its own sessions when it shuts down.""" + + def test_hooks_once(self): + """A loop is hooked the first time and left alone after.""" + loop = asyncio.new_event_loop() + + try: + original = loop.close + sessions._hook_loop_close(loop) + hooked = loop.close + sessions._hook_loop_close(loop) + + assert hooked is not original + assert loop.close is hooked + finally: + loop.close() + + def test_closes_the_pool_on_shutdown(self): + """The sessions a loop opened are closed when the loop is.""" + loop = asyncio.new_event_loop() + session = loop.run_until_complete(_open(loop)) + + assert session.closed is False + + loop.close() + + assert session.closed is True + assert not sessions._SESSIONS + + def test_tolerates_a_pool_that_will_not_close_on_shutdown(self, monkeypatch): + """A loop whose pool cannot be drained still closes.""" + + async def _close(loop=None): + raise RuntimeError("the pool is wedged") + + monkeypatch.setattr(sessions, "close_sessions", _close) + loop = asyncio.new_event_loop() + sessions._hook_loop_close(loop) + loop.close() + + assert loop.is_closed() + + def test_tolerates_a_loop_that_rejects_the_hook(self): + """A loop whose close cannot be replaced is left to the caller.""" + + class _Frozen: + __slots__ = () + + def close(self): + return None + + sessions._hook_loop_close(_Frozen()) + + +async def _open(loop): + """Open one pooled session on the given loop.""" + return await sessions.get_session() diff --git a/openbb_platform/providers/deribit/tests/test_websocket.py b/openbb_platform/providers/deribit/tests/test_websocket.py new file mode 100644 index 00000000000..44d05c8f35a --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_websocket.py @@ -0,0 +1,130 @@ +"""Tests for the Deribit ticker subscription.""" + +import asyncio +import json + +import pytest +from websockets import ConnectionClosed + +from openbb_deribit.utils import websocket + + +class _Socket: + """A stand-in for one websocket connection.""" + + def __init__(self, frames): + self.frames = list(frames) + self.sent: list = [] + + async def send(self, frame): + """Record what the subscription asked for.""" + self.sent.append(json.loads(frame)) + + async def recv(self): + """Return the next frame, or stall when there are none left.""" + if not self.frames: + await asyncio.sleep(10) + + frame = self.frames.pop(0) + + if isinstance(frame, Exception): + raise frame + + return json.dumps(frame) + + async def __aenter__(self): + """Enter the connection context.""" + return self + + async def __aexit__(self, *args): + """Leave the connection context.""" + return False + + +def _tick(symbol): + """Return one ticker frame for a symbol.""" + return {"params": {"data": {"instrument_name": symbol, "mark_price": 1.0}}} + + +@pytest.fixture +def connect(monkeypatch): + """Install a canned connection and return what it recorded.""" + + def install(frames): + socket = _Socket(frames) + monkeypatch.setattr("websockets.asyncio.client.connect", lambda url: socket) + + return socket + + return install + + +class TestSubscribeTickers: + """The subscription returns one ticker per symbol it was asked for.""" + + @pytest.mark.asyncio + async def test_collects_every_symbol(self, connect): + """The read stops as soon as every symbol has arrived.""" + socket = connect([_tick("A"), _tick("B")]) + messages: set = set() + received = await websocket.subscribe_tickers(["A", "B"], messages) + + assert set(received) == {"A", "B"} + assert messages == set() + assert socket.sent[0]["params"]["channels"] == [ + "ticker.A.100ms", + "ticker.B.100ms", + ] + + @pytest.mark.asyncio + async def test_ignores_duplicates_and_noise(self, connect): + """Repeats and frames carrying no ticker are skipped.""" + connect([{"id": 1}, _tick("A"), _tick("A"), _tick("B")]) + messages: set = set() + received = await websocket.subscribe_tickers(["A", "B"], messages) + + assert set(received) == {"A", "B"} + + @pytest.mark.asyncio + async def test_reports_an_exchange_error(self, connect): + """An error frame stops the read and is reported.""" + connect([{"error": {"message": "no"}}]) + messages: set = set() + received = await websocket.subscribe_tickers(["A"], messages) + + assert received == {} + assert any("returned an error" in message for message in messages) + + @pytest.mark.asyncio + async def test_a_closed_connection_returns_what_arrived(self, connect): + """A connection that closed early keeps the tickers it did deliver.""" + connect([_tick("A"), ConnectionClosed(None, None)]) + messages: set = set() + received = await websocket.subscribe_tickers(["A", "B"], messages) + + assert set(received) == {"A"} + + @pytest.mark.asyncio + async def test_timeout_says_how_many_are_missing(self, connect, monkeypatch): + """A subscription that stalls names how much did not arrive.""" + monkeypatch.setattr(websocket, "SUBSCRIBE_TIMEOUT", 0.05) + connect([_tick("A")]) + messages: set = set() + received = await websocket.subscribe_tickers(["A", "B"], messages) + + assert set(received) == {"A"} + assert any("1 of 2 contracts" in message for message in messages) + + @pytest.mark.asyncio + async def test_a_failed_connection_is_reported(self, monkeypatch): + """A connection that could not be opened is reported, not raised.""" + + def _connect(url): + raise OSError("no route to host") + + monkeypatch.setattr("websockets.asyncio.client.connect", _connect) + messages: set = set() + received = await websocket.subscribe_tickers(["A"], messages) + + assert received == {} + assert any("subscription failed" in message for message in messages) diff --git a/openbb_platform/providers/deribit/tests/test_widget_contract.py b/openbb_platform/providers/deribit/tests/test_widget_contract.py new file mode 100644 index 00000000000..04395b5763c --- /dev/null +++ b/openbb_platform/providers/deribit/tests/test_widget_contract.py @@ -0,0 +1,962 @@ +"""Tests for the contract the Workspace widgets rely on.""" + +import importlib +from functools import cache +from typing import Literal, get_args, get_origin + +import pytest +from pydantic import ValidationError + +from openbb_deribit import deribit_provider +from openbb_deribit.deribit_router import router +from openbb_deribit.models.order_book import DeribitOrderBookQueryParams +from openbb_deribit.models.settlements import DeribitSettlementsQueryParams +from openbb_deribit.models.trades import DeribitTradesQueryParams +from openbb_deribit.utils.constants import ( + MAX_SETTLEMENT_COUNT, + MAX_TRADE_COUNT, + ORDER_BOOK_DEPTHS, +) + +MODEL_DATA = { + "DeribitAnnouncements": ("announcements", "DeribitAnnouncementsData"), + "DeribitAprHistory": ("apr_history", "DeribitAprHistoryData"), + "DeribitBlockRfqTrades": ("block_rfq_trades", "DeribitBlockRfqTradesData"), + "DeribitBookSummary": ("book_summary", "DeribitBookSummaryData"), + "DeribitCombos": ("combos", "DeribitCombosData"), + "DeribitCurrencies": ("currencies", "DeribitCurrenciesData"), + "DeribitDeliveryPrices": ("delivery_prices", "DeribitDeliveryPricesData"), + "DeribitExpirations": ("expirations", "DeribitExpirationsData"), + "DeribitFundingChart": ("funding_chart", "DeribitFundingChartData"), + "DeribitFundingRateHistory": ( + "funding_rate_history", + "DeribitFundingRateHistoryData", + ), + "DeribitFuturesCurve": ("futures_curve", "DeribitFuturesCurveData"), + "DeribitFuturesHistorical": ( + "futures_historical", + "DeribitFuturesHistoricalData", + ), + "DeribitFuturesInfo": ("futures_info", "DeribitFuturesInfoData"), + "DeribitFuturesInstruments": ( + "futures_instruments", + "DeribitFuturesInstrumentData", + ), + "DeribitHistoricalVolatility": ( + "historical_volatility", + "DeribitHistoricalVolatilityData", + ), + "DeribitIndexHistorical": ("index_historical", "DeribitIndexHistoricalData"), + "DeribitIndexPrice": ("index_price", "DeribitIndexPriceData"), + "DeribitInstruments": ("instruments", "DeribitInstrumentsData"), + "DeribitOptionsChains": ("options_chains", "DeribitOptionsChainsData"), + "DeribitOrderBook": ("order_book", "DeribitOrderBookData"), + "DeribitSettlements": ("settlements", "DeribitSettlementsData"), + "DeribitTicker": ("ticker", "DeribitTickerData"), + "DeribitTradeVolumes": ("trade_volumes", "DeribitTradeVolumesData"), + "DeribitTrades": ("trades", "DeribitTradesData"), + "DeribitVolatilityIndex": ("volatility_index", "DeribitVolatilityIndexData"), +} + + +def _widgets(): + """Yield every model-backed route with its widget config.""" + for sub in router.routers.values(): + for route in sub.api_router.routes: + extra = getattr(route, "openapi_extra", None) or {} + config = extra.get("widget_config") or {} + + if extra.get("model") and not config.get("exclude"): + yield route.path, extra["model"], config + + +def _data_model(model: str): + """Return the data model a widget returns.""" + module_name, class_name = MODEL_DATA[model] + + return getattr( + importlib.import_module(f"openbb_deribit.models.{module_name}"), class_name + ) + + +def _literal_fields(params): + """Yield each field of a query model whose type is a Literal, with its values.""" + for name, field in params.model_fields.items(): + annotation = field.annotation + options = [annotation, *get_args(annotation)] + + for option in options: + if get_origin(option) is Literal: + yield name, get_args(option) + break + + +class TestQueryStringChoices: + """Choice parameters must survive arriving as text. + + A widget sends every parameter in the query string, so a choice typed as + ``Literal[20]`` is handed the string ``"20"`` and rejected by FastAPI before + the model is ever reached. Every choice must therefore be spelled as text. + """ + + @pytest.mark.parametrize( + "name", sorted(deribit_provider.fetcher_dict), ids=lambda n: n + ) + def test_every_choice_is_text(self, name): + """No query choice is typed as a number.""" + params = deribit_provider.fetcher_dict[name].query_params_type + + for field, values in _literal_fields(params): + numeric = [v for v in values if not isinstance(v, str)] + + assert not numeric, ( + f"{params.__name__}.{field} offers non-text choices {numeric};" + " a query string cannot carry them" + ) + + @pytest.mark.parametrize("depth", ORDER_BOOK_DEPTHS) + def test_depth_accepts_its_own_choices(self, depth): + """Every advertised depth is accepted as the number it is.""" + query = DeribitOrderBookQueryParams(symbol="BTC-PERPETUAL", depth=depth) + + assert query.depth == depth + + def test_depth_accepts_the_query_string_text(self): + """A widget's query string carries the depth as text.""" + query = DeribitOrderBookQueryParams(symbol="BTC-PERPETUAL", depth="20") + + assert query.depth == 20 + + @pytest.mark.parametrize("depth", [7, "7"]) + def test_depth_rejects_an_unlisted_value(self, depth): + """A depth the exchange does not offer is refused.""" + with pytest.raises(ValidationError, match="not 7"): + DeribitOrderBookQueryParams(symbol="BTC-PERPETUAL", depth=depth) + + def test_depth_is_offered_as_a_dropdown(self): + """The widget offers every depth the exchange accepts.""" + schema = DeribitOrderBookQueryParams.__json_schema_extra__["depth"] + options = schema["x-widget_config"]["options"] + + assert [option["value"] for option in options] == list(ORDER_BOOK_DEPTHS) + + +class TestExchangeLimits: + """Limits are bounded to what the exchange accepts, not left to fail at it.""" + + def test_trades_ceiling(self): + """One over the trade ceiling is refused here, not by Deribit.""" + assert DeribitTradesQueryParams(limit=MAX_TRADE_COUNT).limit == MAX_TRADE_COUNT + + with pytest.raises(ValidationError): + DeribitTradesQueryParams(limit=MAX_TRADE_COUNT + 1) + + def test_settlements_ceiling(self): + """One over the settlement ceiling is refused here.""" + assert ( + DeribitSettlementsQueryParams(limit=MAX_SETTLEMENT_COUNT).limit + == MAX_SETTLEMENT_COUNT + ) + + with pytest.raises(ValidationError): + DeribitSettlementsQueryParams(limit=MAX_SETTLEMENT_COUNT + 1) + + def test_limits_have_a_floor(self): + """Asking for nothing is refused.""" + with pytest.raises(ValidationError): + DeribitTradesQueryParams(limit=0) + + def test_trade_kinds_exclude_what_the_exchange_refuses(self): + """The by-currency path refuses spot and the wildcard, so neither is offered.""" + kinds = dict(_literal_fields(DeribitTradesQueryParams))["kind"] + + assert "spot" not in kinds + assert "any" not in kinds + assert DeribitTradesQueryParams().kind in kinds + + +def _widget_config(model, field) -> dict: + """Return the widget config a model declares for one field.""" + declared = model.model_fields[field].json_schema_extra or {} + + return declared.get("x-widget_config", {}) + + +class TestModelColumns: + """Every field a model returns carries its own column definition. + + The platform generates a column for every field of the response schema but + never sets ``chartDataType``. A field that does not declare one is charted + by default, which is how identifiers ended up plotted against prices. + """ + + @pytest.mark.parametrize("model", sorted(MODEL_DATA), ids=lambda m: m) + def test_every_field_declares_a_chart_disposition(self, model): + """No field is left for the grid to guess at.""" + data = _data_model(model) + undeclared = [ + field + for field in data.model_fields + if "chartDataType" not in _widget_config(data, field) + ] + + assert not undeclared, f"{model} leaves {undeclared} undeclared" + + @pytest.mark.parametrize("model", sorted(MODEL_DATA), ids=lambda m: m) + def test_dispositions_are_valid(self, model): + """A disposition is one the grid understands.""" + data = _data_model(model) + + for field in data.model_fields: + disposition = _widget_config(data, field).get("chartDataType") + + assert disposition in {"series", "category", "excluded"}, ( + f"{model}.{field} declares {disposition!r}" + ) + + @pytest.mark.parametrize("model", sorted(MODEL_DATA), ids=lambda m: m) + def test_at_most_one_series_and_one_axis(self, model): + """A model nominates a single series against a single axis.""" + data = _data_model(model) + counts = {"series": [], "category": []} + + for field in data.model_fields: + disposition = _widget_config(data, field).get("chartDataType") + + if disposition in counts: + counts[disposition].append(field) + + assert len(counts["series"]) <= 1, f"{model} plots {counts['series']}" + assert len(counts["category"]) <= 1, f"{model} axes {counts['category']}" + + if counts["series"]: + assert counts["category"], f"{model} plots a series with no axis" + + +class TestChartedWidgets: + """A widget that enables a chart has a model that names what to plot.""" + + def test_an_enabled_chart_has_a_series(self): + """No widget turns on a chart its model cannot fill.""" + for path, model, config in _widgets(): + table = (config.get("data") or {}).get("table") or {} + + if not (table.get("chartView") or {}).get("enabled"): + continue + + data = _data_model(model) + series = [ + field + for field in data.model_fields + if _widget_config(data, field).get("chartDataType") == "series" + ] + + assert series, f"{path} charts but {model} names no series" + + def test_a_router_defines_columns_only_where_the_model_cannot(self): + """Columns live on the model wherever the platform can read them off it. + + A model publishing one object of columns rather than a row per record + gives the grid nothing to infer from, so that widget writes them out. + Any other router doing so would be duplicating what the schema already + says, and the two copies would drift. + """ + for route, model, config in _widgets(): + table = (config.get("data") or {}).get("table") or {} + + if "columnsDefs" not in table: + continue + + assert _is_columnar(model), ( + f"{route} writes its columns out, but {model} publishes a row" + " per record, so the grid already reads them off its schema" + ) + + def test_a_columnar_model_has_its_columns_written_out(self): + """A model the grid cannot read columns off names them in the router.""" + for route, model, config in _widgets(): + if not _is_columnar(model): + continue + + table = (config.get("data") or {}).get("table") or {} + + assert table.get("columnsDefs"), ( + f"{model} publishes columns rather than rows, so {route} must" + " name them; the grid can infer nothing from its schema" + ) + + +SYMBOL_PARAMS = {"symbol", "index_name", "combo_id"} + + +@cache +def _served_widgets() -> dict: + """Return the widget catalogue this install publishes. + + Reading the catalogue rather than the routes covers the analysis endpoints, + which carry no model and are published under a different suffix, and honours + every widget the routers exclude. + """ + from openbb_core.api.rest_api import app + from openbb_platform_api.utils.widgets import build_json + + return build_json(app.openapi(), []) + + +def _is_columnar(model: str) -> bool: + """Return whether a model publishes columns rather than a row per record.""" + from typing import get_origin + + fields = _data_model(model).model_fields.values() + + return all(get_origin(field.annotation) is list for field in fields) + + +def _served_widget_ids() -> set: + """Return the identifier of every widget this install publishes.""" + return set(_served_widgets()) + + +def _choice_routes() -> set: + """Return the path of every choice feed this install serves.""" + return { + route.path + for sub in router.routers.values() + for route in sub.api_router.routes + if route.path.endswith("_choices") + } + + +class TestSymbolChoices: + """Nobody can guess a Deribit instrument name, so every one is offered.""" + + @pytest.mark.parametrize( + "name", sorted(deribit_provider.fetcher_dict), ids=lambda n: n + ) + def test_symbol_parameters_offer_choices(self, name): + """A symbol-like parameter names an endpoint to read its choices from.""" + query = deribit_provider.fetcher_dict[name].query_params_type + extra = getattr(query, "__json_schema_extra__", {}) + + for param in SYMBOL_PARAMS & set(query.model_fields): + config = (extra.get(param) or {}).get("x-widget_config", {}) + + assert config.get("type") == "endpoint", ( + f"{query.__name__}.{param} offers no dropdown" + ) + assert config.get("optionsEndpoint"), ( + f"{query.__name__}.{param} names no options endpoint" + ) + + @pytest.mark.parametrize( + "name", sorted(deribit_provider.fetcher_dict), ids=lambda n: n + ) + def test_options_endpoints_are_served(self, name): + """Every dropdown points at a route this extension actually serves.""" + query = deribit_provider.fetcher_dict[name].query_params_type + extra = getattr(query, "__json_schema_extra__", {}) + served = _choice_routes() + + for param in SYMBOL_PARAMS & set(query.model_fields): + endpoint = ( + (extra.get(param) or {}) + .get("x-widget_config", {}) + .get("optionsEndpoint", "") + ) + path = endpoint.replace("/api/v1/deribit", "") + + assert path in served, ( + f"{query.__name__}.{param} points at {path}, which is not served" + ) + + +class TestParameterGroups: + """A group binds one parameter across the widgets that share it.""" + + @pytest.fixture(scope="class") + def apps(self): + """Return the dashboard as it is served.""" + import asyncio + + from openbb_deribit.deribit_router import deribit_apps + + return asyncio.run(deribit_apps()) + + def test_groups_bind_served_widgets(self, apps): + """Every widget a group names is one this install serves.""" + served = _served_widget_ids() + + for app in apps: + for group in app["groups"]: + unknown = [w for w in group["widgetIds"] if w not in served] + + assert not unknown, f"group {group['name']} names {unknown}" + + def test_groups_bind_a_parameter_each_widget_has(self, apps): + """A group never binds a parameter one of its widgets lacks.""" + widgets = _served_widgets() + + for app in apps: + for group in app["groups"]: + for widget_id in group["widgetIds"]: + offered = { + param.get("paramName") + for param in widgets[widget_id].get("params", []) + } + + assert group["paramName"] in offered, ( + f"group {group['name']} binds {group['paramName']}," + f" which {widget_id} has no parameter for" + ) + + def test_widgets_only_name_defined_groups(self, apps): + """A widget never joins a group the app does not define.""" + for app in apps: + defined = {group["name"] for group in app["groups"]} + + for tab in app["tabs"].values(): + for widget in tab["layout"]: + unknown = [g for g in widget.get("groups", []) if g not in defined] + + assert not unknown, f"{widget['i']} names {unknown}" + + def test_group_membership_is_declared_on_both_sides(self, apps): + """A widget in a group's list says so, and the reverse.""" + for app in apps: + for group in app["groups"]: + members = set(group["widgetIds"]) + claimed = { + widget["i"] + for tab in app["tabs"].values() + for widget in tab["layout"] + if group["name"] in widget.get("groups", []) + } + + assert claimed == members, ( + f"group {group['name']}: listed {sorted(members)}" + f" but claimed by {sorted(claimed)}" + ) + + +class TestGroupEndpoints: + """An endpoint-backed group binds one options endpoint, not several. + + The Workspace reads a group's choices from a single endpoint. Binding + widgets whose dropdowns are fed by different endpoints leaves the group + unable to resolve a value, and the widgets are called with no parameter + at all. + """ + + @pytest.fixture(scope="class") + def bound(self): + """Return each group with the options endpoints its widgets declare.""" + import asyncio + + from openbb_deribit.deribit_router import deribit_apps + + widgets = _served_widgets() + groups = [] + + for entry in asyncio.run(deribit_apps()): + for group in entry["groups"]: + endpoints = { + param.get("optionsEndpoint") + for widget_id in group["widgetIds"] + for param in widgets[widget_id].get("params", []) + if param.get("paramName") == group["paramName"] + } + groups.append((group, endpoints)) + + return groups + + def test_each_endpoint_group_binds_one_endpoint(self, bound): + """No group mixes widgets whose choices come from different feeds.""" + for group, endpoints in bound: + if group["type"] != "endpointParam": + continue + + assert len(endpoints) == 1, ( + f"group {group['name']} binds {len(endpoints)} endpoints:" + f" {sorted(map(str, endpoints))}" + ) + + def test_endpoint_groups_actually_have_an_endpoint(self, bound): + """A group declared endpoint-backed has widgets that offer choices.""" + for group, endpoints in bound: + if group["type"] != "endpointParam": + continue + + assert endpoints != {None}, ( + f"group {group['name']} is endpointParam but offers no choices" + ) + + def test_plain_groups_are_not_endpoint_backed(self, bound): + """A group bound as a plain parameter has no dropdown to reconcile.""" + for group, endpoints in bound: + if group["type"] == "endpointParam": + continue + + assert endpoints <= {None}, ( + f"group {group['name']} is a plain param but its widgets offer" + f" {sorted(map(str, endpoints - {None}))}" + ) + + def test_a_group_starts_somewhere_or_its_widgets_do_not_need_it(self, bound): + """A group names a starting value unless its widgets can do without one. + + A widget bound to a group with nothing selected must still have + something to show. Most do that by starting on a named value; one whose + parameter is optional does it by deciding for itself what to show, and + an empty string is neither -- it is a value that means nothing. + """ + widgets = _served_widgets() + + for group, _endpoints in bound: + if group.get("defaultValue"): + continue + + assert "defaultValue" not in group, ( + f"group {group['name']} starts on {group['defaultValue']!r}," + " which is a placeholder rather than a value" + ) + + for widget_id in group["widgetIds"]: + param = next( + item + for item in widgets[widget_id]["params"] + if item["paramName"] == group["paramName"] + ) + + assert param.get("optional"), ( + f"group {group['name']} names no starting value and" + f" {widget_id} requires {group['paramName']}" + ) + + +class TestCellClickGroups: + """A clickable cell drives a group its own widget belongs to.""" + + @pytest.fixture(scope="class") + def emitters(self): + """Return each Deribit widget's clickable columns and the parameter set.""" + found = [] + + for widget_id, widget in _served_widgets().items(): + if "Deribit" not in (widget.get("source") or []): + continue + + for column in ( + widget.get("data", {}).get("table", {}).get("columnsDefs") or [] + ): + action = column.get("renderFnParams") or {} + + if column.get("renderFn") != "cellOnClick": + continue + + param = (action.get("groupBy") or {}).get("paramName") or action.get( + "groupByParamName" + ) + found.append((widget_id, column["field"], param)) + + return found + + @pytest.fixture(scope="class") + def apps(self): + """Return the dashboard as it is served.""" + import asyncio + + from openbb_deribit.deribit_router import deribit_apps + + return asyncio.run(deribit_apps()) + + def test_there_is_a_clickable_column(self, emitters): + """The optimizer's Legs column is clickable.""" + assert ("deribit_options_optimizer_custom_obb", "legs", "legs") in emitters + + def test_the_emitting_widget_has_the_parameter(self, emitters): + """A click sets a parameter the clicked widget declares.""" + widgets = _served_widgets() + + for widget_id, field, param in emitters: + offered = {item["paramName"] for item in widgets[widget_id]["params"]} + + assert param in offered, ( + f"{widget_id}.{field} groups by {param}, which it has no parameter for" + ) + + def test_the_emitting_widget_is_in_a_group_for_it(self, emitters, apps): + """A click reaches other widgets only through a group the emitter joins.""" + for widget_id, field, param in emitters: + joined = [ + group + for app in apps + for group in app["groups"] + if group["paramName"] == param and widget_id in group["widgetIds"] + ] + + assert joined, f"{widget_id}.{field} groups by {param} in no group" + + for group in joined: + assert len(group["widgetIds"]) > 1, ( + f"group {group['name']} holds only {widget_id}" + ) + + +RATE_FIELDS = { + "apr", + "ask_iv", + "bid_iv", + "change_percent", + "current_funding", + "current_interest", + "funding_8h", + "implied_volatility", + "interest_1h", + "interest_8h", + "interest_rate", + "iv", + "volatility", +} + +NOT_A_RATE = {"interest_value"} + + +class TestRateFormatting: + """A rate is shown as a percentage, at the scale the exchange publishes it. + + The exchange sends funding as a decimal fraction โ€” 0.00011627 is 0.0116%. + A field left unmarked renders that fraction raw, which reads as a stray + decimal rather than a rate. + """ + + @pytest.mark.parametrize("model", sorted(MODEL_DATA), ids=lambda m: m) + def test_rates_are_marked_as_percentages(self, model): + """Every rate-like field declares itself a percentage.""" + data = _data_model(model) + + for field in set(data.model_fields) & RATE_FIELDS: + extra = data.model_fields[field].json_schema_extra or {} + + assert extra.get("x-unit_measurement") == "percent", ( + f"{model}.{field} is a rate but is not marked a percentage," + " so it renders as a bare decimal" + ) + + @pytest.mark.parametrize("model", sorted(MODEL_DATA), ids=lambda m: m) + def test_rates_are_stored_in_percent_units(self, model): + """A rate is stored as the number that precedes the percent sign. + + A chart plots the stored value and never applies the grid's formatter, + so asking the grid to scale a fraction leaves the axis reading 0.44 + where the table reads 44 %. + """ + data = _data_model(model) + + for field in set(data.model_fields) & RATE_FIELDS: + extra = data.model_fields[field].json_schema_extra or {} + + assert "x-frontend_multiply" not in extra, ( + f"{model}.{field} asks the grid to scale it, which a chart axis" + " does not do" + ) + + @pytest.mark.parametrize("model", sorted(MODEL_DATA), ids=lambda m: m) + def test_values_that_are_not_rates_stay_plain(self, model): + """A number that is not a rate is never dressed up as one.""" + data = _data_model(model) + + for field in set(data.model_fields) & NOT_A_RATE: + extra = data.model_fields[field].json_schema_extra or {} + + assert extra.get("x-unit_measurement") != "percent", ( + f"{model}.{field} is not a rate" + ) + + def test_the_same_quantity_is_scaled_the_same_way(self): + """A field carried by several models is marked identically in each.""" + seen: dict = {} + + for model in MODEL_DATA: + data = _data_model(model) + + for field in set(data.model_fields) & RATE_FIELDS: + extra = data.model_fields[field].json_schema_extra or {} + mark = ( + extra.get("x-unit_measurement"), + extra.get("x-frontend_multiply"), + ) + seen.setdefault(field, {}).setdefault(mark, []).append(model) + + for field, marks in seen.items(): + assert len(marks) == 1, ( + f"{field} is marked {len(marks)} different ways: " + + "; ".join(f"{mark} in {models}" for mark, models in marks.items()) + ) + + +class TestColumnDefinitions: + """A grid column names a field the endpoint actually returns. + + A column pointing at a field nothing emits renders as an empty strip the + reader cannot explain, and a field with no column is shown with whatever + defaults the grid guesses. + """ + + ANALYSIS = { + "deribit_options_optimizer_custom_obb": ("optimizer", {}), + "deribit_options_straddle_custom_obb": ("straddle", {}), + "deribit_options_strangle_custom_obb": ("strangle", {"moneyness": 4.0}), + "deribit_options_spreads_custom_obb": ("spreads", {"moneyness": 4.0}), + } + + @staticmethod + def _declared(widget_id: str) -> set: + """Return the fields one widget declares a column for.""" + widget = _served_widgets()[widget_id] + table = (widget.get("data") or {}).get("table") or {} + + return {column["field"] for column in table.get("columnsDefs") or []} + + @pytest.mark.parametrize("widget_id", sorted(ANALYSIS), ids=lambda w: w) + def test_every_analysis_column_is_returned(self, widget_id, loaded_chain): + """No analysis widget declares a column its endpoint never fills.""" + import asyncio + + from openbb_deribit.routers import options + + loaded_chain() + command, params = self.ANALYSIS[widget_id] + output = asyncio.run(getattr(options, command)(symbol="BTC", **params)) + emitted: set = set() + + for row in output.results: + emitted |= set(row) + + declared = self._declared(widget_id) + + assert declared, f"{widget_id} declares no columns" + assert not declared - emitted, f"{widget_id} declares dead {declared - emitted}" + assert not emitted - declared, ( + f"{widget_id} returns undeclared {emitted - declared}" + ) + + def test_the_chain_declares_the_fields_it_publishes(self): + """The chain widget names a column for every field it fills. + + Its model publishes one object of columns rather than a row per + contract, so the grid cannot infer them from the response schema and + they are written out instead. + """ + from openbb_deribit.models.options_chains import DeribitOptionsChainsData + + declared = self._declared("deribit_options_chains_deribit_obb") + published = { + name + for name, field in DeribitOptionsChainsData.model_fields.items() + if not ((field.json_schema_extra or {}).get("x-widget_config") or {}).get( + "hide" + ) + } + + assert declared == published, ( + f"declared but not published: {sorted(declared - published)};" + f" published but not declared: {sorted(published - declared)}" + ) + + def test_every_table_widget_has_columns(self): + """No table this extension serves is left for the grid to guess at.""" + bare = [ + widget_id + for widget_id, widget in _served_widgets().items() + if widget_id.startswith("deribit_") + and widget.get("type") == "table" + and not ((widget.get("data") or {}).get("table") or {}).get("columnsDefs") + ] + + assert not bare, f"{bare} serve a table with no columns" + + +class TestMultiSelectCurrencies: + """A multi-select sends its choices joined, so the model must accept them. + + A parameter typed as a bare ``Literal`` renders as a multi-select but + rejects the comma-joined value the moment a second choice is picked, which + the reader sees as the widget breaking on its own dropdown. + """ + + MULTI = ( + "DeribitHistoricalVolatility", + "DeribitVolatilityIndex", + ) + + @pytest.mark.parametrize("name", MULTI, ids=lambda n: n) + def test_several_currencies_are_accepted(self, name): + """Picking a second currency is understood, not refused.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + assert query(currency="BTC,ETH").currency == "BTC,ETH" + + @pytest.mark.parametrize("name", MULTI, ids=lambda n: n) + def test_a_currency_is_read_however_it_is_typed(self, name): + """Case and spacing are the reader's, not the exchange's.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + assert query(currency=" btc , eth ").currency == "BTC,ETH" + + @pytest.mark.parametrize("name", MULTI, ids=lambda n: n) + def test_an_unlisted_currency_names_what_is_listed(self, name): + """A currency the exchange does not publish is refused readably.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + with pytest.raises(ValidationError, match=r"no .* for \['DOGE'\]"): + query(currency="BTC,DOGE") + + def test_every_multi_select_accepts_the_joined_form(self): + """No parameter offers a multi-select its model cannot parse.""" + broken: list = [] + + for name, fetcher in deribit_provider.fetcher_dict.items(): + query = fetcher.query_params_type + extra = getattr(query, "__json_schema_extra__", {}) or {} + + for param, config in extra.items(): + if not config.get("multiple_items_allowed"): + continue + + choices = dict(_literal_fields(query)).get(param) + + if choices and len(choices) > 1: + broken.append(f"{name}.{param}") + + assert not broken, ( + f"{broken} render a multi-select but are typed as a fixed choice," + " so a second selection is rejected before the model is reached" + ) + + +PEGGED = {"USDC", "USDT"} + +VOLATILITY_MODELS = ("DeribitHistoricalVolatility", "DeribitVolatilityIndex") + + +class TestVolatilityIsOnlyOfferedWhereItExists: + """A currency is only offered where the number it produces means something. + + A dollar-pegged token has no volatility against the dollar to read, and the + exchange computes no volatility index for one at all. Offering either is + offering a reading of the peg. + """ + + @pytest.mark.parametrize("name", VOLATILITY_MODELS, ids=lambda n: n) + def test_no_pegged_currency_is_offered(self, name): + """Neither volatility view offers a dollar-pegged token.""" + query = deribit_provider.fetcher_dict[name].query_params_type + offered = { + option["value"] + for option in query.__json_schema_extra__["currency"]["x-widget_config"][ + "options" + ] + } + + assert not offered & PEGGED, f"{name} offers {sorted(offered & PEGGED)}" + + @pytest.mark.parametrize("name", VOLATILITY_MODELS, ids=lambda n: n) + def test_a_pegged_currency_is_refused(self, name): + """Asking for one anyway says what the exchange does publish.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + with pytest.raises(ValidationError, match="USDC"): + query(currency="USDC") + + def test_the_volatility_index_is_only_what_deribit_computes(self): + """DVOL exists for the two coins the exchange computes it for.""" + from openbb_deribit.utils.constants import VOLATILITY_INDEX_CURRENCIES + + assert VOLATILITY_INDEX_CURRENCIES == ("BTC", "ETH") + + def test_settlement_views_still_take_the_currency_that_settles(self): + """A stablecoin is a currency contracts settle in, and that is unchanged.""" + from openbb_deribit.utils.constants import CURRENCIES + + query = deribit_provider.fetcher_dict["DeribitSettlements"].query_params_type + choices = set(dict(_literal_fields(query))["currency"]) + + assert choices >= PEGGED + assert choices <= set(CURRENCIES) + + def test_the_dashboard_does_not_bind_the_two_meanings_together(self): + """Settlement currency and the asset measured are separate groups. + + They share a parameter name but not a set of values, so one control + driving both would send a settlement currency to a volatility widget. + """ + import asyncio + + from openbb_deribit.deribit_router import deribit_apps + + apps = asyncio.run(deribit_apps()) + bound: dict = {} + + for group in apps[0]["groups"]: + for widget_id in group["widgetIds"]: + bound[widget_id] = group["name"] + + volatility = { + bound[widget_id] for widget_id in bound if "volatility" in widget_id + } + settlement = {bound["deribit_reference_expirations_deribit_obb"]} + + assert volatility and not volatility & settlement + + +OPTION_LISTINGS = ("DeribitBookSummary", "DeribitInstruments", "DeribitTrades") + + +class TestOptionsAreListedByUnderlying: + """Options belong to the asset they are written on, not the rail they settle on. + + Only BTC and ETH are both. USDC settles options written on seven different + underlyings, so a listing scoped to it is a mixture rather than a chain, + and USDT settles none at all. + """ + + @pytest.mark.parametrize("name", OPTION_LISTINGS, ids=lambda n: n) + def test_a_settlement_rail_cannot_list_options(self, name): + """Asking for options by a currency that only settles them is refused.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + with pytest.raises(ValidationError, match="it is not an underlying"): + query(currency="USDC", kind="option") + + @pytest.mark.parametrize("name", OPTION_LISTINGS, ids=lambda n: n) + def test_the_same_rail_still_lists_what_it_does_carry(self, name): + """Futures settled in USDC are a real listing and stay reachable.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + assert query(currency="USDC", kind="future").currency == "USDC" + + @pytest.mark.parametrize("name", OPTION_LISTINGS, ids=lambda n: n) + def test_an_underlying_lists_its_own_options(self, name): + """BTC is both the rail and the underlying, so it lists options.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + assert query(currency="BTC", kind="option").currency == "BTC" + + @pytest.mark.parametrize("name", OPTION_LISTINGS, ids=lambda n: n) + def test_naming_the_instrument_bypasses_the_rail(self, name): + """A named contract is not a listing, so the currency does not apply.""" + query = deribit_provider.fetcher_dict[name].query_params_type + + assert query(symbol="SOL_USDC-23SEP26-124-C", currency="USDC", kind="option") + + def test_no_listing_offers_a_currency_that_lists_nothing(self): + """USDT and EURR carry no contracts, so no listing offers them.""" + from openbb_deribit.utils.constants import LISTING_CURRENCIES + + allowed = set(LISTING_CURRENCIES) | {"any"} + + for name in (*OPTION_LISTINGS, "DeribitCombos", "DeribitBlockRfqTrades"): + query = deribit_provider.fetcher_dict[name].query_params_type + choices = set(dict(_literal_fields(query))["currency"]) + + assert choices <= allowed, f"{name} offers {sorted(choices - allowed)}" diff --git a/openbb_platform/providers/deribit/uv.lock b/openbb_platform/providers/deribit/uv.lock new file mode 100644 index 00000000000..cf4b6e440f6 --- /dev/null +++ b/openbb_platform/providers/deribit/uv.lock @@ -0,0 +1,4647 @@ +version = 1 +revision = 3 +requires-python = ">=3.10, <4" +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/4d/4a99fb425c5e0cad715eea7bd190aff46f38b959a0a2dadb993705d34b26/aiohttp-3.14.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb0495d778817619273c108784292be161a924b9f5ae5cbbc70a2caa6838250b", size = 765848, upload-time = "2026-07-23T01:52:08.217Z" }, + { url = "https://files.pythonhosted.org/packages/74/e8/43b85dc55b8e950dc644babe762add781319ea881b57b33d2cce12017d12/aiohttp-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3c200cf9757edd785051dc699c7ecbec22110dbfcb3fefc7a9f9695eda8ea7a", size = 517476, upload-time = "2026-07-23T01:52:10.846Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9e/73b582c4dbbc3c12ef4473822475effaabf1f934b56f14f5b03fe5d3a2af/aiohttp-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5", size = 515334, upload-time = "2026-07-23T01:52:12.636Z" }, + { url = "https://files.pythonhosted.org/packages/79/03/e98c3c9e05a5bdf97defe5ff9169baba4f0ec9a901f2d60e0f060c2f051e/aiohttp-3.14.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:134ac5ddcf61c6fad984b9a5727d83492ada43d63471db20fb73042c13fca62f", size = 1708830, upload-time = "2026-07-23T01:52:14.538Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2c/26e60b694844dfd2176c57f913a22d0cd6a16f9ff202cbda7580d0328b98/aiohttp-3.14.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:70c987b27534f9ae1a723f47ae921571d616da21d3208282bf4c52af5164ac43", size = 1674012, upload-time = "2026-07-23T01:52:16.486Z" }, + { url = "https://files.pythonhosted.org/packages/38/65/672df92e3172cd876aacfa97a952ac560877eb169384b2991ac5b273de4c/aiohttp-3.14.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1b59533861b70a2185c8f4f350f791f39d64358ef6944ce71c5240c9ec0982c9", size = 1767015, upload-time = "2026-07-23T01:52:18.28Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c5/228dec7bfec1c373cc2217cdeb47d6456dcd7a13a4c55144930a75ae3851/aiohttp-3.14.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c5281acc88b92396f88c7e1e2748f8466689df22b80170e4f51efa712fb47a8", size = 1858700, upload-time = "2026-07-23T01:52:20.08Z" }, + { url = "https://files.pythonhosted.org/packages/bd/ff/cb36724e8c8d17f90ada567a9ff3efe1d6e9b549fba697a242aece180f21/aiohttp-3.14.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48d67b87db6279c044760787eb01f6413032c2e6f3ba1cafaa492b1c8e578479", size = 1714075, upload-time = "2026-07-23T01:52:22.071Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3a/296a4135c6366376263aeef54b15caca1f07676c2ae0c525d7832f2f808a/aiohttp-3.14.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f53bcd52f585e1ac3e590d61434eb61f9a88c38df041b4ea126d97144344a77b", size = 1588234, upload-time = "2026-07-23T01:52:23.757Z" }, + { url = "https://files.pythonhosted.org/packages/7d/81/9d5d853ef892dc066d1eb6db0e87a47348b920c1c879aa554612fdbd9d79/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0fdea2281997af69da84c77ffa6f5938a0285f21fb3887c249d67419ca865b3d", size = 1677300, upload-time = "2026-07-23T01:52:25.861Z" }, + { url = "https://files.pythonhosted.org/packages/68/96/021d386ae32d9b26d4b88df2e794546232ff56bb6be952bf6be227c0bbc7/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cda5fd5c95ad7a125a2e8464acc78b98b94c475a3780d6aa0aa157c93f470f4d", size = 1691501, upload-time = "2026-07-23T01:52:28Z" }, + { url = "https://files.pythonhosted.org/packages/29/9f/af66adce26a14af135c003cbd0f44ccaa68cebd30ff8ac99ca47fb4958f7/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6debfa7312ff9d4c124dc71d72e9a0a4b9e0879e48ba6fcb42bef5c3300289e2", size = 1735113, upload-time = "2026-07-23T01:52:29.995Z" }, + { url = "https://files.pythonhosted.org/packages/2f/90/28c390d4c9851effe52ac25b5a2e1d92246acd00728b4fc7975dafb67484/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f4e05329faa0ea1a404b37de4f034fd2c2defcca06a68dc6745e4e56c88e8a48", size = 1577486, upload-time = "2026-07-23T01:52:31.937Z" }, + { url = "https://files.pythonhosted.org/packages/db/c2/00e23a1bf2abb70dd353f6987db7e7f2491d0261f7363997738c71c98f95/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a3a8296e7ab5c295f53f1041487cb088e1480775aafbf7fe545d93b770a0f96f", size = 1751353, upload-time = "2026-07-23T01:52:33.688Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7d/d51a706a8cbfa57f0611127daf61ab3ae02ab8420b0407412079227d1c65/aiohttp-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5373dc80ad1aa2fb9ad95c83f24eef418bbda3a61375f128e5b0192e4f3f9b32", size = 1698681, upload-time = "2026-07-23T01:52:38.167Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b0/90bd5cd9fdd9787cb4211d284d1fb8401339a933cb0227a15b71e789232f/aiohttp-3.14.3-cp310-cp310-win32.whl", hash = "sha256:a3e22975f905b89a55a488c2a08f2fdb2186175349e917d48985cc468a3d4c6e", size = 456733, upload-time = "2026-07-23T01:52:41.823Z" }, + { url = "https://files.pythonhosted.org/packages/d8/15/fe5b8f6a71ae112bc677163d0b0701bda5dc15005249582258ede0eb88c7/aiohttp-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdd0e2834dce1a26c1bbe26464861e16bbe217042cbff619247c11594472518c", size = 480460, upload-time = "2026-07-23T01:52:43.905Z" }, + { url = "https://files.pythonhosted.org/packages/54/00/45e98b6645cd7f00a4b78b749ebd309094b0eaeb2d2e96157eadbc0d0050/aiohttp-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:eac645b09bcfdf73df7536331f0678c1086ea250981118ddb5199e17ccef72bb", size = 453479, upload-time = "2026-07-23T01:52:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5c/b3e4ff8ad43a8afef9602c5e90285936da1beaea8b029016b793891f03c3/aiohttp-3.14.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3", size = 764250, upload-time = "2026-07-23T01:52:48.525Z" }, + { url = "https://files.pythonhosted.org/packages/0e/da/f1b384465e51449d844056b75070461da03a9a23e6c1747003695bf4172a/aiohttp-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a", size = 516281, upload-time = "2026-07-23T01:52:51.047Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3f/01264f820ee2e3712a827892b1cd6ff80f3300c1fcbffbb45714a915d47a/aiohttp-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8", size = 514742, upload-time = "2026-07-23T01:52:53.779Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8d/a71c6f2db52ac1ed142b133f7feddaa6b70539c3f4de24d7e226c95b794c/aiohttp-3.14.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239", size = 1780613, upload-time = "2026-07-23T01:52:56.948Z" }, + { url = "https://files.pythonhosted.org/packages/a5/11/3dd9b3fb3a170f6ec9011b5291d876a6fab4086714c9e158600edf01b4fd/aiohttp-3.14.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f", size = 1737688, upload-time = "2026-07-23T01:52:59.294Z" }, + { url = "https://files.pythonhosted.org/packages/6d/3e/834c26918be7d88068822b40e0db30fca50b5f4fe79104aa16a93f1d74e6/aiohttp-3.14.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06", size = 1845742, upload-time = "2026-07-23T01:53:01.641Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c9/49ab8572df7d66bc13d11e31f781292badb04180dd87ba98733066c6aed7/aiohttp-3.14.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929", size = 1928412, upload-time = "2026-07-23T01:53:04.018Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b9/2b8f0c0ce09c87a1daf80fd483431b56b1435d3f62789bc86f572e1245de/aiohttp-3.14.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db", size = 1786220, upload-time = "2026-07-23T01:53:06.481Z" }, + { url = "https://files.pythonhosted.org/packages/85/00/9c45f81de11710460edfa1dc81317b6e882703b160926c879a9d20da9fcc/aiohttp-3.14.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce", size = 1637231, upload-time = "2026-07-23T01:53:10.258Z" }, + { url = "https://files.pythonhosted.org/packages/19/ce/967d628e910756f3539c6107cb7844a1b69440dcb3029a5ee7871b09ab63/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c", size = 1753161, upload-time = "2026-07-23T01:53:13.817Z" }, + { url = "https://files.pythonhosted.org/packages/11/b2/0c3d4114f0aee4f580f5b3b4eb71b24d7a23b834ea506a4dfebe76513f35/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15", size = 1756356, upload-time = "2026-07-23T01:53:16.211Z" }, + { url = "https://files.pythonhosted.org/packages/63/5d/99e7d91c82f1399d1ae2a854e080bd1493fbc31e5e959dbc4ec33dac3bec/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c", size = 1819846, upload-time = "2026-07-23T01:53:18.289Z" }, + { url = "https://files.pythonhosted.org/packages/ad/05/d5e1cb6480eeffd3f901d40a2c5e2d1e7effdc797837da3b490272699f13/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae", size = 1628531, upload-time = "2026-07-23T01:53:23.86Z" }, + { url = "https://files.pythonhosted.org/packages/c9/90/b934682bcaefae18a9e04f3dff5b68522ba810906358ae5029b68110ea3b/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910", size = 1832712, upload-time = "2026-07-23T01:53:27.551Z" }, + { url = "https://files.pythonhosted.org/packages/21/df/6061679faaf81fac746e7307c7adb71e858071a5d34c27583afefc64f543/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7", size = 1775014, upload-time = "2026-07-23T01:53:30.223Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1d/f854878bbc69b88faefe924b619a34a6f59ec05fd387c77690667eaa75eb/aiohttp-3.14.3-cp311-cp311-win32.whl", hash = "sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa", size = 456006, upload-time = "2026-07-23T01:53:34.97Z" }, + { url = "https://files.pythonhosted.org/packages/73/0c/2af9d1674baccd1dbd47282a93d660a22e57ef6167c856deb24b4214fbab/aiohttp-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d", size = 481069, upload-time = "2026-07-23T01:53:39.673Z" }, + { url = "https://files.pythonhosted.org/packages/8e/76/88401ff3fc95e85c5fc38d588f36f55e61ecb64343b2bc8d69326f453cc0/aiohttp-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39", size = 453021, upload-time = "2026-07-23T01:53:43.749Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/eb96299230e20acf2efae207cb8d69051f1f68e357e5ea5e479bf6fb097a/aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5", size = 754690, upload-time = "2026-07-23T01:53:47.332Z" }, + { url = "https://files.pythonhosted.org/packages/88/11/e7a70a209eb9a067c0d3212b518a0134e3484f5178c7533878b6b514d469/aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228", size = 509484, upload-time = "2026-07-23T01:53:51.159Z" }, + { url = "https://files.pythonhosted.org/packages/30/07/4bbc222cc8dbe31d4c3e8a5baad2286e4d42026ac0c570027b89afce6344/aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee", size = 511949, upload-time = "2026-07-23T01:53:55.083Z" }, + { url = "https://files.pythonhosted.org/packages/54/b9/42e74c46b7b7c794b995bbc1f573fb48950c38b19d8600c62a6804ee2d67/aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a", size = 1765282, upload-time = "2026-07-23T01:53:59.662Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ed/62bc4d74363ad346d518e0720363a949f63e2e23439a79eb5813d4d29bb3/aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b", size = 1741511, upload-time = "2026-07-23T01:54:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9f/181e8a8bc79e47d13c7fc4540bd7a3b729d9505609c61f392a8dd2fbfe55/aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529", size = 1810680, upload-time = "2026-07-23T01:54:09.882Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/dec94d6ad694552fe3424e3f1928d7a606a5d9d9433a04e7ecdd9d38ae7f/aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787", size = 1905646, upload-time = "2026-07-23T01:54:13.475Z" }, + { url = "https://files.pythonhosted.org/packages/52/b7/7cd31f29d6055bd711ae6e669367fba6f5ae9de463910a793e30556a8db7/aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42", size = 1792122, upload-time = "2026-07-23T01:54:15.752Z" }, + { url = "https://files.pythonhosted.org/packages/66/73/10b1ef93afa61f4963c746257b70ced619cf31a4798671de5fdb2608501d/aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b", size = 1591127, upload-time = "2026-07-23T01:54:19.489Z" }, + { url = "https://files.pythonhosted.org/packages/49/ed/3b203fa6de1b338c14acdc06bf6ca9b043b7944f005966958c2ced932cde/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043", size = 1725210, upload-time = "2026-07-23T01:54:24.129Z" }, + { url = "https://files.pythonhosted.org/packages/28/b7/1c2aab8c706436dcc28598452488ac9cd7c409da815237c28c27d58993e6/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427", size = 1764848, upload-time = "2026-07-23T01:54:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/54/50/94c28f08b131c4bf10984ea2c7a536c9920608bb2d6e7f95642c30cc87b7/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d", size = 1777102, upload-time = "2026-07-23T01:54:31.775Z" }, + { url = "https://files.pythonhosted.org/packages/13/d4/e7d09ba7d345fb2d74440fd2fa033c5e079fac05552927705986f41a364f/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0", size = 1580205, upload-time = "2026-07-23T01:54:34.518Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/072a91d68e1e1eb587985b54baab94221277f877e8ef274fc213a0ceae28/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d", size = 1797219, upload-time = "2026-07-23T01:54:36.995Z" }, + { url = "https://files.pythonhosted.org/packages/e0/eb/aad34e897e668424d6e995da5dff8a4a09af93363d3392488772957a63aa/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19", size = 1768629, upload-time = "2026-07-23T01:54:40.103Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2b/6bb88ddba0fecd9122aa3ebcad25996cf6c083a4a7040dbb3a4f97972af6/aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559", size = 451481, upload-time = "2026-07-23T01:54:42.547Z" }, + { url = "https://files.pythonhosted.org/packages/76/9b/f2f8f108da17ecef2cc3efc424e8b7ad3782b1a8360f7b8eae8ced84f6ea/aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a", size = 476845, upload-time = "2026-07-23T01:54:44.853Z" }, + { url = "https://files.pythonhosted.org/packages/3e/44/28dac80a8941b604f4da10ce21097614ca1bf905ce93dca28d8d7de9c1e7/aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c", size = 448050, upload-time = "2026-07-23T01:54:47.087Z" }, + { url = "https://files.pythonhosted.org/packages/57/be/5afd201cc0ab139029aadb75392efe85a293403d9dd3a3226161c21ce00c/aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86", size = 506269, upload-time = "2026-07-23T01:54:49.075Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/dec8189d62b45ade009f6792a2264b942a90cb88aeaf181239933cd72c3c/aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627", size = 515166, upload-time = "2026-07-23T01:54:51.894Z" }, + { url = "https://files.pythonhosted.org/packages/28/24/2854869d29ed8a8b19d74f9ec6629515f7e04d02dd329d9d179201e58e47/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82", size = 486263, upload-time = "2026-07-23T01:54:54.223Z" }, + { url = "https://files.pythonhosted.org/packages/d4/dd/57187c8be2a35aea65eaee3bd2c3dcbbcf0204f5106c89637e3610380cd1/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c", size = 492299, upload-time = "2026-07-23T01:54:56.236Z" }, + { url = "https://files.pythonhosted.org/packages/b9/11/06ae6ed8f0d414edf4068861e233d8fe23ee699bfd4b3ceb8663db948a62/aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f", size = 502235, upload-time = "2026-07-23T01:54:58.377Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a3/559639c34a345d2cf7c52dff6838119f2eaf29eb508227b5b83f573af813/aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80", size = 750883, upload-time = "2026-07-23T01:55:00.65Z" }, + { url = "https://files.pythonhosted.org/packages/91/cd/41e131f13afd1e7b0172a9d9eda085ef90eb8439f41f0d279db81ed3ae60/aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0", size = 508473, upload-time = "2026-07-23T01:55:02.945Z" }, + { url = "https://files.pythonhosted.org/packages/bc/6b/e7f13410d391c6e55b4c007a8de024355389d7d459e3d64c42b2d33617e5/aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf", size = 509190, upload-time = "2026-07-23T01:55:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/97/21/6464573e53d69672cc1eada3e5c5cb2d2efa82701e8305a0f2047a576967/aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd", size = 1761478, upload-time = "2026-07-23T01:55:07.383Z" }, + { url = "https://files.pythonhosted.org/packages/1a/81/d217043a4c17fbce360905e3b2bdd20139ebc9a2de836d035d179c4da006/aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807", size = 1735092, upload-time = "2026-07-23T01:55:09.803Z" }, + { url = "https://files.pythonhosted.org/packages/a1/66/e13a02d0eeb1a9a502402a977abb4e4abff9fe4051c26f80558c57a7c975/aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8", size = 1800546, upload-time = "2026-07-23T01:55:12.012Z" }, + { url = "https://files.pythonhosted.org/packages/26/5e/57d42fca1d18cb5acc1cad945d017fabc5d6ae71d8a08ad66be8dc3ee544/aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24", size = 1895250, upload-time = "2026-07-23T01:55:14.357Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1c/7da8d08e74d56f00070822f9638ff3f1c563f8ad87d1efa996c87bfc8644/aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5", size = 1789289, upload-time = "2026-07-23T01:55:16.668Z" }, + { url = "https://files.pythonhosted.org/packages/cd/0f/cf16bcf56896981c1a0319f5d5db9337994b5165730c48a8fa07e9b34be6/aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4", size = 1586706, upload-time = "2026-07-23T01:55:18.913Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6f/76eac12a7f2480e1e304f842efdb07db33256b0d9165b866b6ef0806c202/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9", size = 1724652, upload-time = "2026-07-23T01:55:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/39/b6/19c8c592baeeb94b75f966547d40c02ac7590902306ec5863d5c027cf506/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1", size = 1756239, upload-time = "2026-07-23T01:55:23.705Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c9/4e9383150296f97f873b680c4de8fb2cd88608fb9f48c79edcb111611abc/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371", size = 1769161, upload-time = "2026-07-23T01:55:26.082Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1e/147bdc6cc5de5f3ab011be8bf5d6e786633249f22c20bae06f85e45f5387/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde", size = 1578759, upload-time = "2026-07-23T01:55:28.846Z" }, + { url = "https://files.pythonhosted.org/packages/fd/31/78388a9d6040ece2e11df62ea229a822cf5e52d238374b220ae9975b2623/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e", size = 1792025, upload-time = "2026-07-23T01:55:31.457Z" }, + { url = "https://files.pythonhosted.org/packages/03/51/a3d29fdf2c25d796746af8ad6fe56a45d6256c38b0a8a2ed752e1160b3a2/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71", size = 1768477, upload-time = "2026-07-23T01:55:33.87Z" }, + { url = "https://files.pythonhosted.org/packages/29/a6/442e18b5afeade534d877a2dc3c3e392aff8d49787890b0cf84790410267/aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0", size = 451069, upload-time = "2026-07-23T01:55:36.121Z" }, + { url = "https://files.pythonhosted.org/packages/9d/69/3d876ac02659f271cf7f6769f14a8e3de5b6e888ed8b5a7e998086a4cec8/aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883", size = 476518, upload-time = "2026-07-23T01:55:38.303Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0e/50d6e6471cd31edce8b282bdec59375a3a69124d8a989a0b1313355cae52/aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2", size = 447676, upload-time = "2026-07-23T01:55:40.451Z" }, + { url = "https://files.pythonhosted.org/packages/c8/20/887fdcf832326571b370ffc347b3e70abe101096f3720126aac161b1d872/aiohttp-3.14.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062", size = 509067, upload-time = "2026-07-23T01:55:42.618Z" }, + { url = "https://files.pythonhosted.org/packages/ad/a3/92cec936f78cc4bf0fa5554ebe593b73459d94e3c62303e1902a4cccb6f7/aiohttp-3.14.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6", size = 514774, upload-time = "2026-07-23T01:55:44.937Z" }, + { url = "https://files.pythonhosted.org/packages/29/ba/2a0c38df3fc557620b6a5acd98364af050053b6285b4dc7ee74100c63c18/aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919", size = 488134, upload-time = "2026-07-23T01:55:47.135Z" }, + { url = "https://files.pythonhosted.org/packages/48/d6/d51b7d4bf309af3693940d8ffd2b9ed0b682434ef85959b7c9c137f60cf8/aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7", size = 494201, upload-time = "2026-07-23T01:55:49.451Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5a/8f624384e5f1efabb5229b94157eb966b021e97bdb188c62860c2ae243c2/aiohttp-3.14.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0", size = 502766, upload-time = "2026-07-23T01:55:51.656Z" }, + { url = "https://files.pythonhosted.org/packages/a6/26/4ff0164370deec18fb19254ee4ab10b7a73304ac0c860b13f5f84663759b/aiohttp-3.14.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924", size = 756557, upload-time = "2026-07-23T01:55:53.964Z" }, + { url = "https://files.pythonhosted.org/packages/97/a3/7056b86dc0d9ec709ea9777eae3b0161428f943372f8b98c01c11593b682/aiohttp-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646", size = 510168, upload-time = "2026-07-23T01:55:56.22Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/0357a015892fd68058bf2d39d3fd1958e459b997a7db30aaa6aaa434ae96/aiohttp-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b", size = 512957, upload-time = "2026-07-23T01:55:58.437Z" }, + { url = "https://files.pythonhosted.org/packages/47/d1/8aba53f15ccb2238405f5e9d30e2a8ca44f93878c26e7165ade00d374b1c/aiohttp-3.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30", size = 1750149, upload-time = "2026-07-23T01:56:00.856Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/40c3fee327529284375c6701cbb0fa4600cc2e8432af1378f897e2ef7d3a/aiohttp-3.14.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9", size = 1707685, upload-time = "2026-07-23T01:56:03.371Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a3/ca0cc6724cca8114b05694abd916060758c79894c3aa5b012cdadc1bc28e/aiohttp-3.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f", size = 1803911, upload-time = "2026-07-23T01:56:05.817Z" }, + { url = "https://files.pythonhosted.org/packages/95/b5/85b099c299c3ffd38ad9b3e43694c8a346934e4a30c88c4fd5a841234f77/aiohttp-3.14.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d", size = 1876929, upload-time = "2026-07-23T01:56:08.413Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b7/1da684a04175473fa4cddbf9a2f572e79514c3fd27a74597f43057d4f3da/aiohttp-3.14.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147", size = 1761112, upload-time = "2026-07-23T01:56:10.918Z" }, + { url = "https://files.pythonhosted.org/packages/d1/16/bc4b55e3e5cb175fd69c53c90d60d2f47797cb343da5106e23863dc4dba4/aiohttp-3.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c", size = 1583500, upload-time = "2026-07-23T01:56:13.613Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e8/13a9d957a1ee40837f46aa30f0f4c657e673ad86a2e6362a9f9be20d26d9/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a", size = 1713940, upload-time = "2026-07-23T01:56:15.969Z" }, + { url = "https://files.pythonhosted.org/packages/38/05/d33c680c1bcf1c7e130f9cbfc1fc02fe8bb0c4af2a94a53dd5fb56131e5c/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0", size = 1724413, upload-time = "2026-07-23T01:56:18.591Z" }, + { url = "https://files.pythonhosted.org/packages/85/1d/af798d306f7a74b6a632dbcabcf62a4c91391b7582d2a8c6d7712e2cc54e/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661", size = 1770748, upload-time = "2026-07-23T01:56:21.074Z" }, + { url = "https://files.pythonhosted.org/packages/a8/92/ad720d472556a995049206867765e9410969684f86ee09423ff9969044c1/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22", size = 1577564, upload-time = "2026-07-23T01:56:23.475Z" }, + { url = "https://files.pythonhosted.org/packages/60/ad/0ed7586cbef7a884e23a752fa2bb987a122e6a5dd50dab109258d0a95193/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41", size = 1782080, upload-time = "2026-07-23T01:56:25.994Z" }, + { url = "https://files.pythonhosted.org/packages/97/ea/dbaed0d73e8a69aad653b045dab451c67c2454bb731a37b45a86593e9422/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf", size = 1745813, upload-time = "2026-07-23T01:56:28.604Z" }, + { url = "https://files.pythonhosted.org/packages/81/1b/6893d4bc57e434fc93a6c9217c637d967a0b651d989f6e3265179375754a/aiohttp-3.14.3-cp314-cp314-win32.whl", hash = "sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da", size = 455872, upload-time = "2026-07-23T01:56:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/f5/8b/c7baa1ba1eda4db6989baefe5de6d99834921b84ebd7918624febcb9f290/aiohttp-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100", size = 481030, upload-time = "2026-07-23T01:56:33.365Z" }, + { url = "https://files.pythonhosted.org/packages/22/8c/c29d067df825a2df88ca432db848aa2fe8199598359cc06c12b09320cac9/aiohttp-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc", size = 453669, upload-time = "2026-07-23T01:56:35.731Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a4/9c033beb355d39b6147980597ec9645e4729243f686ee4dc73945de72030/aiohttp-3.14.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b", size = 791403, upload-time = "2026-07-23T01:56:37.972Z" }, + { url = "https://files.pythonhosted.org/packages/80/ca/87c32a0a7704583cfc49660bd817889bae5b830bf53b5dcb4e92145ac2da/aiohttp-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0", size = 526413, upload-time = "2026-07-23T01:56:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d8/8ec0e471248c500acdce2be3f46db8fb62b5eb60efef072529cc85ee1d26/aiohttp-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e", size = 532135, upload-time = "2026-07-23T01:56:42.876Z" }, + { url = "https://files.pythonhosted.org/packages/fe/45/f8919fd936e8b79fcd9bda7b6d8e62613462a713f4f17987fd7c34399142/aiohttp-3.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716", size = 1922742, upload-time = "2026-07-23T01:56:45.528Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ec/9ca76b28a27525b0cc53e20842e0228b022f301ce1f436b7d814b4aaf2df/aiohttp-3.14.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f", size = 1787371, upload-time = "2026-07-23T01:56:48.045Z" }, + { url = "https://files.pythonhosted.org/packages/b1/04/6acdbf17315f7b55f1937e3387acb89a3cddeb4995689553d064af8e92ab/aiohttp-3.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553", size = 1912623, upload-time = "2026-07-23T01:56:50.605Z" }, + { url = "https://files.pythonhosted.org/packages/86/e6/438b0c79ca6f45eb9fd9817dd4c01a91919a38c0de5ee9e05e2b4dc0ece7/aiohttp-3.14.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100", size = 2005515, upload-time = "2026-07-23T01:56:53.153Z" }, + { url = "https://files.pythonhosted.org/packages/bb/6b/62cbd6577758699525f5c712d1ddef57d9875fbab0ae8d5f5a202fd598f8/aiohttp-3.14.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85", size = 1879906, upload-time = "2026-07-23T01:56:55.818Z" }, + { url = "https://files.pythonhosted.org/packages/00/95/18bcbf830a21dc3aae24d8f6b6feaf3db1d2090242d00a7868db2ffb0b67/aiohttp-3.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33", size = 1675849, upload-time = "2026-07-23T01:56:58.861Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/47f4968659c5e23606c3790c80fc624e691c153d036148449ee84d31b287/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f", size = 1843496, upload-time = "2026-07-23T01:57:01.591Z" }, + { url = "https://files.pythonhosted.org/packages/64/af/38c33c4dd82fddcb4e56c4653b6f1072a8edbc6b7fa15809f14932c41e2d/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0", size = 1827746, upload-time = "2026-07-23T01:57:05.131Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9d/0537cda4885ac8f5b7053d164dd06312f4c483a4edcb8ee5b8aaf2a989bf/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098", size = 1853810, upload-time = "2026-07-23T01:57:08.043Z" }, + { url = "https://files.pythonhosted.org/packages/19/fe/26f9c5e6458385aa86497836b0dea6fb2f027827d63f37c7856cce9286ee/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25", size = 1668895, upload-time = "2026-07-23T01:57:10.837Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4c/618b1db9b9ba079b8875d2cdf78e7c4a3bf72903bd5850fee7dd9544600a/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9", size = 1883833, upload-time = "2026-07-23T01:57:13.672Z" }, + { url = "https://files.pythonhosted.org/packages/94/c6/bd959bd1e4771f9fd944e9e436224c48c77b018b73b519b5aad346335bcc/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb", size = 1844251, upload-time = "2026-07-23T01:57:16.593Z" }, + { url = "https://files.pythonhosted.org/packages/5e/19/08d41839658bdd44a0ed2480f3891705ecb487ce28c0dde62c9040c997e0/aiohttp-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963", size = 474180, upload-time = "2026-07-23T01:57:19.306Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/3cd6ef0a2b2851f7ab913b5b079334781bd50ff56a323e4454063377a080/aiohttp-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b", size = 500528, upload-time = "2026-07-23T01:57:21.762Z" }, + { url = "https://files.pythonhosted.org/packages/a4/37/cfd1ed540a4d318da025590d96b728e63713c09e9377950fc655dadeb856/aiohttp-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7", size = 469280, upload-time = "2026-07-23T01:57:24.241Z" }, +] + +[[package]] +name = "aiohttp-client-cache" +version = "0.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "attrs" }, + { name = "itsdangerous" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "url-normalize" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/21/070849a673103328285964419caa12de583229bbcd3552e73799b4ca86d0/aiohttp_client_cache-0.14.3.tar.gz", hash = "sha256:329f4038c6a8ed0b410023980b6d1a2c484af33e667a89ce245c899d62c1fba1", size = 63188, upload-time = "2026-01-07T20:43:32.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/c8/c647b16eaa97f6e668a9311e21392e58a954ec373f8fa6d1855304715a14/aiohttp_client_cache-0.14.3-py3-none-any.whl", hash = "sha256:1154497739dcf9c7f6f6f1f27dc3985d8a7f5f8f31fb76710c06044ffac6f983", size = 33553, upload-time = "2026-01-07T20:43:31.585Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "aiosqlite" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821, upload-time = "2025-12-23T19:25:43.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405, upload-time = "2025-12-23T19:25:42.139Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, +] + +[[package]] +name = "anyio" +version = "4.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, +] + +[[package]] +name = "appnope" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/f7/a82489c2b6ebe32d3e2831895ae19c77861f0eadf3bb16034484d965dbb2/appnope-1.0.0.tar.gz", hash = "sha256:685db59cb6043c3c2e528adc0b3bce3a5f8d09bcf7492c6ea650d1b7421f3c49", size = 5454, upload-time = "2026-08-20T21:36:13.748Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/c7/6b687cb0f83d2a51017d47953f2ee430ffad6fec2a8ebc964e0718a33eb1/appnope-1.0.0-py3-none-any.whl", hash = "sha256:6fe0c04218aab65c54c4ff81638cdbf848d89f5653b74d68638a137f200dd16e", size = 4158, upload-time = "2026-08-20T21:36:12.444Z" }, +] + +[[package]] +name = "astroid" +version = "3.3.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/74/dfb75f9ccd592bbedb175d4a32fc643cf569d7c218508bfbd6ea7ef9c091/astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce", size = 400439, upload-time = "2025-07-13T18:04:23.177Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/0f/3b8fdc946b4d9cc8cc1e8af42c4e409468c84441b933d037e101b3d72d86/astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec", size = 275612, upload-time = "2025-07-13T18:04:21.07Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/1e/faf0f247f6f881b98fc4d6d07e14085cb89d13665084e6d6ac1dc2c03d0b/asttokens-3.0.2.tar.gz", hash = "sha256:3ecdbd8f2cc195f53ccada3a613538bb5f9ef6f6869129f13e03c30a677b8fe2", size = 63136, upload-time = "2026-07-12T03:31:49.084Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl", hash = "sha256:9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933", size = 28702, upload-time = "2026-07-12T03:31:47.542Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + +[[package]] +name = "black" +version = "26.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/84/b3f55026206a9e8820a91503308075ca48eadc515e436731ca01dbe043b3/black-26.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9942db8888e06943c5dde66ca0037dcff82a2a4ec1ad0ada9e0d2ee9d9823893", size = 1987719, upload-time = "2026-05-18T17:05:02.757Z" }, + { url = "https://files.pythonhosted.org/packages/c6/34/7db312c5e5783d6e76cffd9d5ac8972a32badae4c6e3288dac0eed8d3bed/black-26.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:89c93167a74d3a75dfaa38a5c7cca015537d5820dd7f17d63267d674a61cae90", size = 1810083, upload-time = "2026-05-18T17:05:04.302Z" }, + { url = "https://files.pythonhosted.org/packages/33/e2/e0101e73c2c8727634e2efcb35e2b34bd23ad70dfa673789f5773a591b21/black-26.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f2cd76d069cc54c71f10360744ba8983fbb616903b4304a85b734915c8e1b4", size = 1860633, upload-time = "2026-05-18T17:05:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4c/e15c0c5b23cf3651035fe5addcce90e283af3548a3f91bb03d81b83106ab/black-26.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:87ed5c6f450580a2f6790bc7cbfb016dfc73bc750249762268a3695361315eef", size = 1477886, upload-time = "2026-05-18T17:05:07.96Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3f/59d43ade98d2ce5c8dc34a4e46cbecd177e6d55d7d4092969c6003ccc655/black-26.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:58b4bd92cf88aacf83d88479c8f9caee044b1ec55f2451a337354a7ea2590a22", size = 1277111, upload-time = "2026-05-18T17:05:09.473Z" }, + { url = "https://files.pythonhosted.org/packages/4b/96/3c3e09f09f44a37aac36b178a279cd19aa7001bd796187a7b162a294c81f/black-26.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96ae2c733b2aabdd9986e2c5df628ff3473676cd1c5faded1ff496cf6d74083c", size = 1970639, upload-time = "2026-05-18T17:05:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/83/ea/5ad117b9ee3ecd933c712bcbae610006e5b7cc9f41c526cd7ed3b6c4124c/black-26.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e48b87e03bf109288e55cfceadcfa15ff5470aca2851a851950ed2926f450d7", size = 1792130, upload-time = "2026-05-18T17:05:12.983Z" }, + { url = "https://files.pythonhosted.org/packages/06/3a/7c448bc623fcdfa96672531beb5a616ea5e64f6975955254d7731ffb0ad9/black-26.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5119fa92ae61f786e8c3662fd60aece1d0a2dd5cca5d0c79417a95e7a4272a59", size = 1846134, upload-time = "2026-05-18T17:05:14.506Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5b/0b39b3a5917f0657ac014ad2edb58c139553a478adfe7f817abf1622ff6e/black-26.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:30d3c14661f2792e9142cce3eeeb1cbc175b3eb5f733be0c8eeb99651e52b0c3", size = 1478883, upload-time = "2026-05-18T17:05:16.542Z" }, + { url = "https://files.pythonhosted.org/packages/4c/48/dc222692e0f95030db1bbfb6c857e76858bad09058221ea7aae815255327/black-26.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:1ef92b76f7733f282fd096ea406200b5a286c42947412b0eaff3a74e3616cefe", size = 1277776, upload-time = "2026-05-18T17:05:18.029Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/7744b906703228264ef73bdd534df88ec1ef3de45c4e78f6d31b9e32d0c9/black-26.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ad6fa01f941920f54f2bbb35f3df7673428a0ef98a0b0840c2eaef3b110efa8", size = 2012518, upload-time = "2026-05-18T17:05:20.108Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c0/c5a3b1636dfd09c42534f2b3cf33506814f6d3e066fb0879ffa16c1ae860/black-26.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3915f256e75a2d7cf88d8953d37f780455dc586cc72dee059c528fe77f581217", size = 1816016, upload-time = "2026-05-18T17:05:21.84Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0e/36044316b65ca471d3bb6d3703fd06fb50c6b727c3562f6a5a3153634f88/black-26.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d98d4137277c75dfb898ec8d846c4fd68ba1e9cf77f95e2865c203dc18f4c3d", size = 1884150, upload-time = "2026-05-18T17:05:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/b3/33/dafc5808c2af43672912111d7c3354af1615f7e2be3bed7a878461abbe4d/black-26.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:a1dca32d9f1784af512a13410ec204c6f7f0aa9797a111c42e1c03449821c264", size = 1486825, upload-time = "2026-05-18T17:05:25.004Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/b965ee6ad2a311f28bdbf692def3ee9848d2ae289dab28b27657fcee3e78/black-26.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1037d5ac7b7b310b2632ad867ec8d0e4c4819dcdb0b820f63135da746a24e418", size = 1288646, upload-time = "2026-05-18T17:05:26.477Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020, upload-time = "2026-05-18T17:05:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335, upload-time = "2026-05-18T17:05:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614, upload-time = "2026-05-18T17:05:32.718Z" }, + { url = "https://files.pythonhosted.org/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925, upload-time = "2026-05-18T17:05:34.259Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883, upload-time = "2026-05-18T17:05:36.019Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, +] + +[[package]] +name = "cachebox" +version = "5.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/f6/85f176d2518cf1d1be5f981fc2dadf6b131e33fefd721f36b330e3434d6c/cachebox-5.2.3.tar.gz", hash = "sha256:b1f68246685aa739bbbd2734befb1465363a1e1042407c154feadb065f17a099", size = 63686, upload-time = "2026-04-10T12:21:35.028Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/9e/88193fcb7a2a43fe8ed9d9888374d43fa5c7176aa802651e68b28f1aee4a/cachebox-5.2.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c2c89720547271d36e10cad2c7302bbe11f46eb39eead0a2c321c2d371b8f8b6", size = 374393, upload-time = "2026-04-10T12:20:20.424Z" }, + { url = "https://files.pythonhosted.org/packages/98/8d/e0b13d9bfd43f295cce7824ebaac1970f818a7027c16f290de404934cafe/cachebox-5.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7f33d24e90dc8aa26762e25898c91a1223b66685420a28a3628fa2e006924f5", size = 356318, upload-time = "2026-04-10T12:20:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/bc/02/8ae1b63dbdebb2ebf600523f48b54e9bfb10db5a28551c3432346f49e1dd/cachebox-5.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56cb03ec6289a2ac5daf7422d755683324f02d821bfa796087100df2a7ebd5de", size = 395782, upload-time = "2026-04-10T12:18:50.054Z" }, + { url = "https://files.pythonhosted.org/packages/e4/2f/79a8a0057f354581c25a1a00ddabbd5db4b8631d192670d7a0cc4271dbb7/cachebox-5.2.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a71a71df463ba4c86bc843fa01c3a2a721033adefad888af28c6b65e1915a75c", size = 353194, upload-time = "2026-04-10T12:19:03.083Z" }, + { url = "https://files.pythonhosted.org/packages/3b/57/a1fead35cf481432bd87def0653cd4a069b1ea5847589255795e49ae74b8/cachebox-5.2.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbe4655371d19fc9f4f5874312bcb6e5b5b6182989979ac33d93c34c8d10c012", size = 371090, upload-time = "2026-04-10T12:19:16.019Z" }, + { url = "https://files.pythonhosted.org/packages/8c/58/53f1fab8bcc3238fd6c533ef3ab146097986a8acb722863c688a2410c1b2/cachebox-5.2.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4974476d1779961df89d6e6f79e6103a1659289d3ee11c92adcb52e236a8aaeb", size = 390902, upload-time = "2026-04-10T12:19:28.258Z" }, + { url = "https://files.pythonhosted.org/packages/11/2f/5abff74666f8388d2c9516c265f99c33484c827f7fcb3cd703c2f3cbb17e/cachebox-5.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad16d733219f4cab3eec6533af30ab7b9c919c6e3e22ad1ef4eb82629a62edef", size = 395855, upload-time = "2026-04-10T12:19:54.207Z" }, + { url = "https://files.pythonhosted.org/packages/dd/11/30b429db12ab5df663aa108bcfac42805f733da65b0bf452f60bfaf4a530/cachebox-5.2.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:12a9e0a93774ca2b3a9fe8a2a0d0812e399fac4af0fce6246a5bca1e7009b8fc", size = 425760, upload-time = "2026-04-10T12:19:41.138Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b4/fdac1bb902b954c03d23eb301d645a328c9664caff5898930fdbd92fde80/cachebox-5.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:be89497a011eb7a638d13cc520244d77579c0f515b95bf759b3de0b90a015203", size = 564988, upload-time = "2026-04-10T12:20:34.673Z" }, + { url = "https://files.pythonhosted.org/packages/4e/63/76cd5405b0339f15bf86593258bf9bc5608f10a5e0fa6f37a282b42a6caa/cachebox-5.2.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:dd01fc0c1934cccb76493eb4b149a9232d299e5e0275f557adf875c3d25cec81", size = 669110, upload-time = "2026-04-10T12:20:49.039Z" }, + { url = "https://files.pythonhosted.org/packages/d9/bc/52d154aa0407bafce94d1d8d3ff27ca5e842f8311be43cfabdefcbb0f6b7/cachebox-5.2.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a0dfd97b0968f8bd48c33098a03d10f797964559c3a437c84bf97a9973545714", size = 643768, upload-time = "2026-04-10T12:21:04.095Z" }, + { url = "https://files.pythonhosted.org/packages/51/d9/82627eb8cecaf5e7e601bbc65d474a1c3053a2fbc21618ddc6aac19c47dc/cachebox-5.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:223ccf7ac60f595def258e7bc74c0b1d6f43991c9cae6d06749c803d22786d99", size = 610047, upload-time = "2026-04-10T12:21:19.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2e/cc5b303746418fde00c93ddbc295733b4e2d131d2e8f5afbc6f45f50454e/cachebox-5.2.3-cp310-cp310-win32.whl", hash = "sha256:745b805fdd99931c3ce1d87d2ee21ca3fb62cba6b4e1f674907af87aad73dce4", size = 275529, upload-time = "2026-04-10T12:21:49.84Z" }, + { url = "https://files.pythonhosted.org/packages/31/72/fb10d6f779d041f701b89f0b7830329f51d1846fbc600869f9f7d635b7b5/cachebox-5.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:a87b19c0a3d8d665a9805b5b4afd64b40082395b70ebe2756131ed1edb0c8f02", size = 287988, upload-time = "2026-04-10T12:21:36.41Z" }, + { url = "https://files.pythonhosted.org/packages/81/88/154179d492f2c000fe6efab3c3ff6b8eb94fbfaa09efe47999bce6b1e29f/cachebox-5.2.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:996f49d04b234082530afcc650bdd00556afbebc19c6c0daaafb85950340cb3c", size = 374245, upload-time = "2026-04-10T12:20:22.042Z" }, + { url = "https://files.pythonhosted.org/packages/7d/9d/3b03f2e063161bcb1a5e0969d521b5c622c2da02252a5c8bd4ef0e4f9914/cachebox-5.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23a3300ebbb526fa12ce6fa53699002f5fba6da23b4bbbaf8ba8b18a3f03e6b3", size = 356308, upload-time = "2026-04-10T12:20:09.149Z" }, + { url = "https://files.pythonhosted.org/packages/bb/9b/8da38af731e3832e9f987548e4bfb610d7f3054019e12c44a94ba9272b37/cachebox-5.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79c63ee1589364caa04c018405e625d2e44e0bf9994f2715b2f322075d8c45b6", size = 395666, upload-time = "2026-04-10T12:18:51.89Z" }, + { url = "https://files.pythonhosted.org/packages/01/dd/1522aa808f94c904c5eb3640991799fed14dd43c1dd99a9f7b71bd95b1e3/cachebox-5.2.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebd0f8d4ebc3943c1ddcbbdc54f1a8ddf95505c862ed5731319cebd1eb98ae41", size = 353362, upload-time = "2026-04-10T12:19:04.536Z" }, + { url = "https://files.pythonhosted.org/packages/dd/52/95bf883ec9b69a76f3a7d9fb14d015d9a4bdab0143a3eff62ceebc8b1419/cachebox-5.2.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:569966efcc6309aa7d774443e3513cdbb8671efae0158138ba2ebb7d8cc9d8ed", size = 371007, upload-time = "2026-04-10T12:19:17.484Z" }, + { url = "https://files.pythonhosted.org/packages/4b/3d/cc02066d5ccfcb8b35adbaf867977fdb54572cda56ace56da396f0caa3bf/cachebox-5.2.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5774d06f0da37dd566239a4376d6ca8cf983d3e4c3228712ec22b4130f662f21", size = 390670, upload-time = "2026-04-10T12:19:29.685Z" }, + { url = "https://files.pythonhosted.org/packages/b3/50/8e4d59b3e344405d8393d6cc5cc92754d3cc1d81134041ebffd3f5ab73e6/cachebox-5.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5bf8755bc66bcf42e7ca5c42d703a041a7aaad58f9a0c3be54d5b1cefd2641", size = 395765, upload-time = "2026-04-10T12:19:56.169Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d4/d731cff1c4cec22404bd3ddda05b233c5efaa5f13d7abf4e2728905b7cdd/cachebox-5.2.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63f061cc6a5ca70bbce2e6be0588fe2fee00a93a1b0581b1086d54b10288cdb6", size = 425707, upload-time = "2026-04-10T12:19:42.714Z" }, + { url = "https://files.pythonhosted.org/packages/36/01/3ec8aadceb0dcc66dbd0b9b32966cf7b6928ed84471424c24d21b0af62d0/cachebox-5.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577c781f18b559f4dc9eea176c6aed008843ef4b8e045cf61bb519e09dccc9ef", size = 564759, upload-time = "2026-04-10T12:20:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/db/23/31cbc8623ecc2e25900f7e8f20f11bfb84786989a59a8046e70b27cbea6d/cachebox-5.2.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:7f691e25572a3ddbb018e19d796f774713bd6b0f7ce9be2e71f6e18572de264a", size = 669309, upload-time = "2026-04-10T12:20:51.117Z" }, + { url = "https://files.pythonhosted.org/packages/34/29/5a9e92bdc7b32dc865e73dd776638244f900136daee5bb0591a67e1530fa/cachebox-5.2.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:33368adf86669c29b936fbae5d6219cf90aacd4b1db71dae2e23d584a8219cd6", size = 643705, upload-time = "2026-04-10T12:21:05.882Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/5273a412855fdc11f674e4749aee6d5ec0a91f5c1a9f6e922f7fa0cb7a83/cachebox-5.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:38ce67b7b45713e49459a09411d07f82de04022c04aecde6202cd32f934c2b1f", size = 609751, upload-time = "2026-04-10T12:21:21.331Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a4/0fadb5e6a00f373cc3fe56b4415cdea2fc0147f6ec475611762d16eb4b05/cachebox-5.2.3-cp311-cp311-win32.whl", hash = "sha256:a7cd2c81347063ab6c512d0f569aeb5f75fc2dfe686c8486258ffd08052324f4", size = 275485, upload-time = "2026-04-10T12:21:51.563Z" }, + { url = "https://files.pythonhosted.org/packages/03/83/67c1bf83f815294d2c3acd7631f25b5cbe6067e1d56495f76829dd60057b/cachebox-5.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:7e45798d6b969794840bb302857946d710ecb32af78dfcb3ab40f4e68ee7fdaf", size = 288024, upload-time = "2026-04-10T12:21:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e7/6fa6abfc9c4c07b88f09a88466fa93c7081fd679d8e06f8f558bb4ac845c/cachebox-5.2.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:09c0340e9daa7b4530801e5a570cb0c1a1ad941a85d245d360020d3986d0e787", size = 377791, upload-time = "2026-04-10T12:20:23.87Z" }, + { url = "https://files.pythonhosted.org/packages/3a/79/89e4423352d0ca33bbf80fc1b4b665e654a93de8b16cf41e96fcac81801a/cachebox-5.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3162758792626685ec34950eedd565d015b115d0ff0d751d2716031fc32d51b", size = 359562, upload-time = "2026-04-10T12:20:10.626Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ab/e533c2751e6a3411ebe369277aaed03199b9e4586a48f0a3712a1f4b418b/cachebox-5.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a189a780c3ccd7b9d157074ba6bf3e191e522b39abbdb590075111851f02d50d", size = 397910, upload-time = "2026-04-10T12:18:53.336Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0d/b8492d6ca53278499a37c9f9d51afd4ad77bfbe813d6281944d45b97a1e7/cachebox-5.2.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:410b67baa99d433644199b11289627f7ebba4ee5786f95ca9858f238afcee157", size = 353699, upload-time = "2026-04-10T12:19:06.248Z" }, + { url = "https://files.pythonhosted.org/packages/78/d4/fd20b3a5362651303fa12d3ee62f56af2bd396e4a7303d7014a1a1e5b392/cachebox-5.2.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f81474dc19d3865fa5e57263f834bc6bbc00e471a594fb9d934ed552732c02fd", size = 372510, upload-time = "2026-04-10T12:19:18.997Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/3ec55c946d300cc4eaed3a0f79740051ac6e11ef4032421332c6ca15f5d5/cachebox-5.2.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85ccd827193b3e3e887a88a16b88ef7ed174e7e65be515b5253322aa75e665c3", size = 392802, upload-time = "2026-04-10T12:19:31.196Z" }, + { url = "https://files.pythonhosted.org/packages/01/b1/1a3c4e436ad8a4c4ba3e70f4c62e1f927cbbb3c943a9bba5813b8b815bde/cachebox-5.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a1e7d3cb8a5e7e68996a8619e3ef8771a124d14568c251f9e586eba88d759c1", size = 398223, upload-time = "2026-04-10T12:19:57.583Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ea/d36ad3976c4396b350b96a1582411b7a00e56c144eec0bb5ba5f36ce7d86/cachebox-5.2.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:adcedfcfcb933b21e7fdcfe560c79887bc8287abceab0586aa3730417dd0277d", size = 427696, upload-time = "2026-04-10T12:19:44.361Z" }, + { url = "https://files.pythonhosted.org/packages/a8/36/71845b5c7a9ffbd85e6fdb470c11a174f499bd5238fa37b1214157c2454d/cachebox-5.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c7f0c72c51a3a9e7049ea6ff2a43cd3877ab7fee966eb65771a59621563b75e3", size = 567854, upload-time = "2026-04-10T12:20:38.357Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a2/baf0e5a8392e64e352b137ccd7356b3d98068c842fd19f510a7790c05d34/cachebox-5.2.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c48c10e498d573511aafbd545570e7f43b40a7428dc282183bf5adc334d9e1a8", size = 670306, upload-time = "2026-04-10T12:20:52.903Z" }, + { url = "https://files.pythonhosted.org/packages/a5/22/cd4e4c1d624b8ef9fb4b8bebf0bf5d2d74a399cf1ac46b667bb79d15359a/cachebox-5.2.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2f1e086ab5ffd082a68bb63699d517655a59b06414927bfc84e01df91b81e34d", size = 645943, upload-time = "2026-04-10T12:21:08.238Z" }, + { url = "https://files.pythonhosted.org/packages/0a/d6/55859981f5ec6a9e412baaa4db6aa5973a00008750b3f054cdefcb6491fc/cachebox-5.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:649d18399f13735bb82daa33800196f815529c49e967767c40ca221723e68afa", size = 612309, upload-time = "2026-04-10T12:21:23.404Z" }, + { url = "https://files.pythonhosted.org/packages/d7/1e/313f650467ac85824c4199188f8f1ee3386cd12eb665dbf7c88d372e4956/cachebox-5.2.3-cp312-cp312-win32.whl", hash = "sha256:0a17aeb4e5b1c6ef1c3db8fc5186f9986e215ba5ea5a5d08baa45bcf55f261b2", size = 279789, upload-time = "2026-04-10T12:21:53.215Z" }, + { url = "https://files.pythonhosted.org/packages/c5/50/3b334f887accfa811cf5c7533b8ce22c523eb009363a86401198899dadd2/cachebox-5.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:cfd69114141ab362acaa2099e425a1b965cf7b021a539a4e953143d593930b74", size = 290917, upload-time = "2026-04-10T12:21:39.696Z" }, + { url = "https://files.pythonhosted.org/packages/31/3b/16d5c295f6ec2913ef595b39986dc7b7cc179fdd2e73f5ebd1814c38fd51/cachebox-5.2.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9527c5c70f8735f2d696331d8bcf77254f03b4dc8542046807823bd36ed4e8ba", size = 377408, upload-time = "2026-04-10T12:20:25.444Z" }, + { url = "https://files.pythonhosted.org/packages/cd/87/45f834154f79721e5b64a80ffab4f9710834c4f9c01fa977f94a9116c32a/cachebox-5.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40ac878af00d5969862c1f6bc076de1e34ca248662fce6aecca1761f52e33e32", size = 359274, upload-time = "2026-04-10T12:20:12.127Z" }, + { url = "https://files.pythonhosted.org/packages/46/17/794e5f93e0a172aa14ecd692f6d89bdf094f71eb35fa923d0a0af25cef1c/cachebox-5.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5ff26bfd8f7e95b3becf6d5f65c25edaca50fa68078868648b70d79bcccc260", size = 397520, upload-time = "2026-04-10T12:18:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/19/9470b1a96de6e480192b1a92b2fafa72aa052efc2509a5418a5652205b33/cachebox-5.2.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82e7002dd343afeeba2fcf0e483131b342a27ec3bc34b2214dc617691bda40d6", size = 353183, upload-time = "2026-04-10T12:19:07.797Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2b/72813f80397ed4640e337cbd1a14ab7eaafe33e479291d3623b6a6a55fec/cachebox-5.2.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ccbdc54a6c4b5758408c1083bdfa217bd382894a8331c7d0a54b84ba0cf51e5b", size = 372239, upload-time = "2026-04-10T12:19:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/05/17/47dc9687288fa55486573627089ecd9aae124de5924a4bce008af96d80b6/cachebox-5.2.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df5135a168f143d186b1cc3be0ca16b66446897ab5cedc03bd80bcc926fcd403", size = 392568, upload-time = "2026-04-10T12:19:32.73Z" }, + { url = "https://files.pythonhosted.org/packages/13/95/450765b971a3bed9d7cf003c3833c1976482eb83b0241b6dbb840a25b43b/cachebox-5.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10bedf96db8f9766cc956f9adcc623e604264e5d6fa2e255432f8c2ed7519143", size = 397920, upload-time = "2026-04-10T12:19:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3e/dd8f4c1f92e58d479913ce9cbaa3227c911128e6046c82f4fd44309f685a/cachebox-5.2.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f22732d0d69bb84ad2dca7480bffdfd0430c647152d488936e152ecbbfee52fb", size = 427332, upload-time = "2026-04-10T12:19:45.888Z" }, + { url = "https://files.pythonhosted.org/packages/7e/20/80d8c26ce63e78da3874a5bb07a3a78de53a2b0356ba80583a4927f0a074/cachebox-5.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:26ae0b68979204d360327f4c0725cfdc95cfc34ab73ab1a8f528e3bd2f6d023c", size = 567494, upload-time = "2026-04-10T12:20:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/10/35/7249885dfed3602b3b48c1e67781197dcdc536c50f72caeabe3944348af8/cachebox-5.2.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f3d628b816e28a6e7661d460e02dd5b421247cc2cd275814f80ea79621245fc4", size = 669968, upload-time = "2026-04-10T12:20:55.155Z" }, + { url = "https://files.pythonhosted.org/packages/2d/8a/e5b58f0bbd6fef74da5d8e5ab49e67898ce7e6df28c16280a0f2b78461f7/cachebox-5.2.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:64057caa6b741320655cd3c5997fe642dae5dbff571eb530e6f53e58272bb43b", size = 645547, upload-time = "2026-04-10T12:21:09.948Z" }, + { url = "https://files.pythonhosted.org/packages/d8/25/51783a4c6f25ca87ef1b4b762ff0364bd98053a02d597b30d26ff4cf13c5/cachebox-5.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa325306084aa2dc0b21e07723d7700f4d43dece3732c7fdaf7a269dc5e35aa7", size = 611844, upload-time = "2026-04-10T12:21:25.286Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c5/b26c4b046e296d0e249448fe297626b3caca2e851837712f03c358662cb7/cachebox-5.2.3-cp313-cp313-win32.whl", hash = "sha256:55003089d21c2f5515089c307be063b45558e884a4a1cc9593944374c89975c4", size = 279421, upload-time = "2026-04-10T12:21:54.921Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7f/a49420670393bfea618de7a893d45cae9294cf3293d7b158e7af20e8f39e/cachebox-5.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:dcc5edb6ecf2b516e90b773d232360c5e4ed8fdcda038b19441da2ed9cf208ab", size = 290702, upload-time = "2026-04-10T12:21:41.458Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0b/bf83bda13ef6fc490d208a1d4dd712034624526a88f61713cca0edc9884f/cachebox-5.2.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a4b7559fa4994c4032dd07466c2041d57e055feb814762e1f73f4e8beef188d0", size = 371704, upload-time = "2026-04-10T12:20:27.253Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ea/aa5162273238e84f9e41b33600c69299572dc1c8f0f768d07660b71be07d/cachebox-5.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f57afada3d9327adf87f3b5cf0094348c6fd49354ab2e9bd20b044648eb094ae", size = 353385, upload-time = "2026-04-10T12:20:13.668Z" }, + { url = "https://files.pythonhosted.org/packages/47/96/3ca013e2e48df5c1d7855669b208f4bf8014ccb842ccf7a3a0eaac07bee0/cachebox-5.2.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8342ff350ce86f062492752d612e9f056ac5dc56375713d75c3bf6e83b4d18db", size = 392181, upload-time = "2026-04-10T12:18:56.385Z" }, + { url = "https://files.pythonhosted.org/packages/63/ca/1bacb4efa0b0ce8065d1fb7c8dc7c382ec4e1cc3f007eb08417732be2725/cachebox-5.2.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:405f9cc8492fc9d953b5a6b9e2b661e99583755c6639ab8d09a287fdf336503c", size = 349494, upload-time = "2026-04-10T12:19:09.505Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2e/75db4bda3768658f5baa5a54f6a4f643bc2de1a16788e40581a080e803c7/cachebox-5.2.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94aae393ec1d9b26565d346445bb6afa3963d2a0d3eb5e4188d0e510fab871a0", size = 369216, upload-time = "2026-04-10T12:19:22.224Z" }, + { url = "https://files.pythonhosted.org/packages/f5/82/e1f833be0d57e29a8c5eb0a0275cd34b962f3c7f5b9e0517ec4bf75e7cc3/cachebox-5.2.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8b0b575066fc09f6fae0d4bd30d6ff56584a6870cbe7d202916c5e0d725cfd4", size = 385922, upload-time = "2026-04-10T12:19:34.198Z" }, + { url = "https://files.pythonhosted.org/packages/53/d6/615a3c16c1d63839f2c67644eb414c4dc9769ab2e169d935110fd8e268d5/cachebox-5.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41e99c1240106d39b63ce7868a6cd8c9da9243fef08848b85d428164e0769fd2", size = 393276, upload-time = "2026-04-10T12:20:00.925Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a6/7844c9c84b170dae1005b22da174639968e64c8055d66a209a1598663771/cachebox-5.2.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:432ca62b99f7eafc21af669d76c88c1b7377db179b89fb6fca3ea93b8f9fff19", size = 421355, upload-time = "2026-04-10T12:19:47.691Z" }, + { url = "https://files.pythonhosted.org/packages/c9/0f/43f62355846cae3dc41cb4daccac0a4bb2b7b8b3c7d77d1b6a220bae6d54/cachebox-5.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e51d9c59006b53447f806145406eb37a7fc3c25553d4fd24c3887f3b268d214e", size = 561656, upload-time = "2026-04-10T12:20:42.161Z" }, + { url = "https://files.pythonhosted.org/packages/9b/fc/a453813c6d000d69a41a06c6a3143a6c4d0d0e41f23c155db2f82ea0edfa/cachebox-5.2.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:5e48a405f699fb001b8af120a6e0b4a981277f84eb5dd66a1faa21e4b6fe9485", size = 665791, upload-time = "2026-04-10T12:20:56.842Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a3/f6a9e75f1e602b67b6d67088a9a766adfc4e0a740a9c4b68e4e6207c1006/cachebox-5.2.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8cbfc007ea78af61d75d7d26e5854df53dc5da6877d074afd4b4696c074f4ee7", size = 640975, upload-time = "2026-04-10T12:21:11.641Z" }, + { url = "https://files.pythonhosted.org/packages/a3/15/4ac98277f7fd9d855c8ed337e8e2a3386d17997cce2dd3eadb23dedc08e3/cachebox-5.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6a94d0da8133b3a0707ae11c9ea321f8fc37e3b5a14517019a05d632218b0f56", size = 607242, upload-time = "2026-04-10T12:21:27.27Z" }, + { url = "https://files.pythonhosted.org/packages/9c/0b/ce61907a803f75854e0cc91b84c16e14dce0e4e939efbda26293eb4c8784/cachebox-5.2.3-cp313-cp313t-win32.whl", hash = "sha256:5fee33549877c03c2494ec5359a57a7667f872fe8e296a7f39d3dfe08dd3914c", size = 271619, upload-time = "2026-04-10T12:21:56.768Z" }, + { url = "https://files.pythonhosted.org/packages/b0/06/fece190ad5173d06b2779494aaad5528907f2e55c809618e5b67c2e3dbb5/cachebox-5.2.3-cp313-cp313t-win_amd64.whl", hash = "sha256:67548a05cd41fcc4f7af80a2f97f742fef3d436537ac2e1a1dce0fcba5d41190", size = 283133, upload-time = "2026-04-10T12:21:43.037Z" }, + { url = "https://files.pythonhosted.org/packages/b8/8b/72c0e80aad08e09867ce14a621bce689a733552f20cdf2ef96d4b052da10/cachebox-5.2.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:37fa0891f0defee053c09f5f43f802f731e36e6e6ca055d7d174af07f77232ca", size = 380523, upload-time = "2026-04-10T12:20:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fc/62/33aaade81b181d5191cc39c867c297aa7c65f3191aa9749bf99b77496b88/cachebox-5.2.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dc6315902f2ef4afbf10bc8e08c54ff34de5ce124546b8e0016c9b0d327be21e", size = 362424, upload-time = "2026-04-10T12:20:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0b/3eedaf9ea4b41c931f4340bfa42056efe2bb5fe3a79649d6c8a1dce585a5/cachebox-5.2.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7df1735ca778480d51b8232fed397ffe3935158f20d34fb1c5ed171b53d5a6e2", size = 399572, upload-time = "2026-04-10T12:18:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/be/69/c79b8a6a5b889ac4a60800bacea3553cb3b86f6fd13b2262bade1cb962c6/cachebox-5.2.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e22451cde8f884051e941b21870e4fc91fcf58d0d8c285bb8964107e1f02445c", size = 353803, upload-time = "2026-04-10T12:19:11.21Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c3/bc7838de51039f8c50506d8dc82f22ff9a652794339a223b12af595e1d2f/cachebox-5.2.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dcbccf3015d9a42bcf41260fa5cc048a5bdb75aa10997d514d6c976117f30ee2", size = 374474, upload-time = "2026-04-10T12:19:23.658Z" }, + { url = "https://files.pythonhosted.org/packages/65/61/e5231ad2ae952ca482f9b9df55df4b96add1a80de28de537c5f574605987/cachebox-5.2.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:311eae5079e256cbbfafdc3dcff1714b6598a767f9c1ef8c3709e74ea0cc12b0", size = 393045, upload-time = "2026-04-10T12:19:35.651Z" }, + { url = "https://files.pythonhosted.org/packages/78/c4/c9b3fa764ac5420a9e079ad53fa8840d4a26b74c4ccda56acbef49cf76ff/cachebox-5.2.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f4d2a80a5cd3380739c67f7d89e596634f5897b8d5a4a3dc1598312cb077535", size = 398700, upload-time = "2026-04-10T12:20:02.513Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3e/c4e3acd4cb04e01c5fb7cc7a4de16059b9594d90672fff85af8670275267/cachebox-5.2.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3977515b727a5203f494c44c4566fb936c4b940351c01d3d8e7b5d104dff4f53", size = 426725, upload-time = "2026-04-10T12:19:49.385Z" }, + { url = "https://files.pythonhosted.org/packages/25/5d/610b79479719951581109d985244d34c97f86a308c3d7c83443e2b1dac46/cachebox-5.2.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c5be17dd5c4fabcfecd5bcf6d54f9c6fb719daed3ef01ac1c03a14af0e2b26c1", size = 570042, upload-time = "2026-04-10T12:20:43.793Z" }, + { url = "https://files.pythonhosted.org/packages/8c/63/cad8a05db4d0c0f5ba6bccb32e57d15c472276de9476f56004445b40711f/cachebox-5.2.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6d37334fc218fdaee31db8a4f938938716e7c3b1b4059e25de27c8447fc95fde", size = 670974, upload-time = "2026-04-10T12:20:58.528Z" }, + { url = "https://files.pythonhosted.org/packages/54/d1/9cff7c2b9048d1c38b7ad8199ce856596d09720b3bea74043f3bad71970b/cachebox-5.2.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1e5f1b7e23411b748d919348c3b65db1f9f8927ab8f6f3acae19bd617543df2d", size = 646213, upload-time = "2026-04-10T12:21:13.619Z" }, + { url = "https://files.pythonhosted.org/packages/27/ae/2e1ad162ec13903e84469c8a753baf385f1bc324279d6c7cb6365e7099df/cachebox-5.2.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7b06a75a898b31fd73c4d8bf727a9b9f8b5b7738cccd0ab5e6fd2a9cf659d3c", size = 612787, upload-time = "2026-04-10T12:21:29.271Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8a/07b5ffd841e1ff534bb6e8721c39fdfe0d7cdaac1398e1783b2a0c37bd22/cachebox-5.2.3-cp314-cp314-win32.whl", hash = "sha256:3b798052719f09a2ce7bf9fa9452dc0a7d4dc53b50a2d3aba6ce6ebc12d39df7", size = 278559, upload-time = "2026-04-10T12:21:58.482Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/b88a82ce9ec7a2fa0f09ed1cdd031692c8664c41f9ab71831e177c7ce2df/cachebox-5.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:4afc8b8575e3228a42ad8d819de5fbbecc6bd0b521295966b00244be37ae3b9b", size = 291928, upload-time = "2026-04-10T12:21:44.621Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/8c79c07c8c6517fb2fe7d479dd87044e38aac5b9af0245b33fcd695eae37/cachebox-5.2.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:0e8a34b82be30d3d9fb7dfaf9a86ec2b3ab9bc264715909ef27fc3d3587324d2", size = 374325, upload-time = "2026-04-10T12:20:30.923Z" }, + { url = "https://files.pythonhosted.org/packages/7f/51/0fc26b923e80ab857ac99d5f7f3784dc941e7b4de361c204835233176ddf/cachebox-5.2.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4d4e336aebf866463878ccd28a4d0ef4003ea216708cf4a02a7f198481b3af81", size = 355444, upload-time = "2026-04-10T12:20:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/c1/6d/a6b399221f8dc4b3e01b37d3240ef5b8a7eb78cd9bfbb99b0e655dd01649/cachebox-5.2.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b102fcdd97b0602bf5d6ba1a571bba3e3d6fa912b89fd768b0da5427408eab8", size = 393978, upload-time = "2026-04-10T12:18:59.753Z" }, + { url = "https://files.pythonhosted.org/packages/bd/f1/4c8f998c117c1941a82bd824d6687280c50167f21fea6392e41531d641e2/cachebox-5.2.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:245a79fb2c5d3bff252f4263f76210ef3ad7c2ff9b0234859b26974830a80491", size = 349298, upload-time = "2026-04-10T12:19:12.843Z" }, + { url = "https://files.pythonhosted.org/packages/d1/dd/683bc5a32a0da660d02fa248b880b71a2b834e9b54b8d272b5801282f402/cachebox-5.2.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd0e8dbd8fd4cf664c645c08f9e10508e133353756705c4a738e90a5406224b5", size = 370619, upload-time = "2026-04-10T12:19:25.298Z" }, + { url = "https://files.pythonhosted.org/packages/81/49/d6c47c78a7769b355076c5b635c2b538c8b88e8ceeb408e104d0f269b515/cachebox-5.2.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdb74294bdc33e39e26606919a9b2229038d5fac0edb80c9056683c08584d4a9", size = 385988, upload-time = "2026-04-10T12:19:37.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/e2/b669555ada7fa1392e4cdb8a19f3367db5c6abef0fde8ab034a9747760df/cachebox-5.2.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bba3e9a7f52fa196b434522f39675f3b32a076976ef2373ded6f1065e99f4d20", size = 394090, upload-time = "2026-04-10T12:20:03.978Z" }, + { url = "https://files.pythonhosted.org/packages/8f/01/42916249e53fe4fcbdf0419fb55dbc09b9f377475376e1d7f4ae9c9bd6cd/cachebox-5.2.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abb21f0f937fb66528f1b9f1a04874d6aa503e78bbb26f4cf33bf67faddbdd68", size = 421632, upload-time = "2026-04-10T12:19:51.048Z" }, + { url = "https://files.pythonhosted.org/packages/a1/54/34eebe18c6ed8ba27b1331b5e3d08bd8bb62f03ba81fbf47a2db0fa646f7/cachebox-5.2.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dab6fd3189b0c746fb03e1915fd947aaca9112cedf26ef3a0c39383acf87d2e5", size = 563871, upload-time = "2026-04-10T12:20:45.417Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b2/f92da0d54e4f18609588709090de8c81dd7c8b20ed6ac30f9b91bedbedf5/cachebox-5.2.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b4e7d2935b9df11d3717f99c7237b6780f1f8c70e6a99b69b8430d89929ec825", size = 665677, upload-time = "2026-04-10T12:21:00.512Z" }, + { url = "https://files.pythonhosted.org/packages/43/9d/bf2d3dc949afe4d21fc7eb15b7524255e834b9252df6bba111e6686d1c6f/cachebox-5.2.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:611aa260fe1b2506330ff72f415e2cb4053c9c4e3776ac68fe2eedee0e1b91b1", size = 642067, upload-time = "2026-04-10T12:21:15.727Z" }, + { url = "https://files.pythonhosted.org/packages/6e/4f/a789eda189550d239fbaf165b9810f148e733e97a2a4eda7c4192295c7f8/cachebox-5.2.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a424ffb8514a9cb49bacff7995b7c767625cb2239692bd6524245e8579e375cc", size = 608048, upload-time = "2026-04-10T12:21:31.156Z" }, + { url = "https://files.pythonhosted.org/packages/41/c3/590e161c04ffbd36e33933e6dcca5ffa40b5548e3121a21d77aad42af138/cachebox-5.2.3-cp314-cp314t-win32.whl", hash = "sha256:83988dd8e9075ee837e8407e26db49a9944ae74924d5db57b477444d7d98622c", size = 271694, upload-time = "2026-04-10T12:22:00.589Z" }, + { url = "https://files.pythonhosted.org/packages/66/f4/f60b8506df467261178afe918801df37c02c46ec2b8ce019760a14e2abe7/cachebox-5.2.3-cp314-cp314t-win_amd64.whl", hash = "sha256:dbda6390fa5070a19157ae35ab8066d3fe468634e0e9e21452c68ce7999c7d0c", size = 284212, upload-time = "2026-04-10T12:21:46.241Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7b/5eead1ca0d437b1993a742c6571079ae58ae4db50d94d42e87b514aed6c3/cachebox-5.2.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c798cddfb780156db09d3d96ed5da4c2d5fc01dad4bc7b54db5b20c34f221926", size = 376199, upload-time = "2026-04-10T12:20:32.674Z" }, + { url = "https://files.pythonhosted.org/packages/77/e3/5e45042f9b552a5087cafc2e0fed834e632531fca17818201d72e78593ce/cachebox-5.2.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:c8f3de4afeb3fd721620be3d02f2338bcbc3fdbd464ca14e1c474088c9669db0", size = 357109, upload-time = "2026-04-10T12:20:18.554Z" }, + { url = "https://files.pythonhosted.org/packages/d4/51/3c4743b718b42e4b80166fa61f8722b603eba7bf206768a7892c4699dce7/cachebox-5.2.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b39022c258872185327acffa9ad42d6bdf42f37d006d35c825a684eb5fa98d40", size = 396433, upload-time = "2026-04-10T12:19:01.463Z" }, + { url = "https://files.pythonhosted.org/packages/f8/9b/678da91187bdb2836db2b8da62519da75359b46bc28697799a7caa314519/cachebox-5.2.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a0599fb85dcb6df9a86502435643fe90c793bbcd50b5d85217c70f2bc2e38fc", size = 354287, upload-time = "2026-04-10T12:19:14.55Z" }, + { url = "https://files.pythonhosted.org/packages/df/06/769446da6c9f2855499aaa19e2d7260aa47934bc2e15a931e5b737f8685a/cachebox-5.2.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3cdbe8f1b7716a44dc82ef3a6830a612260c7379478cfa80804632e2e6252b8e", size = 372507, upload-time = "2026-04-10T12:19:26.763Z" }, + { url = "https://files.pythonhosted.org/packages/79/cf/86c60994a7be734abef0395e440dc11714f84ffcd369cbcd8e61c3d58126/cachebox-5.2.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:783d1b9a0b3c77c43e7ae331b9d6561ad75827e16b2484e2a6cc289ec4d392ee", size = 390831, upload-time = "2026-04-10T12:19:39.591Z" }, + { url = "https://files.pythonhosted.org/packages/9d/db/acfb55f8d5ee4ea1c5f2d32ede25d4d04e944ba09d2832c27c085022490d/cachebox-5.2.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c6476a2a842906fee782d92f8fbcb03ecfd22eecc39adb7fb5b047d7e1cf020", size = 396277, upload-time = "2026-04-10T12:20:05.735Z" }, + { url = "https://files.pythonhosted.org/packages/dc/4f/35e27e85a48e15671c5863addcabde910eb311800a621c3e47c04bd36d17/cachebox-5.2.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:184bbcfa1370415b6d1f09e4fb74ab697dac8df09f522aa217a2fac65f973744", size = 426980, upload-time = "2026-04-10T12:19:52.622Z" }, + { url = "https://files.pythonhosted.org/packages/09/4b/50f2cadf20c02db9e449f2e9fee95f3eb5768ab1804dd0a5eba6c98119ad/cachebox-5.2.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:f89df36b46f8f5e11c0c49701ec3cebddf51191f96afb7bb75c394faf3c1cbc8", size = 565539, upload-time = "2026-04-10T12:20:47.051Z" }, + { url = "https://files.pythonhosted.org/packages/43/53/b8e948cadb48b8bcf1d13c2aa4a788ff0e95b50ddb808c18e998499b4680/cachebox-5.2.3-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:fb0bdcd9e28686e3b91d5210c843542858f0f10de151181aee27a7978fe4992e", size = 670870, upload-time = "2026-04-10T12:21:02.141Z" }, + { url = "https://files.pythonhosted.org/packages/29/7b/d68ca3f59a9d6963c2f6b19bc4b1926a37db2e4a4f6c9891d12788e49ce2/cachebox-5.2.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:5196f0d2c2f99c92ddf0d2c37803ff90509d14a5df211b7754feb8b61ffd8740", size = 644542, upload-time = "2026-04-10T12:21:17.541Z" }, + { url = "https://files.pythonhosted.org/packages/f8/c8/44ae6d5dff09f044d61a92591e6a8db17f3b2ee51a54d375cce90271527b/cachebox-5.2.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:73671850d8c3634ab217398c83715d3feb52589ec97bd8e2f4d22e472741ea48", size = 610235, upload-time = "2026-04-10T12:21:32.93Z" }, + { url = "https://files.pythonhosted.org/packages/9a/1b/31cf2449da9a296f6c6c0002c7ae91a25c3a4bfef071763bbeb85300b402/cachebox-5.2.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:70c718f6bb77e6ba142b9a055b81ce85412a0c0e5e82a154489b45e6f91d09ec", size = 287614, upload-time = "2026-04-10T12:21:47.909Z" }, +] + +[[package]] +name = "cachetools" +version = "7.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/2c/3f18755527b03ca9ff6be724bd5370cb777c76a87f17301377cf04a4729b/cachetools-7.2.0.tar.gz", hash = "sha256:bcac1a1b8da6909994a2957238a57b8140dab7c5c5c69a43669654fe87a33c1d", size = 41129, upload-time = "2026-09-16T20:48:27.209Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/bb/1c6e7a89b11da19f137e4ef5a5c5fe47a5bb686449ba343944ccf8fb34b2/cachetools-7.2.0-py3-none-any.whl", hash = "sha256:3045213f186b89fdd95d94441354c4bd87c570b7a38d8c3fd1d9dd37f6dc90d8", size = 16918, upload-time = "2026-09-16T20:48:25.575Z" }, +] + +[[package]] +name = "certifi" +version = "2026.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "cffi" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" }, + { url = "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" }, + { url = "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" }, + { url = "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" }, + { url = "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" }, + { url = "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" }, + { url = "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" }, + { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" }, + { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" }, + { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" }, + { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" }, + { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, + { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, + { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" }, + { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" }, + { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" }, + { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" }, + { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" }, + { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" }, + { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" }, + { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" }, + { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" }, + { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" }, + { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" }, + { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" }, + { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" }, + { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" }, + { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" }, + { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" }, + { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", size = 171764, upload-time = "2026-08-15T08:20:44.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/aa/554e2614f38fc34c58ff1d0911ae8535ad2516440d5482d76fe59f1088b0/charset_normalizer-3.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa", size = 369072, upload-time = "2026-08-15T08:16:22.964Z" }, + { url = "https://files.pythonhosted.org/packages/03/6d/439231dfc3ccfa6f8c06477b7da2219cbd41a2de3d49084df8ec7b5100f2/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda", size = 251142, upload-time = "2026-08-15T08:16:24.81Z" }, + { url = "https://files.pythonhosted.org/packages/55/53/7d819bd23a00ef45039146fa2cce1daa2f0771e758c5653ee1f6edac91ed/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45", size = 240714, upload-time = "2026-08-15T08:16:26.392Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2c/45847198c16f4b38090cc7423b2b6a9008e438704d8ab413211832498d31/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba2f37ee79e6338845261a3c5b1784e5d1acdff2c0785b284f1b633033d136ab", size = 279637, upload-time = "2026-08-15T08:16:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/69/2b/d8be3523ddf9f0b0f3e56d1359034aa10653a4d11564c697f802b4775766/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ce854f5f478050ade5a238731c4ca985a7d3b3cb53ff600a9b5c3b689b5f0a7a", size = 276543, upload-time = "2026-08-15T08:16:29.399Z" }, + { url = "https://files.pythonhosted.org/packages/32/cd/4f564b8f132de25db594efc706897069f016790cea63a5669c9df2675f64/charset_normalizer-3.5.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3", size = 261644, upload-time = "2026-08-15T08:16:30.722Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e3/38b975422534a608f98c360e79c2f07c763d66dd4272300d45fb1fee54b0/charset_normalizer-3.5.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:366ec70f5547c640d3ce1985722490f23faf4eb5216a7eeba78277490e78dacb", size = 259609, upload-time = "2026-08-15T08:16:32.248Z" }, + { url = "https://files.pythonhosted.org/packages/87/bd/fbc24d825c66f1c74f6ccdea3742c3d8354a4888e86d1315a197fee69061/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:950f23cb393f85543777b0433f082cddd25b51ab398eac7971146495679efe5f", size = 252457, upload-time = "2026-08-15T08:16:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2d/918d0e98a0e679469ed05bb2d90c2088b4d315bb612969d8499f76fb5210/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1dcc36dcb96abc02236e182d17e0f71430152a6c2c7447421da2d2dc144edea", size = 242240, upload-time = "2026-08-15T08:16:35.396Z" }, + { url = "https://files.pythonhosted.org/packages/20/c8/c36f6e0b2dfec351bd38cbc05362697e58bcd073d7dbd95154290c9714ce/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:07ffd07412fc5d5e84cd8952acf9ff7e4ed7a708e69d1bada19d8ba91711353f", size = 280308, upload-time = "2026-08-15T08:16:36.825Z" }, + { url = "https://files.pythonhosted.org/packages/ca/7b/311b3e02e8c4092400c449c850a760d8c45d900983c83a70cc07208c551d/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f5542f9b941279d82d41eb0aa9f98eba36fe4df5c7086c651df7944935b37182", size = 258679, upload-time = "2026-08-15T08:16:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/b9/90/082cc45599c392f28c036a497f49e0634041a785fc3849c80ccf396d096f/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a545775cfe815855ea32d7c27731d79da358ef2055b4a25830231b1622dd18aa", size = 277221, upload-time = "2026-08-15T08:16:39.62Z" }, + { url = "https://files.pythonhosted.org/packages/58/ad/b9aecf38d805cbcf84fa94f14c5d972a16561e20296a11dc799a5dcf3763/charset_normalizer-3.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:494b70049a4d69aec6e8137c13af4cf8db8c9f9820a1392ac293b0dd2987a818", size = 263799, upload-time = "2026-08-15T08:16:40.885Z" }, + { url = "https://files.pythonhosted.org/packages/b7/23/b38a20598d5a825f85d9d7636860e56ff0db1479f86497a6e485aa9326f7/charset_normalizer-3.5.1-cp310-cp310-win32.whl", hash = "sha256:94fbf1c0c6cc0d3d5e50f9a9313a8cdca90dd696d34b381cd1704f8c9e939f20", size = 182037, upload-time = "2026-08-15T08:16:42.198Z" }, + { url = "https://files.pythonhosted.org/packages/d2/21/83fffb77864408b8bf0fe1ca603926401d6f8775a8e150b39aacc9958f8a/charset_normalizer-3.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:be47f99644b208bff7766314013f9acf57b056b04191d570d68ad14022cf5b1d", size = 206030, upload-time = "2026-08-15T08:16:43.787Z" }, + { url = "https://files.pythonhosted.org/packages/86/2e/b93135b5034b1157fb29554b0d06d4844ce62282f0e0a14036f93d7ee2e7/charset_normalizer-3.5.1-cp310-cp310-win_arm64.whl", hash = "sha256:a6d095662e73e74f0a49988e0593373e243e3a52e27bfeea0a859e88acf4a0f5", size = 185092, upload-time = "2026-08-15T08:16:45.177Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b6/034f6802e9c3f6418966cfabb7db8c9252cc2429c5098f41cc43af804149/charset_normalizer-3.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30", size = 363585, upload-time = "2026-08-15T08:16:46.646Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fa/6a7e2a7c4b5451912b8c417732df79574354443592a88d616de03da66ae5/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488", size = 251189, upload-time = "2026-08-15T08:16:48.287Z" }, + { url = "https://files.pythonhosted.org/packages/a4/c8/ab42b07cfd82e919f427fcfaa7c41abae8242833ad1aad66d42bae40b669/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b2b1b3fa5670c127b246df1d0c059defd41f689a868a3b9d79df9b1cac42d22", size = 239724, upload-time = "2026-08-15T08:16:49.67Z" }, + { url = "https://files.pythonhosted.org/packages/e7/80/b9348b5d3041209f98b4cdad7655766369233f1d533f4f4f7558e9717bec/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e5e4d73d588ca5ed09df1b7dcd1b203d1df3c542e3f50d126c947d432b10731", size = 280078, upload-time = "2026-08-15T08:16:51.228Z" }, + { url = "https://files.pythonhosted.org/packages/82/38/083a24028304bc85bb9e376fed801178423dcbb67495f73b6ea0624e1894/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b54e7e13267d49ffbfe68e25b3cbd774dab38fa37238f71265e91b36146eb21c", size = 276650, upload-time = "2026-08-15T08:16:52.625Z" }, + { url = "https://files.pythonhosted.org/packages/0d/35/731ac04aa0a097fc1c97f0994c375bdb230c6c96619db794208fe664e9ce/charset_normalizer-3.5.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8", size = 262325, upload-time = "2026-08-15T08:16:54.085Z" }, + { url = "https://files.pythonhosted.org/packages/f5/28/c2028e7021fb89c6e56868ed0e387b8e9aa811abdd2ab3208d6578d2c930/charset_normalizer-3.5.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ba32c4d2abf1d2fe7cf27d280f4cca5664233b0f885549c7761719eb977f486", size = 261140, upload-time = "2026-08-15T08:16:55.604Z" }, + { url = "https://files.pythonhosted.org/packages/28/f0/0c0ceec6d98b7daa62e361e418135d59685811d79ba11529aad5cdf15e84/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0722590aabf9dc6a6c0343d523c05458fa2b5047dbe6302fd526bb570600753f", size = 252791, upload-time = "2026-08-15T08:16:57.103Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3e/48f4cd187b1c33189d86039e9cbe4f92c05454175504b44ff81806d4d1bf/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aa1099b956fb795e686d073568f6dc002a0bb89765ea6d5b055dd7d9bf1b116c", size = 240730, upload-time = "2026-08-15T08:16:58.418Z" }, + { url = "https://files.pythonhosted.org/packages/42/85/f9e22af69af67c54cce42be9455d9c81294f918b4ccc454db01f66efcac2/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bd6c173f04743d483881bffa1478d5a4624475b8cd1d2194956a75548e191c18", size = 280791, upload-time = "2026-08-15T08:16:59.918Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4c/9044135f42127630b6fa742feb51256353f6ab87a78f2fdd1de3de955a7f/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f298e218441525d3794428b4c8b8fb8662c6d3ea79925d4807ee6b9a96a3bca5", size = 259598, upload-time = "2026-08-15T08:17:01.421Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ed/1dd7cfebb4e75812934c49ca3b79757d11948053f7937ab7070c151f3c55/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6e2912d4babbc65196ac13c2f53468dc57fb8b9c25ef913e8c59ddf7c6dc0e1b", size = 278217, upload-time = "2026-08-15T08:17:02.782Z" }, + { url = "https://files.pythonhosted.org/packages/bf/eb/239c84503cc9e3ba6eb34686a24bc66e84f3924efdd7e38e751a19f6bc10/charset_normalizer-3.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3d27167433c0d5f18dc850f07d0b3816221984fecdc405d6c157a6f0b8f8e9e6", size = 263417, upload-time = "2026-08-15T08:17:04.216Z" }, + { url = "https://files.pythonhosted.org/packages/37/ab/4e4510e1e288478e2c8333131d1c1382382ba8cd2165053c79e39d1da961/charset_normalizer-3.5.1-cp311-cp311-win32.whl", hash = "sha256:ac00177c4831ffa650f8609e4bdddd5fe09c03b1c0c47acece7e6ea20421598b", size = 181774, upload-time = "2026-08-15T08:17:05.58Z" }, + { url = "https://files.pythonhosted.org/packages/e3/57/32f0ccea59e8612057c61d6fd22ef2cb63cca93c9fe594094919696ac170/charset_normalizer-3.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9b1e28d0e8dbfa858abdba91d6b547beaf2df1a59bec6da6faae7b96a4991a9", size = 206653, upload-time = "2026-08-15T08:17:07.075Z" }, + { url = "https://files.pythonhosted.org/packages/17/d4/b65c433fc521e58b5f54293982a5e51c05cb5f2dd3f1c7a6acb65b75324e/charset_normalizer-3.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:ae31a1a1db2ee6cc2942fccaf695c934bc7f3db9f2133a3fef1f367cf1a4ab10", size = 185630, upload-time = "2026-08-15T08:17:08.502Z" }, + { url = "https://files.pythonhosted.org/packages/30/27/78873dc8b6a56357517b74b6bb9568b80450e7bb4f6ef7e3fa9d22aa0bd7/charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f", size = 344456, upload-time = "2026-08-15T08:17:10.072Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4c/be49ada26b1f0232d57aa89bbebf997a5cc2332a5616b6eca26ff680044d/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa", size = 238530, upload-time = "2026-08-15T08:17:11.563Z" }, + { url = "https://files.pythonhosted.org/packages/76/84/6f1290fa07ae6978d3960caa3eb1b8019bf9284ab7c2297b00c099ef4250/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369", size = 230200, upload-time = "2026-08-15T08:17:12.919Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a0/47b18adeed31c8f16ba9700f32c1b18594cfa09f47eb672a488c273c22bf/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893", size = 262222, upload-time = "2026-08-15T08:17:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/38/fe/341861ac118dae06f3ec0eb487488af52128f2ef2faf0b11003944d22259/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0", size = 258951, upload-time = "2026-08-15T08:17:16.158Z" }, + { url = "https://files.pythonhosted.org/packages/6f/89/bb5108dc6c3651dca963f2b0a3ba19bbcb370c94e1b6d3e0e844a58e6dca/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08", size = 248801, upload-time = "2026-08-15T08:17:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ba/ef83ae3aca816393decfa3530976f38a79812d707b80b580ac33b83f9877/charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada", size = 244070, upload-time = "2026-08-15T08:17:19.191Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0b/c5292a2462d69b7378ea89793bbb5b2b6fcf6f7dd6d1667f9619094ad553/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9", size = 240110, upload-time = "2026-08-15T08:17:20.547Z" }, + { url = "https://files.pythonhosted.org/packages/46/22/111e5be3b740d5c2a5bfcedb3d237b6591e5c2e82ae9d6ffcb121fe0909c/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e", size = 232836, upload-time = "2026-08-15T08:17:21.895Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d2/d2aad6fe0dbb44b194bf3becb60f5a0ac48446ade999a47fe7bb41eb09a7/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6", size = 262712, upload-time = "2026-08-15T08:17:23.727Z" }, + { url = "https://files.pythonhosted.org/packages/35/5a/337e4663a5eae6de99db940ee8066d4145caafb61327db62deda15313cce/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf", size = 242977, upload-time = "2026-08-15T08:17:25.157Z" }, + { url = "https://files.pythonhosted.org/packages/ca/85/f82f8a92e31c7519410e2e1afdc630f28ec47490ce2c09a11c1a43cbb459/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71", size = 260207, upload-time = "2026-08-15T08:17:26.602Z" }, + { url = "https://files.pythonhosted.org/packages/b7/52/643d11ffd60e9ac2fd1fb87e167a19285b9eefeff4a40e63c87cbfbeab36/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573", size = 250562, upload-time = "2026-08-15T08:17:27.971Z" }, + { url = "https://files.pythonhosted.org/packages/62/16/46556278c2168d12df9da7fede5dc6fc70e60301b26a82bbeec238c9cfe3/charset_normalizer-3.5.1-cp312-cp312-win32.whl", hash = "sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2", size = 178507, upload-time = "2026-08-15T08:17:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/4c6c298171e6b3e745633180ff59350fc0ca0db1ffd28df1e369e0579f71/charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2", size = 200551, upload-time = "2026-08-15T08:17:30.668Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d7/eb95a042f0dd22e304b0b6472b154f3546a1a039a9ee89ccb2a7f61591fc/charset_normalizer-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a", size = 180700, upload-time = "2026-08-15T08:17:32.028Z" }, + { url = "https://files.pythonhosted.org/packages/bc/61/2cb6ad133dbbb449fa2d37ccae973232f4827e799af258d15e589a3d1e9e/charset_normalizer-3.5.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9", size = 211584, upload-time = "2026-08-15T08:17:33.597Z" }, + { url = "https://files.pythonhosted.org/packages/18/57/a305c968be1ca13f3dd1b32f445877e97addf55d80b65c7cb35fac82b777/charset_normalizer-3.5.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491", size = 223359, upload-time = "2026-08-15T08:17:35.022Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/d3646670292ce8d8f8cc11ac067d44885e697a5591f57a9221128da5e7b3/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7", size = 194464, upload-time = "2026-08-15T08:17:36.452Z" }, + { url = "https://files.pythonhosted.org/packages/de/93/d51ec556e01042fed6f993ea859311bc7917b466684182fbbceb6ca24762/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e", size = 197676, upload-time = "2026-08-15T08:17:37.819Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a0/562247944386f7d4ef94467e84876600cc1e0f1b93239aaa9213d2bc3cbd/charset_normalizer-3.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d", size = 340473, upload-time = "2026-08-15T08:17:39.303Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/1d994be1b93d41e9502b8b0460eaa88a1dd8df335df415db87d6c3e91ab2/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a", size = 240156, upload-time = "2026-08-15T08:17:40.66Z" }, + { url = "https://files.pythonhosted.org/packages/09/53/27923ce5cc6cbccb832037b27dca98882d9c53e9b69e866bbbef4aae7fc8/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe", size = 228246, upload-time = "2026-08-15T08:17:42.003Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5a97e84d63af1d55c07439cb80e56d99a8efb4295700eb4e18c0d1615d2c/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac", size = 263660, upload-time = "2026-08-15T08:17:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/071575791dcc88316c0a9a65ce38897a82e4cfe4a325f0f7fe1b1ac47bcf/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e", size = 260354, upload-time = "2026-08-15T08:17:45.094Z" }, + { url = "https://files.pythonhosted.org/packages/fb/af/63240b0c0248c075c2535a1f1bd992821d8251b9f173abc13329661d09e4/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3", size = 250638, upload-time = "2026-08-15T08:17:46.496Z" }, + { url = "https://files.pythonhosted.org/packages/4d/66/70dfad64f15be09c15ccfee81330a7e515895dbe296dd23114e9a231268a/charset_normalizer-3.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876", size = 244583, upload-time = "2026-08-15T08:17:47.963Z" }, + { url = "https://files.pythonhosted.org/packages/c0/24/ef36367d38b9ddd4bccbf72888c342e8de1f5ae506fa0b2dcf970e2732a1/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6", size = 242038, upload-time = "2026-08-15T08:17:49.481Z" }, + { url = "https://files.pythonhosted.org/packages/db/ab/55e683ba0fff2e43adafc10daa3001eac90fdaa419a97227d5a7067eedde/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2", size = 233677, upload-time = "2026-08-15T08:17:50.845Z" }, + { url = "https://files.pythonhosted.org/packages/bd/67/0f40eaf8d1b6e7cf15e82382a2965efaca787fc1c2794b7021d37aaf5036/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591", size = 264491, upload-time = "2026-08-15T08:17:52.61Z" }, + { url = "https://files.pythonhosted.org/packages/5c/64/12b4c2a11ee8df4fcc518c78b0d93e3a92bd3d5253d1617ce74ff0e8c7ef/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c", size = 245196, upload-time = "2026-08-15T08:17:54.023Z" }, + { url = "https://files.pythonhosted.org/packages/37/2e/651d910af6d0fba325eee1cda37ec5443462ed25360e666c144166eb6091/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c", size = 261660, upload-time = "2026-08-15T08:17:55.491Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/b09e05e6db7f64338e0dc067c79577b1138da86c1e38369096851d96be88/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f", size = 252618, upload-time = "2026-08-15T08:17:57.025Z" }, + { url = "https://files.pythonhosted.org/packages/76/4e/362d4f9fdcdf5556fb2aa3ce7d4a58ebce03ed1ff03aa1d9aca8d02f13f3/charset_normalizer-3.5.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4", size = 140362, upload-time = "2026-08-15T08:17:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d4/703be739b26acce318bd29eb3b25b7209e1b1f527f9eae3d1f1f01fdde2b/charset_normalizer-3.5.1-cp313-cp313-win32.whl", hash = "sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3", size = 177755, upload-time = "2026-08-15T08:18:00.037Z" }, + { url = "https://files.pythonhosted.org/packages/8a/33/56d97ade41c8db611e727168c52ae46c9224c362ec28d4b65d7e9869e8da/charset_normalizer-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6", size = 199295, upload-time = "2026-08-15T08:18:01.506Z" }, + { url = "https://files.pythonhosted.org/packages/5b/75/5b20dd1e6573a01a08158fe104104fa2c8abf941745596954185726cd46c/charset_normalizer-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0", size = 179856, upload-time = "2026-08-15T08:18:02.929Z" }, + { url = "https://files.pythonhosted.org/packages/29/cd/2b812ce5e888f1ce69a5350281e58aab07ae64a958ecae8912f30865718e/charset_normalizer-3.5.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:774d157f112367ff4abd29019f38f023c24e00e56edc7829c20e358a5a913ad8", size = 212318, upload-time = "2026-08-15T08:18:04.403Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4a/a6ee107430768a5334e6d63f31f148a04a1a491ef161a1ac9415a73f2fa8/charset_normalizer-3.5.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:26422d45fd13551cf564c58932f7d72b4f58b93b0fcf18c35ba6be12b46bb102", size = 224897, upload-time = "2026-08-15T08:18:05.997Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d9/35ae3f64f29d0179c35c3baefe575904df2913dde519129c7f75995a2b1d/charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:09a7bba9f739468c8e78c36a75c33768e53cb1959fc638f510454c14683f00d5", size = 194848, upload-time = "2026-08-15T08:18:07.397Z" }, + { url = "https://files.pythonhosted.org/packages/74/76/f2fc7380f056cc273a53af37f50d08ad54b2c59f61078f31432edcf1c2bd/charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4c9548dc78002099910abaebc0a72ac58b7d30931869e0351c09b507dff4ece3", size = 198163, upload-time = "2026-08-15T08:18:08.989Z" }, + { url = "https://files.pythonhosted.org/packages/e9/40/095ce62fa078483cccc1fa2b36e6bc9580b85422a20ee9f925341c50e44f/charset_normalizer-3.5.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c", size = 341823, upload-time = "2026-08-15T08:18:10.458Z" }, + { url = "https://files.pythonhosted.org/packages/f1/5a/0e58b1c04a1596e0256f407274a92d5fb2ee21324409d1fab1da48a65b5b/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0", size = 242458, upload-time = "2026-08-15T08:18:11.989Z" }, + { url = "https://files.pythonhosted.org/packages/22/95/b4618ce912e6db0b1aae89ba788e38e8a7eba0f3025cc66e8c0699f977b2/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b7430cf5728e68f6c462254009a6ef4086e1bea43cf2f57aa9c55fb4f50ff96", size = 226717, upload-time = "2026-08-15T08:18:13.401Z" }, + { url = "https://files.pythonhosted.org/packages/8a/76/c681192bbda3d55356db5dadd64381d5202b37c6b598fcda5282e88b5d3d/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab743e9bc90c1f73552ec33e10e3331315acd2c397b36065b591b0181de533cc", size = 266111, upload-time = "2026-08-15T08:18:14.961Z" }, + { url = "https://files.pythonhosted.org/packages/88/be/55127bfca72c0cff6c022488d140d7c5b04c771e3b72e9bdb4836d54979d/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6f7deae3feb4edfa2efaf7c574fe88cbf055038a6abdb40188e4fff66d5699f", size = 263128, upload-time = "2026-08-15T08:18:16.515Z" }, + { url = "https://files.pythonhosted.org/packages/e0/91/39c3af510b0aa32bbda03374259200f28430febfd1bf5e511fe765282ce5/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90", size = 251240, upload-time = "2026-08-15T08:18:18.127Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a5/cbe418bbc6ecdfc3e05a0116002897c4b403a5e838d697e64c78e9f0190d/charset_normalizer-3.5.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:823f82903d189af463d7df250ef1f7f696f3cee08cc8d91deb565e8d425f6506", size = 245282, upload-time = "2026-08-15T08:18:19.625Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a4/689bb42e8e7cd492f3cb64907c6bc00ad247ec9a3628cd3f8eed126e8ae1/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:01e93745f7f219b703b60ba7afead36cfc4242782be5af484673fc500df12da5", size = 244597, upload-time = "2026-08-15T08:18:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ce/9962938e179cf9f699d3f1e7b3114b5d7642dee6a893745229f9dd04f274/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329fc3ccb63ad22d867d84c2adea759a64079a37ba4a343433b02c7a2816871e", size = 231376, upload-time = "2026-08-15T08:18:22.57Z" }, + { url = "https://files.pythonhosted.org/packages/85/54/46000450ada53bd9eac5429a2c8c54cd2d9b39c0c255f229aea9af0948a5/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:bb57753e36e4855b8ca375069482250a6246372331a3e4f3407eaebb007443f5", size = 266715, upload-time = "2026-08-15T08:18:24.235Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bb/618749d70f792b44252a777bf89bfb86823b9bbc1ea13fe8ce759b07f38a/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fce8cbd4997efeb450bd298b54f755dcdff18d496f7a5ddbb4867c6d7c88fdc3", size = 245848, upload-time = "2026-08-15T08:18:25.726Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3f/ffb64458527c7668031d5eb095d978de561958dc9f5b53f8e488a533e603/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6c9cdde8becb25a7fde49924511aa2644d6f8081cc8df8e9452724303348d8e3", size = 264521, upload-time = "2026-08-15T08:18:27.193Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ab/74a55fd803916a35ac461daf002708191aac19b546b80dc8cabfedc63d98/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9ac4444d8d4fd4c4bd08bf451ed3167aa9e7ec6cdb41b648794f1d1103652e36", size = 253054, upload-time = "2026-08-15T08:18:28.568Z" }, + { url = "https://files.pythonhosted.org/packages/a0/2a/6a9034b7d3c60b17499afb482df5878bf9fa20b50cc3887d5ef017a833db/charset_normalizer-3.5.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f03ac127268b43ef4fe9e6ab6794a6794b49485a0cc0c1db79876d2f33f75bc7", size = 140580, upload-time = "2026-08-15T08:18:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/f3/46/1d362e1a00d035d66b9869e1281eee115907f7e390a16a07824ab5737360/charset_normalizer-3.5.1-cp314-cp314-win32.whl", hash = "sha256:1f5883d77fd409a261abb5dc8ccbe335720d798b1de4abb3b1d47ccbbc76b53b", size = 180325, upload-time = "2026-08-15T08:18:31.877Z" }, + { url = "https://files.pythonhosted.org/packages/7a/7c/4938c329b6a9d446f6a59aa2092ff7118f274209b5ed0e26893d1d30a63c/charset_normalizer-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:c658c50ac0c98cd755a2dd50b7977d3bca7df401dcc47fbdfa87db53ef7d4e8b", size = 204175, upload-time = "2026-08-15T08:18:33.466Z" }, + { url = "https://files.pythonhosted.org/packages/ac/33/eeb384dbd8dec570661354592f4f2e1b2fcc92585624d146a000caf53841/charset_normalizer-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:4bea7f8ebe90bbd7f0e4a2de42ca6924ba23e3e76418c408ff82f1d46fabd687", size = 184123, upload-time = "2026-08-15T08:18:34.913Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6c/c73fa9d5a85f6ab05395de61c5f6984e0a9ff40bb5ff888d46dff02526c6/charset_normalizer-3.5.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:fbc597639158fd7c14d55e808718848319540f51b0e6746e3eefa59723a4a348", size = 381682, upload-time = "2026-08-15T08:18:36.349Z" }, + { url = "https://files.pythonhosted.org/packages/30/c7/63565f860921457feba93bae6c86fb7746deb4cffeed2f375cb845318146/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71c909f353863b2b89c83de2ebed71ea6d0df8a6ef65a128193c5e650766bef", size = 240826, upload-time = "2026-08-15T08:18:37.887Z" }, + { url = "https://files.pythonhosted.org/packages/06/ae/7ae8807410dfa33f8e6f1715740adeaafa8a816cc4cb33508f54b1f7c896/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7ac76cf9afd34929d76eb7fcb63be476a4853d8a96f0dcf2d0db68a0cbdf9885", size = 227861, upload-time = "2026-08-15T08:18:39.315Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a3/887c1642f0da26000b0e0652d91071113c0e72cea33952e225cf589f49a9/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a3a370082ce34d0612f421e15fe011c53bb1feff21a26d06ad4fb244dab5a375", size = 260758, upload-time = "2026-08-15T08:18:40.88Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/e6f5b9a3d0e55b0ef7505cd3765cdd48f22db89994c947b316f52f801fd8/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:256dd4d85d9e4dc595e2bc983c980e73f62ddeb3165c58b4c3dfe78c5c8548c1", size = 259950, upload-time = "2026-08-15T08:18:42.351Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ee/e4e10a94d51cd1ee638aa7e00b65399e6b2a4e8376ab6d2eac9f95586671/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58d4aa13a59c969dbfdf9e6a9560e242cbfd9e8a8f50c2747714df1a423adf65", size = 249329, upload-time = "2026-08-15T08:18:43.914Z" }, + { url = "https://files.pythonhosted.org/packages/c4/25/d5f4198819e6059735a84e8d0bfb72dc33976da67b97adcd3fb5a5e07ec6/charset_normalizer-3.5.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c6dfb5ca6723eeed15aa8e564a014d69fcb8812f94eef11fe3631e0508199f5", size = 243137, upload-time = "2026-08-15T08:18:45.368Z" }, + { url = "https://files.pythonhosted.org/packages/a5/e9/e925ca7569cf9fb9701fd82503fee73eea5268fdb856bdd64947092d3daa/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c010f5581d9c612804cc59fcf7b524b707fbcb72828551237ab545bb5c7034af", size = 242820, upload-time = "2026-08-15T08:18:46.842Z" }, + { url = "https://files.pythonhosted.org/packages/34/17/672c251a888ed2aebcdd2fe830ad0104e25ff83c43f5c4f9c15e9fc6853c/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:52ec005752a56ae79547a05c0139ca2501a0c866390b6115008456b9f0e7cde1", size = 230504, upload-time = "2026-08-15T08:18:48.353Z" }, + { url = "https://files.pythonhosted.org/packages/3f/fc/f6a85abebd42ce4da2f1db0aa56cc6a0df1995e318b3875d14401b8381d1/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2bced4061f000f7187254a02ad3433ae17eaf991747ceea2f478422590a5bba9", size = 263087, upload-time = "2026-08-15T08:18:49.859Z" }, + { url = "https://files.pythonhosted.org/packages/98/66/7c42677e739ba66746b297e2046918d793078094dc239e1e72768cffccc6/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9eea3ab2597a5e65fe65296e2d6a84570845a6b55532d90333d740d48bbc850a", size = 243269, upload-time = "2026-08-15T08:18:51.601Z" }, + { url = "https://files.pythonhosted.org/packages/de/d8/a50b79237f417af10f8c2a501ce8d1ca87829a22e69117891ca4ba20a69e/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:496846868fea80e479324862fa877f02411f2fd0f83b79ccee2607aa68b2a032", size = 258766, upload-time = "2026-08-15T08:18:53.23Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1d/0fc91aeaeb3c83b748f532399ce67cf84604b48297405d740000f7a9e786/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:85d5855daafc240cc045c026d7a15fd198a09b0fc8ff6f5ecbb5297b509cb11e", size = 250814, upload-time = "2026-08-15T08:18:54.768Z" }, + { url = "https://files.pythonhosted.org/packages/ae/10/3d8c777cf9024615295aa1b808324ad5b4a77855869c00824bad74ffaf8a/charset_normalizer-3.5.1-cp314-cp314t-win32.whl", hash = "sha256:58d3e12c88e0950bca850ae1f7c256055c097639c2edb9eb123af9807d8b15e4", size = 191074, upload-time = "2026-08-15T08:18:56.305Z" }, + { url = "https://files.pythonhosted.org/packages/4d/81/ae557d3c44d1a1d688696d60563413a0866a91b7ebc50f20df838be3d8c8/charset_normalizer-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:acaf604462bf330b0d07e7a07c1d6e4adac79e5fb13e9c5140590542cafacc00", size = 216476, upload-time = "2026-08-15T08:18:57.889Z" }, + { url = "https://files.pythonhosted.org/packages/27/e9/61c01fb8b804692569c036b3fc50495814502dcf13a60649c6055390b02c/charset_normalizer-3.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f", size = 194115, upload-time = "2026-08-15T08:18:59.418Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4e/8544831ef59d8f27ce92c80871380fdacc8076a8a56ed62f82e54f991333/charset_normalizer-3.5.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9085f87b0e38a2b92b8923059b4e8789fe40d9279712d15dcc670048d77079af", size = 342048, upload-time = "2026-08-15T08:19:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a6/e3b46852424246065355644f4fb6dbccc0239a42a2eee27ecfc8957f0bcd/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2679de311c7946dde5d3b6f44941844133ff5c7cb86099c0061ab1e8901c20a8", size = 242997, upload-time = "2026-08-15T08:19:02.492Z" }, + { url = "https://files.pythonhosted.org/packages/03/3b/0cc9a26777334ab2f2e3089b948bbf4e4fe72ea70b897715ef6415043ec8/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:baf3775a2635e5a11fbd5e4e64ee69c7e86875d224a5c72aca4c141064589a90", size = 237014, upload-time = "2026-08-15T08:19:03.943Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c2/027335f0aa337a2a2e121bac1ad88c4f02ba6053ea0926802784f3db11af/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ac8c94b6539074e0f40899301273ac8402b9b3e01c7b7ba269ff30340aaaf20", size = 266174, upload-time = "2026-08-15T08:19:05.598Z" }, + { url = "https://files.pythonhosted.org/packages/86/d3/e367787febe4e74769dec0f406f2c3c8d1b955fce5aee1fd0f94e8367a45/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe532b3c966d1fb794e0698e4589d0444017ae77fc0b31edea13c0e35bcc449", size = 263361, upload-time = "2026-08-15T08:19:07.251Z" }, + { url = "https://files.pythonhosted.org/packages/af/3d/391b193eb9f3e84b02f9314088c386debdc0debee843535aaea2e2c6715d/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c84bec0ab5ae0c64bfe73a7d2adcb5ce73b467523fc27fd6a28ab2aa6cbe35a", size = 252143, upload-time = "2026-08-15T08:19:08.816Z" }, + { url = "https://files.pythonhosted.org/packages/2e/57/de221f1745a90d418199761967e2776bfe2c275a1194220985e8c1d37833/charset_normalizer-3.5.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:854066be00447fa8de2ccbbe893e2ffc4b123ef16d897af794c1e18bd4a714b0", size = 252086, upload-time = "2026-08-15T08:19:10.255Z" }, + { url = "https://files.pythonhosted.org/packages/c8/e3/d119f86a01f9331e8186175f24873b1d74a7ee9e2e4b4d68f9947dae5afd/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:21b82d8082f6f5e7f456ef0bd16323d08de1266efbfeb476e64b2a91d1471a4e", size = 245231, upload-time = "2026-08-15T08:19:11.807Z" }, + { url = "https://files.pythonhosted.org/packages/26/de/d8e48c135ae480879539cdb179c8d3b50c7879497d75dd899b5763b69cee/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:838648accb3a7fd9803fd45c87bce8509648eb0c11bc34e216141300977244f2", size = 241546, upload-time = "2026-08-15T08:19:13.416Z" }, + { url = "https://files.pythonhosted.org/packages/67/c4/217755fd1abc50d326c252922cd642002758095a81ff45010337b8b3ef65/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:195ce897c6153c0700078142cf8efe3e6454ca4cf4357499e4078dfd83396626", size = 267033, upload-time = "2026-08-15T08:19:14.981Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d7/34d8e404e358d2adcc5a228c2134643af00104c8fb0bf525f3688d756f05/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:978eab16f55b4ab2c2a745be9a0a840bf8f09a7f227d9c76eb30214d078865a5", size = 252045, upload-time = "2026-08-15T08:19:16.618Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fa/40414471acf0aa0692ca77305aa00e434fcd8288f0941c93c30e9a5f8f2f/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:cc0329df4caaceb950d2f580b5ac716a377f7059624a0bafaeaf8a218c6ed774", size = 264866, upload-time = "2026-08-15T08:19:18.101Z" }, + { url = "https://files.pythonhosted.org/packages/32/90/fcc850bae791abd2e0c041847f13e270aa08692a79f3e00de6d2dce1cb50/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:687c9ca3035544b113bea2055e180af96fb63c0c476e22a9180f51925186e7b7", size = 253932, upload-time = "2026-08-15T08:19:19.734Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/53afe99068b3c10b4cbae592a52ef72a7c92c0188440e83ee3a078fd8f75/charset_normalizer-3.5.1-cp315-cp315-win32.whl", hash = "sha256:706bfd38730a5ac7a365793269a00f4e988178cec121391f4248d84ad8c972e9", size = 180320, upload-time = "2026-08-15T08:19:21.37Z" }, + { url = "https://files.pythonhosted.org/packages/c9/bc/f46a132041b29e4a8779ed712d3df1bf112e94ca8de58b66d7ec2c0cf8b9/charset_normalizer-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:92caef967d287a407085d61176fce4012b1dd62daed4eb6d5ceb26d3d2538712", size = 204174, upload-time = "2026-08-15T08:19:23.088Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5d/9ed554480eda8e447b673648628fdc29574d23dbad01fe11837adedd1cae/charset_normalizer-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:5fc45d653ea8c9a20479167e11d4a0f8cb2fa3470737ab6f9c827532313187b7", size = 184126, upload-time = "2026-08-15T08:19:24.471Z" }, + { url = "https://files.pythonhosted.org/packages/3b/32/9b8929bf384061ee1fe5d9c27c6f9776d3d824039ad4e14c88ec00c7808e/charset_normalizer-3.5.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:59171c6e45bf07d0d5cab3b0bf81d945035530f6873398b3b531c31184d46663", size = 381441, upload-time = "2026-08-15T08:19:26.038Z" }, + { url = "https://files.pythonhosted.org/packages/96/10/e9aa7923d3ddac652c99a1c5f7be494e737e151566a44abe018daf757f2c/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9dbdd9205662134957cf0c324f639bdc5031c0ca056e2369e238db75187c0f11", size = 241742, upload-time = "2026-08-15T08:19:27.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/53/a2d249ebddf47b889a100c0bdcb61a2f9dbb8bc24ef325cc062e4f476877/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4b018dc5a0eee4676e38fe84a47a427816c590b93b55d9025274ec4d6ffc2dc", size = 235298, upload-time = "2026-08-15T08:19:29.274Z" }, + { url = "https://files.pythonhosted.org/packages/7d/07/469f78af590f7d5cd48e20d8dbfa3d66deeff9ba37768c04d886b5afd45c/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced3fdd71aaa83ce593746c2edb42b7a59cb4c19c8b5c407781c72e493aae55a", size = 262500, upload-time = "2026-08-15T08:19:30.955Z" }, + { url = "https://files.pythonhosted.org/packages/55/66/3bb56a47f7dcba014055b1a1d33c6f08bbe9c1e74dba154cfa25f90ae885/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:19a3dd5aa73cef1c99687c4fc57db016a9c17104ae1185da88ba566a5d3bebe4", size = 258888, upload-time = "2026-08-15T08:19:32.458Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c1/2adc2800903fb013210349313b710a5376856578d9e33e6b9a1d8b36714a/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc5d36d96478aa9c60654bd932525bf32964c62a7281eafdf16d85003a8d6004", size = 250243, upload-time = "2026-08-15T08:19:33.94Z" }, + { url = "https://files.pythonhosted.org/packages/95/b5/a18d0dd1157ab655cc2cb14a545f4a4784bbad70ab3502412e36097502d9/charset_normalizer-3.5.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04368edf83514385ffc3e1cfd4546e595f4f1272dd23ba437a93a9cc3741d47b", size = 249871, upload-time = "2026-08-15T08:19:35.413Z" }, + { url = "https://files.pythonhosted.org/packages/ad/c3/525f508cd1e58d0450ac55ed40ac75bc3a97482c59def5278456a5fbf03c/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:9b5db6052055d34d41230fb78d7c439c23dc536a9896f6cb039e8dd92cfc1263", size = 243580, upload-time = "2026-08-15T08:19:36.886Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c1/49a91fe7e97c8140094ca5c64161ab623a70d9f636bf834eace14048acb5/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:252d099029bcbea642f2a06c4ed5046bdf8b5a8150b64afa5e027e88b106e5ee", size = 239807, upload-time = "2026-08-15T08:19:38.392Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/56a48c296601274c4689b864a8e2dfb209b81dfcb39472753ce95eea662b/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6199d5606e2bbf2b096cf64d03f8b6790c91081d5ac866b8e7bb6422738cc60c", size = 264083, upload-time = "2026-08-15T08:19:39.856Z" }, + { url = "https://files.pythonhosted.org/packages/10/4c/dc48409274a1817ff349711d26c62aa0c597df865d4d69ef79160c859193/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:77efcff2b23071c349402ac1066667a3d011f62398d81408c9b88ad991747c9e", size = 250317, upload-time = "2026-08-15T08:19:41.53Z" }, + { url = "https://files.pythonhosted.org/packages/81/58/d325912115caec62d6bdd77bbab5e0b7da5d234a9f20affdffcbcb530d0b/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:a5cbd90ecf0fc62e64726917ad083b73001f0563657a87ec3c0b504e277dc90d", size = 258173, upload-time = "2026-08-15T08:19:43.07Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/b13b1ccae2c8ec63980d13be1890eb73f8aeabbfce02a24aabc0908788f5/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:4d26f14f041e83dd8edfd61f4cd4fa7285d31798b5bf1f28e70c367ba6c41d61", size = 251960, upload-time = "2026-08-15T08:19:44.587Z" }, + { url = "https://files.pythonhosted.org/packages/1e/25/ed3f9919c5aef8cc818be1f972f565f7610d7b2076b8ebb98839516ffc3c/charset_normalizer-3.5.1-cp315-cp315t-win32.whl", hash = "sha256:ac13b004224fb341e1e25a1ed5e19d32f57cdb2a403e01f003b46f051a550f6f", size = 191186, upload-time = "2026-08-15T08:19:46.293Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/43c2b3e9d8267092b913eb8b0603f0f71993c395632886bd37a7223f96cf/charset_normalizer-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:35aea775dc2bd5f54cd84a1cd2696cc3207c479cb9cf0bd346f0d343e4300ddb", size = 215947, upload-time = "2026-08-15T08:19:47.853Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/9aad3e9c8865e5e0efa9a7f6f81c37a67635a985145ecd44528a81e088ee/charset_normalizer-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:fb78f6e7fcd8ad785d28cd577168bc1aaee827b25bb8755638f694794ea98f0a", size = 193909, upload-time = "2026-08-15T08:19:49.383Z" }, + { url = "https://files.pythonhosted.org/packages/5b/97/fb4e82231aba271ffd775a1b4993b0defc4e3059f286ae41d9433409fe85/charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2", size = 331467, upload-time = "2026-08-15T08:19:50.959Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", size = 253057, upload-time = "2026-08-15T08:19:52.654Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c7/9e48cee5c161fe24da823b61bf381921d77cb994a0a4de148e95018c1984/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2", size = 240930, upload-time = "2026-08-15T08:19:54.163Z" }, + { url = "https://files.pythonhosted.org/packages/49/e0/716601f3cc69be7b198951150c75ead1ece33c3c8036ff6ffa46029659a0/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235", size = 230822, upload-time = "2026-08-15T08:19:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/d3/05/71bfc5caa0abcc45aea1f6a4d50ac68e59605ddc7666fe8494f4cd229665/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598", size = 260037, upload-time = "2026-08-15T08:19:57.312Z" }, + { url = "https://files.pythonhosted.org/packages/c3/92/de7e32ed05341e7a9c4c877c318418197b7f2d66a3b68d561bf2ac57ca3e/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96", size = 255097, upload-time = "2026-08-15T08:19:59.056Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7b/ade0a122600319dfa0b1000ab0f9731c94a817904cf3c5de408c73a4ede7/charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962", size = 250166, upload-time = "2026-08-15T08:20:00.612Z" }, + { url = "https://files.pythonhosted.org/packages/75/9c/019fbb9f4834491a160951349b1a3714439376f66e5f7cf18b4f18f0c7aa/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3", size = 241821, upload-time = "2026-08-15T08:20:02.321Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/11d4840bfc99330cc7fbcc2681ee5a044553a6e77655508d8f9b2bff7b34/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950", size = 232529, upload-time = "2026-08-15T08:20:04.008Z" }, + { url = "https://files.pythonhosted.org/packages/18/96/2b3a21492d9f65171ac75d872f5018260013d00bfa0ff70ec9f179148cbd/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8", size = 260348, upload-time = "2026-08-15T08:20:05.877Z" }, + { url = "https://files.pythonhosted.org/packages/d6/aa/a69a2028e8bd052476c245460ab19d7de595de084dd968f2d75cd50c3e25/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031", size = 247234, upload-time = "2026-08-15T08:20:07.487Z" }, + { url = "https://files.pythonhosted.org/packages/35/8a/3d130aeabcaf3d2466af76b7b141c08d9e89c9016ab4b7cdd0f7dc2d1c62/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072", size = 256917, upload-time = "2026-08-15T08:20:09.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", size = 254846, upload-time = "2026-08-15T08:20:10.727Z" }, + { url = "https://files.pythonhosted.org/packages/01/65/d43b714731bb2f40d4053dfa00ecfc1c5a301f8e3316c5db3a09af59fe94/charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc", size = 174216, upload-time = "2026-08-15T08:20:12.334Z" }, + { url = "https://files.pythonhosted.org/packages/35/4f/b911ed898b26a09789eba9c9200c999aff6c61b4bafaf4838e56d1a1e1a3/charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959", size = 199764, upload-time = "2026-08-15T08:20:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a7/920baf467bfd9bf689f3b318340f37aee4572a71f162bd8db51da55ba4fa/charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e", size = 287318, upload-time = "2026-08-15T08:20:15.551Z" }, + { url = "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", size = 68658, upload-time = "2026-08-15T08:20:43.306Z" }, +] + +[[package]] +name = "click" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, +] + +[[package]] +name = "codespell" +version = "2.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/19/45e941380f69c042b43423513d201e6592346f992394347f5e7174c31407/codespell-2.4.3.tar.gz", hash = "sha256:cbe085e331227b37bb86ef8bddd08dc768c704ee9a07ca869852c093fa2793e2", size = 352773, upload-time = "2026-07-15T11:51:54.159Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/bf/bdb951d34eb169140b546f44be9ec4525d1acefb9eb5572071f5492b19fc/codespell-2.4.3-py3-none-any.whl", hash = "sha256:af2505b335e8573dbd2d384d1c4ef498f4006f4ba2d6fceca01e55b91f52628a", size = 340736, upload-time = "2026-07-15T11:51:52.925Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, +] + +[[package]] +name = "coverage" +version = "7.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/2d/c738872f477f5687152acae68635790387425d407ae37dd3d3a8a6692307/coverage-7.16.1.tar.gz", hash = "sha256:f83981779bcf9dfa06fa0a8d4cb43e0faec1706328ce07aa3e7b665b4ac0f210", size = 969651, upload-time = "2026-09-13T19:12:21.422Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/62/a8b4dd53308a4b1571e6bdd77ee562c9b1c1742db9886c0aef2f69463067/coverage-7.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f12a9e27ca7b65e40a8475d27899b2d45064d9020e6a89148939e01987b5853", size = 223178, upload-time = "2026-09-13T19:08:21.805Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7a/b9325b8d486a5c05fd4a9eb1cd9c9f697c361ddeda0e73567d5c2d8959f6/coverage-7.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f590d46c30d9e4c1fda3efefe5443f4c2f6a4192c5ca2ba403653e9ebadf097", size = 223701, upload-time = "2026-09-13T19:08:23.391Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ee/3ab3ac6e9e7165a623695b73a83d1bfbdf5069681a68a012cb5abb096060/coverage-7.16.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3df82f0a3cef4e1bcfc799436056f0b978dda319d0bfd4460c6e479b2802d98a", size = 250434, upload-time = "2026-09-13T19:08:24.948Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a9/31ffc231e9879876f611cf1ac7622cf092d76762c07ca64dce47b29a200b/coverage-7.16.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb462d59146656e278d1e8ed913ce374d0ba68a4e081acde1867f6d3377fc881", size = 252264, upload-time = "2026-09-13T19:08:26.734Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f4/4fb4f6b16de07dbae3d3a111ae28d32ad657ce6ff4d364c49c10962daad5/coverage-7.16.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7db888dd0a1df1a653cae7f99d4047430d2187a3626eda51bc847b0fd6b9b43", size = 254127, upload-time = "2026-09-13T19:08:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3d/048e88eb610f44b7542dac10fa45bb2ffad5fbfcdab0f03289a843da934b/coverage-7.16.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:38a7e16f061504ac2b45370bf5bf97e8250d8d3f25e37385bae884978166554b", size = 256043, upload-time = "2026-09-13T19:08:30.019Z" }, + { url = "https://files.pythonhosted.org/packages/97/59/d27b113c80b04c7fc64c69457132648e1d287b83a1de41189a504b50b52b/coverage-7.16.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b26b55b18e1a53e1a159dd743728c4ddbbef28ba19000a91aaff5ce023197ec", size = 251092, upload-time = "2026-09-13T19:08:31.57Z" }, + { url = "https://files.pythonhosted.org/packages/45/42/b7eafaceff08dc1dcbfc924fa9233031fd00c8b8287cc4a1a0dcdedcb0f9/coverage-7.16.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fdb2f528b50953e29d22033b3256c396a700193c6e45b2490222ef9c333cbbf9", size = 252170, upload-time = "2026-09-13T19:08:33.191Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8f/57dcc5360aa6d8d24370c12fe855f6ebf5fbf2718c154fb9fdec7161de07/coverage-7.16.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3284754371dc78592aa3ae4d661d30ee20e02b2b6b0de3590a181a936d0d3b38", size = 250173, upload-time = "2026-09-13T19:08:34.75Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/379a8c7ac0f3063769dc43fd2475e9fc9b2722cb88eabe71f4d862660c93/coverage-7.16.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:802d1246c540e07486d4ee1adfa19a797e4b33529ffc371dc140644e8f27da0a", size = 253986, upload-time = "2026-09-13T19:08:36.737Z" }, + { url = "https://files.pythonhosted.org/packages/45/25/70218d2a6077730843b75ee2525fa64a750283d71cc4c063dc0b75ba6979/coverage-7.16.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6fc735d6fe6d57f803e7ba021be4dde48e43e6aff94e6954350555d5332b0594", size = 250444, upload-time = "2026-09-13T19:08:38.434Z" }, + { url = "https://files.pythonhosted.org/packages/58/ac/6c41c441baa6c8a1fed5615e4e1e7d0ca649b33b472ee28591a92d70a1ff/coverage-7.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed5ade1bb18f62edace1bd198c66f9d4c75a8385d5fd24e87d917ea1a5958773", size = 251046, upload-time = "2026-09-13T19:08:40.133Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/01166d6f8aa42acd148b4c2c88173d60eefdd0270ea2bcbba79be35bc193/coverage-7.16.1-cp310-cp310-win32.whl", hash = "sha256:0c309096926b119543dc16438a11ef4c80783d2f4e59ff94f7f462651a944cdc", size = 225242, upload-time = "2026-09-13T19:08:41.672Z" }, + { url = "https://files.pythonhosted.org/packages/d2/28/a76060478919becf0fbea2df2053a6e1a7195f11edf8dfd51c58c1636b43/coverage-7.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:7562f8067ed9360e8b9739e5703403a7686dd1b36bf0f89fc538047c54cdea90", size = 225868, upload-time = "2026-09-13T19:08:43.22Z" }, + { url = "https://files.pythonhosted.org/packages/af/3a/d09495dfd5191b5593852bbe2f037afae768157443378d066d9ecea69a49/coverage-7.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72e013665e25cf9d44779f01f340af26319756f9a76822b7c94ce6b1d93813da", size = 223307, upload-time = "2026-09-13T19:08:44.914Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/310196a80258e28ebf6e5aa814a5a6ffbb18c03cf3c0c0c04191ed893e88/coverage-7.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb05c0ff98b56ba6969adf35556bc43bcb8d094df8bc9cb403acff53460c4e07", size = 223817, upload-time = "2026-09-13T19:08:46.738Z" }, + { url = "https://files.pythonhosted.org/packages/73/29/ed0fda1fcd440cdfc07e72a07f3c9c3b43f65a6b3d53bff2cba9b9de50e8/coverage-7.16.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d0ececb32090e3fbb03e0d352b973a0485879b4de6c58daf47227b9988b99e5", size = 254222, upload-time = "2026-09-13T19:08:48.358Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4e/5e2507fbd71ec1047620978f39568b0d2ed6c4468d08495a0c24f5677f65/coverage-7.16.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f0ba3892d81aacf36996c52f16bca04e39af31a6c5de930b7688ab617f4a6475", size = 256134, upload-time = "2026-09-13T19:08:49.926Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b1/a9f97846554bc240473656f9c3874591105f29c08eccf4282daadc6bc281/coverage-7.16.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6410b75fe07d5271eaa95fc24bd0a9177ed588d9d1c10c0cf67829adb8f0567", size = 258240, upload-time = "2026-09-13T19:08:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c3/2b1ab208d06719394851cdd7dc322157870a93b178410b1bda6cac7755f9/coverage-7.16.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d1039cb2de093225d597109342ce1675626bd127565e80b3044f4eca07c15b2e", size = 260202, upload-time = "2026-09-13T19:08:53.204Z" }, + { url = "https://files.pythonhosted.org/packages/af/14/ff4db31d6e3126d5b72aab1e868d35ec94e1b71dee46f5c3b54cb1b2438e/coverage-7.16.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf047bc39fde5425be2628666d0f435ed8817859111c3aacc84b32d858069f5d", size = 254305, upload-time = "2026-09-13T19:08:54.833Z" }, + { url = "https://files.pythonhosted.org/packages/ce/27/c28faa4818f61c49ca3bfb4a77e1c191e79a40447504b89af0bd859cd6fc/coverage-7.16.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c05913d0d5badf7ac83200f35dcf9514cce5df16a7cc89e7d1d7fff0461813b", size = 255935, upload-time = "2026-09-13T19:08:56.453Z" }, + { url = "https://files.pythonhosted.org/packages/46/5a/42e64e5b716048cae33e2e15f8c6fe04a927467056c533b8e88900da895e/coverage-7.16.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4027bf6d7bc0a16df058ce913b69f10c5687f8e1ca668f08caa659ce101744bf", size = 253995, upload-time = "2026-09-13T19:08:58.159Z" }, + { url = "https://files.pythonhosted.org/packages/84/e5/860287acd5a1e29acd337f302b476b54148203590091b1a1555b2e914b50/coverage-7.16.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4f48b345f831eaf4402ab6333c2dc3e2e2b5bc7b9c1b8fe12680dee3f0538f01", size = 257767, upload-time = "2026-09-13T19:08:59.958Z" }, + { url = "https://files.pythonhosted.org/packages/a5/c5/4b68006da567b4b413352f891776c35776b3557a0479a28187ab57c21659/coverage-7.16.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8643baeb590726c558b2faed6cd59b0917480f9367fcc692026f1a86d824fd08", size = 253715, upload-time = "2026-09-13T19:09:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0e/b2d0c47cfbf11770e197f1d0f11a92864eb29fd7eaca45bc9915573d9725/coverage-7.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d06dcc420b570bf683cdb647cc8fe62b672d9e429ef711c3cbbb7a6880ca1572", size = 254624, upload-time = "2026-09-13T19:09:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/2e/14/3bab8821b2b942155578fc34e88e21b0670d922f039ab23651f6035ba7f7/coverage-7.16.1-cp311-cp311-win32.whl", hash = "sha256:946f58aa59b08bcd6afcc6a7bd0ff54ed5eee844f32ad69fe6814836d15856a2", size = 225404, upload-time = "2026-09-13T19:09:05.174Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ce/4f4ce667a97d1c538b46a79b89161395ad2beffc248b6c782afc9f521927/coverage-7.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:684c7ee9b4c04358fe6ac8b517ab51ec35fcd79d08ff0f105dd8bcd96885bbb7", size = 225879, upload-time = "2026-09-13T19:09:06.81Z" }, + { url = "https://files.pythonhosted.org/packages/26/17/f3dce5e47351ab34522dd037fd64b111742958e24861bdd2820971bc1a26/coverage-7.16.1-cp311-cp311-win_arm64.whl", hash = "sha256:1b24f79e25bcf6c73931aeca7a3dfc7595c0cb5e9364aba3fdf387a3de4b1c22", size = 225428, upload-time = "2026-09-13T19:09:08.502Z" }, + { url = "https://files.pythonhosted.org/packages/1a/f7/7cd4c9f2a3b7574414222ec425d5eee21cc690d867a013315e3be8ba185c/coverage-7.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b7f2c26ce6ce0b1e0ca0d5fae96ea510e3a2e78b7207f06e76b7f2c87fa3d0af", size = 223475, upload-time = "2026-09-13T19:09:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f2/9ac65f9cedd43f91e2d3657c11f13aef82aebae89b2243dc9e44fe58a740/coverage-7.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070acb9da788dff743a4d36fc015feee12d68f0349959017542017c79f59c21c", size = 223845, upload-time = "2026-09-13T19:09:11.988Z" }, + { url = "https://files.pythonhosted.org/packages/62/4d/f13db452d4367fe1122abfd98ae330596f65a3b40498f8e1a5811f68f123/coverage-7.16.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e366587b370bc9b8b51b7b7272c610c56db5d5b4795b9e4a29d28ff2f440f809", size = 255341, upload-time = "2026-09-13T19:09:13.708Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6b/85ed86e82835a96ddfbe7705c387c28ce1cbbecd1b6d606f5ddfeeb712df/coverage-7.16.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e82e10b9d290f60b63459cfb245a841aec347603997206296b93881463a93dcf", size = 258078, upload-time = "2026-09-13T19:09:15.368Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d2/f66945853d850b9c05f4e012e37396f1f31d0cd40d062394b229c22e7b56/coverage-7.16.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d73bb1f85c4150ac208fb0755beb04b2e44897bad81414de9380f98dd74729f", size = 259191, upload-time = "2026-09-13T19:09:17.046Z" }, + { url = "https://files.pythonhosted.org/packages/77/a1/7b907abe62461f289035c7ac60ab3e6334efb8c425151aac4abfa0c97820/coverage-7.16.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3397b9032553d281ad6a9253b12675b65e0cc8cd7a3b0633cf48872c9eb13360", size = 261452, upload-time = "2026-09-13T19:09:18.756Z" }, + { url = "https://files.pythonhosted.org/packages/51/e6/e26f4d6b1069a2a2fee3238a132f85c5b4c1dc25aebd88e015451ff0bd68/coverage-7.16.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77890395cf37026a5907d3ad32376aa51f41c0f163b7477fdbd4f94966cc1d08", size = 255698, upload-time = "2026-09-13T19:09:20.786Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ec/99db7450e813050ebce300e31bbd4f997c76a8c6e3a6d168c5dffc104109/coverage-7.16.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b0944dc3bee3091039bf970d73caaf930c906013128a421bdc132e797494d941", size = 257111, upload-time = "2026-09-13T19:09:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9d/500df9d3cd8c541ac84b5e0bcab00646be75654e05aa34e8410ec851282d/coverage-7.16.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b89d22a89d5bc05dd95b64e08295b8394aa96dc88e08f8ba210c9ebfebbe0489", size = 255258, upload-time = "2026-09-13T19:09:24.429Z" }, + { url = "https://files.pythonhosted.org/packages/c0/24/53318d96da332bb4946f8ac646fa19fd37fecbd0c49144735c716ac69bf0/coverage-7.16.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:550a2a1faf7559f13d5344f12d1eb886ad87955155d7dfab2a3fe5c8ec8fe776", size = 259326, upload-time = "2026-09-13T19:09:26.32Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ce/abc0462b2e6ae96ae22197d2bc30fe33b6b4e52937e782745436ceb2a761/coverage-7.16.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:55eb268e5b81aefac759766c9162625b06c1bedb7b77d936225bafc4f038a6f6", size = 254827, upload-time = "2026-09-13T19:09:28.191Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e1/0a04eedaaf19196b51f0180968134228c7646e469ed29914e48690a7cf3e/coverage-7.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65a8fc80898c9ce59f04349fe8b4849b1f9787f14e52ead990e5f849ff4727a0", size = 256698, upload-time = "2026-09-13T19:09:29.922Z" }, + { url = "https://files.pythonhosted.org/packages/de/1d/d441a55cf22ce9d8e9e34814806c47441ab844bd534e0f4b64e1c6ae8bd1/coverage-7.16.1-cp312-cp312-win32.whl", hash = "sha256:528a61be40977c340cf201d23b69bd6a6bab507da60e9dbda85f8b30e935d70d", size = 225541, upload-time = "2026-09-13T19:09:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/73/27/ec3d032375735dd331477caa051419678079ff90fcb53d0284a6c2bfb757/coverage-7.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:d0f02c633630e2b74522108ee95a84ad6e1204a8016a6cca5297f335ea27147e", size = 226075, upload-time = "2026-09-13T19:09:33.556Z" }, + { url = "https://files.pythonhosted.org/packages/94/00/90e9f5c4434878494306b9c0ee8068ebbd829483737d8cefccc58884d728/coverage-7.16.1-cp312-cp312-win_arm64.whl", hash = "sha256:2959978f9d1d20a2c0c15d0a68baaeccf615ac1aa214cf4a05a10d6f568926c8", size = 225461, upload-time = "2026-09-13T19:09:35.48Z" }, + { url = "https://files.pythonhosted.org/packages/aa/74/c08c0c4dc9fa6bcd1d90728a63660aa1b17b488a806948598456c48f75d1/coverage-7.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ee5465db6e9152a7d09f3215309326878c6aa3ac509195a369f9d264ff4bfbd9", size = 223501, upload-time = "2026-09-13T19:09:37.276Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c8/784986d326663285258a8e39835c46fb730cc85284f0dbcd82078586dd22/coverage-7.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b8256f8b525ba233d2e4cdcdce0d6673c66fc9bf70df1fd5e67c54a74e2d245", size = 223876, upload-time = "2026-09-13T19:09:39.385Z" }, + { url = "https://files.pythonhosted.org/packages/15/76/73fb792928872bbb07e553f920ff55c65ee962c469265feb5d1d4ae5f97a/coverage-7.16.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d57cc400275b9a2892e905fc893f732b21ddb95271bf96406c88e2f6367848b5", size = 254863, upload-time = "2026-09-13T19:09:41.326Z" }, + { url = "https://files.pythonhosted.org/packages/ca/8d/1fd78899513244b065ccecdd1cfc6aa8b8f1bdee796d8c4f2aa8e8e5397d/coverage-7.16.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b3fd0f3435ebb7a7183b32a6062a8b755f08242ced1f3f22761d30b56b3c2a5", size = 257460, upload-time = "2026-09-13T19:09:43.086Z" }, + { url = "https://files.pythonhosted.org/packages/47/1c/b23ddfcb7ef9bd7b3fdb7be9a5dccf9925ae88e556df7aa5b655283c78f2/coverage-7.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e5eb1762e7eb5fad34ef913e8107c7788a66f19d328e598ce95bf7217f9e5c8f", size = 258697, upload-time = "2026-09-13T19:09:45.161Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c9/19d0c6ca35778a7e9415c30c46a0c64c9d4374219d004a35563132296896/coverage-7.16.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:46cd3a73e9140410de62cceb66214bce0e08fb3922b9176fbfc1522fec151b41", size = 260827, upload-time = "2026-09-13T19:09:47.061Z" }, + { url = "https://files.pythonhosted.org/packages/97/fc/5862f7344382c62b85df43d483e579168cc062e007f54d895dfa51fe3e7d/coverage-7.16.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1ba5142d68dd2cb775cbd0ac8601819298152047803c8efe4eec6d7d7aa7878", size = 255038, upload-time = "2026-09-13T19:09:48.945Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9f/5c26583199a68df8d15124b4590520903f8eb7cef17db293fea712bb0783/coverage-7.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c389c6f9d1d518e1249ddcb8a7f158135644ce2c508fa6cc17b680777dad5bf2", size = 256827, upload-time = "2026-09-13T19:09:50.738Z" }, + { url = "https://files.pythonhosted.org/packages/95/13/605198bff079b107336710f28a797312ab132587168600d70a290e7ecd2e/coverage-7.16.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cdc57746c7ac0ea063351b4d651c3bb4dd4fd35e64dbb8e90c10e14eb03c4080", size = 254794, upload-time = "2026-09-13T19:09:52.577Z" }, + { url = "https://files.pythonhosted.org/packages/43/b7/0bbb32dc5ccdac766a13763fdfbffd7d73fc80349a69230c46c6b71e7508/coverage-7.16.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5597180ed7670cc94c04c65347418a467d3a43d5f0cf52fcac647f5425f42037", size = 258947, upload-time = "2026-09-13T19:09:54.372Z" }, + { url = "https://files.pythonhosted.org/packages/13/bd/65c31ddd43ff4e61b63721b3dad17cca412dba6e2ce89f92abdf75734339/coverage-7.16.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9647a0ac46255b8fef59a433a2161f03e5483f3a35e1cbd9dfe4600baff0c6b", size = 254613, upload-time = "2026-09-13T19:09:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/25/20/d278115f2ba522b3e3128c6852be265f4e0df7202b2effca4db96cf0217d/coverage-7.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e306e98186b9cd109121f3583aeb7978797ad21d948f22944c5c08845cd554d0", size = 256388, upload-time = "2026-09-13T19:09:58.095Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ca/d43aa396fb3a2f71c9411b99d926475f5acaf5c124f605c592865c80c8d5/coverage-7.16.1-cp313-cp313-win32.whl", hash = "sha256:48a78a66fcce49d7f6156524bf979c0ac633d584199717c68c6ffa949fc14e6a", size = 225550, upload-time = "2026-09-13T19:09:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/0e/30/df2f6114f6a17cffe94721bd1d298f77f86aad493717fa5bc03cb291a1a6/coverage-7.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af03247d598a353bbbbe1b925deb735276e4d845e7197c4073dc89352b236fa", size = 226091, upload-time = "2026-09-13T19:10:02.331Z" }, + { url = "https://files.pythonhosted.org/packages/ee/31/6f90d8aab72a112492bd50e3b5485b53b772b1f5fa70da0a620e723caae6/coverage-7.16.1-cp313-cp313-win_arm64.whl", hash = "sha256:166adae25b05b04c9a84135912066d9c97482115af38df1a419a38aacc6b6f5d", size = 225481, upload-time = "2026-09-13T19:10:04.151Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b4/2a7c793965bae9f067aabab793a44d7a2f3ee7fb16b01ce1976bbd4a0218/coverage-7.16.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:cc0b37fe6f5ce5f1ccc62ad4fa9b1ad201d8e9b6027fd5e0170877beee4b2d15", size = 223546, upload-time = "2026-09-13T19:10:06.019Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e2/633469076a2dbbea036cc15a268a3a5d6b2c7dd5d9a9567b2553dfc5ad61/coverage-7.16.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6618f481053b63fc6121faf8fc676bd9b7163c2a19d9e984a2e850002c28ab57", size = 223881, upload-time = "2026-09-13T19:10:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/de/c3/f06150c13284569d53273b909f31222874276a595637b7852571dfeb2c18/coverage-7.16.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fa02d561eb1d8d2f8ba43ba6e3cef4c6c402a3b632a9460fa329fcadcd5df6a3", size = 254919, upload-time = "2026-09-13T19:10:10.254Z" }, + { url = "https://files.pythonhosted.org/packages/d5/40/47e25b215ae18a29010c8e29be8782a6e04d18ba6224be2bf6cebfce6427/coverage-7.16.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc5354a124799f1f87b7637bbe6f18cd4bc66a1f37f6aa2b5db40f9adad531dc", size = 257428, upload-time = "2026-09-13T19:10:12.124Z" }, + { url = "https://files.pythonhosted.org/packages/27/4b/1e2a4267d14cbd12a8489364a9d40020233e6be836d929b363f0e77209e2/coverage-7.16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34bafe9f4094315248573e6223e11af0ec1b25f9cbca43bf0e9a26a189ba2751", size = 258771, upload-time = "2026-09-13T19:10:14.031Z" }, + { url = "https://files.pythonhosted.org/packages/be/2e/9aa6146cea929fab9185bb2642ffef7f47520a6e5efe407f75f9b12f4cf0/coverage-7.16.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:29c4d3e32a3b5efa420a3dc627c7e570deb80ef997def52c7686a474f5edc7ab", size = 261086, upload-time = "2026-09-13T19:10:16.213Z" }, + { url = "https://files.pythonhosted.org/packages/13/3c/f9ad8bcd4fb3d21c9d20a16d6d6c6f999eee8f4498ed7659a3dbd2f4b74a/coverage-7.16.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2066c447fdd0bca39a9633a082d8ce67bf9a539a203b85059a364a405dc9fe9", size = 254895, upload-time = "2026-09-13T19:10:18.602Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d1/47eda9fd1eaeea39fa7b5b13a63b2bed92ab901841fb120b3f9f5e1dc30c/coverage-7.16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd8ac10cd2458b3c6343aac082fb9bd0e3fa806cb2c4975f2280153474b88412", size = 256783, upload-time = "2026-09-13T19:10:20.778Z" }, + { url = "https://files.pythonhosted.org/packages/38/c3/565edf044877cb8cd3373c56885347ffc38f0edfd1f1679a487b208c19a8/coverage-7.16.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9d8c54ec32e5c102b9241f75d88ae26538b53662868ca491736611db448d9c7a", size = 254742, upload-time = "2026-09-13T19:10:22.733Z" }, + { url = "https://files.pythonhosted.org/packages/fd/88/87d2b2aeaba719192b2089ff1c2cf89a06cf73a6d2e9f1f145626617700c/coverage-7.16.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6dd8dda3402a01a1a8fe8b753a282466f615128574a5590a9108acd07b1f8540", size = 259016, upload-time = "2026-09-13T19:10:24.769Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1b/70813185b125768abdcf7899fec4d37edc2e5fc9b60c7045c8f4271ec757/coverage-7.16.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:79afa9726438912e5cddd1fe541815cea9763c92935f594835e4c432565b68a9", size = 254559, upload-time = "2026-09-13T19:10:26.781Z" }, + { url = "https://files.pythonhosted.org/packages/d8/fa/e7aa5af279aafda633a1ede8bfd7d6916b0c8b2082be86759e0b52e73a61/coverage-7.16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3db3978211c3cead5437a80136ca0556bab8bc7828de15a762884b0598c41361", size = 256215, upload-time = "2026-09-13T19:10:28.714Z" }, + { url = "https://files.pythonhosted.org/packages/38/87/7a894fa4f8c6662d2b6a87a3436950e15b1fa56e01765c9d6634fb2cbeb8/coverage-7.16.1-cp314-cp314-win32.whl", hash = "sha256:49c39c7068a494f8eb427155f5682f44feee43f9b3107fd54b1e52465379c54b", size = 225719, upload-time = "2026-09-13T19:10:30.743Z" }, + { url = "https://files.pythonhosted.org/packages/8b/01/fa7193c8005fb85488f02b0e1cc3c05a233cf2640206dd978af447aeecbf/coverage-7.16.1-cp314-cp314-win_amd64.whl", hash = "sha256:c510dad19552d912058e4c3e3cbec3fb155dbe8d0ce0ceb7e7dbf5c5822bae0b", size = 226208, upload-time = "2026-09-13T19:10:32.698Z" }, + { url = "https://files.pythonhosted.org/packages/da/5c/a08634c714924c3eaef811bb3576c044128aa5e7dfa86c75e52f0761849e/coverage-7.16.1-cp314-cp314-win_arm64.whl", hash = "sha256:b7d4d7e6dcaf33e85f1919f03346403bdcc27437c420a78835f3805bca0ab71f", size = 225633, upload-time = "2026-09-13T19:10:34.79Z" }, + { url = "https://files.pythonhosted.org/packages/43/df/ddb8a4c664046b1a0ee29c9c2d25b993e5dbc8fbde715df3694a64532781/coverage-7.16.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:3d0a3681c12d3e0bcdea3d9414b04087828d6c1a482802d6f7f42c37ed530152", size = 224281, upload-time = "2026-09-13T19:10:36.853Z" }, + { url = "https://files.pythonhosted.org/packages/e2/d0/9076e0c762d8afd91182e60a520fa5c92c4a334785eeb9fd6b8ef8fe7e3c/coverage-7.16.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f3b4469d3da3ecced775d1a8c9c5d9fc80f259e30b7b89f9fed0700d6035ecb", size = 224547, upload-time = "2026-09-13T19:10:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/03/e5/9c59e64b6161704f35fe91549bb19b2bb355e95caf596c26a2065564807c/coverage-7.16.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c08ae35c1be2fe1ce4b4c628df5c6fc0dc9a87f8e5fe8e20238d249678984741", size = 265906, upload-time = "2026-09-13T19:10:41.434Z" }, + { url = "https://files.pythonhosted.org/packages/57/5a/13ccaffb77f766101bf6f38be9dba9e468b02cc92da4552a57877dbf1c1f/coverage-7.16.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ee71a38c54bb2676bbe762b8b0943a79ccb1c2fd6a52054f66e63eda392f8c1", size = 268023, upload-time = "2026-09-13T19:10:43.533Z" }, + { url = "https://files.pythonhosted.org/packages/ad/a1/05cfcf01d3c7c922832698ad46e51d3441d820ce87a943014bb5cf5710dd/coverage-7.16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76491917771f179f9772efe218c5ccc65950dbdb35f4439298d8a8dfc6ec1f72", size = 270442, upload-time = "2026-09-13T19:10:45.895Z" }, + { url = "https://files.pythonhosted.org/packages/72/15/a2f1544b8e3835d7b769f7dabcc9ac0283e0b646ef3344703ff8f18d83e6/coverage-7.16.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4aa0b0a6f81fa3deb211e643f6954e78b4376b62b9c218271236cfa757664e8", size = 271565, upload-time = "2026-09-13T19:10:48.123Z" }, + { url = "https://files.pythonhosted.org/packages/df/5b/963c2993a82bd313f298d663afe03e164b96ace4d9d4c7561740a559e13d/coverage-7.16.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:756ba2d96d073c5a2a55d67fa22784763710fadbe22c41adde2d9cfa4dd78a8c", size = 264959, upload-time = "2026-09-13T19:10:50.195Z" }, + { url = "https://files.pythonhosted.org/packages/12/59/5eba06d1943735d7cd61d46d8c8a20ffe8ddd2da06b3c94366078dadeb9b/coverage-7.16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:99bf9ea435cefcefd220f8687c3ddbbf78dc2de0bd11b57c3ae9fbbdf8d5561a", size = 267897, upload-time = "2026-09-13T19:10:52.252Z" }, + { url = "https://files.pythonhosted.org/packages/bd/48/af6c30f6ea431bb9b83f9070d268a9cc4fc97490abd32080164177ea999f/coverage-7.16.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:35cbc81f937fc402971df45c897d2df2bfb2014efcd990360032aa0a651635da", size = 265504, upload-time = "2026-09-13T19:10:54.432Z" }, + { url = "https://files.pythonhosted.org/packages/80/f2/6e13852a8656d05fa83284567dd5a5b1e6d89bef79fe3effca2787159eab/coverage-7.16.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:8fae08e85b334ac6ac886002b5041396a31bcf805225bbe19847627203da99e2", size = 269235, upload-time = "2026-09-13T19:10:56.563Z" }, + { url = "https://files.pythonhosted.org/packages/c2/32/b4fe465daa64ece674f83a750dfa4ba0fa3c5c74d6ef5dbb8dfce892cf0d/coverage-7.16.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:83362b64e215ef00b0ba33fcf13655ace6c9fdd144d5ad2ab59ac86c2daf166e", size = 264347, upload-time = "2026-09-13T19:10:58.634Z" }, + { url = "https://files.pythonhosted.org/packages/54/f3/88b5c0e4ca3994c6d5feb7b1bf4c9a62cee205553159184968426930a7b1/coverage-7.16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:33300f2e140ccf26af3d8152e62bff71993f9310cfc63ba7a20940b0d246a0ae", size = 266660, upload-time = "2026-09-13T19:11:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/97/72/6eff5456d7ba7f1c4678af531c33f9d957cae3201bd229b056fd13a204a3/coverage-7.16.1-cp314-cp314t-win32.whl", hash = "sha256:5539304fdbb2cc144df684d35a33b81145334d23e1c2367b5a923d25107f70b2", size = 226026, upload-time = "2026-09-13T19:11:02.846Z" }, + { url = "https://files.pythonhosted.org/packages/8e/c8/6e5ae3d8d4d0f2c0078985bf4db55fafd90e8107b1bf91ee3547a13f5694/coverage-7.16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:715dcb72c3280c428c3a20134b87e42c29acec9669136e899ab2de69ca86218d", size = 226862, upload-time = "2026-09-13T19:11:04.921Z" }, + { url = "https://files.pythonhosted.org/packages/be/c7/68f9f0734afc904a92b974b489545b6a15700f3b1c4bd36eae764561e661/coverage-7.16.1-cp314-cp314t-win_arm64.whl", hash = "sha256:dac8b84c03e6029d272b8249c77018db83de59ca009a9adef7c144b4a62ee5e6", size = 226171, upload-time = "2026-09-13T19:11:06.969Z" }, + { url = "https://files.pythonhosted.org/packages/ae/16/e11addf5322d98e86307da9e00c94644b97cb72c534c74cda89705bebeef/coverage-7.16.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a337dc2d54c74430cd2febb8ee04f7c508ba8b3b412bf0463f077a66cfc73743", size = 223544, upload-time = "2026-09-13T19:11:09.108Z" }, + { url = "https://files.pythonhosted.org/packages/92/d9/7ccd6484f615961d94b09c75904f87d86375109596069ae3a495a205dbbe/coverage-7.16.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:3acd1d78397dead78dd1b011b5fc19cc823c190349acd549e63856dff649c80e", size = 223882, upload-time = "2026-09-13T19:11:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/59/37/2fd78152a557df4a7e4ad78d6851ede90c211838d526e4916c6016f45144/coverage-7.16.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73a32694603a34ad01d7e51a481a4023410d8099e1d0757e067945695c10f0ae", size = 254987, upload-time = "2026-09-13T19:11:13.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4a/58e2f9b8b13cc422aa7f474a498945ec7f446ef473b5b6bae18d2981f2a9/coverage-7.16.1-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fbbe8265736659a6be2e6042b6a35be13545d14b243cc1d7ecf65f90d788a370", size = 257902, upload-time = "2026-09-13T19:11:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/3c/74/63f8f6a67c03b86455c726be273b2aa5b57fd1122f22350d141bbb142e91/coverage-7.16.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b0359eb4c62f9993e176bc8f50450fc736a6b90dbcc05bb8584e948812699ae", size = 259522, upload-time = "2026-09-13T19:11:18.206Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3b/1f2261da7483e345fe55de11e41ffc16a543511e37c0912134c0dccc4f6c/coverage-7.16.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:da506e669a8a851b59e122b4b219ea70996a6296f44f3a9348a852526ff961de", size = 261723, upload-time = "2026-09-13T19:11:20.628Z" }, + { url = "https://files.pythonhosted.org/packages/a7/8a/7eb1a361e044b7293f49c921a974b532627ed37f3bef5e7e9f58bcd0d5a2/coverage-7.16.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:64a2a5985d81810ed605ff0dc4ccd6555efcb5700353825532a9a0aea65826e1", size = 255462, upload-time = "2026-09-13T19:11:22.733Z" }, + { url = "https://files.pythonhosted.org/packages/67/66/6d94f7ea87e99c519def5cfa5d4a1ab20247d6b403eb6e6b5b63d6aa5985/coverage-7.16.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:66d70132b69b861805dc1ca46cdd733e54c416890e8f1371d2fd103f70b59c9c", size = 257617, upload-time = "2026-09-13T19:11:24.933Z" }, + { url = "https://files.pythonhosted.org/packages/3d/52/c3de0a3868589a1463a4f1cb0222eca0e5fa9f91da865ccf20d2e19ec19c/coverage-7.16.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:db651a9cf325a542bc2b7b8cc8f1b2bdc6492739bae3103731b2f1c85b96cff6", size = 255495, upload-time = "2026-09-13T19:11:27.158Z" }, + { url = "https://files.pythonhosted.org/packages/9e/a3/ea10e75f20fc1e7be10b166a494826a965d19b1dc9c8a81c511a01153311/coverage-7.16.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:4184e78a4465dcda359fb403172b8951dd220929cb0984c02fabca1742fff06f", size = 259728, upload-time = "2026-09-13T19:11:29.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/96/9d8020d97de46f0390739beda196322586cdd87e1fc357223e6a28bc4135/coverage-7.16.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:bc53c3f3adaa939b7a063533ffe0ae1259e7073393c618043a99a6970a87e3df", size = 254904, upload-time = "2026-09-13T19:11:31.86Z" }, + { url = "https://files.pythonhosted.org/packages/5b/21/2ff8867d4560f4f639a82d8fcf13c27eea88a96193daba096027c48bb929/coverage-7.16.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:b44308854ef210b9b78df9cdfd4e159513382a859f5ef8464306d14a54c2a040", size = 256828, upload-time = "2026-09-13T19:11:34.078Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4b/feacad51cb5163e3baa13281f8a3098f15d7934cc1f28cfe656557eb4e46/coverage-7.16.1-cp315-cp315-win32.whl", hash = "sha256:dccc142614d3419ed71857deb43f1d757829a4c7fce9994464b71e7e38309827", size = 225722, upload-time = "2026-09-13T19:11:36.314Z" }, + { url = "https://files.pythonhosted.org/packages/39/34/66bbc6ffd3c51fa67604c566920e772c5e3baa57f88dcf7b109c7f18b2cb/coverage-7.16.1-cp315-cp315-win_amd64.whl", hash = "sha256:961fc424e9d5229a99f8f1189942d8e7f4e1519147c3af64842f944aca03914d", size = 226195, upload-time = "2026-09-13T19:11:38.493Z" }, + { url = "https://files.pythonhosted.org/packages/f7/96/4bad920d4caed127c37c136a64b0730fa495517d2eb58809e0d81e6e7dff/coverage-7.16.1-cp315-cp315-win_arm64.whl", hash = "sha256:681a9488c5a234397c4f013da065aa9e53eb7af4c78f1f80c6f15e7208acb855", size = 225625, upload-time = "2026-09-13T19:11:40.664Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2b/a88c7906eeb7da4394e932060d7008e153b3a2cd8d11782d82faa03bb7a3/coverage-7.16.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:8bb09a2d19b04db1fa0e087a7ca4f12458f7e0e7364cfcd838441d86fb1c61f6", size = 224271, upload-time = "2026-09-13T19:11:42.876Z" }, + { url = "https://files.pythonhosted.org/packages/1f/1f/98db59c595680d16f553890deac6d72610e04f192c1964ec9b69cbe790ab/coverage-7.16.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2270a794600b635ca9452ce4c32e2fe81a35f9caa17ffac0eba99f14f275bd4d", size = 224564, upload-time = "2026-09-13T19:11:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/699e236d4cb97ec282b0655afc85acf12349e71a02fff0f1a7d0fb643079/coverage-7.16.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4eff405b545dfcf79cf0d9d3ff750e5c5a887aa066114175193a81d249c5ee6", size = 265423, upload-time = "2026-09-13T19:11:47.666Z" }, + { url = "https://files.pythonhosted.org/packages/96/1b/6eaa21912863b3e3bf23cfe605fb62929bb1a41dc33d2bfa8e92000232bd/coverage-7.16.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ee1d5fc9e3bd6a217906929cc97880239a91d20dae7746f538eb0eefee705ab1", size = 268503, upload-time = "2026-09-13T19:11:50.12Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ac/4eb46bffa98c28a80559a58c12f17e19308d52b31174af1bd49decf76606/coverage-7.16.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4ec944947de098ad5a1738413f9364689a57067ecbc328e9de37218aa1e5cc1", size = 271059, upload-time = "2026-09-13T19:11:52.363Z" }, + { url = "https://files.pythonhosted.org/packages/ed/d4/17a51ef1f6a084c9abbead522cfbee0fe9d29a593128781e724b7a0795b3/coverage-7.16.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3f73ee3956fde2d461c9e2955dd48166e4821fc8587d135e8780fb84da2a098b", size = 272039, upload-time = "2026-09-13T19:11:54.935Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ff/9fdeba8b8aa0848030f45f869ddedc306795a8c6e53e901b8553afbf5415/coverage-7.16.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ec9a4ee989c0d06ad95add0dbfdbb72b00ef53f43431ca0b612384e7878e5de", size = 265869, upload-time = "2026-09-13T19:11:57.423Z" }, + { url = "https://files.pythonhosted.org/packages/95/eb/ab3eb506b2e4dd278440fbbbc7ed18424a86d8a981db0b90fdba66f5d34d/coverage-7.16.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7e5727b2508f817f3126d6c33327dda32fe69d15514badfcd61db8bc4209ecef", size = 268883, upload-time = "2026-09-13T19:11:59.71Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0c/f4edefdce33f01954a74237df5ee83ccef5dcd1165e04abf0adf47543fca/coverage-7.16.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:19a3ea2f364012ef06678118fffdc92442a16bef4a5c8ad4f4019dd8f9ac8876", size = 265359, upload-time = "2026-09-13T19:12:01.953Z" }, + { url = "https://files.pythonhosted.org/packages/df/86/ce5ed885fc7ce2adc9a5971134ace257d0f84f19c3d117da89c179b9662e/coverage-7.16.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:996c2b891b441ec2b39725ee3e8386e2f11b4894b92be225fdfd54a3eeada2c8", size = 270056, upload-time = "2026-09-13T19:12:04.46Z" }, + { url = "https://files.pythonhosted.org/packages/48/a1/5dbd5f95070cece25c6df9e6ea4ded2a87dc695ce82a49bcb2116452b342/coverage-7.16.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:a125fac1f6b1e88488d208a86b578e1790e3c4937f2e1568d23356141d236220", size = 265498, upload-time = "2026-09-13T19:12:06.811Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ff/254b26f3300f87c7c53ac97da41ae75f0e2ac066aa964bc073c0f3750b38/coverage-7.16.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b10095528b866d322d33d6bf1709b7f8cbf959f12e8cb2ba22fc59c8717866b0", size = 267459, upload-time = "2026-09-13T19:12:09.479Z" }, + { url = "https://files.pythonhosted.org/packages/61/20/bf9958f8ddbbf6a2d39da3d4009f33e0fcf19c2d2fd99715c6494cdc8e8c/coverage-7.16.1-cp315-cp315t-win32.whl", hash = "sha256:531d9be377fdcc05593b974656872eb82e808ebeb42a72515e3aaeb8bb7166f5", size = 226021, upload-time = "2026-09-13T19:12:11.933Z" }, + { url = "https://files.pythonhosted.org/packages/35/8f/12d46948704d9e437c8dca2718e5d54ecc2f85396b64b500c29d8dbe10db/coverage-7.16.1-cp315-cp315t-win_amd64.whl", hash = "sha256:46a88f51770df7c9bc376bd57d3f86cdc7624b8e16ac4b585a655c22b7a1b4db", size = 226853, upload-time = "2026-09-13T19:12:14.254Z" }, + { url = "https://files.pythonhosted.org/packages/a5/7f/1ca5fd0601054fce5a3539375b997e67683646e2bf62f60c41e70b529fcc/coverage-7.16.1-cp315-cp315t-win_arm64.whl", hash = "sha256:7580432cbe1e8b762660ae5806f04f869e1c02e519836a43f8094437e561e9f0", size = 226163, upload-time = "2026-09-13T19:12:16.589Z" }, + { url = "https://files.pythonhosted.org/packages/96/1a/d6d16babd0a5fe4c3fae40702158c570351694e74516d8d81b86c5637448/coverage-7.16.1-py3-none-any.whl", hash = "sha256:3d8bd4e58b6a5c2018d808f297905393c6c61da466a48c3f0596a76a4900ebe4", size = 215264, upload-time = "2026-09-13T19:12:18.895Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "50.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/ad/5d6702db60b1e40b41ef513b6967ff5848f307d50f8449baf1634f5908f1/cryptography-50.0.1.tar.gz", hash = "sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20", size = 880381, upload-time = "2026-08-25T19:45:45.499Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/19/797e2aaac9df6a66f1550f49979dc1b1e39ecd2077501c30efa81e8d5d67/cryptography-50.0.1-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986", size = 4010153, upload-time = "2026-08-25T19:44:03.155Z" }, + { url = "https://files.pythonhosted.org/packages/90/34/9ce9a62ed9dc82ca9fd6a34445b6904af56e5f38b3eae2ed32e49c36053d/cryptography-50.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f", size = 4723133, upload-time = "2026-08-25T19:44:05.461Z" }, + { url = "https://files.pythonhosted.org/packages/57/26/e6d4fc8512a51a5f9ee7bfdbfb853bce1197087df40c9ad993ad370b846f/cryptography-50.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef", size = 4712478, upload-time = "2026-08-25T19:44:07.375Z" }, + { url = "https://files.pythonhosted.org/packages/e6/de/d3cdc2815697aae84126cbd6a030ca7b6b452e28a88b501b836bd3aa7a86/cryptography-50.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8", size = 4730726, upload-time = "2026-08-25T19:44:09.294Z" }, + { url = "https://files.pythonhosted.org/packages/55/32/38c0d344b98c06d34b5df8946565a9c0d6dbf32c8e0730a7f05f0a3c6cab/cryptography-50.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45", size = 5353524, upload-time = "2026-08-25T19:44:11.96Z" }, + { url = "https://files.pythonhosted.org/packages/e1/1b/82f0f0d8858d4432be1af790477edf62aef90324041aa07c57e57bef1af7/cryptography-50.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad", size = 4746720, upload-time = "2026-08-25T19:44:14.051Z" }, + { url = "https://files.pythonhosted.org/packages/29/ba/042ca458b8c64348c768284b5d23e69b92ed53d057ab779fee628564676d/cryptography-50.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49", size = 4361866, upload-time = "2026-08-25T19:44:16.167Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/e96c1ef71edef71057c7e3c3d982ce8fda554e0c52d0cc19c18845cde3eb/cryptography-50.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f", size = 4730028, upload-time = "2026-08-25T19:44:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/e3/38/45abd72ef63f2e7d0754a6cacf97bd8b69512ace7f6130d24c39ece65da2/cryptography-50.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527", size = 5308405, upload-time = "2026-08-25T19:44:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/85/66/6ccca4722987ddedaa7fc9c3f4708af7431f5535666c174350830888c6b7/cryptography-50.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a", size = 4746230, upload-time = "2026-08-25T19:44:22.376Z" }, + { url = "https://files.pythonhosted.org/packages/13/0e/b1f92e013228111413f2e6743948b80bc24dfd3c1b87ba98ceea16f5df89/cryptography-50.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959", size = 4862596, upload-time = "2026-08-25T19:44:24.472Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/c3654cccc856e9d682817b04ac3ee79731cb09ca6f95996a95c904de2883/cryptography-50.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b", size = 5014082, upload-time = "2026-08-25T19:44:26.709Z" }, + { url = "https://files.pythonhosted.org/packages/42/8b/cb12b1b60c91b074ca6bf0fdd59aa8f10d8bc5f73af8faece86ef0421b37/cryptography-50.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:aed8db4f6d71c51efb89530e12d9464e7bf2923d46c3205dc794a2a93f8c0648", size = 3842826, upload-time = "2026-08-25T19:44:28.784Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f0/424cb557d99aa86ac55da5e2add02e2882e44047b6264f93ade1b975a993/cryptography-50.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30a125032e5642a21ff816e021152bd4e7e94f03eff3f4b7fca41cd22bc3110f", size = 3973525, upload-time = "2026-08-25T19:44:30.7Z" }, + { url = "https://files.pythonhosted.org/packages/4d/72/3a2711d967977ab5fc80b782837c7e8d1ac7445e764c20c381a265c57ef3/cryptography-50.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a0b1a59e3a089064a0ec309e9428c8e3ae4e161419d20ac33600767e83fc658a", size = 4708817, upload-time = "2026-08-25T19:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f2/bb1f56e10815b789df0b409a69fa4992ff3d3fef9c72747f4a6b26fed38e/cryptography-50.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8921d58f426793c5f1b47f0b59575780de9a095214958d0eb37d909593db8367", size = 4697300, upload-time = "2026-08-25T19:44:35.144Z" }, + { url = "https://files.pythonhosted.org/packages/08/bd/ed5396be499ffcf8807a585bfe38b71a1fbdd1c342b4f9b6d0ef5162a946/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a8f40ea47330e71b594a7e246898f93177c259490c63183dbaf9e571d71ed9a5", size = 4716039, upload-time = "2026-08-25T19:44:37.192Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6e/1cf405c5c8e8df7545378048e954792f00b7f2367af8863ce8b8f3e10607/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:a255449073358275b64b67d3f595f268bbef70e72b6edb65e0c70c735bf739c9", size = 5332388, upload-time = "2026-08-25T19:44:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/47/92/b4317e8c32c4f47b062f5398bd79106b220a124546f42be83bf32b761e2a/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:8df2de9102026855887e4587084f6eabd80ed0f345b8ad8a7ac27ab9bf4723e0", size = 4730293, upload-time = "2026-08-25T19:44:41.298Z" }, + { url = "https://files.pythonhosted.org/packages/39/0d/a1e7633e2c744d0f2983320a27e924ef2264c79c56e1a58d5fb0a1cfd413/cryptography-50.0.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:ac02b07824d4d1001bd4367599f839c19cb171924c796e52c23508ac14c2c0cc", size = 4346031, upload-time = "2026-08-25T19:44:43.245Z" }, + { url = "https://files.pythonhosted.org/packages/88/dd/b215616f9bab3fc18510c78a4e5c9f362d77838503c363dc747c7d4f5c6f/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:cbf74a81765ee67413503ca6e26dcc4f6f5a519822436cc0a1b97aab6c1b8a17", size = 4715344, upload-time = "2026-08-25T19:44:45.291Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1b/ec3ebd31741d0e963612c4fe43caa39341b9b1e031e469820e42e4c83918/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:16c5ecd954b3330ebfb6605eca4fd952da8bef376551d5cc264534e3770a9ee6", size = 5287201, upload-time = "2026-08-25T19:44:47.297Z" }, + { url = "https://files.pythonhosted.org/packages/1a/01/0127d11a762b31a9ee0221894f540318761783f3fdc4bc5d057698caebd5/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:79bf008d1f9af6071c797ad133e39915dfee7614f18f18f4db9072eb715064a3", size = 4730023, upload-time = "2026-08-25T19:44:49.435Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b9/e7425ebfb599241a0c1d7000f1b466c3062da66c19d9525031315dff7213/cryptography-50.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:330fbb252391c596f1ae42c5754449dc924e6ad012dca8efe0d703f9f2d12ec6", size = 4847362, upload-time = "2026-08-25T19:44:51.94Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fd/60d0ddf4defa12e482c9d5e0f554384d6e8ab25341fd15f060028fd92e6a/cryptography-50.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:42be3bb70596b3abe4ac097b75be223e8b3ab614a0e5de068e3dcc54d71d6149", size = 4999247, upload-time = "2026-08-25T19:44:53.876Z" }, + { url = "https://files.pythonhosted.org/packages/4d/56/bc4f2b209e766c93372cfcd59b781a0b2b59700f62a969580415b699c2b2/cryptography-50.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f74455bb086a85d5e81246412602aaa97ed095e504cd40dd261ef50be42205bf", size = 3825806, upload-time = "2026-08-25T19:44:56.209Z" }, + { url = "https://files.pythonhosted.org/packages/84/a9/ee16a903f13755e914d1eecc482fe64d1f10761c3960e5d8fa6837377aff/cryptography-50.0.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0", size = 4035307, upload-time = "2026-08-25T19:44:58.305Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a5/9ec7e81e8526c0d7a387d73386b2daed3f39e10d81a85930bd1b6bfba65c/cryptography-50.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23", size = 4751900, upload-time = "2026-08-25T19:45:00.401Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3c/0e77bd5ffcf078e9dd27d3074aad6c030d9b10d0bf69329d573c927a188c/cryptography-50.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733", size = 4738357, upload-time = "2026-08-25T19:45:02.786Z" }, + { url = "https://files.pythonhosted.org/packages/27/3a/3c5f80daa4dcd47323c7af8a2fcb90de27a33564d4fcac69846c0972691a/cryptography-50.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88", size = 4758474, upload-time = "2026-08-25T19:45:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2b/214cf0cf93db9628c3c20c896b229f327f6fb1b20e4b3743d8ad3f00af8b/cryptography-50.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054", size = 5375862, upload-time = "2026-08-25T19:45:07.163Z" }, + { url = "https://files.pythonhosted.org/packages/d6/51/3f9701867a46b6c1740c9b52fc4d3bed6cbdcfedcc9b6e64305c07f39cff/cryptography-50.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5", size = 4772942, upload-time = "2026-08-25T19:45:09.396Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5c/13ea642e08e2544d0f5396122055f4820cfacb3203562197b5967125ea97/cryptography-50.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361", size = 4383347, upload-time = "2026-08-25T19:45:11.659Z" }, + { url = "https://files.pythonhosted.org/packages/84/d5/7d1fe1cb93f91c428093ff234e128c89ba8ea61a6f26aab406081f9b996e/cryptography-50.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71", size = 4758050, upload-time = "2026-08-25T19:45:13.745Z" }, + { url = "https://files.pythonhosted.org/packages/dd/04/557fc5ead96a829e0bc812a3b9dc4a52a2f27e4f7f5950da7ff27653a805/cryptography-50.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80", size = 5332955, upload-time = "2026-08-25T19:45:16.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/eb/5d7124083e8d8cda8f5b348f544b71ad6f707ad63193758ef4d8e569da02/cryptography-50.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239", size = 4772694, upload-time = "2026-08-25T19:45:18.315Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/f1f955e0921dd2b6d22eae7e8d24a4c4b638d10735ffbf6a71f99eb0fcb8/cryptography-50.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558", size = 4888413, upload-time = "2026-08-25T19:45:20.4Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ab/89e2b798d2c3925f82e2bb72d5979f3d2f6da2dd22ef4a8cd8b70d920039/cryptography-50.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e", size = 5044355, upload-time = "2026-08-25T19:45:22.353Z" }, + { url = "https://files.pythonhosted.org/packages/99/89/87ef49ffe383ef4e147d27b7bf2088fb0b54ea409dd87b5a89442e5828a5/cryptography-50.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:55d16b1ef3ee0958d893a977b19777887e546c9954ea81b200c3301a864013f2", size = 3875429, upload-time = "2026-08-25T19:45:24.418Z" }, + { url = "https://files.pythonhosted.org/packages/c7/27/8d207af749c453ee17ea087340b3f2b4adef75aadd1d277b1b129bdda84e/cryptography-50.0.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9cb3cb952cf5a8abd50c782a98a89d71699715e802fe349704b47f2425b42a94", size = 3974350, upload-time = "2026-08-25T19:45:26.551Z" }, + { url = "https://files.pythonhosted.org/packages/14/9a/6d3a4d7852e22d657438b7bf51f66102c7d71c0e1fafeec652281d0403e5/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5fe939deeb161024a6be98229c953b6591fef1f41214497a78fe793a244c017f", size = 4698675, upload-time = "2026-08-25T19:45:28.658Z" }, + { url = "https://files.pythonhosted.org/packages/73/35/5c3717edf9e68a0550ce04e28eab493fe545eccd81742af03f6a75fe260b/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fb4b9672d389c738b175c4166e78310f8a70358886aacd9173ee03a85ffdc671", size = 4707410, upload-time = "2026-08-25T19:45:30.816Z" }, + { url = "https://files.pythonhosted.org/packages/1d/e0/e786934472e3ac4ecdecc7b129a0ca1a2a40dffdafcf2c3ea9d4397f8def/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d63ae8f6481fec907ac0f588eee8a90aefde112c633131fe540e5711ddbb5a4e", size = 4698378, upload-time = "2026-08-25T19:45:33.043Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/5b3f53a0b74d122f023476ede40ba5d3e70d5cf475f73b899740d26a4fb2/cryptography-50.0.1-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:804728ce710890870f3aaa344b2e161172d258d768ac139d02cfd9092d0d94e6", size = 4706889, upload-time = "2026-08-25T19:45:35.086Z" }, + { url = "https://files.pythonhosted.org/packages/71/44/711e61f7d014be825ef79b285b047292d1bf893732ac1bc030a351fb517f/cryptography-50.0.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:693c99b49bd37d0d096e4334c10232c77248c415b98d35236094cdf96d57258b", size = 3824006, upload-time = "2026-08-25T19:45:37.281Z" }, +] + +[[package]] +name = "debugpy" +version = "1.8.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/9d/3cb6693342acf96802dba89934a5b8da43c201d764ebd1f2c0514a3d42bd/debugpy-1.8.22.tar.gz", hash = "sha256:e489c7268e1c7b41e13b438d9c533d2a7af73fb59bf8cd30fead8286c1c39c4e", size = 1710877, upload-time = "2026-09-15T21:44:02.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/e9/ced401ba1088e1d2b3228dbbcae33e15aa8c08a1368433bedebfa8efcb02/debugpy-1.8.22-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fa47099176d1d612bef69e9f74658a5687a9f05bd80fb5710f99959bace85735", size = 2128369, upload-time = "2026-09-15T21:44:04.775Z" }, + { url = "https://files.pythonhosted.org/packages/4f/97/5c8e839a7f73e92cd4a733408bb85bb764e829e52a9fb097f99d6e2cbbc0/debugpy-1.8.22-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:7bf29e0d8ce80b100d37fb333e1b193b790d962739f3067796c2b03ffdc9afee", size = 3022081, upload-time = "2026-09-15T21:44:06.382Z" }, + { url = "https://files.pythonhosted.org/packages/11/ed/fbb045d18a8b733a798bb834a03a20cd64f1446206d844b674bb638da6c3/debugpy-1.8.22-cp310-cp310-win32.whl", hash = "sha256:56b877b37ed73f0bf53ba7afc394816ff1eb5d701bae24a24ed9e1ea6f7ce34f", size = 5325298, upload-time = "2026-09-15T21:44:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/82/7f/633160132b80b424c6816391f239682435c6b77cdd58288d04b3443b9f80/debugpy-1.8.22-cp310-cp310-win_amd64.whl", hash = "sha256:1bd0c6df3c68c0a3f71db8baa3780a953abb65537ec4b3bc6b935ad5b3b3d45c", size = 5353598, upload-time = "2026-09-15T21:44:10.125Z" }, + { url = "https://files.pythonhosted.org/packages/26/4a/8c24c588088c622df6dacdc0110e9cc81b44781fe7a408113441e76aa955/debugpy-1.8.22-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:66e4ac3d6e7026e83e7d93d7ee2f51dd4a4e8dff673578d424e60796893e5b2c", size = 2217799, upload-time = "2026-09-15T21:44:11.57Z" }, + { url = "https://files.pythonhosted.org/packages/c7/14/1c9ff33eba51da70a8dfe2277cc13153bb3ffec79282b9acfcdbe1641d30/debugpy-1.8.22-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:192b73e8d53bbd60225220c0943627bf249ac93d0ea3090e23c45f3e0ceb6a35", size = 3074375, upload-time = "2026-09-15T21:44:13.349Z" }, + { url = "https://files.pythonhosted.org/packages/10/e8/1fa16a94af7d8b86c3ca336d7d32b78cb2c475266d0bb889732d2bc50310/debugpy-1.8.22-cp311-cp311-win32.whl", hash = "sha256:745e1800ec2961e5660c1a317c0a20e28c0fef4de36c04f17a21c33f2b37a92a", size = 5258392, upload-time = "2026-09-15T21:44:15.182Z" }, + { url = "https://files.pythonhosted.org/packages/32/1a/c086b883a4561017bfdd83c1bc70bf2b05ab6540ff767dedf207cadab938/debugpy-1.8.22-cp311-cp311-win_amd64.whl", hash = "sha256:1e76339d5510bc17e9181dba9577508afcb21aad5728f1a55ef74d7d97d255f3", size = 5277955, upload-time = "2026-09-15T21:44:16.986Z" }, + { url = "https://files.pythonhosted.org/packages/73/21/5c7f5c66eaeee57c9dadb0bbff22efddc8d99cdbd3d036c806175f1828cc/debugpy-1.8.22-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ad076f4f66cb8acb79e4384d48b1ea60a0b1957aa0b1112d4887ea3a4df60e0", size = 2499375, upload-time = "2026-09-15T21:44:18.478Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ec/7389798843643ea5bc6a08480ba1b855377c70e2ed90189c0a03594e401e/debugpy-1.8.22-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:8a697acec45dbc70d17fb5d9f4f61989fc294d273c69de487fd10cb35fdd75eb", size = 4000941, upload-time = "2026-09-15T21:44:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/20/f4/64ce7a2499550929e08654360b270409eeda3e321b7c27027d9c919aa36b/debugpy-1.8.22-cp312-cp312-win32.whl", hash = "sha256:12bc7f368182b517cf26c76a2393fff65354e365fa1552b6241e66edff17997b", size = 5359040, upload-time = "2026-09-15T21:44:22.218Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fe/e7d3b72874cdd327bda295d1995a01e426857ae9cbaaba74fd997bc3afbc/debugpy-1.8.22-cp312-cp312-win_amd64.whl", hash = "sha256:371a4ba4a5975eb958393903f3254cf983da7b1c7c178b3f987ee427f42515e3", size = 5398304, upload-time = "2026-09-15T21:44:24.119Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f4/fee6d40f5865aa84e1100c9df1a0f2cf8991776b61f11f433ff277df4415/debugpy-1.8.22-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:c21dd7e1ec22556bb41dc3bf6b86c603e440ec889c4acb27d7206354b2106c54", size = 2493531, upload-time = "2026-09-15T21:44:25.835Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4f/8df91270afbf61fc5ab202951f28451194a7eb093704b26da11cb0ecd308/debugpy-1.8.22-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:c1ffb9953708b648ecf6acd2e5ad2c002bf68785ed618197dd4463a2a7226f39", size = 3994351, upload-time = "2026-09-15T21:44:27.31Z" }, + { url = "https://files.pythonhosted.org/packages/4f/11/2e34a4f21d6155b0c58003fd6fca9819df7d6fe7d7acd83b146b648a57ee/debugpy-1.8.22-cp313-cp313-win32.whl", hash = "sha256:ba810a66b437e3c43ca0ce0892404f010338286263ca8e5108442d76a9485337", size = 5359071, upload-time = "2026-09-15T21:44:29.064Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f5/c2b3e8260388d7b76bc4e5a3a29648a0ca47dca6a2721d6549ff2d944eee/debugpy-1.8.22-cp313-cp313-win_amd64.whl", hash = "sha256:f49b1d6cecf326b63c06d7f517e4a6642777f758c58cb799e7bc5a6dd74721c2", size = 5400362, upload-time = "2026-09-15T21:44:30.857Z" }, + { url = "https://files.pythonhosted.org/packages/ea/95/d97240bb68b86db8d0a07602be068aae9e50f17063d696a4c6f619975311/debugpy-1.8.22-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:e6744ac1850c73c2ba7b29a126cf9ab74efd1831190720e8fee17ef5389c8f5d", size = 2493148, upload-time = "2026-09-15T21:44:32.518Z" }, + { url = "https://files.pythonhosted.org/packages/70/9e/994fe1fb23dd4a5a8d059a0dc95da4b0821907200a61c2d5a4fdc21b6cf8/debugpy-1.8.22-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:0c1104233340196e5cbf5514e7dfdbb98968e730cfd7fdd6f3d72a082be838a9", size = 3961025, upload-time = "2026-09-15T21:44:34.387Z" }, + { url = "https://files.pythonhosted.org/packages/37/e0/66319918883cf6dd6dea3805210c3cafd9e65dfaa6f8b852482cff10a026/debugpy-1.8.22-cp314-cp314-win32.whl", hash = "sha256:feea785c7bbeb8cfd5b01a63f9061c899dc468c8be81671141a29305774fa294", size = 5359408, upload-time = "2026-09-15T21:44:36.128Z" }, + { url = "https://files.pythonhosted.org/packages/94/53/5b4d91f7fffb9393ac907133201d1da0c070d57e463ee8e2ec1db3cb63b6/debugpy-1.8.22-cp314-cp314-win_amd64.whl", hash = "sha256:d593a330297332ec435f3965c448b6d500e03f33675cf2063178bc76377a788e", size = 5397429, upload-time = "2026-09-15T21:44:38.429Z" }, + { url = "https://files.pythonhosted.org/packages/24/71/580df4aed96c9b23c9cbb049907d8ac1347050afdb8f450df20649d51578/debugpy-1.8.22-cp315-cp315-macosx_15_0_universal2.whl", hash = "sha256:a9eca6ab09a61534e923064400081e016f7e1d8430cc6e8b7a9cecd2f59e2b07", size = 2493380, upload-time = "2026-09-15T21:44:40.074Z" }, + { url = "https://files.pythonhosted.org/packages/b0/10/a323945b0966e560c40fe48470d2847bfc7692a8f550d7086e9a28f5b623/debugpy-1.8.22-cp315-cp315-manylinux_2_34_x86_64.whl", hash = "sha256:b7dbde1fb822d100802d505b2aed6d0813b1a0a2015d495cb8798b2215d5d1e5", size = 3975456, upload-time = "2026-09-15T21:44:41.623Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f9/4383635281999c3eaa44bf327de523ba42240b35eb87c1130adac4a829c6/debugpy-1.8.22-cp315-cp315-win32.whl", hash = "sha256:225d063f81708c2546999e7edfac0198b2d5c2f144797dc64858f867948633e0", size = 5359469, upload-time = "2026-09-15T21:44:43.234Z" }, + { url = "https://files.pythonhosted.org/packages/7b/14/4fe9f46a9c825c8cbc6dc4aebc2a885b0bceb37ce2a37699c170482e7a40/debugpy-1.8.22-cp315-cp315-win_amd64.whl", hash = "sha256:b17a4896520f1c6da09ce76ec8215df0b85b0b6f3617526a4c65c2315340875a", size = 5397869, upload-time = "2026-09-15T21:44:44.783Z" }, + { url = "https://files.pythonhosted.org/packages/56/3d/c7dc9f35bc9e22cdb53679f844bee40fd5cff871862f60595869a433400d/debugpy-1.8.22-py2.py3-none-any.whl", hash = "sha256:a9e9d3550e15ca479c59333e90845029190531f0cacfedab3b815a57bd913947", size = 5374857, upload-time = "2026-09-15T21:44:54.454Z" }, +] + +[[package]] +name = "decorator" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084, upload-time = "2026-05-18T06:03:28.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365, upload-time = "2026-05-18T06:03:26.517Z" }, +] + +[[package]] +name = "deepdiff" +version = "9.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachebox" }, + { name = "orderly-set" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f9/6b/6a4a5aaf38535eb332c2856aa08e73ed7c549d0851b1215401af0a2db1a7/deepdiff-9.1.0.tar.gz", hash = "sha256:07e9e366fab4297755153c4eab795ad4ef3cbd0d51660e847f5751c6bd727687", size = 382149, upload-time = "2026-05-15T20:18:05.751Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl", hash = "sha256:80c0460e1993b04f6f0ca79abf25548b129fd218478c4ebb08f80560f5d10610", size = 184662, upload-time = "2026-05-15T20:18:03.956Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/02/bd72be9134d25ed783ecbbc38a539ffaefbf90c78418c7fb7229600dbac7/distlib-0.4.3.tar.gz", hash = "sha256:f152097224a0ae24be5a0f6bae1b9359af82133bce63f98a95f86cae1aede9ed", size = 615141, upload-time = "2026-06-12T08:04:52.847Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", hash = "sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", size = 470628, upload-time = "2026-06-12T08:04:50.506Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + +[[package]] +name = "fastapi" +version = "0.141.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/02/91e3416a8fdd715abb903a952a6bec7cdd8d14eed55d415fc8595524c319/fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1", size = 425799, upload-time = "2026-07-29T17:18:05.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, +] + +[[package]] +name = "filelock" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/38/88cd6eda96c40594a1e3da7d8b40f04bc40ace5a6aef9ac5cb407540f173/filelock-4.0.1.tar.gz", hash = "sha256:fdefc3f3e87716d855ae2b732c1cfd521dd99799ef2b4d00e8c0d4dcdc7cc94b", size = 238888, upload-time = "2026-09-19T01:08:15.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/33/af0635ab07fe83b1788a1dbe370ff3e226062495a998335cb18a1cac81aa/filelock-4.0.1-py3-none-any.whl", hash = "sha256:481a321a27bef441e23c53371c6abc8d7d16e26b97090074ba44f7538a3fd55a", size = 106219, upload-time = "2026-09-19T01:08:14.49Z" }, +] + +[[package]] +name = "freezegun" +version = "1.5.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/dd/23e2f4e357f8fd3bdff613c1fe4466d21bfb00a6177f238079b17f7b1c84/freezegun-1.5.5.tar.gz", hash = "sha256:ac7742a6cc6c25a2c35e9292dfd554b897b517d2dec26891a2e8debf205cb94a", size = 35914, upload-time = "2025-08-09T10:39:08.338Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2", size = 19266, upload-time = "2025-08-09T10:39:06.636Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore2" +version = "2.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11", marker = "python_full_version < '3.11' or sys_platform != 'emscripten'" }, + { name = "truststore", marker = "python_full_version < '3.11' or sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/f3/1db7aa2bc2524062192bb0e0323969492d1883152a232fe36eea65f4e35c/httpcore2-2.13.1.tar.gz", hash = "sha256:e0aa977abe17e69a3b820a24542a6fa88702676d83880b8d194dcd18408e5103", size = 68071, upload-time = "2026-09-23T07:47:22.372Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/ba/a4568248771ce81957bfb7cc600264a40fbcda092391ee1c415c50be4bea/httpcore2-2.13.1-py3-none-any.whl", hash = "sha256:e1e05d4f25f7d7d496bfb96748f6f4b67657b03da069b3a68c36069f3db73d0a", size = 83423, upload-time = "2026-09-23T07:47:19.365Z" }, +] + +[[package]] +name = "httpx2" +version = "2.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform != 'emscripten'" }, + { name = "httpcore2", marker = "sys_platform != 'emscripten'" }, + { name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" }, + { name = "idna" }, + { name = "truststore", marker = "sys_platform != 'emscripten'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/44/474bef2a0e9d90f1715d32cb98b0738695ca17ba324095fb2497ed7fbd59/httpx2-2.13.1.tar.gz", hash = "sha256:e48744a19e3af5ee48313d0ce5fe941d5422fae5705ea922a4aabf94d7800dfa", size = 100405, upload-time = "2026-09-23T07:47:23.052Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/9c/6fe8931fd9f381042a9e4c7d5a7b4cbf7016b252bec0c99a49fce42c3326/httpx2-2.13.1-py3-none-any.whl", hash = "sha256:6dff50fabc270ee5fd25d845d0b078ed20564579744d6d962850975996d2f9a4", size = 95597, upload-time = "2026-09-23T07:47:20.995Z" }, +] + +[[package]] +name = "httpx2-jsfetch" +version = "1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" }, +] + +[[package]] +name = "identify" +version = "2.6.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, +] + +[[package]] +name = "idna" +version = "3.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "9.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/7e/1e7e8dc30634b93ebb3d58a3dea569ad146e656218d3960ab04f62047b29/importlib_metadata-9.0.1.tar.gz", hash = "sha256:ab830580bc0ef3db61ce8fae716389e5462b67e033018bab6d8f80ef17172f99", size = 59124, upload-time = "2026-08-28T15:30:34.646Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/55/ecca97ae19075f1fac62def77731e7f535e6c1fb8f92ff08160c5e6dade8/importlib_metadata-9.0.1-py3-none-any.whl", hash = "sha256:bba5600596a7e21f3eef53281cf28d6a5195634d2f2b78ff9501a3272c6eaab0", size = 27920, upload-time = "2026-08-28T15:30:33.433Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "ipykernel" +version = "7.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio2" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/c4/e4a38f579de4225a561305666f7541cdabb30075def2aa1ac17bd73c1fb5/ipykernel-7.3.0.tar.gz", hash = "sha256:9acaaaf97d16355166e4085afe9d225bfbdf2b7ef520f9df3be8f2b248275e09", size = 184899, upload-time = "2026-06-10T08:41:25.481Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/02/77b271f5dc58bfbc0b577c877b2365d1ffea2afe66a80c13f2312820348c/ipykernel-7.3.0-py3-none-any.whl", hash = "sha256:897eb64da762549ef610698fca5e9675195ec6ac8ec7f19d81ce1ca20c876057", size = 120583, upload-time = "2026-06-10T08:41:23.648Z" }, +] + +[[package]] +name = "ipython" +version = "8.39.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.11'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "jedi", marker = "python_full_version < '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, + { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "stack-data", marker = "python_full_version < '3.11'" }, + { name = "traitlets", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl", hash = "sha256:bb3c51c4fa8148ab1dea07a79584d1c854e234ea44aa1283bcb37bc75054651f", size = 831849, upload-time = "2026-03-27T10:02:07.846Z" }, +] + +[[package]] +name = "ipython" +version = "9.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "psutil", marker = "python_full_version >= '3.11' and sys_platform != 'cygwin' and sys_platform != 'emscripten'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/32/99451b1283ec5d92ad77073f12e1c667dc10775384d8f15c2914207149dd/ipython-9.17.1.tar.gz", hash = "sha256:8919be8c27f20a6f4423145028063f6637b42a03ce57665bb12015ee1f073529", size = 4539289, upload-time = "2026-09-01T08:29:32.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/1e/65b59cf518c106aa755e7f7da3099027738687a862ec785060702a481320/ipython-9.17.1-py3-none-any.whl", hash = "sha256:6d1645743cfd1a07eb695d85aa2b5fa66721f8cbae9431d4049f7084bbf06509", size = 639038, upload-time = "2026-09-01T08:29:30.673Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "isort" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/82/fa43935523efdfcce6abbae9da7f372b627b27142c3419fcf13bf5b0c397/isort-6.1.0.tar.gz", hash = "sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481", size = 824325, upload-time = "2025-10-01T16:26:45.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/cc/9b681a170efab4868a032631dea1e8446d8ec718a7f657b94d49d1a12643/isort-6.1.0-py3-none-any.whl", hash = "sha256:58d8927ecce74e5087aef019f778d4081a3b6c98f15a80ba35782ca8a2097784", size = 94329, upload-time = "2025-10-01T16:26:43.291Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jedi" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416, upload-time = "2026-05-01T23:38:47.814Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812, upload-time = "2026-05-01T23:38:43.919Z" }, +] + +[[package]] +name = "jupyter-client" +version = "8.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/2a/906772148a06e48885039e0250c340b770a55bbf37b08d6ee0449df369c3/jupyter_client-8.10.0.tar.gz", hash = "sha256:9f7116294dca55f1785be880057d44544db9b1567718d92cb33c58886afb9497", size = 360653, upload-time = "2026-08-28T12:17:10.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/88/7c548de1f6c2ade7c931a3282da73f9274fa6a1531091be682f89c85efb9/jupyter_client-8.10.0-py3-none-any.whl", hash = "sha256:5f73f24f22fa25192cfff6b23c051932a2473a797b05734aff495b392103e14e", size = 110184, upload-time = "2026-08-28T12:17:09.028Z" }, +] + +[[package]] +name = "jupyter-core" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, +] + +[[package]] +name = "librt" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/9b/356320fbae2ac8467e21c5e73e1389c80468e4998c62cc7d3536cc51b614/librt-0.15.0.tar.gz", hash = "sha256:4e66cbe84437497d951b799d3e1551291b6fb3d643820a7014b3655d57a59162", size = 214338, upload-time = "2026-08-07T10:49:42.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/12/e2e9ca532cf5a0e08c9489826c4a35c6958c92ba0313fda70e8c6c3912be/librt-0.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1a49adf16a7c9d9646816c2946135527197b6fcf4347c7b8b761cf1bfbf4489", size = 148673, upload-time = "2026-08-07T10:46:22.569Z" }, + { url = "https://files.pythonhosted.org/packages/6d/7c/02005e23478bd5950618d9712e0fd2b4c511657857f3efd8ba6a5feabcdd/librt-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81a398f45b45a59200e13cd5ad1ae1d3f44334de98b148331afe2cdfee701c52", size = 153547, upload-time = "2026-08-07T10:46:23.931Z" }, + { url = "https://files.pythonhosted.org/packages/a0/90/d8848a735f5642077fc4b3b4bebcdb08edf10178e3add45597f5201a368f/librt-0.15.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4eafbaff06b9563f8b1c850621ce51605de05208e09d4d71ce490bc972b7b9e8", size = 494355, upload-time = "2026-08-07T10:46:25.122Z" }, + { url = "https://files.pythonhosted.org/packages/e1/0b/8604f41ea02feace490e9e405a338a15f9905369f55b239a9ce31c946f24/librt-0.15.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b0411b4066db926b80258c60dcb0e6db4c9cee312eab45b7e8866b17ddf9ada1", size = 485459, upload-time = "2026-08-07T10:46:26.447Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ac/84153bda1ce0da609182527ab92b40d961809e544eefdc5a1c2422971416/librt-0.15.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:febb1ce6cac545a54e6b769982824e955a700fdd9fbf3a08a3d82c990968b57d", size = 498398, upload-time = "2026-08-07T10:46:27.701Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3a/5ca6cd282b2c244bec8ec84102e09773264e9c02891d56ab3a8f0e4d7083/librt-0.15.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b230acc1c3bfe2d6f2627ba2b95dc92e58aa494600e9722d0e6ccbc931e59702", size = 515474, upload-time = "2026-08-07T10:46:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/73/d3/bd34110234779eb843c6ed66aba7c9b2091d3dd85989f1fb9922f564cb7a/librt-0.15.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6da110e5f314c19ab8478464d02ae18808ae73d522c15260fa4918acdcd64da9", size = 509484, upload-time = "2026-08-07T10:46:30.124Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/43c3f7f071d71631a7daa3b835ef2168ea39f20692d81464d4e47fbaa6d6/librt-0.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:eab9208b00ca55bf75983ec99f7bf13acc746a36102e98953addaad7f7ea1e1b", size = 532534, upload-time = "2026-08-07T10:46:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/c5/1c/b854adf036ea817c40408873a5b794d65a91d9f0f39826f2ad2a2d5d7f48/librt-0.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6c013cd3a1721e69e14380ada97eaa4b7b0cdf1c6b96fa765d4ea47c875088db", size = 537087, upload-time = "2026-08-07T10:46:32.734Z" }, + { url = "https://files.pythonhosted.org/packages/25/5c/c9a890e244e7dd725d3bd8b560e41f0aec787eaf343b46956a290ab7b841/librt-0.15.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:567b1c430f8bd560e689421468278ac5941bab4a05303b5d95b6ae10db03f451", size = 536575, upload-time = "2026-08-07T10:46:33.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c5/c8e70b60b704299555f55db468eb46b1c81bfc60201ffbfe20407d89870c/librt-0.15.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:29c4cab9df457b19672c39be7f384ebb2bc925c4e2684b8780c222b43eb36389", size = 517142, upload-time = "2026-08-07T10:46:35.577Z" }, + { url = "https://files.pythonhosted.org/packages/56/d1/767a90c41f5d381b3195bc88ac0ec4afda35777c9c781e1f9848fedd965e/librt-0.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bccbd8e5b0bffb7106cf18eb1baa3d7194b1cebb3b4b1cdbd4bdb19382a6ee6c", size = 558714, upload-time = "2026-08-07T10:46:36.829Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b4/3c0624b8dc8301ab808f2b3a910995bcabe28df070fb9a0e5505ae997dae/librt-0.15.0-cp310-cp310-win32.whl", hash = "sha256:8ae493ed5f659a7761c43d42f183db514536073ded9bcf671d2d1df47e29a07e", size = 104426, upload-time = "2026-08-07T10:46:38.594Z" }, + { url = "https://files.pythonhosted.org/packages/31/98/e91c0382304bedb2db9c6801897319a9dcb68daac5e975819b562362f20d/librt-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc25fb356d0c7810bb49ff3df908ad1fda6995d660ab099ded69244ed7ab6053", size = 125057, upload-time = "2026-08-07T10:46:40.052Z" }, + { url = "https://files.pythonhosted.org/packages/59/52/06790ced2ac7117f890c21bda43c39c958ec82aa665c0718e821d33ff939/librt-0.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823b92cf3c18ecd08afc70c42473888b41b6e8ef5046f3b82c05c154a2fa3d22", size = 148039, upload-time = "2026-08-07T10:46:41.165Z" }, + { url = "https://files.pythonhosted.org/packages/e7/1d/8e150b7fc449a1f33c8a760965cc1f43b14fc1577d9d0b50ab2701420e74/librt-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70bc1b602cf59917e8f0c7a2cbc8bcc6fbc14d5486136b00707a79619121d63", size = 153067, upload-time = "2026-08-07T10:46:42.418Z" }, + { url = "https://files.pythonhosted.org/packages/51/87/a162bc5a66a35599dc619ecb215145f4de7d68e886b479b6d12593139f7c/librt-0.15.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:814ff83a25b5fce8b9c80c4dd803153fb5c5599fc74db9e022466938368957ef", size = 493087, upload-time = "2026-08-07T10:46:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/e5/3a/aeea1fc620cf48060d3065b37614edbf97043c099d0f50782bc8ca61d897/librt-0.15.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:57f5eeb6ad4c180de583b1038e61fe5fbd9796bb69a8a1c1a0c7ddbec4c8c60f", size = 485608, upload-time = "2026-08-07T10:46:45.038Z" }, + { url = "https://files.pythonhosted.org/packages/52/ff/fe571ad416f0856fd0d5578ffc2e6dc531891e586e36b647bcf50569cab8/librt-0.15.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82909c8f7eb9952656b65d3147afde4cf8e6d5a991eebc86418b5e65843b0ab8", size = 498723, upload-time = "2026-08-07T10:46:46.35Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e1/7a65eb5dedb1f00aebd948cdd8e17add48bf066cab3514e9daf84ab45a6c/librt-0.15.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f779070399f991400fc451719e0ea388eb7de313388bada2c127a35de05f798a", size = 516002, upload-time = "2026-08-07T10:46:47.599Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/59832b0ebfbd08c2742e6ece372ceb53f18bf1faef5d33c8daf3abebf749/librt-0.15.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bac89069bc496ebdf4f79ebb57bbd10d0b214c8454225deb672d91002bd17e18", size = 508607, upload-time = "2026-08-07T10:46:48.873Z" }, + { url = "https://files.pythonhosted.org/packages/ea/0d/37fa73f3b43ebd8259f91ae9102a15e5a54e65d581e48dea72df3e81d7a4/librt-0.15.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e0d00c708fb2f5822b152429b1ac80a58dbbbc3f6c232c4d13a3f7fcf2ea5b4c", size = 530422, upload-time = "2026-08-07T10:46:50.45Z" }, + { url = "https://files.pythonhosted.org/packages/26/02/e046c6fe7a5881ac34623242192f484426ba8a75595fd18f22c53a3f530f/librt-0.15.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6c6624fe268625869485553dd7cc1daf30d22558215bb2a4ff16f67a9801a31a", size = 534303, upload-time = "2026-08-07T10:46:51.693Z" }, + { url = "https://files.pythonhosted.org/packages/95/32/d5e6d861ab0366f3edf74f887ab0c9eb9f535aaf01d32b80b4f734daa179/librt-0.15.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f56b397858a23dacf35ede366ed2212fdc03a6a57a1ad36468ad6e9dc5fac091", size = 536084, upload-time = "2026-08-07T10:46:52.951Z" }, + { url = "https://files.pythonhosted.org/packages/2a/de/d69d725513fe53fc90c6d7a1f86e4428939bad2fb905b17fe4c18d413dde/librt-0.15.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4388184646efe2054911c5b00a1077d6d1ee86a95b7e8ba96dc7850a809f3f40", size = 514307, upload-time = "2026-08-07T10:46:54.194Z" }, + { url = "https://files.pythonhosted.org/packages/36/93/f8aded0d6682b4f25820fa86e0690f87f01df9fd7bd09ddb04d9167ad021/librt-0.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:97335f59082f9fe2ce6c2a9cc6433a0114bbb6cd4d5c09dd76c95c68b9f9a8b0", size = 557686, upload-time = "2026-08-07T10:46:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/74/09/ffeb6bdeb6cd862b4272fddc8ad05f938dd25d020ed517e631813917d80a/librt-0.15.0-cp311-cp311-win32.whl", hash = "sha256:83380ffde38062a2e9bb55d83e74474f6614665528b98a6928720fc006dfffbb", size = 104917, upload-time = "2026-08-07T10:46:56.605Z" }, + { url = "https://files.pythonhosted.org/packages/96/28/7e2313a3ffbf0b4de7ba3da58a09e488507b4bd1ea2b5e69378354a23415/librt-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:f75720477ee05d509a310e856cacc8d909adc182f7b91193c207bcc26d7ee6db", size = 125886, upload-time = "2026-08-07T10:46:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/39/9e/04b8c3cde014ef255ee785730425268354543acc38902093a40afa0dc164/librt-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:256237037a3ab001ae8d9803b2d43562a4c3aa38739843694349e4d5ebb0fd56", size = 111885, upload-time = "2026-08-07T10:46:58.787Z" }, + { url = "https://files.pythonhosted.org/packages/ba/39/99c25030e782bdfb7a21be8c05254806a2e4bbb05c8d50c2a2130acbfa05/librt-0.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e87bc679f86a99aa3b26e3c78eeb821a247c9a28eae48eaafcc32c3bf4c3bb9e", size = 151021, upload-time = "2026-08-07T10:47:00.057Z" }, + { url = "https://files.pythonhosted.org/packages/14/43/f4b1bd1b2888798a1409808889a25ea1ba49eaabce7d681ed27734c2df9d/librt-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71599e011ac880e8e45d46047d714871894c7d4ab6f25626f8d4f89da21f368d", size = 155267, upload-time = "2026-08-07T10:47:01.311Z" }, + { url = "https://files.pythonhosted.org/packages/0c/db/3ad9c965c72f1e1d6beeec44ec10a54e17be8ae042fbb4baade16cbadced/librt-0.15.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c802434092b769b1d613ed2e13fac15fbfce1934a74bd10283b03c0fae231cd1", size = 503136, upload-time = "2026-08-07T10:47:02.45Z" }, + { url = "https://files.pythonhosted.org/packages/4b/07/5888a6d76acd62ebce66c61b74d94e9370b9c32929f111e487bb6546f8ed/librt-0.15.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5500eeae393a184d14e1f35645962c27129d20c81afa4069e6ef826ebc2b3aaa", size = 496670, upload-time = "2026-08-07T10:47:03.675Z" }, + { url = "https://files.pythonhosted.org/packages/29/39/ab57cc2f5b276156da02bb7f5a8921bada1cb1993ffec99acf811c602c23/librt-0.15.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6ecfc32dfb46fb7b565bcd6abf9412acf978775a998273d22888a6d7953730dd", size = 513688, upload-time = "2026-08-07T10:47:04.981Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/bdbb0b648b5c2befb031f4c6f3b1dd857415e8fb492a25a3c764a6681e6c/librt-0.15.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cc46cfd15022e35084355478c9ac809d90b1152222706ac9a7655ec21df6fa", size = 531904, upload-time = "2026-08-07T10:47:06.211Z" }, + { url = "https://files.pythonhosted.org/packages/93/26/473c2e4b6c104e9e58e27ce95fc8005c8bd4fc36cae4f254371125a92db8/librt-0.15.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5f51401d102c885b9ca509e62c79b1dbff286e1b9b047fde6f763780789356d", size = 524427, upload-time = "2026-08-07T10:47:07.592Z" }, + { url = "https://files.pythonhosted.org/packages/26/60/03b3abb82b41714671b907bf6989b228e31e6a8af52dec82b5b0728dc250/librt-0.15.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cc30523e3f1a23fb7511cc659834a0d01a1042bb9de359bc1c131cc4ec6c9656", size = 543155, upload-time = "2026-08-07T10:47:08.866Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0e/9bb1f0a4affbd0a1888f4f79dc03ed2a299d9a2c26c59ab2a97dcbf11903/librt-0.15.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:59fe030d8ae4a57e3fb7756bf35a858de74e04066fc8555c53d0af979132af81", size = 546890, upload-time = "2026-08-07T10:47:10.327Z" }, + { url = "https://files.pythonhosted.org/packages/dc/84/6937a280d461f7de6e031ffb02edc2b7c3c90d49d630565ce8ff27cbc5f2/librt-0.15.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a6526a2a956bbb1e4ae3568c82e650fc99119c66bb011ea60715744955a2b4d", size = 555163, upload-time = "2026-08-07T10:47:11.798Z" }, + { url = "https://files.pythonhosted.org/packages/bc/95/2a2853c1ee014bf102116e7f897a04beeaeb2461b45b79af98bdfb95f1ef/librt-0.15.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:85ea21ec6730194d67156b0e0b5430ccb1d61f8b8b907e39b37f9812b74a13f0", size = 535812, upload-time = "2026-08-07T10:47:13.279Z" }, + { url = "https://files.pythonhosted.org/packages/c9/4c/cf9601c1b4c5f09280acd5d83abdb2e68527a2be8257136eb42304218622/librt-0.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1e47b8ba865d7ede071a91a7163073bbaeb72541f1ef8a07d512c45c7b5007f2", size = 573688, upload-time = "2026-08-07T10:47:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/47/6d/9ac7cbec46189a7625af4b5acbd25f10d827f4141b2002181848c8418923/librt-0.15.0-cp312-cp312-win32.whl", hash = "sha256:a5207ec414d1c4a2a7231b2086970dc036f94293cdf338190984958a013a42f1", size = 106138, upload-time = "2026-08-07T10:47:15.973Z" }, + { url = "https://files.pythonhosted.org/packages/38/d0/2ae99c83be86ce23f925ac1aeeedc777e97f427c4a8d190c70d0a16e9a87/librt-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:73b30cfa976659b3917c8f6153bdb0591c6a9ec6583599fd24a689b690622022", size = 126974, upload-time = "2026-08-07T10:47:17.049Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ef/dd24f9635c730b86b87587967dda7516b1845e8b17684603d31607fed598/librt-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:a54cf9e0ef47b96af580849db5471142200568ce1e02cbf416addab551369570", size = 112292, upload-time = "2026-08-07T10:47:18.222Z" }, + { url = "https://files.pythonhosted.org/packages/e7/42/467b53a601b406ccd7b97c1fd54b59cb34f9185ad5ce7e9d5c3c4e8961c8/librt-0.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:db13ca398005abcbe538deda87b686d9bd08b7001cf40c4c06b444960ae10a26", size = 151029, upload-time = "2026-08-07T10:47:19.312Z" }, + { url = "https://files.pythonhosted.org/packages/3e/e6/36c2299b7a94b84fdd01220d8a777a71be5be0925bb0dbdf71c0a06a34d9/librt-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa1f1995789dca3698bc550aaceb09a51bd5df0a057ff84ff15296cd1975b801", size = 155194, upload-time = "2026-08-07T10:47:20.398Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b6/ed5071f9325845e670bd36012757419767fbf56af77ed483077b9e4db541/librt-0.15.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55456ea87d8df21808446d03817be2f65e20391c1c615d9187440dff28cd08dc", size = 502568, upload-time = "2026-08-07T10:47:21.652Z" }, + { url = "https://files.pythonhosted.org/packages/7f/81/6450c67c3615d87704bcbc21323fafc69c799b06a044c447529f725d4b01/librt-0.15.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5a86a5a08c2235316bdb359d5dbb6ce0abfca7fac06363103e2c5af571d92f95", size = 496153, upload-time = "2026-08-07T10:47:22.925Z" }, + { url = "https://files.pythonhosted.org/packages/e1/d6/5f52b722bc75076954b3bfd49be15ea362df4d580c6fb315d0f617100d30/librt-0.15.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56b6a368529bed262da40ce13f8fef590db0479819cca84f16a1f01ac356d0b", size = 513336, upload-time = "2026-08-07T10:47:24.213Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e2/c08fd1d36ce63ea5a12b85c5d37f4550b5f86a692167e41e5a74222607ae/librt-0.15.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:234d8d394721fa0d786af15ebf1f3fb7f3ed82fd1cd0cde45c2f247b5d4281d2", size = 531661, upload-time = "2026-08-07T10:47:25.507Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d8/d9482fcbeb177b9eb87bb3899eeb3b42be690313c652f9e146b1d0681fb2/librt-0.15.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8363d7accb0286ac3a0e633f396e93800dafb8150494505daf9515bbda591f3", size = 524487, upload-time = "2026-08-07T10:47:26.79Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/075171517b41f861753034fbb151b42cfc83bcc853849f24f5e66fd60ccf/librt-0.15.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0f0ee3644d951f31055ad07d77d92520e84505dd7a432cc4cd501dd70ee06785", size = 543201, upload-time = "2026-08-07T10:47:27.999Z" }, + { url = "https://files.pythonhosted.org/packages/b0/03/42c2330f37eeb475b6affeedd06518f60035f323af3a839335e3fc9fef2d/librt-0.15.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2cfd1a81a648806e6a7717be4cc4d1bb392fa229752bf8444ba365e381e984d6", size = 546467, upload-time = "2026-08-07T10:47:29.396Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/1ad4c5638f7e64d8560328bd25c54b409a661bdb6ff254b38ff90744288d/librt-0.15.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6cd22c9da0d866558e46a041f1cc0c2bbb26b61b137b2347fa834c332e1d101", size = 555139, upload-time = "2026-08-07T10:47:30.815Z" }, + { url = "https://files.pythonhosted.org/packages/49/41/39fa7d15db1204cd1cbe6514680fbdc243adf754a0885061308f43afc013/librt-0.15.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6d5225ef8801e4ea5e482fa9b5dfb891dd9ef6f6d870f1f25d449ca2c70ac218", size = 536050, upload-time = "2026-08-07T10:47:32.222Z" }, + { url = "https://files.pythonhosted.org/packages/1e/88/c6dcf0dd8e26dc0c9a499a2abab8646c86dcaf9ecea9524cb46d3686331a/librt-0.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d28a05796b99f749bf8794f17ba9ba1612d0076b802e9cfc62c554634e9ce3b", size = 573700, upload-time = "2026-08-07T10:47:33.527Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9b/ab54c71a7918a7c34fa5327fb61390a77446a07a146fbfb1165250a61035/librt-0.15.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:2067ff438048cead9d223ca5675bae2a25e520a7c3e6c1498bf9c6892d22caab", size = 82194, upload-time = "2026-08-07T10:47:34.835Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b2/4f9a243bb892395f3becb80789ade13771701091f9f07ab8230247953ba8/librt-0.15.0-cp313-cp313-win32.whl", hash = "sha256:1cd3b721f24c206398b9e26da3c3a9c011e6e89d06f318ba8ebefc30f1003890", size = 106231, upload-time = "2026-08-07T10:47:36.251Z" }, + { url = "https://files.pythonhosted.org/packages/bf/af/64aff4885a40b93132382f2c314647d722574605416504379184ef3045ea/librt-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:f395a4a9a03ac062dbe9a9f82e0c720502e590a38feee6a757bc82e9c63afbd8", size = 126996, upload-time = "2026-08-07T10:47:37.453Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/335bccf6c7cb9028cb0b54aead27d9ece3f01f83bc6baa2abace5da655c1/librt-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:0a15cb554761247d84a3ec0cbdf4078d70725384f0e4662c0fa3b26266eb60ad", size = 112188, upload-time = "2026-08-07T10:47:38.729Z" }, + { url = "https://files.pythonhosted.org/packages/a8/93/949053fb462eecc4a9a5ee770a81f4b40be7b79538b245545d4aebc6b58b/librt-0.15.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f5de7feedc56337a088eb15cd9fafa9938367362221d8cc62c642b7f94821993", size = 149833, upload-time = "2026-08-07T10:47:39.86Z" }, + { url = "https://files.pythonhosted.org/packages/61/ca/8281aa6cd560a3420e4497729f6b704b53be3eeaaef82d5aeadddaf7441f/librt-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6c0eb900c0e91f4aebe680845242e614f1864edfd44106380d0752ac29522bf8", size = 154088, upload-time = "2026-08-07T10:47:41.065Z" }, + { url = "https://files.pythonhosted.org/packages/dd/02/1a1662dceaba6a086360891448d5ce9a7d3555976cae59a31a39d744b9c7/librt-0.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8c9a650a188e38bac005048cbe6342e81407782944d01934540ab75e417df21", size = 494215, upload-time = "2026-08-07T10:47:42.388Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/99211619dc656370a3740c33d2b0b6d5a3fb1e73689314f6ed477a397dc4/librt-0.15.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:92bfed8deec93df30286b9fe9e3b1dd17329cc076a192b4ee5ec223841d54953", size = 491173, upload-time = "2026-08-07T10:47:43.683Z" }, + { url = "https://files.pythonhosted.org/packages/d4/aa/5448d0b05f4579b635d3899176817ebf561af0e57bacd425b5b1887264c1/librt-0.15.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec4b19788f835711a2072f9dbe6b03b3bf32ed1f0fb30cf399bdd59d9f0c33fa", size = 505512, upload-time = "2026-08-07T10:47:45.314Z" }, + { url = "https://files.pythonhosted.org/packages/95/82/01940e40b83c43a546c4a3c896cf34ca272a9690899d55914e4827b3dcce/librt-0.15.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4c7bacb70930f3d0a56f4ecf1be474a1f0d941b01dd73b756f3c256d42cb879", size = 523073, upload-time = "2026-08-07T10:47:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/88/fa/759c0030f3ee371439eb26de34fc745807caf0abb878af7af4b8b7c3dd3d/librt-0.15.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3e79f05e4a08b4d880342673312bbc895b56df7765605796f15902eb5367d3ae", size = 515080, upload-time = "2026-08-07T10:47:48.319Z" }, + { url = "https://files.pythonhosted.org/packages/0b/27/894e072228fcb159703c655da69f8cd10dbed489c36e3df7dd032a2483be/librt-0.15.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a417149c0cba4d50b61e992e5a15e69eaf96746609b461cc4ed168aeef6b79dd", size = 534164, upload-time = "2026-08-07T10:47:49.875Z" }, + { url = "https://files.pythonhosted.org/packages/98/a3/0078e91c1f36f8815db17827de15650b9a3fe56c55fbf998c854b34e40d3/librt-0.15.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:da7a94d6a3411f579d72aa3e3bc5fbca7ed4549f3dbd7e5de3aa567333374285", size = 540616, upload-time = "2026-08-07T10:47:51.408Z" }, + { url = "https://files.pythonhosted.org/packages/86/33/81a29b796dd52a45e9ef7974c7732926e8f10f15b8d2be505665979f896d/librt-0.15.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:856f743ae607f2c1380eccb566c0038a9fb3eabf0fc2be2704d76d9f73557239", size = 545890, upload-time = "2026-08-07T10:47:52.818Z" }, + { url = "https://files.pythonhosted.org/packages/05/82/8be1baa1350e5d30cfd70ae79d0a6f4dc5862ef47f7bb2808aabc9bb86e5/librt-0.15.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:779a6e7c894737e5983e7790a9c78c4000c30e23c9aada08081bdbea53b0fa60", size = 523287, upload-time = "2026-08-07T10:47:54.165Z" }, + { url = "https://files.pythonhosted.org/packages/c6/4f/d1be6a01a35c20ef734e0e44113f87d4af756a9354a89dcfbe3b4f8af5e1/librt-0.15.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96bb17dbe8bab3c0954fbebfc69ed395599de75b6bbc35e3270a878e15d4dd65", size = 565868, upload-time = "2026-08-07T10:47:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/67/88/649cfa33f5825927b160610f670bdab012a64d627eddb94fa795ea4292fd/librt-0.15.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:7220697efaa6e5348fc3d18ee7f8563d4bfecd9872b37ffb915bfc1d08840622", size = 81619, upload-time = "2026-08-07T10:47:56.886Z" }, + { url = "https://files.pythonhosted.org/packages/22/31/8e88a8d5e48fc8d1a817787fb6811dfff6499acd6c8683dd83934aa6ede0/librt-0.15.0-cp314-cp314-win32.whl", hash = "sha256:f54598964d357b1c5ab77cf5d92f21e598fe0e23cdbe9618480807f81b4eba15", size = 100138, upload-time = "2026-08-07T10:47:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/80/92/20fd6c4b6a1b1a564b076d55cd3d427d8428217d7638dc25a654cc4791d4/librt-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ff5893a2c23d886aa9ce786de5ac6ddc74aeeaf90743682b74d920e117d2e28", size = 121258, upload-time = "2026-08-07T10:47:59.564Z" }, + { url = "https://files.pythonhosted.org/packages/fc/28/6af430b44d9ebb897b865a3c363b6dcace51357be2347cc0f8f869656a86/librt-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:3722a099730704c9a3d70c879fc0f51daec25fe5f1555672d97bc595abeafb95", size = 106467, upload-time = "2026-08-07T10:48:01.097Z" }, + { url = "https://files.pythonhosted.org/packages/7e/aa/b42bb798942ced219f6d63b27e07f91237887a8d0bd0921666db79a13790/librt-0.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:38c0c7d4b6fc06c3324b3f9162c8391bfc4fd9dde53afe1033ce7edb48d5a714", size = 159523, upload-time = "2026-08-07T10:48:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/75/03/1b53cd4ef904e73b1d828a5f90143bf94a2967d7cfff0b9ccf93e12aa9b4/librt-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8b2fdd7ead3c995c37940a790690660d0ca006c302db26cc51933f6766866fc3", size = 161638, upload-time = "2026-08-07T10:48:03.725Z" }, + { url = "https://files.pythonhosted.org/packages/ac/c4/9f9c9fba097d49e9e694c2b4dc331df31884645ecbc58a93b4b5fc69d2c5/librt-0.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fde98cf1fc4bac144ce23c2c4c017b924ba714509ea9334977b0b27050c837d", size = 701795, upload-time = "2026-08-07T10:48:05.135Z" }, + { url = "https://files.pythonhosted.org/packages/4c/05/0966840bda0380c8ae167b9043c6230202941cc90ea29c48e096964c765e/librt-0.15.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:e3b461183c5fa7681b48560f91515f53a953122fb30c71e07abc67d7ddf58c38", size = 682147, upload-time = "2026-08-07T10:48:06.555Z" }, + { url = "https://files.pythonhosted.org/packages/18/af/1c47ca573c30ea47d195aec26133af522fea1104afaace028d7b32247ea8/librt-0.15.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4bbcc257e3babea20a91715c361b24554ec4e8f51aa578568afc230799fe1a19", size = 696397, upload-time = "2026-08-07T10:48:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/2e/0f/1aed6223d4f9f9d1171a8596ff100ea4c3f7699fea7a4ba657c3e60daa6c/librt-0.15.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b845b8d48088fad0cadc84be4b8fda63203be7e9237b71015b3925443c1f35ab", size = 722542, upload-time = "2026-08-07T10:48:09.569Z" }, + { url = "https://files.pythonhosted.org/packages/c6/22/9e3a929aea456c97d69e6ef3884efea56d4807f97399471cc946baebd8af/librt-0.15.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b30e600e8f337b9bd7f39b86d9fdfedc73cc46e3d0f745931a23a234220bb7e2", size = 729709, upload-time = "2026-08-07T10:48:11.129Z" }, + { url = "https://files.pythonhosted.org/packages/e9/1b/c327ef6018e3a9ca0b8e7c5eddeeb331ba8f9b76c24e126d37d0f6d62faf/librt-0.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:64b0c8c35aa4c4ed79896359f3e0b285cbe4e610042106500da4811c322cc108", size = 752891, upload-time = "2026-08-07T10:48:12.558Z" }, + { url = "https://files.pythonhosted.org/packages/d7/d1/d5f1ea02c56930087009e39db9b70660a663e76c730b27b925d786718457/librt-0.15.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0da0d94cb802f32a0524653e7201f2cef72d5f700a5407678f5290483d4fcd08", size = 745301, upload-time = "2026-08-07T10:48:14.55Z" }, + { url = "https://files.pythonhosted.org/packages/d9/3c/5f7c585d15ebb2250c73e7c0ee4e9e47be72c65d520c07ddbcdc62037674/librt-0.15.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4a6369168d371207339b1e50d4532b06a7121586141f82599505a3f315751d47", size = 747921, upload-time = "2026-08-07T10:48:16.453Z" }, + { url = "https://files.pythonhosted.org/packages/7f/52/1443a446486eba966bcbca1696b472e4f210320ec42f490a47f48fbf0fdc/librt-0.15.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c434e072557ade9cbc642d052c89d031efe47d5c9614523619d0d74a02378e81", size = 727561, upload-time = "2026-08-07T10:48:18.089Z" }, + { url = "https://files.pythonhosted.org/packages/79/91/2270a9380f11725cf83ce1925a5e32dd1dde2be9bba597f25c10a38644e7/librt-0.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7eec6a42018bc1d45763b1c162d3d2bf7c3b9a1b0ed30d3e91dcba390efefcc", size = 774417, upload-time = "2026-08-07T10:48:19.611Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/f4b1548d4f5b99186737fe27aec238e9823e8d5d23bf4df007c030689dc5/librt-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:6912fa5e635d74529ac7cdb1bdf6ca3af4453da8d1edbe0110ee1cb4ad407ebf", size = 104381, upload-time = "2026-08-07T10:48:21.048Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/134afad262def1de04c0843c376d02135f1168af43f22e09a52bd8394727/librt-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8e11699ed745931c395acd3621b07062e0f840efa6935aad87a64ed0995f0915", size = 127034, upload-time = "2026-08-07T10:48:22.561Z" }, + { url = "https://files.pythonhosted.org/packages/99/5f/1b6846b20572bd699c9e9ec321a5f781845bee477df2aa2a43b28bc40119/librt-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5d2a91724463bfed4f573cd7a9fdc856d2e230d0c0e5a61416a93481dccd8605", size = 110827, upload-time = "2026-08-07T10:48:23.804Z" }, + { url = "https://files.pythonhosted.org/packages/c6/44/4de9f4ddadb009a55c7758eb5736d62534a7daaf27bd71bc50e64b606b06/librt-0.15.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:8443e38dcfcfdbcf5add5118c623efd788d65ac2e25756d6251a54a06a4d0aca", size = 149843, upload-time = "2026-08-07T10:48:25.148Z" }, + { url = "https://files.pythonhosted.org/packages/1f/eb/5d9ab71e30119c44094e0275f38b47dd327aea0f843a080396677029d508/librt-0.15.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:6d15a29033c57490cfe2069097c6fc4049e4e65ffbb749be7dc453b7c4c68965", size = 154510, upload-time = "2026-08-07T10:48:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/8505d1b8f5e8c19587bd03f7429993b3e9ce5c06819d856bfb11d919374c/librt-0.15.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d2c05c729b589e734c09578bf5964be48a911765484840d017bbc84f49d4c4ad", size = 497543, upload-time = "2026-08-07T10:48:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/1d/9a/3a8390775cb095765aded027ac9c63e7c8ea74e731498607544c6505de0e/librt-0.15.0-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:fa60887537e1d0cd2d9982269d33a709bf54b195cd2b9364fc0a758022af5bd9", size = 480452, upload-time = "2026-08-07T10:48:29.531Z" }, + { url = "https://files.pythonhosted.org/packages/e7/40/258a4a7117ee915d66de5cd9b8ade65a440993161107ce3a686f1859955c/librt-0.15.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d8bc24219b24c0af375718942ab75e3544b2763085f40f965be4326734ae8328", size = 507768, upload-time = "2026-08-07T10:48:31.007Z" }, + { url = "https://files.pythonhosted.org/packages/6b/c6/2f4dd296c97a0b85b98894519b279408ec9dd602d4f692b1ea0e25dee670/librt-0.15.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86a21a7bd3fe3a419512ef424cc1c020f6771d0b29cfddff36d1635a855e63f0", size = 525122, upload-time = "2026-08-07T10:48:32.7Z" }, + { url = "https://files.pythonhosted.org/packages/49/dd/29eab42be13b2bf0ea8cb227135a45d44693e30a7e8b92871981ff56b82b/librt-0.15.0-cp315-cp315-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dbab647e88d90b3167b91efe7091e248653688ed4337e4f90907a722c7361bb9", size = 520371, upload-time = "2026-08-07T10:48:34.294Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/4bad71adeca8fe208b775c2a35417fa5a2584c8f4791daaf89a89450fea1/librt-0.15.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d8edcf6f550e918dca779c069b9e156385c60b406f99fc7641f32c52f7193659", size = 537258, upload-time = "2026-08-07T10:48:35.88Z" }, + { url = "https://files.pythonhosted.org/packages/4c/63/59dba6143fdcc7240c54458b629f3250000a61b8945890fc9efd451b19c5/librt-0.15.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:8b62076030baa2d8b1501a46bf0e19c27a489aa90671c55665bff7887f7660b0", size = 527432, upload-time = "2026-08-07T10:48:37.466Z" }, + { url = "https://files.pythonhosted.org/packages/ec/21/21a24c6a2327d8362580efebe77286bf47b0f4062ec5ea41766e609d3c7d/librt-0.15.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:d00d20d1818e82a07a0ee0aa89a98b17ed7916b92441090b683719cb20a59b6d", size = 548108, upload-time = "2026-08-07T10:48:39.384Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6d/fc68c89a7971418b41f9a873623ff935cb864097544c6a2f8ce491c8ef5d/librt-0.15.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4e6ee93fc3cf848dcbf0cce2eca73d8e7dcd0cc2b6df3a529d57750b30a4c55c", size = 529681, upload-time = "2026-08-07T10:48:41.392Z" }, + { url = "https://files.pythonhosted.org/packages/65/7e/c2d98766124400d722063a630b0fde38a9fc768705d37eecca15c47dc192/librt-0.15.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:32896a0af72508ea979e0acb4e4c04cbeeae04938167950d535c83c45597167d", size = 567736, upload-time = "2026-08-07T10:48:43.124Z" }, + { url = "https://files.pythonhosted.org/packages/55/6c/f8c34a95e3a515c6e1c192b89511e7253c89a7760c6b500d57ffdb8d2dc8/librt-0.15.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:ec3ba415afaf951f6951b1dd16d3c8e4f540065fc382d7e70b823a79567ca374", size = 81673, upload-time = "2026-08-07T10:48:44.645Z" }, + { url = "https://files.pythonhosted.org/packages/c9/9e/e23fa8e78679ec45728188650b39e8ff476c83b691c96f749217df3b1b7c/librt-0.15.0-cp315-cp315-win32.whl", hash = "sha256:d2813ba2503764f0450680c533d13df7cff9b49df1411062eded5f67db4195b9", size = 100081, upload-time = "2026-08-07T10:48:46.171Z" }, + { url = "https://files.pythonhosted.org/packages/e1/dc/3eb4c5e297343f0620a55532cd7c8d764d3001fa2159212dadf480464827/librt-0.15.0-cp315-cp315-win_amd64.whl", hash = "sha256:b87d67e33afaf265262f2a66db578284b88ee2e6fcd224579cb5c15518677ad8", size = 121228, upload-time = "2026-08-07T10:48:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/97/70/43abce19f04e49762f8ec834c8fafee13cc40fd6b94a72a24e534febfcd0/librt-0.15.0-cp315-cp315-win_arm64.whl", hash = "sha256:713bd7df21170b982e729e46870f31d6b437bd1a9b4648cffb529bd3c2ec5c4b", size = 106487, upload-time = "2026-08-07T10:48:49.095Z" }, + { url = "https://files.pythonhosted.org/packages/de/15/83f2deddb9368b8951ec8c9477269b5b9b8bd9bbf15e57402d0f38817dca/librt-0.15.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3de789c82752730f94782a5ee518baf9c05edf85733aeaf73bb6e518755cdf54", size = 159448, upload-time = "2026-08-07T10:48:50.649Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/043097353f9b3c73b583d07f6b8e552795463f4bfc8caf85e42eee50c26a/librt-0.15.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:e0b5deec9a8664eb722c797241970fd4aa1894d25fda36a1ddac0f7407606bd6", size = 161686, upload-time = "2026-08-07T10:48:52.174Z" }, + { url = "https://files.pythonhosted.org/packages/f4/2a/8ae77f9719d42ce71cd708560a3557b38ac3c17a0383e57f87084de45bbe/librt-0.15.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5563302a8359bc2295bb7084d1a8ed1519df96afb30eb2aa4e0bff7b54228988", size = 710668, upload-time = "2026-08-07T10:48:53.782Z" }, + { url = "https://files.pythonhosted.org/packages/61/34/c0436ea134deb9a0d6da80a396a2739a81cb31e0418f7227239e23140898/librt-0.15.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:22d6263b9d39d7bbb286fa791945646e3218f1be2d693e36fb630f1d0e59cd13", size = 679396, upload-time = "2026-08-07T10:48:55.645Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/001e0d99aa9250d5cd5715a9081291a20656083459f9019cda15255329e1/librt-0.15.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39ffd14646190c454f0d86e0d256b33f00a87a26ab410e619773b841d0e41416", size = 704313, upload-time = "2026-08-07T10:48:57.46Z" }, + { url = "https://files.pythonhosted.org/packages/2d/53/b34fa9d0ff00f136f4d58ebb4c411ff634baed1eb412bb602a2bc8dcafcb/librt-0.15.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c47318cd3a61401452de11282242937e3e057c4fd3dbaf601e269d0928a06c0a", size = 729847, upload-time = "2026-08-07T10:48:59.231Z" }, + { url = "https://files.pythonhosted.org/packages/86/ac/fa4d7a424665040e95baf480a6d523446057684b6758624c85338e8a23b2/librt-0.15.0-cp315-cp315t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a56a1d4f859a82ca5b99fc4b82c9b027b15e3c455c5cd99e7d0719f27bb20b6c", size = 742736, upload-time = "2026-08-07T10:49:01.151Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/e17a9bb5de6fb8c3186ed1a7d68d21618b027ac2d3633e03d3b6109c67ae/librt-0.15.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:077471b3182db4e17c36ae91555f36a4d2c00080b267f749bcad34a478a9a302", size = 763454, upload-time = "2026-08-07T10:49:03.039Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ec/ecd02cd30935b931b9cdbfed6ab5a099c51b280b4e7baa274da80978ed27/librt-0.15.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:411ca4d1b905b860ceba7570dd6717a71dedaddcc4b0f77ece710aa41ee11f8d", size = 743296, upload-time = "2026-08-07T10:49:04.941Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b5/b3c2b8353ce820a4854f78d19321344242f89fa71c975b71132ba9bf242a/librt-0.15.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:1256589e0b0adb31751d685a68bce29d73407ddf4ef05d4188f49d5dcf9566d9", size = 756217, upload-time = "2026-08-07T10:49:06.825Z" }, + { url = "https://files.pythonhosted.org/packages/3c/52/6cc22542ba59146b05cca2a656f9ff8bb67e38e63d12c3b0cc183d837bf1/librt-0.15.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:f42b74a53e5f26a0ba0007411a7455b66c67ce4022a39cc1f56fc4efd65bcbab", size = 741934, upload-time = "2026-08-07T10:49:08.839Z" }, + { url = "https://files.pythonhosted.org/packages/40/32/a04b72b1aa86e3be23b2ecff8c1aad2dcc955bd3956d6d26e7e34267e57a/librt-0.15.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:291bf73caf78b9e88d6fae9bfd693207ff7d832e2fdbe2cf8e746bc13f5f892b", size = 783763, upload-time = "2026-08-07T10:49:10.661Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f0/89eb11dffbe9279ff37144dec786927314502ae0b114f1449dc78c458aab/librt-0.15.0-cp315-cp315t-win32.whl", hash = "sha256:c16d15ee371643ab48dc8248a3e680ebbeca573a13af2c3dd0c985b142d77162", size = 104313, upload-time = "2026-08-07T10:49:12.305Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4a/1f1978c200f563beda63c36adff2d65bbecb81e365e8e69e572f5f70fbc6/librt-0.15.0-cp315-cp315t-win_amd64.whl", hash = "sha256:dbd605739f228912dc49027cb764456b9757750bdc2b6b7773164db7096c6fd1", size = 126889, upload-time = "2026-08-07T10:49:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/38/a6/800800bfed7b1fb10fc3f3d557785c3854e80d3f7a9800d784b176a1fc2d/librt-0.15.0-cp315-cp315t-win_arm64.whl", hash = "sha256:84d244b00604d17df3fc7736c327892d6bba66181254aa4087be807b6c342bdc", size = 110700, upload-time = "2026-08-07T10:49:15.499Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + +[[package]] +name = "multidict" +version = "6.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/99/1d4d69c3512d0ddbfa3a1b69cfd9a151012ab2eb4eabbb096201b1f0b7d8/multidict-6.9.1.tar.gz", hash = "sha256:0f06e60fa190aa7abd0914c2a766736fdc8e9f34878c4346338534b73d1b20e2", size = 182404, upload-time = "2026-09-21T17:59:05.362Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/1c/fb2114207afc7f00ab9897ae5beeee27de5ec58ea8b9623e879f2c56f003/multidict-6.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aef74e9beabbd6c4aafc091dabff86d046ccf013ce1e4396c0fbb01b4cad9de8", size = 97307, upload-time = "2026-09-21T17:54:14.957Z" }, + { url = "https://files.pythonhosted.org/packages/b7/23/1ab8c7349adbb30c8cf41b88b0794740df8ea9f7de2e382da040a2f5bd8e/multidict-6.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ca5ebe6d454f1e5496cf052386a559f1080bf2de75bf327ebca0a6003b79f19", size = 58830, upload-time = "2026-09-21T17:54:16.261Z" }, + { url = "https://files.pythonhosted.org/packages/e0/89/b845535014a9ad50537ca50204aaf6351c5368822a3acd9da854fb2a5a34/multidict-6.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f6d0c4b70f783305e73f9944d8efe6be1022550f0974ba0ae9d8893c0350fa", size = 57645, upload-time = "2026-09-21T17:54:17.491Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4f/28ebb40a6f8e8aa7be78bcbe4bf9a1ac9518c40ef88accf2d4cd6ed7a7e0/multidict-6.9.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8050e75af7e4c6e2d5260b84eeedb618f3e452e66473432e085b5d1b81429299", size = 301535, upload-time = "2026-09-21T17:54:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/33/51/0de5fc980df34d1ffed9ef68f60ce86c2d4baa3d422598a53f8730c56757/multidict-6.9.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4736d350371337825cac1793c9f7c40701c32a03912548c2a7608e51679cbb96", size = 298796, upload-time = "2026-09-21T17:54:20.52Z" }, + { url = "https://files.pythonhosted.org/packages/55/58/dd86067bdd2e961fdee8ec659fabc50f4e76403616b508714ec2686b0a49/multidict-6.9.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:02fe09dc197b8ae7e355371e51e5dce2f39060cd5a8badf94904527e23a3f188", size = 282392, upload-time = "2026-09-21T17:54:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/2f/52/5464a2675579e3adb48b7cd4707a59da499660f0e22c024bc7281d2d9bdf/multidict-6.9.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ad4528cdce058b684f75fad1faf4a6a6c992fe2f08376370ca66e4ce5916a84a", size = 314538, upload-time = "2026-09-21T17:54:23.366Z" }, + { url = "https://files.pythonhosted.org/packages/d3/69/7f2b3fafe064181669157ce872a097b08a0546fb6fd2481e8c5604b47ec3/multidict-6.9.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8885e3808aedbd6725b921fb67dacaae0678933561ddd47c2b01315198c70e2e", size = 315675, upload-time = "2026-09-21T17:54:24.808Z" }, + { url = "https://files.pythonhosted.org/packages/bd/53/f65ed68b96eea2b4f6fa4f9031bdfccd8f3097c429e3d371c545d1c40ca4/multidict-6.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3b6e6840421c83ccb60398e333b44f910b0907bb409597685ab2eedd1e22eab", size = 303685, upload-time = "2026-09-21T17:54:26.236Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5f/ade05795c6fb9adfccd61695a0f0509bc793583f589c4b1b87f27215a139/multidict-6.9.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c9648ed33dc8179e4ec04bbc73bd7f0038e1e81217a69467f61f02a78bf07e88", size = 280306, upload-time = "2026-09-21T17:54:27.685Z" }, + { url = "https://files.pythonhosted.org/packages/60/b2/c996349a61e0f43c7c95877943a67476261bf906ff993670afbd33ff5bf4/multidict-6.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d9d6544790ba50438a9c1a903c3c4afb1ec8a7832db5518549275b40ef0dd4b1", size = 291691, upload-time = "2026-09-21T17:54:29.004Z" }, + { url = "https://files.pythonhosted.org/packages/0e/87/8b7016d1084854729c80acfae71c5a51ea7e7c9411d66c3e2729772f5723/multidict-6.9.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1c9f3c25df6c9d3bbae4f6fd3f514b1c5c740a2110f56ccf36069c85417e289c", size = 290357, upload-time = "2026-09-21T17:54:30.491Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/801653b6aa9343a7430cecdefa1bf71ff24432224c02f15cc15e47837652/multidict-6.9.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cf22b43f35b7dbb9f71e8ee2041b5c00029cdfc28ede3a2f31cf8906f9a6c126", size = 305831, upload-time = "2026-09-21T17:54:32.132Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f3/9fc997861920bdbf70db82bdce5c802ec6477754c4bd78cff2898e3c63f9/multidict-6.9.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed78ccad4c7b421804f5d524b7740946a7ef75d89dc0dac52e9d7c24c472410", size = 308995, upload-time = "2026-09-21T17:54:33.571Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5f/278b5a2c99d6de27914b49158dd6f2a6481e53359142cc366dff04821a2f/multidict-6.9.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e3b1aa25f01238886a6baed9e13b9a9240ed344346c79ae280ae65e8603b21d5", size = 279240, upload-time = "2026-09-21T17:54:35.025Z" }, + { url = "https://files.pythonhosted.org/packages/3d/db/0922594c601a691eb4365a83f4f92104ef87a9728d8066fa25c99349e754/multidict-6.9.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bc94a68ea5e18f8e85dc6b522bcb53093f692c8eb62b4837ac047da73956cbe4", size = 305487, upload-time = "2026-09-21T17:54:36.555Z" }, + { url = "https://files.pythonhosted.org/packages/64/cb/07bca86d4a361a87128f3019540d7689800fdcad44e3b3ad08f1f035ed74/multidict-6.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73533644e1f69ea1164d56cc6505f564c6c44072b646b66d67df2d31fed0348c", size = 301997, upload-time = "2026-09-21T17:54:37.903Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e3/99591bf626613ea35779d2460949b45e59e589dc9058d6693c923eccd4e5/multidict-6.9.1-cp310-cp310-win32.whl", hash = "sha256:66987aa68b0f7c2a1cc5f388ca962b8ed92b10f79de38d6d0d8c716154f519d9", size = 51205, upload-time = "2026-09-21T17:54:39.152Z" }, + { url = "https://files.pythonhosted.org/packages/ae/18/10c008aac77297aa618631e8726ece31668f9dc9d9c848e4b5d2e68eb3d3/multidict-6.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a32b78c1e52ebd8e247bb68300b90b233300d8816faa008ed0713bc539fb6af0", size = 59049, upload-time = "2026-09-21T17:54:40.465Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b1/da73a58bef2613571902a824f1e11185207ba756215c99038f6d220bc167/multidict-6.9.1-cp310-cp310-win_arm64.whl", hash = "sha256:ab64ace1a68682d191d9bedd9d4c939406ad86b1d9f628180410644249ad46c2", size = 54610, upload-time = "2026-09-21T17:54:41.605Z" }, + { url = "https://files.pythonhosted.org/packages/e3/24/3823efc330630a1f132bcd0a9b182ddec3c53f452f551c8e8d3aaf5a2d20/multidict-6.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:910d4260512660484c0dc1588a316fbb35a40c081c36fc51d1225351af17cfe4", size = 96739, upload-time = "2026-09-21T17:54:42.815Z" }, + { url = "https://files.pythonhosted.org/packages/6a/69/36331fe1d3aeb0c525a9972d6cf82791075a395aa96f026f060009d691ae/multidict-6.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33fa55b990f81c2927e01399ace0d18926c69d69baa8cdaa819424132fb97987", size = 58514, upload-time = "2026-09-21T17:54:44.064Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ca/5860651f782078ef13f2761c2593f4ba066d76f626b2f30f23a7f838b775/multidict-6.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:369b5aa01b241cd3fea6890bdbb11a1425d87bf1515831500d518f4223e9d72c", size = 57303, upload-time = "2026-09-21T17:54:45.304Z" }, + { url = "https://files.pythonhosted.org/packages/c7/9f/c56e2fa223cb5d182660e1a09eec38880f96b7cd22769582f74447c60418/multidict-6.9.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:803f8b575a71b1b299d677c28db0653459c79b5308874efec813f17b7457c7f1", size = 320076, upload-time = "2026-09-21T17:54:46.826Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/eda4fe0cad51f96096363ed32d3e0f8df28dab00beff4d92adc9db517726/multidict-6.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8a65621b98984a62e59403009591b8a5a7736273aefe1cab64cfb85b365cc07", size = 315489, upload-time = "2026-09-21T17:54:48.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/14/10b1ded0a4085a51c3054125f854da4f47ce4fec724a2d2503231b83f3db/multidict-6.9.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:13849a1d4f54c3809ae721e9e83ab28f5ea602f33660cb84eb6ef261eac706c1", size = 296716, upload-time = "2026-09-21T17:54:49.749Z" }, + { url = "https://files.pythonhosted.org/packages/dc/14/04d155dd18528443cb9009f3a9f35f7417773797d072c95104ff17b8b9b7/multidict-6.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd9a137a4a9becda3094f3831cd026380f75f6855e051eefe4c73ade524f1cc3", size = 330244, upload-time = "2026-09-21T17:54:51.26Z" }, + { url = "https://files.pythonhosted.org/packages/8b/90/284d10b3a9e5b312a8ec5b7a175fc7d560eefdc99886320449891a197992/multidict-6.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9ae9614c317c50836689ce2dfde07c05fe0b16378562e2221746c3913ede3c80", size = 331288, upload-time = "2026-09-21T17:54:52.595Z" }, + { url = "https://files.pythonhosted.org/packages/27/50/f420de3683f9b047fc5588064d84043ef57a7152ef451b8373dfc7068d22/multidict-6.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8f1e362c9352b50ed120f001046fdbb80810c9d56580f4c3fc13bbe30823387", size = 320663, upload-time = "2026-09-21T17:54:53.91Z" }, + { url = "https://files.pythonhosted.org/packages/01/ab/0120a650d7ce4fe167299c13a13cc4ee2dc72458a9c0e9e9330b824b73c9/multidict-6.9.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dd655518f136febd96c05131a76a863e32fc2a1d7acd4e3c959e3ceb77d8345", size = 291139, upload-time = "2026-09-21T17:54:55.313Z" }, + { url = "https://files.pythonhosted.org/packages/b3/3f/c66342f73ee5cd2c9b1dda6cfce89b5f348c48921b8c8b6e59aafbbfe31a/multidict-6.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c5d675da8f1cb5650271c8ad5e95c0a3e5a183c105e72d953b12877b1c8d0fd", size = 310044, upload-time = "2026-09-21T17:54:56.745Z" }, + { url = "https://files.pythonhosted.org/packages/05/0d/e44b90d9e77e44ec3473c4c7ac7ea95764d7e8fbf6d5a085ef9bff7ce0c2/multidict-6.9.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:658f90f49cf5af2441cad0a2b801c3ef520471989a1ec55bcb25b255b2ca8d2f", size = 303279, upload-time = "2026-09-21T17:54:58.178Z" }, + { url = "https://files.pythonhosted.org/packages/97/8a/7741f7c23c211fae31ab546f0ba41569dae7671cf7ca30d5afb59e9d92aa/multidict-6.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:43124fe172ada86d03ac3c8dc8179091341f6724d5e5d5b160e1587e4cd3761b", size = 326177, upload-time = "2026-09-21T17:54:59.825Z" }, + { url = "https://files.pythonhosted.org/packages/e8/63/0cdbfbbe36316b2ef4b8e011afaca2cf291a73fe4fabf34dd0b22e3ee48a/multidict-6.9.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f0adc22a4eb31e545221d93fc73a0f6a8cc2379d0f4309f71d1d17ba938b82", size = 323526, upload-time = "2026-09-21T17:55:01.43Z" }, + { url = "https://files.pythonhosted.org/packages/98/05/0c86e9678e78f4596df0ed7f59d2aa755107da3f2a92f1b7fa150e94cbb8/multidict-6.9.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7fc59e9ba821b220944ccfe0f89c9dc4745f6d097569992356eb03869f21e953", size = 290278, upload-time = "2026-09-21T17:55:02.993Z" }, + { url = "https://files.pythonhosted.org/packages/95/8e/71bd7f43c4883cba6c5f96db88bd2311d52e74ad7680be5915938490d56f/multidict-6.9.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:87cc632c88ee5dc80e12681047839304d98ee5c9a708d686505767001c9b8b9a", size = 321142, upload-time = "2026-09-21T17:55:04.48Z" }, + { url = "https://files.pythonhosted.org/packages/2d/00/bf59d6bb22a4c152e4039574c29490caf3c65107715121807b4bd7cde244/multidict-6.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b828cf64d62dc09ac183f03c1aeedd164ade96a2ce4934109452edf29de1dd13", size = 319045, upload-time = "2026-09-21T17:55:06.119Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3c/f27f4f045198bf1c4bcd6eb3b6cf06be2dceaceb6e67222b903fce7a6749/multidict-6.9.1-cp311-cp311-win32.whl", hash = "sha256:2c1aeb92eea59d824f004341b26d5e4b47a8a441cf9726769b9a90abf9d0e08f", size = 51309, upload-time = "2026-09-21T17:55:07.531Z" }, + { url = "https://files.pythonhosted.org/packages/91/17/c2064aef64efc007bc89a571f843e5772f27946eb9bc018731bf8016e319/multidict-6.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:5f89dad732280e7a10b74d40b91364f88e13c3f2c08c2ef83a8cd42f7a61af2e", size = 59148, upload-time = "2026-09-21T17:55:08.936Z" }, + { url = "https://files.pythonhosted.org/packages/45/a1/3bab1827edd813dfb90712af5cbb7fc1473ed4d9c871d103df4f4bb950c2/multidict-6.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:5800368526647146978389dfaa46da3356291e9f0fff9a4ef12e8c2bef964a0d", size = 54258, upload-time = "2026-09-21T17:55:10.191Z" }, + { url = "https://files.pythonhosted.org/packages/d9/0d/4b5afb6d3e545c9af0cdfe2db8f6f4c6664568c23d863d888674e447e6a4/multidict-6.9.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:29138fef49828542e859828107e42e50d0e587c513b7eb4b2d92bade2b0860fe", size = 98360, upload-time = "2026-09-21T17:55:11.508Z" }, + { url = "https://files.pythonhosted.org/packages/89/ab/1b9ca66251899981b21138b87da9d5a9c2c81af12b1ea7d19466972f7fe2/multidict-6.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:19e31815d41cefc365489e591d105d2baceb2f65aa75d29471fbdbda8651e006", size = 59979, upload-time = "2026-09-21T17:55:12.866Z" }, + { url = "https://files.pythonhosted.org/packages/36/eb/6ae44062466c26c8469ef43f2481a6a48d8cea0587b2d54514ec92e2adfd/multidict-6.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6ed30be8918e18c8bed0a2e8b70639ecf02feb61ed00ca2e41cfcb2a50fa3f42", size = 57736, upload-time = "2026-09-21T17:55:14.223Z" }, + { url = "https://files.pythonhosted.org/packages/b9/5c/a67817593019257a4ac8b0d1b4c426030e637c047b0692ef405439ecea7a/multidict-6.9.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:637f4ae36264bd7b8d9a60193acddc1d735ad52e8ed53a19931ea6d921fea8e5", size = 333097, upload-time = "2026-09-21T17:55:15.591Z" }, + { url = "https://files.pythonhosted.org/packages/90/bf/599ae2e6222822d88a247a8a7ae82fe6fd25d5700757b79603d5edafe6a0/multidict-6.9.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35fc236507fb1b3138f0af5ecd5f94ed752d4d6d826248eae425f86204013eea", size = 334196, upload-time = "2026-09-21T17:55:17.015Z" }, + { url = "https://files.pythonhosted.org/packages/15/10/d8aac5acacbe7f5c117866c776ec26d5f15a868759b6f37ad8e7ed3b5b02/multidict-6.9.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e58952f04772f59f11c6e007471449809a30165188669bca8fdb19dde40a8f24", size = 317465, upload-time = "2026-09-21T17:55:18.412Z" }, + { url = "https://files.pythonhosted.org/packages/19/0a/714f796f7293a8b1c5c3f465a26996231d5450c54e85d64ef1258c091134/multidict-6.9.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d35a4f1c63f07fbb8c8f9946dea98b21eddf6c57421585f71d91864be3ba2a24", size = 345914, upload-time = "2026-09-21T17:55:20.306Z" }, + { url = "https://files.pythonhosted.org/packages/da/b1/e37fbf769c567be277bcf32df6234035a4384677fcc1bd852752be3d6b93/multidict-6.9.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b5f8771aaaed7f80e84a4e471d2f29ab6721e4595075e54d03ae1ed951b2000a", size = 351163, upload-time = "2026-09-21T17:55:22.21Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5b/db08419c1e1f7c9d60cfd2787b2b517d7ae4ebbda8281b48a33eb5141467/multidict-6.9.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:976fd7689d69ec78d67d31d38d396d8adb562f7e8368279f76aed4aa451fa06d", size = 336881, upload-time = "2026-09-21T17:55:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/9b/07/cc9bc8a62651d2d53ab93ee4993b3a71b7cb78eb8ebfc9c757a5b6698617/multidict-6.9.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:95052e8777a86bae87c0bd0b5ab22d809e3d1d02bf69e3e66ddda5ba75a05805", size = 303405, upload-time = "2026-09-21T17:55:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/b6/0c/8e912afafa70e944dbb8bec4b66ca6e008511395278c0d3dd0e89567536a/multidict-6.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1a8adfcaf96f587ab138476eaddef95f29b8a2a8a9afbfea8d2fd62180995d02", size = 324265, upload-time = "2026-09-21T17:55:26.989Z" }, + { url = "https://files.pythonhosted.org/packages/66/6a/62c2af80fb085e6234805017af857b8e913dfac7a55e9c1349c27768c58a/multidict-6.9.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1a53de2772cfb74559df2eb4456ec4eeb908435ec55a84b69370d9d745d62aa8", size = 322085, upload-time = "2026-09-21T17:55:28.732Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6b/35bf801b336fd960811207203ffdcdc24acc3558b3e7ff2e1c914b141e70/multidict-6.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f2ce963299d42fa3f22a90adc0fdf174792ffef5ff4c7ffb68260548fb05580", size = 338107, upload-time = "2026-09-21T17:55:30.303Z" }, + { url = "https://files.pythonhosted.org/packages/80/41/495ef65bf5bba29d142d81b3fe1b8154b919bed70e96491b1e93c3c26f0f/multidict-6.9.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5b30ddf7234e611ca877575b62840e6af5977f92f1f9d532eedbb05a44ff8004", size = 339631, upload-time = "2026-09-21T17:55:32.012Z" }, + { url = "https://files.pythonhosted.org/packages/19/bd/057fdff5f4e04dcd40a960e38f77d19d3c4b67dd243ffa5f43718edb29fc/multidict-6.9.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:63ada7ee2e9345695f9e9bc4c65d72222253f07b1ac94fd0e37555cc6f3c7f60", size = 302080, upload-time = "2026-09-21T17:55:33.817Z" }, + { url = "https://files.pythonhosted.org/packages/3f/de/9ace933ee8dad808632523726f42255b09600087219e3d4ead7369820910/multidict-6.9.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c95601ed98fad3f6e2f8fe809c3b526b0fab31ef525e00a155e227f3d17f58a", size = 341107, upload-time = "2026-09-21T17:55:35.471Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ac/7c1204406097bfc5c283d4a3287d61166807d189917567df4c1318484fc4/multidict-6.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c148e8b596000dd3e4bfe206e70f3e666be18d72032e0012555f2373c52e35d6", size = 333482, upload-time = "2026-09-21T17:55:37.002Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c0/a70c32ea3299ebe00f44533740cb46905717c51faf29bbd3ce8bb5886d9d/multidict-6.9.1-cp312-cp312-win32.whl", hash = "sha256:f9dad513626a33670f17cddc6078e30e311f444c957e8dbfc5b2b4603c8b4edb", size = 52548, upload-time = "2026-09-21T17:55:38.529Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2e/8c9c2591df01e5692ad1bf92febb082a17b1c6c3a19aa8b7ff49988fd989/multidict-6.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:a16a1dc8529f9e734a41c3b856f3eae7ebacdc061dde3f8a844e0c7889c97203", size = 59622, upload-time = "2026-09-21T17:55:39.86Z" }, + { url = "https://files.pythonhosted.org/packages/d0/95/59f4472ec512bc180fd207899594c7803ece96262ff9aaa3a4b6d7da6940/multidict-6.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:361f7206cf341ba94fb015688f5c8b480f8e63bd58a4c14a48aeca7851a241cc", size = 54930, upload-time = "2026-09-21T17:55:41.155Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/ddf7c76860f5f553a23ad3ca38463ccf5247081618977742c8dc4625355e/multidict-6.9.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:d7bf9e43282d69561618e8a0ea33368d532ebef42f15c096f427090521dd74f3", size = 63266, upload-time = "2026-09-21T17:55:42.676Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d3/f4ae5945ea2de597eaeb4eca3a74c59783ace54482c9b49435442b63efa8/multidict-6.9.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:03d47df72f084f757c1cb771188d5f4e3a805e4abc4d67e32509272343ae9382", size = 55771, upload-time = "2026-09-21T17:55:44.016Z" }, + { url = "https://files.pythonhosted.org/packages/93/7d/15468239920040d01c686e5ae669e6382f7bf31fc3e5e0a6b8f1c3b32d3b/multidict-6.9.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6bc94fe17c3c56e5418f79515b786b101845f70609b0d19d0c1ba13448e5633a", size = 57116, upload-time = "2026-09-21T17:55:45.315Z" }, + { url = "https://files.pythonhosted.org/packages/17/1b/b958f06aac2d8b1e485eb1c105b1b159cbf3868249e5142142451577dad2/multidict-6.9.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8f2973bbd2bebd9d2e0cd6394c1292a1a19ccd56bdcbe1e174059f1a39be5b40", size = 97692, upload-time = "2026-09-21T17:55:46.674Z" }, + { url = "https://files.pythonhosted.org/packages/93/6c/d6cfe18e61010166d7237d7527c775eb9843e7078feadea45b7628751b60/multidict-6.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de7738b8c0bb74c4cc16bbd7fb49fc2bcf6430dba11b3432cd52768ae40933e8", size = 59556, upload-time = "2026-09-21T17:55:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/46/46/9b4c1127cece0289fb207d04aae5116382ddfcb2a4dc8e0cb49a33f3c7b3/multidict-6.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e5ccad4b7bac125722f48d6f862bed3b514d8526deea06316bb72f152cd30a7c", size = 57357, upload-time = "2026-09-21T17:55:49.998Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fc/c18b07100a6064573e49e538d13d47eb2b5221f48080512df78aef54ab04/multidict-6.9.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a49ff5cdb33654cb7d6a3c377aa2a83ddefaa1db31eb10bcf3c180aa84f9af8a", size = 330433, upload-time = "2026-09-21T17:55:51.435Z" }, + { url = "https://files.pythonhosted.org/packages/62/ff/52a0082adeb69656b634609d4fb0ae65456deaae4cca3d5a12656626dc50/multidict-6.9.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b22ff30006a2f28f8bff878fb93413cbe3a4d1fd517c28081d848c90e9cfd2c8", size = 332487, upload-time = "2026-09-21T17:55:52.937Z" }, + { url = "https://files.pythonhosted.org/packages/39/3e/80bd4729635a7af371ff4dcc312594cf96e73f98854491be8740792de430/multidict-6.9.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3adf06c66041aa21eeb8a71e82379b74773298c8e6d3d839b151aae441a99b94", size = 316911, upload-time = "2026-09-21T17:55:54.751Z" }, + { url = "https://files.pythonhosted.org/packages/15/f4/7ea4a907e053e924e2e1694d90560f79f72056504bfd5e8483695f9527d2/multidict-6.9.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:963a8d8f97057082679523d0fd4c53a38f86bc58cabe4556faef682ae53fa2fa", size = 344950, upload-time = "2026-09-21T17:55:56.5Z" }, + { url = "https://files.pythonhosted.org/packages/0d/16/9642ae41fbfbdc546aa481c2b07ae1bc087a94ab5f5020b4d9ab77da401f/multidict-6.9.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b66ccc5c2cdd26e74fa5d4c29ffae424cc6148bf93ce574821783fb3b6d452c5", size = 350264, upload-time = "2026-09-21T17:55:58.073Z" }, + { url = "https://files.pythonhosted.org/packages/93/f2/e06c8e42074d0a4b8419bfe92afbd1d262190b69549ecbcd2dffa4f103c2/multidict-6.9.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fd79c521f6290c69125fa2b85fa65d9e657e6a8ffaf722dc881b926bef4aa5c", size = 336213, upload-time = "2026-09-21T17:55:59.723Z" }, + { url = "https://files.pythonhosted.org/packages/98/43/d7cf9ef4700e7c25244590d1b4f20cf12cbed86beba6c17be4d9e48c1099/multidict-6.9.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d804e4caf5d5da37d6dac1325da5629ebef1e27a294c2b568b295814aa36c7b", size = 302555, upload-time = "2026-09-21T17:56:01.666Z" }, + { url = "https://files.pythonhosted.org/packages/7d/3a/54209920324bc3928f5f2ba28b01a6dd957a9d48c3aaff77e38faaf5668d/multidict-6.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb0d664505f4b112f384cffeee82e91e3f6448d8e574989479db4439b68cba05", size = 322247, upload-time = "2026-09-21T17:56:03.265Z" }, + { url = "https://files.pythonhosted.org/packages/33/50/df96f961b178b621ab0adbf9221d4fd21534e1064fd9f5e85c1fa27fea55/multidict-6.9.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9e14d17773b1b3c758ff153659a1824608a0cb562c45f484b5ed8a433428444a", size = 321787, upload-time = "2026-09-21T17:56:04.894Z" }, + { url = "https://files.pythonhosted.org/packages/a7/9b/37f354562a8f82f9c1f63d3a94300fc82126d50320175f0702bbd544f87c/multidict-6.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:083735b7f395894e43adb278d5dae901448883a835ff8f1977e285fefdb10418", size = 335498, upload-time = "2026-09-21T17:56:06.783Z" }, + { url = "https://files.pythonhosted.org/packages/1e/44/78e366efc6c004185295cc9eb9711c10f13eadf3b547603b9c5526329976/multidict-6.9.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b65121091567847a8cb520d364ab22ba90e00d3cc55fa9eb34bb439f0684bcd1", size = 338244, upload-time = "2026-09-21T17:56:09.162Z" }, + { url = "https://files.pythonhosted.org/packages/ad/2b/ab5bd3964691abe4d14b43bdbdb8a621b77cea2ca352dc93159ec0a4575b/multidict-6.9.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ea999ae6e80e66ad5eea287860951b033d0104ca34d6d87c7b5125ebe0e12721", size = 301641, upload-time = "2026-09-21T17:56:11.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/a8/f6bc899f5aafaba755edc8e4bb934bc00b1e405f8d3fbc1dd10283738074/multidict-6.9.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:095d900c242e00fbe5f321ee072e7278b4153e78c5ce9c1efde167d62c1e4771", size = 340162, upload-time = "2026-09-21T17:56:13.076Z" }, + { url = "https://files.pythonhosted.org/packages/eb/03/a8fc809ef364b8c231c065ea2ea2f97ae86d4f9a0e36b5721759fd606778/multidict-6.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6441cc837aea58be7d9baef1b2383eb8311ab9303f500f99ac90b584cd78bb14", size = 332826, upload-time = "2026-09-21T17:56:15.175Z" }, + { url = "https://files.pythonhosted.org/packages/f2/27/32dde80245e2024e9bb7a6ca3c2fb7c695dc74014cba557cc35be6f91e24/multidict-6.9.1-cp313-cp313-win32.whl", hash = "sha256:9c4880d017555d70dea367dd49271830842d48e3891c2da97da7ce8c4abcee40", size = 52378, upload-time = "2026-09-21T17:56:16.959Z" }, + { url = "https://files.pythonhosted.org/packages/be/78/1bac2987edac273a6b27fa50e9204bc3466c94a7b4f6a44828d730f8810f/multidict-6.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:ac51cd64bae51c462ea58ad2492c9b8209667a4ef60c45c4a304518b67598d5d", size = 59449, upload-time = "2026-09-21T17:56:18.336Z" }, + { url = "https://files.pythonhosted.org/packages/e1/32/2a77ce19eea48cb9b3521a202b513ba3349aafa702c8317be0b645ffb74b/multidict-6.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:37a9ebe00c698279213d56e6c64e1962ab1e092918270649b397cac3dc196ca4", size = 54713, upload-time = "2026-09-21T17:56:19.774Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0f/c6041015aa2cdc11e1dff0cae58898ae5525d19e35efd65a4ecf3c6ea235/multidict-6.9.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:fc0dcb22fa9aeabfe3fa4e0430099acff985ec5d77a851382f76cc6146e780e5", size = 62336, upload-time = "2026-09-21T17:56:21.181Z" }, + { url = "https://files.pythonhosted.org/packages/23/db/95bd0afb130a0149c955f1f066143528d60864e64f41ff5a1e1313609361/multidict-6.9.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:024123f0ab402ab33828e24eb80fa8f25167d0d3783ba5f287e39ed741e6abf9", size = 54892, upload-time = "2026-09-21T17:56:22.63Z" }, + { url = "https://files.pythonhosted.org/packages/94/cc/8aa34ae8d09498e8da003f485c03371b55cf1bc6d56cff861b3a7de61251/multidict-6.9.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:af1b5a92315048c3e36bbebfa7d4760a9c3e910bc4166f11b20d77d20d6bcfca", size = 56251, upload-time = "2026-09-21T17:56:24.36Z" }, + { url = "https://files.pythonhosted.org/packages/52/48/22baa3b95375096369b04c4348af14139570f302f951773d2c68bf0ccf2e/multidict-6.9.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:b2483da477932ad1983d1d33c18bc3771c6fb00cfbaaed70a875fd547ef8e840", size = 96345, upload-time = "2026-09-21T17:56:25.777Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3e/b96779dcac28ec6d491fd821112a0156b519b6701bba186cdf6f6df737f4/multidict-6.9.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db4d697b18b6ef5528b1f36bfa25072cd2a421869f5963bc0e92c8a34b9e2800", size = 58901, upload-time = "2026-09-21T17:56:27.364Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b4/8e6d950f02ca6ecc00ecf671f2ddb5dab7017671a8d197326f75621f5786/multidict-6.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:854fd2f1bc6e8a56b89910b5cd7261a8b40f13ebb31572da985ce59c7da0886d", size = 56491, upload-time = "2026-09-21T17:56:28.901Z" }, + { url = "https://files.pythonhosted.org/packages/ed/8f/212ed7e03282c7bc271e3796bf985b53d5727c2827361e3dd9c5ee6b093f/multidict-6.9.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:03731f6fc036180700c9dc2308205a48e5ca6f3ff03087739ab746f294022201", size = 332297, upload-time = "2026-09-21T17:56:30.495Z" }, + { url = "https://files.pythonhosted.org/packages/a0/97/555aab000e03ecba85c0a1837fd5674d8bf10949490df7800da3a40660ce/multidict-6.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989261c5f1735a165f2e4e87cf6d5f17ab734fa18f9ad0383d5adfcdaefce701", size = 329197, upload-time = "2026-09-21T17:56:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/3d/25/f3539b5bdc147beba66015795dfe589eedcc580348fb1a8c69b1df4196b5/multidict-6.9.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b4c9e5d05b126b267ac048a89a0e2d9b48b1b648add62d4872906304a8590610", size = 308456, upload-time = "2026-09-21T17:56:34.657Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7d/4665cad8fc326787d218879be879d86dfe32f006c1f88ae47b04d3b52fd5/multidict-6.9.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9d0a21cf76153de8f2d96a877991d6bc59b9ab5180b949e50db73cc193b4a694", size = 343382, upload-time = "2026-09-21T17:56:36.606Z" }, + { url = "https://files.pythonhosted.org/packages/41/01/e1e9abe27f492b22dede492bc423015597412b14ce074f7475f4ecd80185/multidict-6.9.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e7386aa18d98d6b8af44b92173654ec469237fda35f8e8523e43b581a86f476a", size = 346848, upload-time = "2026-09-21T17:56:38.344Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/8d54118bc228e64e1087f1729647b93bbea225eda4a3f914663a6f410bca/multidict-6.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b69651732c64afb691e50cdc3387cae305e0eeff8804fe3e3ce203876494932a", size = 333612, upload-time = "2026-09-21T17:56:40.041Z" }, + { url = "https://files.pythonhosted.org/packages/76/e1/9509dafc1fc68948e75842a74533ddfa64af57f2b17810e9cacfa68a829b/multidict-6.9.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1fee9a16d88a1c4865610de31ef5c666671020d7a81d1f510eaf3c97d00ebeba", size = 304246, upload-time = "2026-09-21T17:56:42.313Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/96b93808e1bec0b51d49d89859e49b82a94f7b2e58eca0fc44ffcafd3ae5/multidict-6.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7814bbee202acd3bd240204c17d8b87a4c48c81064fd8674dbe527d94d5a4290", size = 321983, upload-time = "2026-09-21T17:56:44.399Z" }, + { url = "https://files.pythonhosted.org/packages/d9/1c/08e5184df026b9d8e05a6dce5da22628e46bc4a7c7f03b8bf3c6939a8c46/multidict-6.9.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:95f273bea318a194f656527ee2ed19494327bc500b8e87d9358e3222579aa28d", size = 315927, upload-time = "2026-09-21T17:56:46.208Z" }, + { url = "https://files.pythonhosted.org/packages/d7/5d/9ff39e7387f6fcc9a81af5dbf055fde08149849a04f8df10ca5b086b0d39/multidict-6.9.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:73bcafa21a78d0776b3ee7cd2a63c66f968eb7db8e8d594e32d7329950f6e828", size = 336827, upload-time = "2026-09-21T17:56:48.021Z" }, + { url = "https://files.pythonhosted.org/packages/5a/be/30fbb2cab22203128928066de94be247dc9a3230bf2bfc54437ac3b5f9f7/multidict-6.9.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1a938761c77e0e6edb0c93d02f4e988d44a69e5195e5a3b893e5553311347132", size = 336317, upload-time = "2026-09-21T17:56:49.778Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f0/2480aef6b7d8e7ab89b064ae14f12ec069570dab3a5c13e0155912293247/multidict-6.9.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:37245ca4105386194dd1d292a6f2aae09bfe1bd7ac6f9ec25093e3cf8e9b143b", size = 303258, upload-time = "2026-09-21T17:56:51.829Z" }, + { url = "https://files.pythonhosted.org/packages/4c/04/dbe59cf4ea3778600e8e70e30b31aa9118ad6da2844bc9f717804cbaa000/multidict-6.9.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f0700527dd5bfa8b7204b08330542f4f388899d3c14d885d8a368992e0eb562d", size = 337232, upload-time = "2026-09-21T17:56:53.537Z" }, + { url = "https://files.pythonhosted.org/packages/f4/8c/abe55548f06f12878588f08eb803c5a391283f01243f784cb00adac79231/multidict-6.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6b7e54fd883671d1a8810704851c044b7173287c552b9f5c3d9e0eb9f00ae194", size = 330261, upload-time = "2026-09-21T17:56:55.335Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f1/ba67f668478f152bdfa18abe72e7588bd078bf8b42c172fcf542fcf45826/multidict-6.9.1-cp314-cp314-win32.whl", hash = "sha256:e81ae656b9935ac4528a71f96bb7a14d949778ed1897c573d3e7ebb9187f8841", size = 51715, upload-time = "2026-09-21T17:56:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/014bb2128aedc8157d2848f0ec8a0209c6b822fb88deea012834e1c4a4e8/multidict-6.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:acddcac38adc8342ba48aba98896faa7928854bebb62542362138655b5367ee3", size = 58621, upload-time = "2026-09-21T17:56:58.531Z" }, + { url = "https://files.pythonhosted.org/packages/66/34/8f43c03c2d8821a2320b8c560d8a49d0be2920627edfac3f546922f91969/multidict-6.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:32217133dddc58c927805cf6c0731d8144584176b768042ee886d51e71860bc9", size = 53959, upload-time = "2026-09-21T17:57:00.15Z" }, + { url = "https://files.pythonhosted.org/packages/08/64/4f2a5eeea10b6d42634ba168ec6efc732ef218772e57b8239cd485c202f6/multidict-6.9.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0b2fb8c349d1103863750b5d8cb5ace766917f4b35f3d883c8f778853eaa9f76", size = 107694, upload-time = "2026-09-21T17:57:01.785Z" }, + { url = "https://files.pythonhosted.org/packages/32/ce/c68d08ac2096c1528ff20d77fd10f7e401db3e534ac5a1c7726e36a77c2f/multidict-6.9.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:56d834b74c993a7d7cb2b8ab33a0d55c3e0d4a3d2f2da2808a4ad3d79189711b", size = 64766, upload-time = "2026-09-21T17:57:03.52Z" }, + { url = "https://files.pythonhosted.org/packages/3a/9a/9f5c270c88fe289b4876fe28f9932a70e015a7916aff1e5b0325f7597b11/multidict-6.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:783ba7d845d79ce976afd9c1e91a4e5714671defa198ee789e8b23316083a485", size = 62723, upload-time = "2026-09-21T17:57:05.104Z" }, + { url = "https://files.pythonhosted.org/packages/7c/63/c9a0b131354dd949426166cef712d708f436a8cf261114f8d5d2aec440ca/multidict-6.9.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d718fa1b5f0d0dd75e86fbbc5b0c93ea3a5d65216c85c61cd5d7cdddfe08455", size = 290445, upload-time = "2026-09-21T17:57:06.687Z" }, + { url = "https://files.pythonhosted.org/packages/9d/41/ed27d1d20ba91f4e13e8f7dcfaa592614ae0e1d5f45e03171d23c3b098ac/multidict-6.9.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a313bad717dde740959d50850c315b75fd4eb0c5e6b4dc8535db0f1c369be125", size = 304375, upload-time = "2026-09-21T17:57:08.717Z" }, + { url = "https://files.pythonhosted.org/packages/66/6f/a7b26167173c3b73d6e5ebb0856c5154ad005156c3a2fa4062329921819c/multidict-6.9.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ef01d29fca550ab871fd99154f82c6472aacf8e7def272dfb07b460123850390", size = 281746, upload-time = "2026-09-21T17:57:10.713Z" }, + { url = "https://files.pythonhosted.org/packages/34/1d/da27bdd49b0cb84263288f5f48dbd915f2d17fd228fee2078ca607e80277/multidict-6.9.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f3bd290711c6e9486173a6ee7cd4e7f00c3971c7908c1ec1b6e5437c5e4c6f9", size = 311499, upload-time = "2026-09-21T17:57:12.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/36/ab0299a7abf53e38ff75d96c64480f1e2ff8edde985f93f7a1ad5e08bd94/multidict-6.9.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:81cdc537e0a42e3c0170752fcadbe450246d5c3b4b7231d6eb9672456605ac94", size = 313553, upload-time = "2026-09-21T17:57:14.548Z" }, + { url = "https://files.pythonhosted.org/packages/5a/44/e21be702efc9c9ba097c8be874c68860b1cb6cf9c85611263039bf95b37e/multidict-6.9.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b193bf7a443c97d81c47052f60c486071a4bdef4a573fa2514f920089414d45", size = 299898, upload-time = "2026-09-21T17:57:16.192Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/e93bc0dc947c7f872e502cfaa7ad9d98f001e1c93cfa6f4940457a6e187a/multidict-6.9.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7a0c98f6a636adf0d7edd60c61589eaf52d149239af754d0bfeb0effedba53a8", size = 273054, upload-time = "2026-09-21T17:57:17.793Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c9/945b44493fbe7af26ffe1e43c506b376daeab070fc1a0923bef9bbf572eb/multidict-6.9.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6e6b7e3a1520c39a2772f414cd9ddf5995f77e4e823dfa1522af383addd465a3", size = 294751, upload-time = "2026-09-21T17:57:19.631Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/0969a9b38b814e5fa3af1b0a57708c49b3d9b3d55c208bac6e98b87bb028/multidict-6.9.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ca65cced0d67a9039e93bcd98a369920e499bf98296844ff56ead01c9085321f", size = 284523, upload-time = "2026-09-21T17:57:21.313Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1e/c32ddf53234c228b1a374ddabca19f542ac7f32752f5155982abe2304833/multidict-6.9.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:372c063f37480f62c1ae32dc3a1a0a5942b180c883f99789075a8a8ec3c4e709", size = 290275, upload-time = "2026-09-21T17:57:23.206Z" }, + { url = "https://files.pythonhosted.org/packages/5d/09/7a99a3daafd987330abb865a2d36e432fd8bf18e790ddda55c5de15eb98f/multidict-6.9.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:827c92145b3b976b39430129c89d213b250dacbe5fce678e9a03940e6e848983", size = 303350, upload-time = "2026-09-21T17:57:25.086Z" }, + { url = "https://files.pythonhosted.org/packages/2e/09/7e6ebaecc4e226d7fe27973c271d58b75c14c24e84c23736d21dc39997dd/multidict-6.9.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9b0124b9c17e9890f0819b2e7a5f65ec9a2f5aabd6c8e7ad090c1675cf70dee6", size = 272232, upload-time = "2026-09-21T17:57:27.193Z" }, + { url = "https://files.pythonhosted.org/packages/1f/6e/420e9e879b21cbdb5036214ee238efabcfab7bf262d3fc124132050f79ff/multidict-6.9.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:23136f5a564654eb61061ec6d5620a4c1ea32c8f552b65e9982a12b72bff601b", size = 300525, upload-time = "2026-09-21T17:57:29.004Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/c6b0896850b3ceb190ca302760d63148843371f027c5dde3d1d732343a08/multidict-6.9.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f2524ec55b3e65cbe235a8b3c36e2af3b635be02ed05e20c94e70c5c943c009e", size = 295933, upload-time = "2026-09-21T17:57:30.837Z" }, + { url = "https://files.pythonhosted.org/packages/29/f2/f0ff3384a756227741c72f4ee94cadf1f9900df0ae961e48639f96115b1c/multidict-6.9.1-cp314-cp314t-win32.whl", hash = "sha256:35ba0263bae5dd3ad5aad767cc9afc01a8598c7dae30f1b3b2de98b1b32c28bd", size = 57776, upload-time = "2026-09-21T17:57:32.835Z" }, + { url = "https://files.pythonhosted.org/packages/8d/87/013e1eed30ad46bec1eb68947bc9caf55ba2fd0d6aed04fe964e3b44c23b/multidict-6.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:7c8d5882ba25ac0282258be435d8a05aa0cbcacfce15799154a338f847f159b9", size = 64881, upload-time = "2026-09-21T17:57:34.489Z" }, + { url = "https://files.pythonhosted.org/packages/7f/95/64dd049bb4c53426e2fee7afb874daaa8b4426c41878785883a94511ecf8/multidict-6.9.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ac348379cf4de5538a0a213be1532d289aa801ec5d267c5909446b9ea2f8e2c3", size = 58582, upload-time = "2026-09-21T17:57:36.19Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2c/26bc1723592cfe965b575652a7749026474b0625b04b04564e1fba265dc5/multidict-6.9.1-cp315-cp315-android_24_x86_64.whl", hash = "sha256:f04551dce5a7db8c9659f2e4245494c182d0663b83661803e08d46bfcae5eda1", size = 62339, upload-time = "2026-09-21T17:57:37.967Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c5/614d2eb9995232d066aae7f17d1d454060b6b5d882b22fbaf6150b05ef1d/multidict-6.9.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:c0c88085affd35c33e124e36930c5ad96aff9294ce195eaa0fd9cec962b64a82", size = 55009, upload-time = "2026-09-21T17:57:39.746Z" }, + { url = "https://files.pythonhosted.org/packages/7f/24/f7179ecdf9cdd3581ace3292c3b883a1a500a374aa4e619bf6eb521c62e5/multidict-6.9.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a8b75dd3d3638d9a19f23e84af4ffab3b8940422c0df2da2a77005ef5aa3d7ea", size = 56273, upload-time = "2026-09-21T17:57:41.544Z" }, + { url = "https://files.pythonhosted.org/packages/ac/72/c9dffcecccd1f8b9a58f960ec4eaecab62c4edb08d5bb984d4de67fe3fdb/multidict-6.9.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:006c4478de0a1876f4834e14255776286f09b9846b505fe63f67f9d173a9487c", size = 96390, upload-time = "2026-09-21T17:57:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3f/76542360e655cecdb7c158c7af88c69c4db0d2aac3421659d41214927229/multidict-6.9.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2784090c30a586d5b45197bd9c32f87fb927216cde302f6cfd76d76577e90f08", size = 58900, upload-time = "2026-09-21T17:57:45.105Z" }, + { url = "https://files.pythonhosted.org/packages/04/15/f28c71af461dd4ff15653a165cfd8ddaf01f3387ff80d2dd0de057d527d0/multidict-6.9.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:7c708566da8014b120a64b1eb6d200c6c0c8cb36296383723cdb6fc82038270b", size = 56540, upload-time = "2026-09-21T17:57:46.913Z" }, + { url = "https://files.pythonhosted.org/packages/19/de/5ee4050c3ddad88c605a89fc1cd18e89abd40a64f183e6fb5276b6eb7f52/multidict-6.9.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:042fb0196047e786936a730bd302de83143950da45f2c16078da8f35e1cf7919", size = 331818, upload-time = "2026-09-21T17:57:48.825Z" }, + { url = "https://files.pythonhosted.org/packages/70/e2/2d4e3c3ee70f583334ff236fe6a3babf7a05ce56169a15df65453af647c0/multidict-6.9.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2212a0c842c723d919ea4a22a9296cb6b244b386e4e6ea92adfc7fbf3095519", size = 329800, upload-time = "2026-09-21T17:57:50.91Z" }, + { url = "https://files.pythonhosted.org/packages/d6/14/f64bd05667343a7250cdd99f81bda2270e3959f00832441f10c7f904e1c1/multidict-6.9.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd882aa29bf402b62bf1fd7c19fd5df4b6528cf468a908864b39368572b662a9", size = 312048, upload-time = "2026-09-21T17:57:53.053Z" }, + { url = "https://files.pythonhosted.org/packages/64/bf/a91256ce00199e7544e3eda79c4e9544eb7388c38fa22f11638c2e357ccb/multidict-6.9.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c57541034d12b215ab0a2bfa371d1a8a198da18176d0426c27105b9161a6862d", size = 343676, upload-time = "2026-09-21T17:57:55.069Z" }, + { url = "https://files.pythonhosted.org/packages/14/48/628b978413518159228c2b2ec38645362ceb6427e935d83d716e26490a49/multidict-6.9.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d73fed4e37158ff00cd271871170b79e40138db51e625fd352fa17c6acb34f67", size = 347028, upload-time = "2026-09-21T17:57:57.157Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/5f9dc2b5ac7dd7a82458ae9cd283b99c185834f23442ace295362dcc5e45/multidict-6.9.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:79da2491348b30810728050b4a8ec0416f85884125c2fd44655b3d01150d9c3e", size = 335670, upload-time = "2026-09-21T17:57:59.126Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/6f319ee000f5ab3fa0055b194ad706fddd552c60c76a21d581c87db1d46c/multidict-6.9.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b4da208a63434d21a3df64d29758e650fc4aa8cb05848554b76949c296539cca", size = 307804, upload-time = "2026-09-21T17:58:01.2Z" }, + { url = "https://files.pythonhosted.org/packages/3f/24/61be5fe1616d033532e8d63dc789c0f35b9793917834713eefe050589ba9/multidict-6.9.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:877ca17fdcfdf5c397493a71e5ff97a87bb181417fe717fdadc77c08c09301ac", size = 323409, upload-time = "2026-09-21T17:58:03.383Z" }, + { url = "https://files.pythonhosted.org/packages/22/a6/eab8aaf59892169a0f4e8e78a5516a6caa6abd8e96e6410111258f4ef0ef/multidict-6.9.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:40aec5299e1ed71fbb988389059da381c3c1a60e0c649acceb2a35d9b128848e", size = 315746, upload-time = "2026-09-21T17:58:05.372Z" }, + { url = "https://files.pythonhosted.org/packages/42/b1/d1e14bab80a74babf6e2ac971a878b84fdcf3a972f3ec3400ca12260fac5/multidict-6.9.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:4a409ccc42aefec904038695d5d7fd6d8f3af2721b7b6401d55135ec7d6d298e", size = 334529, upload-time = "2026-09-21T17:58:07.35Z" }, + { url = "https://files.pythonhosted.org/packages/f5/50/a05b1d36f6d3363a5a334cebbf11624c9c7a69d66fd23345eb53b1d43b7e/multidict-6.9.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:0a5769559e3312dd96731fbe15b4abb6033368ac1cad5a98dadd21946a4c7d6c", size = 336455, upload-time = "2026-09-21T17:58:09.112Z" }, + { url = "https://files.pythonhosted.org/packages/cd/0d/bf33cb097380252b08e1177b5ae0cab822c47ce80d236f3f724953c76721/multidict-6.9.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:03fac50ddfd8302175b77863a015eccfae76767cda5506eef86df559ba861e1f", size = 305865, upload-time = "2026-09-21T17:58:10.921Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ee/a2bd133391204b1cec5a5caf327dc9a06710439b7403220860a8fb164560/multidict-6.9.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:5ecace251ccfa705bf3d7d35c5032cf750f5c629809740405697bce5c118c4a4", size = 337531, upload-time = "2026-09-21T17:58:12.846Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/c0972486f81a06c3bbb23063c85afaac53c51608f7e35bcb101a1006408b/multidict-6.9.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:ecc89dbd4155b2f8a47f4bbd89242a35ed15e8e0ec581cab5ac65fa38407329d", size = 332464, upload-time = "2026-09-21T17:58:14.673Z" }, + { url = "https://files.pythonhosted.org/packages/69/4f/5cbbe852b45d8a7f0ab3ed042386e4894e463010eb230603596cb15d0fee/multidict-6.9.1-cp315-cp315-win32.whl", hash = "sha256:a3ffe881246d28a862f1985824f484cb7361f44d6b99c4c620436ef56462f38d", size = 51714, upload-time = "2026-09-21T17:58:16.409Z" }, + { url = "https://files.pythonhosted.org/packages/d4/77/1fa118386deeae4e7a48870fa5cb073e748b0ac5265a6990651cefc48068/multidict-6.9.1-cp315-cp315-win_amd64.whl", hash = "sha256:59c123d0e948d760a5f930f316cfefa07e8d632ab84327c0693ee6a88171154f", size = 58619, upload-time = "2026-09-21T17:58:18.369Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ee/51c45748acfce9cb69d89dcfcd09c874b385407fd6993e885d6905659865/multidict-6.9.1-cp315-cp315-win_arm64.whl", hash = "sha256:31199204b3ced121ff5407a2c342326a5d27e3870abbf94bd80dbd2451b7bc8f", size = 53969, upload-time = "2026-09-21T17:58:20.102Z" }, + { url = "https://files.pythonhosted.org/packages/aa/57/89d2d3dedfae2558853c1770c7cbcdfd5211eb4c4090b1e8cb570544a490/multidict-6.9.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:8879510a76940670517ea1cb589978da44b86e286ec3e50d664ef330817afce7", size = 107679, upload-time = "2026-09-21T17:58:21.98Z" }, + { url = "https://files.pythonhosted.org/packages/c8/df/0c2a28870181c1762f90952b897f6db46c934ce073efe0442604cbc276db/multidict-6.9.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:aec65b53a07f580606593f877eefbb29a45939bfc0d3fe6e6d9f42b41b749f68", size = 64753, upload-time = "2026-09-21T17:58:23.622Z" }, + { url = "https://files.pythonhosted.org/packages/43/7e/9c6a3e7619459407d8958f7e56ab3dda7f0738f5aed9bb4ad48076e8de83/multidict-6.9.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:14c56f73e78faa1f68bbb826197cd5871994e70e841b8590829c35912ece5c64", size = 62732, upload-time = "2026-09-21T17:58:25.46Z" }, + { url = "https://files.pythonhosted.org/packages/38/eb/fd56c9d83ba0cdc3cf66f9acb278b24ef1f043b620b96d145685a3b57338/multidict-6.9.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:38c9986f9ce50c459b10de216a05f4bd7ed5ac63887d56e500a52bb464b861ce", size = 288648, upload-time = "2026-09-21T17:58:27.115Z" }, + { url = "https://files.pythonhosted.org/packages/df/ef/51e79c2442b0f56ac4ac580108d713078b31297e028d468fc220971a305d/multidict-6.9.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15db6a102cbaf1949cf028ecf080aac76d20bcd29ad4e092574db6c6b7af78a5", size = 304919, upload-time = "2026-09-21T17:58:29.143Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e4/774700cc5739353fe88a7204ed30e78a9403d98d4bb2496cc91f096d2e71/multidict-6.9.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd82c4977196681a499bb6ca9e462afbc5c91c1c15b6a991dfbd72733fea4dd2", size = 289094, upload-time = "2026-09-21T17:58:30.938Z" }, + { url = "https://files.pythonhosted.org/packages/ca/41/0bec327bc3a8271825376b6d8a469d1f80e3ba1e2e730e019836e29663c3/multidict-6.9.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bb58ba73a3f96f9a3e46b1fab69929d7edbbddb3133ee74b5c3c54074a53c4f1", size = 312007, upload-time = "2026-09-21T17:58:32.964Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7a/9906bf5f1fe20e8e755affccc1a69ecd8aebb079ac65386e5ffc8eefbe75/multidict-6.9.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c6eecfab7ce4cd9487ff8ba936fe38cdfd68c04faf3d5c710363c6a8e695659", size = 314657, upload-time = "2026-09-21T17:58:35.285Z" }, + { url = "https://files.pythonhosted.org/packages/33/92/cf6d665a45663bbe216420fe632da10e9f4f842b07ce02bc3ddf8eb882b8/multidict-6.9.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c62e71e6289d78c0108d8aeb495f8bd3cad4bc1632fedddc9297ddf287ecc20", size = 301563, upload-time = "2026-09-21T17:58:37.472Z" }, + { url = "https://files.pythonhosted.org/packages/97/4c/9f1b7945526905d58b4b2d422e2ea81326dda71f9bdc9c32c8002639234a/multidict-6.9.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:539c2cd5fed0947c135cd7eabaaac55f48300dfa1de0f3ca4edb5efa6606f471", size = 278540, upload-time = "2026-09-21T17:58:39.551Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d9/133aa472fc1be181de29a0c2e1c4cd778280882f532b404395f646742e46/multidict-6.9.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:3100d169ceb7bc8f05f89a6db11d1b21f119975fc26f29dc45a472e3569f0879", size = 294858, upload-time = "2026-09-21T17:58:41.791Z" }, + { url = "https://files.pythonhosted.org/packages/bd/38/21f2305dd20ac5038fa581b5d7e0d8dbbd70dc6b50231c22ec173dacddda/multidict-6.9.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:696477ad71385c4795e3b8e4cf10b0d2c28c2a1ca6a955e031cb1e62993e9ee3", size = 285461, upload-time = "2026-09-21T17:58:43.844Z" }, + { url = "https://files.pythonhosted.org/packages/ad/21/465a43c214d1d7e2d0c974e727d4b5935f8a0868838b5df9d0984c83c1dc/multidict-6.9.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:f5844e7befc707367807586f550fa97e23dcfef0728f02b98c7bc498a961a5df", size = 288964, upload-time = "2026-09-21T17:58:46.16Z" }, + { url = "https://files.pythonhosted.org/packages/2d/58/72a3e8d56c1e05146d2d1841dbaacf8986375eded1e5a58d30eef191a580/multidict-6.9.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:abc7c2e4b47bfe6a9aea434d3fdebb9597ee636e914e92ba352f2068b9142f4c", size = 303924, upload-time = "2026-09-21T17:58:48.395Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b9/911084c04530a682a54536b4f738d3992a593b5ff6fea1d6a1f02f3acc6b/multidict-6.9.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:7ab379f95caee071a37cbd8be86d4c65accc651d391f9f97748fef006f38769c", size = 278479, upload-time = "2026-09-21T17:58:50.494Z" }, + { url = "https://files.pythonhosted.org/packages/45/eb/9a333346c02c928a9e82534638bce85697c10c46b4901fcd70af19357356/multidict-6.9.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:557a4e1708df428ebe6c3081c83a273d275dad2446ce0d81fc648ac71afdb18d", size = 301650, upload-time = "2026-09-21T17:58:52.554Z" }, + { url = "https://files.pythonhosted.org/packages/67/2f/1d936c4e45c8199db6082b6836e9182e31aa393de0de652637790831ca4b/multidict-6.9.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:475d04d5192eba487a3e2f935976340baa24529046e9c1c9c7a3b7bf80445ae1", size = 298024, upload-time = "2026-09-21T17:58:55.03Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0c/ca62ae2882b89a4cfeb0bbca873a4afdb5fc32994c4f8a37a7cb52ede7a0/multidict-6.9.1-cp315-cp315t-win32.whl", hash = "sha256:10083a8a0f4e1b26b599889e90b9802504ce5d3f7722f925bbb7ca47dd22a7c1", size = 57810, upload-time = "2026-09-21T17:58:57.141Z" }, + { url = "https://files.pythonhosted.org/packages/02/8b/b2c2805c3eb6fa4cdf5512fb95ac6174dde71e34dd601c5be52f4df5dc82/multidict-6.9.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e96ca64383efa107262ee3949f047af5ee4f1845ba09463466c04d35a83bd3ec", size = 64984, upload-time = "2026-09-21T17:58:59.446Z" }, + { url = "https://files.pythonhosted.org/packages/fd/34/03f1d204d698c408f4754c969882609b6b7a902c0e3d16a7033f219cdeda/multidict-6.9.1-cp315-cp315t-win_arm64.whl", hash = "sha256:501ed8b02a5990c67a91c732843609d43a6be1f7576fcdfc867331239f37fbd3", size = 58742, upload-time = "2026-09-21T17:59:01.726Z" }, + { url = "https://files.pythonhosted.org/packages/be/59/e26cb779be4c591d1a910f59d29aca9fba4de70349840a833beba2652371/multidict-6.9.1-py3-none-any.whl", hash = "sha256:7bf6478188f4e47bf5686e8a33da4ae28bf43b1b2528d9ee144d28492bfac60b", size = 19176, upload-time = "2026-09-21T17:59:03.501Z" }, +] + +[[package]] +name = "mypy" +version = "1.20.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/97/ce2502df2cecf2ef997b6c6527c4a223b92feb9e7b790cdc8dcd683f3a8a/mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4", size = 14457059, upload-time = "2026-04-21T17:06:14.935Z" }, + { url = "https://files.pythonhosted.org/packages/c9/34/417ee60b822cc80c0f3dc9f495ad7fd8dbb8d8b2cf4baf22d4046d25d01d/mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997", size = 13346816, upload-time = "2026-04-21T17:10:41.433Z" }, + { url = "https://files.pythonhosted.org/packages/4a/85/e20951978702df58379d0bcc2e8f7ccdca4e78cd7dc66dd3ddbf9b29d517/mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14", size = 13772593, upload-time = "2026-04-21T17:08:11.24Z" }, + { url = "https://files.pythonhosted.org/packages/63/a5/5441a13259ec516c56fd5de0fd96a69a9590ae6c5e5d3e5174aa84b97973/mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99", size = 14656635, upload-time = "2026-04-21T17:09:54.042Z" }, + { url = "https://files.pythonhosted.org/packages/3b/51/b89c69157c5e1f19fd125a65d991166a26906e7902f026f00feebbcfa2b9/mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c", size = 14943278, upload-time = "2026-04-21T17:09:15.599Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/6b0eeecfe96d7cce1d71c66b8e03cb304aa70ec11f1955dc1d6b46aca3c3/mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd", size = 10851915, upload-time = "2026-04-21T17:06:03.5Z" }, + { url = "https://files.pythonhosted.org/packages/3c/36/6593dc88545d75fb96416184be5392da5e2a8e8c2802a8597913e16ae25c/mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2", size = 9786676, upload-time = "2026-04-21T17:07:02.035Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/9ebeae211caccbdaddde7ed5e31dfcf57faac66be9b11deb1dc6526c8078/mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c", size = 14371307, upload-time = "2026-04-21T17:08:56.442Z" }, + { url = "https://files.pythonhosted.org/packages/95/d7/93473d34b61f04fac1aecc01368485c89c5c4af7a4b9a0cab5d77d04b63f/mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3", size = 13258917, upload-time = "2026-04-21T17:05:50.978Z" }, + { url = "https://files.pythonhosted.org/packages/e2/30/3dd903e8bafb7b5f7bf87fcd58f8382086dea2aa19f0a7b357f21f63071b/mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254", size = 13700516, upload-time = "2026-04-21T17:11:33.161Z" }, + { url = "https://files.pythonhosted.org/packages/07/05/c61a140aba4c729ac7bc99ae26fc627c78a6e08f5b9dd319244ea71a3d7e/mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98", size = 14562889, upload-time = "2026-04-21T17:05:27.674Z" }, + { url = "https://files.pythonhosted.org/packages/fd/87/da78243742ffa8a36d98c3010f0d829f93d5da4e6786f1a1a6f2ad616502/mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac", size = 14803844, upload-time = "2026-04-21T17:10:06.2Z" }, + { url = "https://files.pythonhosted.org/packages/37/52/10a1ddf91b40f843943a3c6db51e2df59c9e237f29d355e95eaab427461f/mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67", size = 10846300, upload-time = "2026-04-21T17:12:23.886Z" }, + { url = "https://files.pythonhosted.org/packages/20/02/f9a4415b664c53bd34d6709be59da303abcae986dc4ac847b402edb6fa1e/mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100", size = 9779498, upload-time = "2026-04-21T17:09:23.695Z" }, + { url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" }, + { url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" }, + { url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" }, + { url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" }, + { url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" }, + { url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" }, + { url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" }, + { url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" }, + { url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" }, + { url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" }, + { url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" }, + { url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" }, + { url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" }, + { url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" }, + { url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" }, + { url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" }, + { url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "narwhals" +version = "2.26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/68/5351e34623d253423240ea7de3f8fc74fa8ab14b1ab3c0ec4ac8997413c9/narwhals-2.26.0.tar.gz", hash = "sha256:6b9cadca82f375c7e4cf584fdc86ca25da54827307a9c58f94547ee6104b82dd", size = 686970, upload-time = "2026-09-08T13:32:08.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/b5/1b84b2c784db76d69442334bc8b8748c840f13ca53be086f4f250ad4a0bc/narwhals-2.26.0-py3-none-any.whl", hash = "sha256:29326d74f107c347fd1009bd58e38d9f7c7c5b51e6de97bc93dbc325d9038b54", size = 474034, upload-time = "2026-09-08T13:32:07.159Z" }, +] + +[[package]] +name = "nest-asyncio2" +version = "1.7.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a6/a2775b388a14b5ea0c894bbe513c0d3e04e65e8ae583e60f5357722b8e03/nest_asyncio2-1.7.3.tar.gz", hash = "sha256:2e9a84d5d1efe6d020c72988d21aec569bac42d98af2ff6b9de24640c5d22a34", size = 15642, upload-time = "2026-09-22T17:15:06.709Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/dd/a8bebc78975a7466932077a0973cbab4361a32368b1d28d40683ec19333c/nest_asyncio2-1.7.3-py3-none-any.whl", hash = "sha256:2bc87bdca654e719425145f5e84eaeb0080b013fdd47d65729a7d66243f4987c", size = 8364, upload-time = "2026-09-22T17:15:05.492Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/13/01/11703282db468b85f6f7b8c7f22d058de5970d5c7e60a3a8aaa313c3de36/numpy-2.5.3.tar.gz", hash = "sha256:df2d5874ff183595a4ba404edd04f6bd9b5505c1d7708573f6a6c17489a67563", size = 20791231, upload-time = "2026-09-06T16:27:47.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/50/8fdbb16af64895706a45f06a4068e29db732ec180f3c1375f14123359138/numpy-2.5.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb189f09db39283b26bfd061ec16189e14f71c6755207f72a0f7540867afe5b9", size = 16994982, upload-time = "2026-09-06T16:24:29.244Z" }, + { url = "https://files.pythonhosted.org/packages/60/39/789131c1188c078dcb3a1692e72e1e050c68b88ffe72c9ccaac9bcd7a9cd/numpy-2.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f59a878c33d6b88122d80d239bb3b845d58708750b0cb06a09aebb9b18ec696c", size = 12009327, upload-time = "2026-09-06T16:24:32.491Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/a312e95696e5f601914dd8b6dd844692ba61670807417e24b68e337b5c70/numpy-2.5.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a72f874bc9e10e4b8f80426fb49716d5141f64442a0c8418065093ec8017fbb0", size = 5445405, upload-time = "2026-09-06T16:24:35.071Z" }, + { url = "https://files.pythonhosted.org/packages/30/d0/5623a1707ed4fe16e3909fe3cf5ee3da004ae677ad23d83bbf3adf1a6faf/numpy-2.5.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fc36dc566135b5eceec4cf89758fcb719266a019ef07dae1754ae7c9f617ef3e", size = 6783213, upload-time = "2026-09-06T16:24:37.253Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/84146fc020ad3c25f805f70ab60da46fe3c540a21369754a7e4369754b6f/numpy-2.5.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76c2c1e6bfa5c84adc6434dfbf013aa92096a7985221762c8f11fedfd20fff58", size = 15687872, upload-time = "2026-09-06T16:24:39.751Z" }, + { url = "https://files.pythonhosted.org/packages/65/af/aa78d1a88805456e212b65461354cd943197fb9acecc4c90fd12295123a3/numpy-2.5.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7e18c623bb5c95acb3b3328861272816ba199fb531921c5d6d0b675f1fde9e3", size = 16717410, upload-time = "2026-09-06T16:24:42.745Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/faa79d865e69a97ba17473b23a1b74094b2259c03e820c70297293b9ea49/numpy-2.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4f8929ee6c96bfbd7b4ed2032e0c03af86fe1826740ab61ddabf9072d06e57ff", size = 17040975, upload-time = "2026-09-06T16:24:45.961Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/8877e629445a7176297dffcaf9c485faa96a95d81728a62521ad55bd4c0f/numpy-2.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b5d93cf48f687479941d12b69c873ad2cc76bbd487f0091c2200636497f34034", size = 18476479, upload-time = "2026-09-06T16:24:49.35Z" }, + { url = "https://files.pythonhosted.org/packages/c8/db/35e1c2d38b04cbd5b731f9d71495e055e813197669d22b612f11748d2ff9/numpy-2.5.3-cp312-cp312-win32.whl", hash = "sha256:bf63afbe037eb5d2fe87fbcc7778e61da53ebaf21d938a4515aa73b62532a5d4", size = 6133378, upload-time = "2026-09-06T16:24:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/accf6d4f0c80c5d9ba9735d6b1550e444180599f34dec69ca01360f717ad/numpy-2.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0a59a421a32580a009e8a1751345bf829631b990dc1794b80514ab722b435def", size = 12567828, upload-time = "2026-09-06T16:24:54.255Z" }, + { url = "https://files.pythonhosted.org/packages/22/43/1764aff32e4652526ae2f71fa8b3efd8d25c8a3d6926914454e47138ed1e/numpy-2.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:ccb32e0525d29e8b0572eb84c9a57af0e7a4e615726927506f55063c62414034", size = 10485432, upload-time = "2026-09-06T16:24:57.278Z" }, + { url = "https://files.pythonhosted.org/packages/79/e5/8fb89cd46d14e35699d13bf943a5f5f441ecee8667120a1f6105ab89e349/numpy-2.5.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:66a78fe4556c60aceda5916f9eacd638b18e9e681016ec302dcb4682d6d4d034", size = 16991061, upload-time = "2026-09-06T16:25:00.411Z" }, + { url = "https://files.pythonhosted.org/packages/2f/06/9dc9e48b5e5e941c8b10350c5ff2d721da42a20517d911d15544246775ff/numpy-2.5.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92f30e89b8ee0ecf363033576c422b2f58fed6a80bed0aa48dff6d14c654663e", size = 12003676, upload-time = "2026-09-06T16:25:03.475Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2a/98282aa5b8f58b1157d440bb6282eed47e3632a5de53a714fbab17e659fe/numpy-2.5.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f9a2353b37a1a9e78fd82b27ad7e2a32a2d036604d18f02b05e3136c62ca3b09", size = 5439695, upload-time = "2026-09-06T16:25:05.978Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f9/b6533d777be9d6ffd29dc1be0867e563e6e8cc9a220ff1b716adc317f060/numpy-2.5.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:ccbc4665079665c3cf3bab4db9f6b095370cd6437d66be549b6c2a1fd19e1958", size = 6779395, upload-time = "2026-09-06T16:25:08.599Z" }, + { url = "https://files.pythonhosted.org/packages/73/85/735720d04ec197c5dcfacdfc9922667c7f1f5f496a279b7ba4d7c74c4cc7/numpy-2.5.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c76d5dde9f445058f83d0c02af00557a4db91de9a9a57c0df87d1535001d654b", size = 15681750, upload-time = "2026-09-06T16:25:11.173Z" }, + { url = "https://files.pythonhosted.org/packages/3a/1b/3b16a9bc514a440a7a0883684111dcb1ef1aee960af2ca95da8fc775f124/numpy-2.5.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5fa86b80fd24bcd1aff83ad23be44ea323de3f787be8f8b15d4a65621e25321", size = 16708577, upload-time = "2026-09-06T16:25:14.171Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/386f397831b07328b639c96c5b62719346cf4baf07c68d927239752b1534/numpy-2.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd4cb9ad3c7889b9b3fe0a9a9fb5d2ed26f9879bff2608d9f01aed147a20d231", size = 17042047, upload-time = "2026-09-06T16:25:17.582Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3e/a700ecbf36e85ae8328fd3b0e12eeddc22ed6358a64cb2bd913e0d195d65/numpy-2.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1302b90c0e52281681b2975adfe8a860cb7b12216a27b4b0b4207c44bf7bccf0", size = 18465724, upload-time = "2026-09-06T16:25:20.949Z" }, + { url = "https://files.pythonhosted.org/packages/41/ee/38e785e88a4045f6ad1d1f2808dcdfafdca48c760260c0587bf171e29fc9/numpy-2.5.3-cp313-cp313-win32.whl", hash = "sha256:1c80eabb4035ecf4ca9cd49cde8a9fdd69a729e63e6474887d1523ade7aa277f", size = 6129003, upload-time = "2026-09-06T16:25:23.664Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ec/100f2b1794ede74a9b3d7ec6b9736927f56713414c1dfe19ab6c383494bf/numpy-2.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:71cad2b2a7451ab79d8f5e71b453485b6775963d5cf794179144a7463fe6e8ec", size = 12560965, upload-time = "2026-09-06T16:25:26.602Z" }, + { url = "https://files.pythonhosted.org/packages/80/b1/7dc825ca94c12acebbce4c37caa5e198695eb31424bc579679f32b1bb49d/numpy-2.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:8e4dd766076855b5ff7ea52fa5f07ce26286726e0f8bff446b7739d02e6ea204", size = 10482343, upload-time = "2026-09-06T16:25:29.772Z" }, + { url = "https://files.pythonhosted.org/packages/70/78/cf416f15dc29375a229d9dfebf8db6e313f291580b39fa1a568b6052bb07/numpy-2.5.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:350ba9783ce969cf9f7ce6e6a9a58e1a6e2a19ca025b7ee448c4db727706212a", size = 16998686, upload-time = "2026-09-06T16:25:33.171Z" }, + { url = "https://files.pythonhosted.org/packages/9e/59/abcc2d8def4fd60eec7d87f92d27c13448ffd9ab14339bcc63a0d7a2fdea/numpy-2.5.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:012e66aca395d795496446e52aeeb5866312a5d4d3f27da270e5a0b43f70dc5c", size = 12013862, upload-time = "2026-09-06T16:25:36.748Z" }, + { url = "https://files.pythonhosted.org/packages/94/75/4640d2d6e4b64a049e48425a82728a41ef4adb61332d2cba68055774878b/numpy-2.5.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:adc1ada2662f8a5f960b8a10d9986897e7499ef07e06d4cfe7197f8cce923c07", size = 5449793, upload-time = "2026-09-06T16:25:39.476Z" }, + { url = "https://files.pythonhosted.org/packages/96/cd/625b57ae33d4ca560f32cc0b47b4a5922146d9beb998ddf773900d440a73/numpy-2.5.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:54a115e5a73b8fc44f0cebef486365a1894b5c9760685d4558b72b7c3eb846e0", size = 6785176, upload-time = "2026-09-06T16:25:42.069Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/12918652e7912ef9751e8694c88820fcd1908e0618cb23f5f3caa6004b7b/numpy-2.5.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be5a8381859b6da607c84f4f7d6847725f1cf1853ef8a2c9e115b7d58bef47dc", size = 15703377, upload-time = "2026-09-06T16:25:45.135Z" }, + { url = "https://files.pythonhosted.org/packages/45/8f/9beacf79ca7c650688ad0baa80931adb988fe6e6e5d5903c23cc3dbd70eb/numpy-2.5.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0521d0f4aebb6e06189451025fa17a913287b13c03d5fe05c017333b654ea5b", size = 16711928, upload-time = "2026-09-06T16:25:48.461Z" }, + { url = "https://files.pythonhosted.org/packages/09/8d/41d0a56e1ac4c87495c897a211b1368691b7237aadabec8b3b8f3a74d48f/numpy-2.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9deb49575e5b0b94ed72c8a64ec4d033381adc27e9060ae842971f697ba96104", size = 17059507, upload-time = "2026-09-06T16:25:51.873Z" }, + { url = "https://files.pythonhosted.org/packages/08/1e/0dfbc5cc251d54e2af790f254d24ec38637fa97ec7d5d11de7ffed787098/numpy-2.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b00eefbcf0f292945c4b4dec2ae845389ef5bcdcd596e6e4328051db5b5ba694", size = 18471002, upload-time = "2026-09-06T16:25:55.233Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2c/dfa40f6991f8185c8c30ffd023dfcbb11888e823cfab9557b920f3bb7bed/numpy-2.5.3-cp314-cp314-win32.whl", hash = "sha256:c2381f82999704f818e2c987a865050e285ec3621262c66d40f5a96c8f899f8e", size = 6180485, upload-time = "2026-09-06T16:25:58.157Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/d2c08231e4fde7e415501fd02c715d96e98599b2d8384445933944152984/numpy-2.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:2c25dfa72943e4336ddb6b0ee4277b47a0c85bede0807530ec68103bf58e2c10", size = 12698179, upload-time = "2026-09-06T16:26:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e9/dcdcc9b95cf5f49815055573aee1b11cfbf5299f38a180e437ded050810f/numpy-2.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:15aa985ac73a8db02db7663381aa109510449d3819d37206caed27b33a65a8a6", size = 10769383, upload-time = "2026-09-06T16:26:04.011Z" }, + { url = "https://files.pythonhosted.org/packages/49/c4/af8bc08a7ef4e1529a7c0cf24969accce316b783999802089a581ec99272/numpy-2.5.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ac7bb1c52d445bd4f8f7f97fefe6abc3a084dc4d63df50d79b17fa2b78e89297", size = 12132668, upload-time = "2026-09-06T16:26:07.138Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ae/0f15eb56d4ec5e13c1f7ff04ff407f997d1acbadb45d3e1f2e2645a8f43c/numpy-2.5.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e6ab667ba76450084eb64013762c438ea76d9d29cc676dcd6c2e9892ba37f841", size = 5568580, upload-time = "2026-09-06T16:26:09.828Z" }, + { url = "https://files.pythonhosted.org/packages/23/fb/c72a8f25d4b6e96c354e7ab45ace3b27dc11e5d6a13b6c7d0cd6b08bf112/numpy-2.5.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f7fabeb6cea87d65f3b926de33d03fb016cfdc29314c90974383b5582ae72891", size = 6882634, upload-time = "2026-09-06T16:26:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/07/a9/968c90ed2ab15060c338e8137f1215b5a60756ae07328e0a60d1c6734df4/numpy-2.5.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fb6f8fb9ff0b3a69f52c66ce397b0246583e9f28616231b0e32ca49259a5fa6", size = 15748923, upload-time = "2026-09-06T16:26:15.092Z" }, + { url = "https://files.pythonhosted.org/packages/59/08/9df04103947b95e3b6b1f2ed1a70521f325647a31b82da6a2aae3a485508/numpy-2.5.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93e1f5447e2b1e479d7bd74701e84746b86450cff1fc368b132d195e2b8f8211", size = 16746748, upload-time = "2026-09-06T16:26:18.43Z" }, + { url = "https://files.pythonhosted.org/packages/41/a0/14c8d5fe5b53a334aabb653deb391c0fef49558f491880ea300ed6785224/numpy-2.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c00abe94c1a69d75d827dcf1c025b25c8a45d230b3bcd77a9020883a1b047653", size = 17111561, upload-time = "2026-09-06T16:26:22.113Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a6/d7e96e42f01522e154c32489640f16dfc4f6181d165d05fc3bec8c2c4999/numpy-2.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:536f963710a4e63934d80ac0dc4f478804a83e9a84b6828018f25d09953ada33", size = 18513945, upload-time = "2026-09-06T16:26:25.401Z" }, + { url = "https://files.pythonhosted.org/packages/25/39/3453afb7119d0449ef11c886874120ff180e2c337760e0e2d88f70f1a945/numpy-2.5.3-cp314-cp314t-win32.whl", hash = "sha256:4c8a6d2ebce6305fd82fbefca827775437147052a976ee7c94b36a0c1b52ac6c", size = 6335421, upload-time = "2026-09-06T16:26:28.175Z" }, + { url = "https://files.pythonhosted.org/packages/99/01/22815d2b19a1a746b1d45205cffebb3fe511a18acb75fba6c88491fc9894/numpy-2.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9a37475425b431b4d060f23b4f52cd2f3aef6bc7c654bd760adf0040eec9d435", size = 12896420, upload-time = "2026-09-06T16:26:31.265Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ee/a7cbba67eeaff038dc29ca8b98a88396c8b0cc9c89d4924f4a27a5c9150b/numpy-2.5.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2d8240cb4c16fd831074aa2b2cf9fc54664d826341d61c372245b96a74a49a9a", size = 10857177, upload-time = "2026-09-06T16:26:34.167Z" }, + { url = "https://files.pythonhosted.org/packages/45/56/78194492883ff5eec90423fe56a3a44b154da047d88a6307f629713c584f/numpy-2.5.3-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a6391fafaba97500887132cd582abc6e19452b1ac775a47caa7b24490e152058", size = 16996531, upload-time = "2026-09-06T16:26:37.287Z" }, + { url = "https://files.pythonhosted.org/packages/11/39/dd55c0af90bbab564b09ae3b0aa60ec5c02b900fa4f1ba23440525c8b32d/numpy-2.5.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:09d5a423c71ad5feb5625844ad58050e35df43871004b52ac9c0ad44a56775be", size = 12012569, upload-time = "2026-09-06T16:26:40.707Z" }, + { url = "https://files.pythonhosted.org/packages/b6/51/04f67d32e4862b281b1cb84ceeaed3421189a84fb6fb51a391cd6d5009f7/numpy-2.5.3-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:f9579f383d1bf9df80081e72760e84960a7fd4f88cf0c9e535a8597c9bb646f5", size = 5448498, upload-time = "2026-09-06T16:26:43.435Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c9/25b4dc0dd1344ec26c7319e84fd4e9809d2b5628f4e12decd618036e5178/numpy-2.5.3-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:86bff898a431c0fb71f7610b75726e75a54d47b37edc9d537f48de63bb3c0b90", size = 6783026, upload-time = "2026-09-06T16:26:46.374Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c7/29285be1e5232a6e7ee3268a33c85843f5a8ee93350c6465cddd66ebbf76/numpy-2.5.3-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f3ed25271581281f2fccb1adcedfcde4c07362eec69189b50baf6f90e3ae159", size = 15697322, upload-time = "2026-09-06T16:26:49.415Z" }, + { url = "https://files.pythonhosted.org/packages/55/49/bbad5335fb4996a16881f853ff3e0ba582f01720e55c89b1c06b8fc42a90/numpy-2.5.3-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffdc76bfcae6b255dff75202c5e7feaf95b40246bc0a17944facc1fecf9f79ab", size = 16708995, upload-time = "2026-09-06T16:26:53.127Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e9/1df35483760b04a65ea44669f89dc64f30e5aca098b48ceb8b1310b0e0fe/numpy-2.5.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:116f96cadd935c6122e9228d676fe7ede19e741f5c8bb1c3cddbe0c51ccebea2", size = 17052508, upload-time = "2026-09-06T16:26:56.464Z" }, + { url = "https://files.pythonhosted.org/packages/b8/99/66e54da8265cc8be8a7382bf96edce17aaa2837d6f484432025932a3caa5/numpy-2.5.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:09ffa5d903faeaa5c4dd05009cf81c8bab9f2cb37c548b8d39b65b4cfa7c97f7", size = 18468224, upload-time = "2026-09-06T16:26:59.966Z" }, + { url = "https://files.pythonhosted.org/packages/01/bc/b5e90a91c115168d793dfd2ad9c69c438c2fe7a13a437e770bc5b078e732/numpy-2.5.3-cp315-cp315-win32.whl", hash = "sha256:e01c918ac3d48e18a927cf7b14a26a3e29ff2bdf2eacb976da0aecd6a43ed034", size = 6179919, upload-time = "2026-09-06T16:27:03.166Z" }, + { url = "https://files.pythonhosted.org/packages/37/ea/780748fd3985109075514ef8fc64cd25f943e40dde13a6d59141eb268fc8/numpy-2.5.3-cp315-cp315-win_amd64.whl", hash = "sha256:e931e4f499e0dc7ef29d269a8e5b35dd722e5d14be07df6240166ea7c6532fae", size = 12697656, upload-time = "2026-09-06T16:27:06.153Z" }, + { url = "https://files.pythonhosted.org/packages/b3/16/407be69a2a87c8cab64d95975a8977a426a29e138f07e276ec258f0fe4e5/numpy-2.5.3-cp315-cp315-win_arm64.whl", hash = "sha256:26e15e4aecd8617dfbaecb37d223e365d7b39411fba20454be2670a96aa74cb5", size = 10767601, upload-time = "2026-09-06T16:27:09.297Z" }, + { url = "https://files.pythonhosted.org/packages/44/bf/a97ffb01e41d50a32a9177aef942a4d0e389a3daf451d04e5f38ef6afb87/numpy-2.5.3-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:6cef4bb1706dfec49243c05d921eefb4e190d41e2528b30d8035ea1f36b4c24a", size = 17090092, upload-time = "2026-09-06T16:27:12.907Z" }, + { url = "https://files.pythonhosted.org/packages/d1/24/136c02f2c2af9a067a84d0c3aa10c99012c0476fa5066732fa4a4202557d/numpy-2.5.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d1c89973648c85069c5046ad460f7b8a00218b29a2e42359ac8cc63e9ab94832", size = 12129429, upload-time = "2026-09-06T16:27:16.089Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6c/b47582d6597789bf946d5efbeb6b9e56fd8bcbd5efc6fbf51dbe1ea31eb3/numpy-2.5.3-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:214045a5bf00113a146ab9ee9730c44501af6723cdf1f6830932f7b5ef2e7af0", size = 5565452, upload-time = "2026-09-06T16:27:19.868Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/ef3cc6da73774202d4deae16bb321fd8298a4e0561e3539f8c4be237d916/numpy-2.5.3-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:8617bbfae4486cf99c9f899966699428d19da931d06ca94ad3da986c76e15997", size = 6876736, upload-time = "2026-09-06T16:27:22.232Z" }, + { url = "https://files.pythonhosted.org/packages/9e/24/e3813329498596cb842703dcacac1741612ed9fb9c4e6a3e0c7e2ebbc597/numpy-2.5.3-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:595d020938c84e320bcf40ad71089e108eac0d377cd018e14a8c094f39e98d85", size = 15745777, upload-time = "2026-09-06T16:27:25.181Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9e/4e7a07fd0776dc2210cdacf2010be8665194d094defc10c419d7dea794cc/numpy-2.5.3-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f24021b9f22bc6301c37b196974a92c1c18dccedb6fef3dd252e95f2d6adbe4", size = 16746949, upload-time = "2026-09-06T16:27:28.576Z" }, + { url = "https://files.pythonhosted.org/packages/91/db/01674c0e20335057813a00c2ebd546ed25bff9ed7914f9bced00f8c55d94/numpy-2.5.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:71b39d9f935b6ec0f8753e3e2afb51e3efba6f2e05b68b32a40754d24bcd4a3c", size = 17108994, upload-time = "2026-09-06T16:27:31.946Z" }, + { url = "https://files.pythonhosted.org/packages/45/7a/584c5e71f8d378e57cac0b033891ed65c683ef90573ba4854e8c28203db0/numpy-2.5.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:6b05c171afb3aa07adbd20abc00aea86fe375beb0fdb9ef780ec5b7f63bab1c0", size = 18512266, upload-time = "2026-09-06T16:27:35.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d2/4e1014173aa3c55e6a756e0e567290743a6ab33a288460374d7ef6bcd239/numpy-2.5.3-cp315-cp315t-win32.whl", hash = "sha256:f54660b0eb6b0b9f36e7fe1cdfdff472028dd0d14acd9b9b65098efbad059469", size = 6330292, upload-time = "2026-09-06T16:27:38.149Z" }, + { url = "https://files.pythonhosted.org/packages/6c/b0/ff5658a58199b7bcaad87bf260eef6713d9d42cca4e028f935b4fc5fbac6/numpy-2.5.3-cp315-cp315t-win_amd64.whl", hash = "sha256:1aad64d99730d013cfc6debafed22783b4fc5a7f4b8bc744d2d8cf7dcc880551", size = 12884918, upload-time = "2026-09-06T16:27:40.965Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0b/b12a2df5d1b774bd9007a6fdff9381145b6223d37f11afc9c37ab0efd9a1/numpy-2.5.3-cp315-cp315t-win_arm64.whl", hash = "sha256:befa1ae5bd6030b3f512b43ff3fa5290bbed6b84411a44244b14adf835f5b89d", size = 10850807, upload-time = "2026-09-06T16:27:43.868Z" }, +] + +[[package]] +name = "openbb-charting" +version = "3.0.0" +source = { editable = "../../obbject_extensions/charting" } +dependencies = [ + { name = "openbb-core", extra = ["pandas"] }, + { name = "pandas-ta-openbb" }, + { name = "plotly" }, +] + +[package.metadata] +requires-dist = [ + { name = "openbb-core", extras = ["pandas"], editable = "../../core" }, + { name = "pandas-ta-openbb", specifier = ">=0.4.24" }, + { name = "plotly", specifier = ">=6.3.1" }, + { name = "pywry", marker = "extra == 'pywry'", specifier = ">=2.0.0" }, +] +provides-extras = ["pywry"] + +[package.metadata.requires-dev] +dev = [ + { name = "openbb-devtools", editable = "../../extensions/devtools" }, + { name = "openbb-technical", editable = "../../extensions/technical" }, +] + +[[package]] +name = "openbb-core" +version = "2.0.0" +source = { editable = "../../core" } +dependencies = [ + { name = "aiohttp" }, + { name = "fastapi" }, + { name = "importlib-metadata" }, + { name = "pydantic" }, + { name = "pyjwt" }, + { name = "python-dotenv" }, + { name = "python-multipart" }, + { name = "requests" }, + { name = "ruff" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "uuid7" }, + { name = "uvicorn" }, + { name = "websockets", version = "16.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "websockets", version = "17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] + +[package.optional-dependencies] +pandas = [ + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.13.3" }, + { name = "fastapi", specifier = ">=0.136.3" }, + { name = "importlib-metadata", specifier = ">=6.8.0" }, + { name = "pandas", marker = "extra == 'pandas'", specifier = ">=1.5.3" }, + { name = "pydantic", specifier = ">=2.12.3,<3.0.0" }, + { name = "pyjwt", specifier = ">=2.13.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, + { name = "python-multipart", specifier = ">=0.0.29" }, + { name = "requests", specifier = ">=2.34.2" }, + { name = "ruff", specifier = ">=0.15" }, + { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0.0" }, + { name = "uuid7", specifier = ">=0.1.0" }, + { name = "uvicorn", specifier = ">=0.48.0" }, + { name = "websockets", specifier = ">=15.0" }, +] +provides-extras = ["pandas"] + +[package.metadata.requires-dev] +dev = [{ name = "openbb-devtools", editable = "../../extensions/devtools" }] + +[[package]] +name = "openbb-deribit" +version = "2.0.0" +source = { editable = "." } +dependencies = [ + { name = "aiohttp-client-cache" }, + { name = "aiosqlite" }, + { name = "openbb-core", extra = ["pandas"] }, +] + +[package.dev-dependencies] +dev = [ + { name = "openbb-charting" }, + { name = "openbb-devtools" }, + { name = "openbb-platform-api" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp-client-cache", specifier = ">=0.11" }, + { name = "aiosqlite", specifier = ">=0.20" }, + { name = "openbb-core", extras = ["pandas"], editable = "../../core" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "openbb-charting", editable = "../../obbject_extensions/charting" }, + { name = "openbb-devtools", editable = "../../extensions/devtools" }, + { name = "openbb-platform-api", editable = "../../extensions/platform_api" }, +] + +[[package]] +name = "openbb-devtools" +version = "2.0.0" +source = { editable = "../../extensions/devtools" } +dependencies = [ + { name = "codespell" }, + { name = "httpx2" }, + { name = "ipykernel" }, + { name = "pre-commit" }, + { name = "pydocstyle" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-freezer" }, + { name = "pytest-order" }, + { name = "pytest-recorder" }, + { name = "pytest-subtests" }, + { name = "ruff" }, + { name = "tox" }, + { name = "ty" }, + { name = "types-python-dateutil" }, + { name = "types-requests" }, + { name = "types-toml" }, +] + +[package.metadata] +requires-dist = [ + { name = "codespell", specifier = ">=2.2.5,<3.0.0" }, + { name = "httpx2", specifier = ">=2.0.0" }, + { name = "ipykernel", specifier = ">=6.30.1" }, + { name = "pre-commit", specifier = ">=3.5.0,<4.0.0" }, + { name = "pydocstyle", specifier = ">=6.3.0,<7.0.0" }, + { name = "pytest", specifier = ">=9.0.3" }, + { name = "pytest-asyncio", specifier = ">=1.3.0,<2.0.0" }, + { name = "pytest-cov", specifier = ">=4.1.0,<5.0.0" }, + { name = "pytest-freezer", specifier = ">=0.4.9,<1.0.0" }, + { name = "pytest-order", specifier = ">=1.3.0,<2.0.0" }, + { name = "pytest-recorder", specifier = ">=0.6.5" }, + { name = "pytest-subtests", specifier = ">=0.11.0,<1.0.0" }, + { name = "ruff", specifier = ">=0.15,<0.16" }, + { name = "tox", specifier = ">=4.11.3,<5.0.0" }, + { name = "ty", specifier = ">=0.0.40" }, + { name = "types-python-dateutil", specifier = ">=2.8.19.14,<3.0.0" }, + { name = "types-requests", specifier = ">=2.32,<3.0.0" }, + { name = "types-toml", specifier = ">=0.10.8.7,<1.0.0" }, +] + +[[package]] +name = "openbb-platform-api" +version = "2.0.0" +source = { editable = "../../extensions/platform_api" } +dependencies = [ + { name = "deepdiff" }, + { name = "openbb-core" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] + +[package.metadata] +requires-dist = [ + { name = "deepdiff", specifier = ">=8.6.2" }, + { name = "openbb-core", editable = "../../core" }, + { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "httpx", specifier = ">=0.28.0,<1.0.0" }, + { name = "openbb-devtools", editable = "../../extensions/devtools" }, +] + +[[package]] +name = "orderly-set" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/88/39c83c35d5e97cc203e9e77a4f93bf87ec89cf6a22ac4818fdcc65d66584/orderly_set-5.5.0.tar.gz", hash = "sha256:e87185c8e4d8afa64e7f8160ee2c542a475b738bc891dc3f58102e654125e6ce", size = 27414, upload-time = "2025-07-10T20:10:55.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl", hash = "sha256:46f0b801948e98f427b412fcabb831677194c05c3b699b80de260374baa0b1e7", size = 13068, upload-time = "2025-07-10T20:10:54.377Z" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/17/d7b106e05bfa642e8694451e7d3d759c6a241c5386a5d962e4f66c047e06/pandas-3.0.6.tar.gz", hash = "sha256:66b07ef7315a31bfe1089cd3d71a7de781c9dca986762d0b4fe7c0ef17465d10", size = 4667686, upload-time = "2026-09-17T23:23:18.345Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/48/88e8d250d28efa8163294f6809a71683c7ee67f63ac7c33021c0503b3547/pandas-3.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:085e3786ae6b2e82b406266bce36690f72b9dc1421903ba9296b2981a9fcf586", size = 10391798, upload-time = "2026-09-17T23:20:20.96Z" }, + { url = "https://files.pythonhosted.org/packages/55/a6/39db5d41f3eb5d7846626312cb30e0cea48e988fc47a3725698bc931ad3c/pandas-3.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7564d86a94c2eb8ab290b07f63ddaae5c032fa53897c29a2ff2197d43aee8af", size = 10023718, upload-time = "2026-09-17T23:20:25.094Z" }, + { url = "https://files.pythonhosted.org/packages/54/b7/707e966129f77ee8a39d41a41bbd989b790b3fef581c6e26b821315cba6b/pandas-3.0.6-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7c0afdcaf6661d795fcefc2f647ddd1136f62cdc153fba177c685d97a87808", size = 10612800, upload-time = "2026-09-17T23:20:27.99Z" }, + { url = "https://files.pythonhosted.org/packages/63/be/dfb6cc9329d0bbe76dadda8a1113d026bb996ec76c509221368dc68349f4/pandas-3.0.6-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47121f9571503f724c9b93e297ab6254ac99c77adf5e9ed085ea419fd585c258", size = 11108900, upload-time = "2026-09-17T23:20:30.65Z" }, + { url = "https://files.pythonhosted.org/packages/1a/6f/3d58f15bbe972f3d7bfa13ee06d731785a76fe8d232c92b2655a8de14127/pandas-3.0.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:994a79608263fe1c14cc48ffa7300e2b834b7d1cb406ffe96a08828cb0cdd79b", size = 11630226, upload-time = "2026-09-17T23:20:33.582Z" }, + { url = "https://files.pythonhosted.org/packages/58/54/9b494de4a3dd92fc6eb19187db1b21afb50fdc21962f32ea1aca9f270cb2/pandas-3.0.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a3a22e07fe75347eaacc75b0e85297947af4fba6b4aae23916bd8b6828d0bba3", size = 12146942, upload-time = "2026-09-17T23:20:36.792Z" }, + { url = "https://files.pythonhosted.org/packages/d3/dc/d2df02854aec5d47659acfb2be352eecc691845b2f86e99c84f1010a8671/pandas-3.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:2e5fa32ff162dfdbc280157d664f44d23049ae414725af9676df339c501d82cd", size = 9859246, upload-time = "2026-09-17T23:20:43.976Z" }, + { url = "https://files.pythonhosted.org/packages/79/1e/2a30df0d7dede5c195300a1820b0bc21cea3aa24e4c8b6c4431565ecc79a/pandas-3.0.6-cp311-cp311-win_arm64.whl", hash = "sha256:5e75072773c1b2f7cb63faa3a6f562aede11f3976f68ed34cb538bc091a28171", size = 9106923, upload-time = "2026-09-17T23:20:46.611Z" }, + { url = "https://files.pythonhosted.org/packages/77/4c/597d588c055d4373cff19cbbc32d4dd046c7be8fadee957585b5ba9e5b24/pandas-3.0.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dac2d65e9087e8e7b5a45fe15c4920911a221df061ab629943ce016489145c7", size = 10397411, upload-time = "2026-09-17T23:20:49.465Z" }, + { url = "https://files.pythonhosted.org/packages/18/8f/48907c7c707b61a8e5018c32e1a3f70623209bfb59020a2f6196159d3aa7/pandas-3.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9dab635a549e58a053c7b0fa054dc0bd7be22f0ed9a720f4a85d5fb993276172", size = 10051279, upload-time = "2026-09-17T23:20:52.409Z" }, + { url = "https://files.pythonhosted.org/packages/67/fa/613d867c3d9554a61bafdec6f79565c8a3e73235feb52cc4a72ad2e0fa6a/pandas-3.0.6-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3dccb584123b399c07562ac4d62543e90ede49ddf8ce3c13ffc64cbe828c281", size = 10303429, upload-time = "2026-09-17T23:20:55.597Z" }, + { url = "https://files.pythonhosted.org/packages/cb/67/0c0f18e38d7f2d2af8c24b3315bc4046e73bbdd4a5540405506671ad0c0d/pandas-3.0.6-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0704044b676496b8350e023b09f174a26772456c974a2b11c36bebb558c9490d", size = 10788193, upload-time = "2026-09-17T23:20:58.617Z" }, + { url = "https://files.pythonhosted.org/packages/39/53/1b57f3162501fe36687e4870e1b918a6458ca7386b173af75663ace95857/pandas-3.0.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e7c1905ef02c3d6d43d9dbd5b6ccb4da4870a0b0c821bbc103fbdb6f3ad2707b", size = 11323501, upload-time = "2026-09-17T23:21:01.911Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d2/b1182e8d39100369d7f13f4a125a3fb6b096fef112c46d0566c25781ff68/pandas-3.0.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:569e114072b24fc4970c12e2b4bab252671668a40b324318903380cab0254c0c", size = 11838173, upload-time = "2026-09-17T23:21:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/c7/33/5b717af24d2f27995e51e0875a269dddd373045216e34cf62e3aa764eaa1/pandas-3.0.6-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:2a8fc94be2ee5f1d86f97aacd8cc566f81680b6498e76f3007421bb5d98151bf", size = 7118499, upload-time = "2026-09-17T23:21:07.661Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/14b3b17cd75cef4a1ee1af4234eb41cc1afe4103b98c2d80e8916abfd42b/pandas-3.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:3ef908d28590b3f42d7070e7ad8f9b34b442b260b7f3c1afb57e0040c58cdb1b", size = 9661439, upload-time = "2026-09-17T23:21:10.959Z" }, + { url = "https://files.pythonhosted.org/packages/3b/11/3d580a604a1e35d69f6676847bd12db7d14344bc677b717f4413e79c5d0d/pandas-3.0.6-cp312-cp312-win_arm64.whl", hash = "sha256:f4e7c52eb108d752e7592268108fd3e98efd76d83a3125cdd06c621c2e44359b", size = 8959806, upload-time = "2026-09-17T23:21:13.851Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/143605a1f6443ad50ebda78a31e5a3a10147fec2590e931584aaa5ff0a09/pandas-3.0.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ae8073aed8e21d1a7fe263dcdc6840743549722a6738198a0a46000fa9476f2", size = 10418900, upload-time = "2026-09-17T23:21:16.594Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ca/87f8548f73d452aab35e4a90f8b39ae303295e0f2ef0b4055c44d6b3f1be/pandas-3.0.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:60d81f9e1799b36f3739e7fff44d1fbb2e8fd5a271b3863e03de9715fccda0fa", size = 10064785, upload-time = "2026-09-17T23:21:19.677Z" }, + { url = "https://files.pythonhosted.org/packages/43/1a/d951442e5607c6e3b2462eff8f420797d428aa74b87c6ecfe4f48553626e/pandas-3.0.6-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:097090508a1dd335013d39106fc10b20f4fd4a171638e47b77d55798ed9dab6c", size = 10245290, upload-time = "2026-09-17T23:21:22.797Z" }, + { url = "https://files.pythonhosted.org/packages/50/fa/96d50e1e6cd0b08b5e2b7c838f65ae644940f75a124063380b5ef73b6866/pandas-3.0.6-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e92d9fa834c7d877130027cddc0cad8dcff97c1f6cca26bd6310f847228b658", size = 10757657, upload-time = "2026-09-17T23:21:25.673Z" }, + { url = "https://files.pythonhosted.org/packages/7b/12/f82d13a2cb703e1a8acee7e01fdc2b898d9cd0c00f07d1dfce63af43e350/pandas-3.0.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b27c8d890e4aa2171437ae2a39de1d215e674158e4865c4023a8b31c932513b2", size = 11249114, upload-time = "2026-09-17T23:21:28.898Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ce/8aef2e561a2f2c8b38c913c67373c65ba6748174e763d27c80271b24bd17/pandas-3.0.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8029ec0f1f89e4f985929ce1f6626dabf3140d61a4e9c1215afdab34eaf9a5d", size = 11820511, upload-time = "2026-09-17T23:21:32.11Z" }, + { url = "https://files.pythonhosted.org/packages/c0/bd/63cb67e6903ef6d9c2871916dbcbc09d254da0fe8b870cf62e16b21945f2/pandas-3.0.6-cp313-cp313-win_amd64.whl", hash = "sha256:f3ce8a6968045481e91a3990e797e348ce13db45ee164a7095bbc824e26c09dd", size = 9638092, upload-time = "2026-09-17T23:21:34.883Z" }, + { url = "https://files.pythonhosted.org/packages/75/2e/e7b35b712edb068d382ddc8b2bea8a04974100515ba2daa22b478b265842/pandas-3.0.6-cp313-cp313-win_arm64.whl", hash = "sha256:cc39303913e2ea129915670de5d1c9fbd647f543bb72e5543bac8baa94e9e42f", size = 8952032, upload-time = "2026-09-17T23:21:37.729Z" }, + { url = "https://files.pythonhosted.org/packages/75/55/1a8875395b05ccd572cbca0b9255dcd2db6e6508e632a558c1a6884b39ad/pandas-3.0.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ee913a91669056c1de1a6b733fbfeab711de9e54e3bee2dfa5fe79d9457247d1", size = 10492213, upload-time = "2026-09-17T23:21:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/35/61/47ae13476995cc8a40cd609e93e7cf11f273d8692925c2903cb6d38aa0d1/pandas-3.0.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ff51a4459ed036e93d1eb1bb5e6e7b28685d3cb6b7c12b91c05b31024e234729", size = 10156618, upload-time = "2026-09-17T23:21:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f2/cc5f2adb8d6e86a85d9fb5128f8cf205a61189336f70d1f7faf0d1b53ec9/pandas-3.0.6-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654aae059295dbba6ecd2328ca12712a2cf1676214c8699f1c29213f7ccf9c34", size = 10375479, upload-time = "2026-09-17T23:21:47.159Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ba/ffdcb19be4ff6bfe7d969e7cef2c567c633df5a3a1cc1053394ad053bca8/pandas-3.0.6-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f51d7f651c8054c5e82a69265c98082e795d1442df7ca6edc3a545d61214b1", size = 10783244, upload-time = "2026-09-17T23:21:50.367Z" }, + { url = "https://files.pythonhosted.org/packages/77/5b/e150075b2c6eb69fae896f2d9239bc6ed07db97735971d53d66de6553460/pandas-3.0.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:22172a92e7ee678ec0140c7af4fc9366b55413834a1cd86af78b3caa0b0574de", size = 11382223, upload-time = "2026-09-17T23:21:53.355Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8a/b441c587dc7355bf6e1f68a91b4f76a6c29740f0c23be3acc5d4ebbeea6d/pandas-3.0.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583be68728a31d0d750d5b8d9e00f02b153df0d4655f858bde93cb84cfc4227c", size = 11854881, upload-time = "2026-09-17T23:21:56.342Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e9/f43410fada510b43fec09993c08f552086c3d247d3ee801a678f3cb10ea5/pandas-3.0.6-cp314-cp314-win_amd64.whl", hash = "sha256:77ccbe5057aece6fc172b9b77f19c04335af6882bc2e10c8f3ee4e6bfb3da553", size = 9791672, upload-time = "2026-09-17T23:21:59.332Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9e/db14c059c21f9baa1907d436f8bf30e0c76c6288225c5e8b79a08ba8b2c5/pandas-3.0.6-cp314-cp314-win_arm64.whl", hash = "sha256:fb625f426b375bcc96e3a04c5d5d266cd7be6ae5d6866e0e703382ab5164068c", size = 9121246, upload-time = "2026-09-17T23:22:02.123Z" }, + { url = "https://files.pythonhosted.org/packages/67/ba/bad0f8dac020ab38a8637fddab01a57a82da7a496a6e6f19590aad53ab62/pandas-3.0.6-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9e492cd4bdba6778de4fe0df7f4590c012161ebcf9902dce01b01dc683105514", size = 10920612, upload-time = "2026-09-17T23:22:05.404Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a9/b500982e9aac6d52a58da4ad3f11e14168a315b06906b3f397c427878065/pandas-3.0.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d7dcd21238cbb4828ff148481ba01cac8946dc5121457b5aeba28636f8f99a60", size = 10570213, upload-time = "2026-09-17T23:22:08.44Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/e6ecd0073c98be8f840ac3125b955272835d7d9fd69f5944383b164deb5e/pandas-3.0.6-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff482fa91fa2bafd92e8fe66ce3645c851824310f295c1f0a2f96e928fc4541", size = 10252356, upload-time = "2026-09-17T23:22:11.302Z" }, + { url = "https://files.pythonhosted.org/packages/04/f5/001e230a7a7803590d9275a1a3f7e1bb605e3a495cfe5e8d3a532090621b/pandas-3.0.6-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:db7ec631f26223beee8e5c9e0b8f23c24d8197bbd1d982421d4e3188bea51965", size = 10655952, upload-time = "2026-09-17T23:22:14.283Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ab/bab587148a3852c96aae26c4b5f9e04ce2221801ad94e166b4fbf969ede0/pandas-3.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd75ed0c840f709fc2ae26ddd9534ac77ca1a48ac0cce521a74acaa85f3340a7", size = 11274177, upload-time = "2026-09-17T23:22:17.352Z" }, + { url = "https://files.pythonhosted.org/packages/f3/32/74b48d87df2b80892d713c149abfe36d5db4de41b4eccb042a2bc07dafc1/pandas-3.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ef738d71d1059245b6bb03e312be06d8b3821326a83486c1ad03b9aba3710e44", size = 11719041, upload-time = "2026-09-17T23:22:20.227Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c6/d64b72d64d7eb0fad9fe424d34138e45dee70ddbea1360dcd0adf30e28f6/pandas-3.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:429d9df32731ab01383ed98f2baa7a60368090d1a94fc06019a12062510e8630", size = 10191388, upload-time = "2026-09-17T23:22:23.524Z" }, + { url = "https://files.pythonhosted.org/packages/7a/30/5e5b2ccabeca73ae2b03fc82bca3eabb7466cf43737f05ac08d591665d47/pandas-3.0.6-cp314-cp314t-win_arm64.whl", hash = "sha256:a4dbd4dc65cbe645b92b8785d0f96dd7311010dc6606cf620e51b07b8788a12a", size = 9387796, upload-time = "2026-09-17T23:22:26.64Z" }, + { url = "https://files.pythonhosted.org/packages/b6/77/47c5fb0be8bdd00116814c2c40d9ec42dbeb943865ef95130fe58a898226/pandas-3.0.6-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:50c44cbf5820b6b91a5f74aae04972472aefadd3cd9fbd1010409d85528bd570", size = 10483960, upload-time = "2026-09-17T23:22:30.071Z" }, + { url = "https://files.pythonhosted.org/packages/09/08/a310cb2fefe6d2b4623ab2150da818d93d4e73e33b58e4d163bb74243c5a/pandas-3.0.6-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:eb6900de08ac85f93ac4948aa6b80842eba555875337b8359035ac9c43e92d34", size = 10149432, upload-time = "2026-09-17T23:22:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/54/36/6af478ec3a26d7754555cd62c3101c589c0931b1d85398e4fa5403910a1c/pandas-3.0.6-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e25e2e1adee99ddfada6f7206a79ae8e9c8a8861b0e3eaaba165006d3eef18e", size = 10360981, upload-time = "2026-09-17T23:22:35.621Z" }, + { url = "https://files.pythonhosted.org/packages/43/20/5ece0e9cb79a6473216620db6a90546f578001c2bd857b77c444b27acad1/pandas-3.0.6-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ff44b2cb51cbd691c91f92c4ea6c71e34003f239ebd67c2e857dc898466b49c", size = 10776585, upload-time = "2026-09-17T23:22:38.427Z" }, + { url = "https://files.pythonhosted.org/packages/2f/b6/cd3038f31ade5e8d2b4e1c9549d4b31e4d598469562f47142b2ad9171c0a/pandas-3.0.6-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:5edd0a7abb0986ecce1ac81f56d99b6763f86aa6946dceb6c661224f90af5a19", size = 11385249, upload-time = "2026-09-17T23:22:41.18Z" }, + { url = "https://files.pythonhosted.org/packages/0a/87/05bb3003737f80375d7311774916a24d161e2e591abe8c672aed7813defc/pandas-3.0.6-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1bcb3e9ed29e74a7439cedff9e2aefd3ea65de84d7de9ccb6c194192541bd60e", size = 11848595, upload-time = "2026-09-17T23:22:44.207Z" }, + { url = "https://files.pythonhosted.org/packages/c0/30/1c0d46acf236d19ef975e9cdd5d1e0dd54924b03f0ed8287096ad4a18152/pandas-3.0.6-cp315-cp315-win_amd64.whl", hash = "sha256:253e12cb9081b0afbac607920f6142975966bc315135e09de275fdbaa415d2de", size = 9783810, upload-time = "2026-09-17T23:22:47.097Z" }, + { url = "https://files.pythonhosted.org/packages/87/03/df3304a9c2833c4810e7f1c887b04105b24731f9deef8b5d5d04522a375b/pandas-3.0.6-cp315-cp315-win_arm64.whl", hash = "sha256:97274c9adf6255bb48c620cd6959805efa7f09ea2167f0e0ae006a448cd2fca7", size = 9116820, upload-time = "2026-09-17T23:22:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/47/25/d5f6cce5efa17c38e4f752a875b4a26d66cfadd529cb8c81672f0376d6a6/pandas-3.0.6-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:265f562fdd1079f69f3de96dd425c3405224038c0af4f920c54bd240ee2c4640", size = 10894614, upload-time = "2026-09-17T23:22:53.223Z" }, + { url = "https://files.pythonhosted.org/packages/ea/8b/e1876bfdc1df06bafc022d33202b5663bd86c81df2a6140aacafd0344669/pandas-3.0.6-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c6e4aae3e9bea26c6c9a20d88d96c86ec4a99b4db5fd516bcb4e829ab2c0ee36", size = 10546973, upload-time = "2026-09-17T23:22:56.155Z" }, + { url = "https://files.pythonhosted.org/packages/e9/27/e0a27a5a5c27f7db66657b44def9e93121fd0ad4f0808355b9898fcbf204/pandas-3.0.6-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a77a1a44e4d88f1c6a2a64d3eb12efec8420875722e14279800b173a7c7c2804", size = 10239717, upload-time = "2026-09-17T23:22:59.603Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4f/4eadb7d86a921c1e8bc70916cfe601ed667c4169118d17d69958611c21f8/pandas-3.0.6-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86fa853a12e0b70927e2b1ee00d56d2224ec9cbb4b9d58348b5ad52d2f21150e", size = 10641275, upload-time = "2026-09-17T23:23:02.81Z" }, + { url = "https://files.pythonhosted.org/packages/c7/d1/eba72e9d905e84e79aefeefdcc6bc1abe15d9077973566643f4211636966/pandas-3.0.6-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:c826e9babb7790142c399f58599d8de679bea059d7b39c5b6efa2096fac37266", size = 11262919, upload-time = "2026-09-17T23:23:06.038Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8a/1c5bd2642b450e374b6191189f47c49538fe41f82348a66de6f647e6ab59/pandas-3.0.6-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:8fe77b408d82e2615674dfed62533b95e18a03610573877422aada4f625d4947", size = 11709148, upload-time = "2026-09-17T23:23:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/73/3d/1b142bd0d0f1326a5d98c91c955b923cd1e06b5eecb428cd9d8817fa01c0/pandas-3.0.6-cp315-cp315t-win_amd64.whl", hash = "sha256:83e91d15738d7783c050197cef2f2cf82fc6353dae9865aa87ed1fa16aa4d55a", size = 10161518, upload-time = "2026-09-17T23:23:12.365Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a3/6419c14da2adc1f09a6a183b8f91d7494d325b287f4ca984ac04f663638a/pandas-3.0.6-cp315-cp315t-win_arm64.whl", hash = "sha256:963ca21199097a84c7827c4678b04e30833084fbf8ef44fde3fa7180a29f8fa0", size = 9364529, upload-time = "2026-09-17T23:23:15.274Z" }, +] + +[[package]] +name = "pandas-ta-openbb" +version = "0.4.24" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "scipy", version = "1.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "statsmodels" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/b3/c8812123c1463480085929aebfeab0f2400cce2c0803223dcfe51f1af7f2/pandas_ta_openbb-0.4.24.tar.gz", hash = "sha256:69ff98beccbbe0ab238371af25a5c0be9f123aa474888cef74892441c353da1b", size = 135689, upload-time = "2026-03-27T08:24:46.585Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/60/d785cb8519613fa50c4cda58cdcabb8078dfa414c3cb3663acebd1a4da04/pandas_ta_openbb-0.4.24-py3-none-any.whl", hash = "sha256:0e6538af573c9ab84a6b13c965ccfc0a930974b876f90473e71b49dcb7a38920", size = 259501, upload-time = "2026-03-27T08:24:47.915Z" }, +] + +[[package]] +name = "parso" +version = "0.8.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824, upload-time = "2026-05-01T23:13:02.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025, upload-time = "2026-05-01T23:12:58.867Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "patsy" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/e3/950e513affdfe1c1864397b4f40d6ed06ec1b54f608e462c27f151dd72df/patsy-1.0.3.tar.gz", hash = "sha256:79ebf4c93ff4d296e58a9d5be2b2ee31bd49d737cf11d70ffbd8a44b2de42e65", size = 399992, upload-time = "2026-08-29T21:35:32.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/70/cd3cf5cff538323076a879edef02cce6f13f7d75e03d12f211b0a0dbd178/patsy-1.0.3-py2.py3-none-any.whl", hash = "sha256:d3dbebe8fd5f46e29912d030b63c6268647b59bf788a99e2af28a30234cf357c", size = 233318, upload-time = "2026-08-29T21:35:30.913Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.11.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/dd/65804b0c2925a1c821a05502ea57517b69a073ff400d25ab9faa3a2cf012/platformdirs-4.11.12.tar.gz", hash = "sha256:e8dc1cb58f1153fd7f61db1374317770baababec2480b37b8f01c6cc25b45267", size = 40016, upload-time = "2026-09-22T01:02:36.973Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/2b/69de6317fbf48865bb12aa56c9cfcfff46b3add0cd45250725d96146dce0/platformdirs-4.11.12-py3-none-any.whl", hash = "sha256:b1ba966ac76153d9985a0787ffab951e7c18416189873d4e4ddb2c8a04a0a254", size = 24976, upload-time = "2026-09-22T01:02:35.742Z" }, +] + +[[package]] +name = "plotly" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "narwhals" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/c3/72b369f5ed7701b04ab0ea3dcf83e9bbce71c0b3bc6f07f87568550d09ea/plotly-7.1.0.tar.gz", hash = "sha256:f860166a4a3d78c69cb1f4a15f28a5c8283eade98a282a698f3bb853a449ace5", size = 6689315, upload-time = "2026-09-15T19:21:21.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7d/905a3a3d51087515719058c94cfbda2ff0fc14417c20d557ae3e82d8b250/plotly-7.1.0-py3-none-any.whl", hash = "sha256:dbb7fa18afce40d0a8e80d1bf162eceb3faa0ce5a77fe741ad09a74cf78f53f3", size = 9692368, upload-time = "2026-09-15T19:21:18.331Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815, upload-time = "2024-07-28T19:59:01.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643, upload-time = "2024-07-28T19:58:59.335Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.53" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ea/39b988c938f75cb75d7045b5c69f8bfed47ee2152c8837fb403de29d6fb8/prompt_toolkit-3.0.53.tar.gz", hash = "sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6", size = 435492, upload-time = "2026-07-26T20:56:14.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl", hash = "sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2", size = 392288, upload-time = "2026-07-26T20:56:12.512Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/9a/9fbf4e4ec0c2d7f1c32519fff782ef467859b8faa9fbc5331a96f6395d43/propcache-0.5.4.tar.gz", hash = "sha256:ff6b113f50bc066a698db5d944d2c6dc7507168dd3341e255a8892fd0715a558", size = 61545, upload-time = "2026-09-16T00:17:14.386Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/997704118aea215cc5f65c277f5323657b4ba44c1f9a32fb11c8064e4997/propcache-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b77c313314524ca9c38fbd70f73515d04597ac58c40c939bc0e71eeb4abff680", size = 87225, upload-time = "2026-09-16T00:13:43.864Z" }, + { url = "https://files.pythonhosted.org/packages/3e/6d/ceeca1762ed51230c08d557591404f844ecef5e294550136155572f7faae/propcache-0.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8f911c395cef73c510bac566da9507bb6a43e7763d0c79138dc60ee53f11207e", size = 50809, upload-time = "2026-09-16T00:13:45.33Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9a/6be90814d8762952594a9e380161802541bff690f3a2e011dcc28ec193ce/propcache-0.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d83b12902eb8bce151259c86c03ba746600b2d994543de46e370cecf96c452f2", size = 52552, upload-time = "2026-09-16T00:13:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d1/7fd3ffb5ca0e669a8fbf55d9fbb17c50ff5f44becfefcab27a9b9b67908f/propcache-0.5.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9281e922c072158c91974d4589f1dbe0fee6d467f284c28e463f9f5a4d933f4", size = 226804, upload-time = "2026-09-16T00:13:47.739Z" }, + { url = "https://files.pythonhosted.org/packages/74/87/a3e199c45b26587f073db655d19c30d2144b0f883827ba6ab77acf5c7d45/propcache-0.5.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9f3551b8a35c1df3e7ea4d2d86edee15f0dde1bddd434a71744048683544d0ef", size = 234452, upload-time = "2026-09-16T00:13:48.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/7c/08c15c7df256f94a6b4563f74165c3242fb554cdd1909b661d093c31b976/propcache-0.5.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ec6a85f424afa8d23e0d9a094e5dbb6eda01da91c92b9183cd433768247ffc97", size = 240399, upload-time = "2026-09-16T00:13:50.416Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/5adee15e12a7e314cece4543fbf295b0fe1b504dc78b32dff4c8eb1e29b0/propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f574e460d1c8a08384a016fdb09ccf3543433263ed6b2f97104f979e64ea57c2", size = 224251, upload-time = "2026-09-16T00:13:51.874Z" }, + { url = "https://files.pythonhosted.org/packages/9d/53/54bd510bb5d473edf66914602885226187218b4e3a5017e9dd80bceea41f/propcache-0.5.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d8e017eeb7482bed34cdb0d61cf2bcfc88d104bbab296a17cd16a6af8aabc70e", size = 202721, upload-time = "2026-09-16T00:13:53.524Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b4/a468700d0526dfb2ca6af5d790125de5894acc7d8ecd99243ecedfa4c4cc/propcache-0.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f273dcf7149a50527c4fd1f55cfe9eac0f60753f5af544b4c9352578e20c0874", size = 219148, upload-time = "2026-09-16T00:13:55.068Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d4/bf563e19ac9a5cc47113431337fdf2ee3573859f3f3e0a58e59833e9b75d/propcache-0.5.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:fc2461ecc45f17893f8207e73b46ea8ba93e33630e51cf4af3fbc21d47462b1a", size = 211011, upload-time = "2026-09-16T00:13:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/4b/3d/0189b6537f4a8cd795e685a130a6f47aeda78a5b7b062574705cffc685ca/propcache-0.5.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:279655a16973f1ee2bd2fe79973137681642fd9ae0d89215bba263726eb0dc3a", size = 228148, upload-time = "2026-09-16T00:13:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b7/59c16e245a549df202b4ebe2de91fa58a67dc4373df9840e372a64224dee/propcache-0.5.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e9f165403b81fea7e89c932d89046a1e3d9a3a60e8d7ef2f249dccdcb0982bf5", size = 201432, upload-time = "2026-09-16T00:13:59.453Z" }, + { url = "https://files.pythonhosted.org/packages/41/0b/7b19eb20bb0b1f9476d08f6e387ae094dc6870fc06d459ea1f35f28b25d3/propcache-0.5.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1783582065a1f07f9d9ee1e992e13f15d7dc8fb1eb3a7476d43eb3f2e69d26bb", size = 228923, upload-time = "2026-09-16T00:14:00.731Z" }, + { url = "https://files.pythonhosted.org/packages/3a/f9/b1a0bd47218216b935d019912d6fec1676ae7c07f15fa82bfb54b8d58976/propcache-0.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d605bb239b796e82a81c6709548b2bd460ab73b4590cb0c83de8a2dd9694d0f", size = 218327, upload-time = "2026-09-16T00:14:02.091Z" }, + { url = "https://files.pythonhosted.org/packages/55/c0/51e5d1ad504e9529831606534c48e272db751072eee9ff07e4abe37e50bd/propcache-0.5.4-cp310-cp310-win32.whl", hash = "sha256:141fdbd73748db0cf7636035030aaac383d2efde8f34e7bc24594cc776d225b8", size = 43096, upload-time = "2026-09-16T00:14:03.427Z" }, + { url = "https://files.pythonhosted.org/packages/80/57/0a0b4b1122f4ed5bcd1c626d910003cdf5282c2a12f8a1cfa17970b3ded2/propcache-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:146f48a9e4812611a7581003b1a39de56c34967046310c4171a68ef908c9a745", size = 46589, upload-time = "2026-09-16T00:14:04.581Z" }, + { url = "https://files.pythonhosted.org/packages/66/87/e71b24adc8ece61782ba3d6f3879e6a07fdb85bce21a9c529524184e3ec6/propcache-0.5.4-cp310-cp310-win_arm64.whl", hash = "sha256:6c7599df2b57ebeea8de011b5f2f7b85de95e76037d43d34b95e328430275487", size = 43982, upload-time = "2026-09-16T00:14:05.728Z" }, + { url = "https://files.pythonhosted.org/packages/1e/40/14b21e505b7921617466576423f188a5c9caddfdaa1cf4b2b8a83d8fe216/propcache-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:897d1ddf6716e8f47200f7aad9a0efa6cc7586df66c6defa572f9eab379c078e", size = 86393, upload-time = "2026-09-16T00:14:06.9Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4b/5a52e1a7b43563f7d408814194bb23cc8bf214eb6b86639b667a33a8d0d0/propcache-0.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9cbfff4423eef4cc6cafc021469641a2b835f610b2647a6c5281903e21b8670d", size = 50431, upload-time = "2026-09-16T00:14:08.025Z" }, + { url = "https://files.pythonhosted.org/packages/05/cf/b5248180bf056cc76acc60c9c6e8c0ebbfdbd1c6cffd31fd14996927b7c8/propcache-0.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fc24f209c1b7f7f688b66b98293954f5504279760999b58920ee12dd8471c1d", size = 52116, upload-time = "2026-09-16T00:14:09.114Z" }, + { url = "https://files.pythonhosted.org/packages/86/a8/7c6cd6bfead1a11f2e411e688640e6d26574cb0bde7dcaa7423b0b65ed7a/propcache-0.5.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62530ca89187827e4a4fe733f971abe81a7542eeea48ff61995f19b64d7199c8", size = 238729, upload-time = "2026-09-16T00:14:10.357Z" }, + { url = "https://files.pythonhosted.org/packages/5c/b4/442715b2e980df51be52d203549279e027728f24c80b00b5e525e31cd5ea/propcache-0.5.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fc3f7599528db40b1efa0889a620116e2704144495273d66066e8164e45838", size = 246121, upload-time = "2026-09-16T00:14:11.735Z" }, + { url = "https://files.pythonhosted.org/packages/bc/5d/df0684fc2b1732a01a7bec26d7897369022712422d25b09c37ce7dbc88a2/propcache-0.5.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f2d880ff60f45898f4acfa152aac8d04e3ee627d90ff4003491bf92239d5757", size = 251735, upload-time = "2026-09-16T00:14:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/c7/06/519a5ebb48b6f94beb48396e55c905f12246a25c3a3608a7ec7bceabf50e/propcache-0.5.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e9368e87a3efc285e559131092c5db643eb8e56de4ee42064d5baec22ef2bb5", size = 235381, upload-time = "2026-09-16T00:14:14.398Z" }, + { url = "https://files.pythonhosted.org/packages/3c/07/1e0a9bb310830f2245edbd5cd3c6d24a783c053c4efd8e08e386e513c940/propcache-0.5.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:004e685b315646c410771836e72a44f143bbe624f29653a42687815069a303d5", size = 208973, upload-time = "2026-09-16T00:14:15.715Z" }, + { url = "https://files.pythonhosted.org/packages/62/5c/9324fab27d6088eecc47fe4332bf7aaf8c1ded93c36f558391e8a06d41a7/propcache-0.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:594eb4c6ec35e7179b058481f4e9f02521b56de16fa577c4b85c76fb1bf8a9f8", size = 233897, upload-time = "2026-09-16T00:14:17.25Z" }, + { url = "https://files.pythonhosted.org/packages/9c/a3/570d92fc952eae93b676f3a1568f4b89264102abd3c982ab6a9ebec58dcf/propcache-0.5.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2dba2f02d2d5c09ef8a0e6c1a42aeaa451f4be9898cb00b04fe98717da2eb23b", size = 223512, upload-time = "2026-09-16T00:14:18.87Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/26810d889d89bba31db397e6a88f8984af775f5ed6bad0a29dce84324cff/propcache-0.5.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c3ef2818d63bc86071e9d2989ae75a1bc32b8f7059cfd9f5abbbee70c32e2ed6", size = 239043, upload-time = "2026-09-16T00:14:20.366Z" }, + { url = "https://files.pythonhosted.org/packages/89/2d/f9c47691aa024c8299a3afacd78d22a01ab57eb627b481b6089708e71017/propcache-0.5.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:dd2ac8f5b643454c2cc6b6118b13da16e88f4a6434fc3ba61aca384029f04f36", size = 208218, upload-time = "2026-09-16T00:14:21.801Z" }, + { url = "https://files.pythonhosted.org/packages/74/6b/d510c0c378cabbf9d0ac7b663af6d00f2e9074073d93b20c85f24aa5c071/propcache-0.5.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4054acf80d40456a0537f2913b349718649d8d6458a14ab7f48d0ce28c30869d", size = 240301, upload-time = "2026-09-16T00:14:23.121Z" }, + { url = "https://files.pythonhosted.org/packages/3d/80/c80f6adaaa1e51f0db2dce8c9b3714d94ec45e358a21f9a1910b10b40a80/propcache-0.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:40e94adb1e7d39ff28a8bd8d8b8fbd1df6b9f40976dbe379134f1ce058e532dd", size = 230785, upload-time = "2026-09-16T00:14:24.458Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e2/c32a7df3f39caa7f11b2eb37ea5b6960a6946f2bc7c4b8ff97bbdf6d6b6e/propcache-0.5.4-cp311-cp311-win32.whl", hash = "sha256:9f86f7259efe2c951f43e57d471c9b41daa5bfc7db9f67189059cf1ae6d77fd9", size = 42747, upload-time = "2026-09-16T00:14:25.715Z" }, + { url = "https://files.pythonhosted.org/packages/0a/8a/3db6a3543d8101263b4c52978b6276a04ead2caff2c5ab880d934f47bd89/propcache-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:e904d4d01f36bd6e197590be1533c44e06058771e0746dd073a8ebb3ef880858", size = 46268, upload-time = "2026-09-16T00:14:26.996Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/5222e2665bbf6e45847492ecbf3b9f3e4975a0ae300e5fd465df7d48ce55/propcache-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:d42a9a856a4a6e2f6c10f1318c07e7daa498d6593abe745c71dae4521a26ca39", size = 43547, upload-time = "2026-09-16T00:14:28.143Z" }, + { url = "https://files.pythonhosted.org/packages/71/cd/348d58f142aebc4873345c6b31087629182ca6e0f2b3caeaa528cf882eba/propcache-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b28f41fa3b8c6900457f858ec5b03998f3a6d535fbc1bb2edec5961ea05ec429", size = 87285, upload-time = "2026-09-16T00:14:29.362Z" }, + { url = "https://files.pythonhosted.org/packages/df/f4/f3ffaee281b276da854ac1d7a6a506d26cbc62ea2e623756f1d0a4a1ba1a/propcache-0.5.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dcbf346a318a5e30063f547630b02bb787ce2f45b6368d5da143660b6a3835d8", size = 50984, upload-time = "2026-09-16T00:14:30.473Z" }, + { url = "https://files.pythonhosted.org/packages/25/88/1d7df7201750b37765ef2b23bc1c526c028dadde80afa0f57a118fc01182/propcache-0.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87a3caecf8095e48dc72f84bfa42e23a848cf410cc9cc13031fba4869b706a21", size = 52460, upload-time = "2026-09-16T00:14:31.692Z" }, + { url = "https://files.pythonhosted.org/packages/83/4f/48865bd02a16ee5236bc46166b2946f37b93e07b0eae355dac0be0b216ca/propcache-0.5.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60a64cbccaa11b7760ce705a14ada17ba459e7ca9f23ba587eb013821032d7ef", size = 251768, upload-time = "2026-09-16T00:14:32.908Z" }, + { url = "https://files.pythonhosted.org/packages/b0/19/3742a5eed62317b03b4002ee865dc9fd720308bdd0da1f29a5786c630311/propcache-0.5.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a74bfa37147cc08fb29df10bd9c16f40fa7f860cd3a6d2fff853323a94f6e17f", size = 257723, upload-time = "2026-09-16T00:14:34.267Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/ee6350fb0be9122bb6c67082a876d34b90d980d100c106af4b81023e04f4/propcache-0.5.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4d7a54719b67338a305dca2ce6aafe366817df94ddfd4b5514374356f5ca546", size = 265597, upload-time = "2026-09-16T00:14:35.56Z" }, + { url = "https://files.pythonhosted.org/packages/85/9f/83a07b6ec0e043c050cfdd35fb0cf1b7897b91d554d6eea293740309afe7/propcache-0.5.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2814ecd8e818f487bee4b0f921bc4d1c176cc5fc71ac0f072d0fa67eda4ac14b", size = 250424, upload-time = "2026-09-16T00:14:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/33/2c/a763a8251f50fba042af0fb1f02bfec4b31381e40aff760db2be7b2e1f84/propcache-0.5.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6af4693716bfb03f1752ef1b30faa593db2c01d5272e9b8564a1549452a979ab", size = 216748, upload-time = "2026-09-16T00:14:38.369Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e2/4d11bea8fd6a777149c6c20645f873952eab5de3a2497aa11648ec9ab6ab/propcache-0.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4fbc1a15dc8cd1689508758d626b372b1f09d28d9577667feaf9e6bfcd8efcbc", size = 246533, upload-time = "2026-09-16T00:14:39.82Z" }, + { url = "https://files.pythonhosted.org/packages/9f/36/6683597de4907e70c717e3588c541202c66086a72ff3db58be49de66e72c/propcache-0.5.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cdee8205a44d0be91bbac4c41b95d86641b72dfc7aef1279400e4fda3f26a937", size = 238173, upload-time = "2026-09-16T00:14:41.259Z" }, + { url = "https://files.pythonhosted.org/packages/85/84/cb08d79f1762daafeb2b030c470cd0c725c97b8ad67412457c6f35c53e9d/propcache-0.5.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9a2a8a50a93dee0268a860a07fa3b4bd968f8ce4dbd794957da772f395368526", size = 251128, upload-time = "2026-09-16T00:14:42.652Z" }, + { url = "https://files.pythonhosted.org/packages/c2/0d/41b848036db6621370c1f2e5471a7da8149c730f8552a5257567721f4576/propcache-0.5.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7ffafcbfc7b549ab940047e505c831eabac5e67de53e1bc174adbc5285c55944", size = 214821, upload-time = "2026-09-16T00:14:44.112Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/adfae4bf9c63bccf12e2d9690a175c6579047a6eec3b5a6a5f51428c15e2/propcache-0.5.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d1f5a500bfcbb2c0ab85e98a0dcd70f5899d34efe365a0187700369a79603031", size = 254793, upload-time = "2026-09-16T00:14:45.429Z" }, + { url = "https://files.pythonhosted.org/packages/51/6f/eeca9647245d5f92e87d53e5f14335bb42fce1a7e6842c8045b364eded8b/propcache-0.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a235f73d6e020855dc29dff012d920c02ee0feab8d73a24185a7569f4be1161", size = 247134, upload-time = "2026-09-16T00:14:46.976Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a9/424e38838793d37160b4379c702f61c74c598fc6cd17204adbe3c554f7a8/propcache-0.5.4-cp312-cp312-win32.whl", hash = "sha256:b3083bfe87f95c756e610bd8025f26cbd1cd4aaa03a422f2d65efb7a97cd53d8", size = 43073, upload-time = "2026-09-16T00:14:48.338Z" }, + { url = "https://files.pythonhosted.org/packages/58/7b/6e8ef26f6d510a7916064fec68d55fcbfbdf7eb01e377480d66a122152d8/propcache-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:98914de2c4d7f0f9f4a8c6ea4bf05841f4175796941e3ef7d47eb718f22311fb", size = 46190, upload-time = "2026-09-16T00:14:49.99Z" }, + { url = "https://files.pythonhosted.org/packages/08/b9/72028c5b56ced97f456de6aefa79435ca64d7f77af78ea8cf3c76fc5195f/propcache-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:8876b39961e33d912afe3c1bee18ee564fdad0206f873cc15d522756b7f50737", size = 43075, upload-time = "2026-09-16T00:14:51.155Z" }, + { url = "https://files.pythonhosted.org/packages/78/4c/3b1365d58a667689e067e13d055fcd92bdf8d9a2fca3d9201b47ed5b3631/propcache-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:36c0d9db44b523ef93d03341b1c42d69ff01d673c053d1b1c6c3a363bcaa39ba", size = 85290, upload-time = "2026-09-16T00:14:52.342Z" }, + { url = "https://files.pythonhosted.org/packages/8f/61/5f9c29c3aa67c30238c4eadf95149b1d983a48f69b86b0cff927a7d6df13/propcache-0.5.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1d52a05dc417279f7e5c7618c5dfbbc29923aaf9bc0a5c1802ddcebf54c61a0", size = 50027, upload-time = "2026-09-16T00:14:53.67Z" }, + { url = "https://files.pythonhosted.org/packages/25/7d/c1ab1ef09e9d4d835be5d58c0a32a1e1de8397abaa4e502a9d4141328cad/propcache-0.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44149f46500a0a41b95b4d99c2e586a77319539730607b9892974a092788b111", size = 51425, upload-time = "2026-09-16T00:14:54.826Z" }, + { url = "https://files.pythonhosted.org/packages/73/36/0093091ebb270fcd1bc1f6e095f93b2e0ed7f1011c28837dc2dbe5f96b99/propcache-0.5.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbab5f5ff6897c81f355d079010cdae85b02e5a0b518b5251523b8ad8ae9ac3c", size = 233595, upload-time = "2026-09-16T00:14:56.09Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/0de9d4c8e05ce0be71b436919a216bd7fc5cc6e2691c0602295efb22b9ed/propcache-0.5.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c3e98c55bde2bcf7db3c70d1aed7ae9aa8aebbf19a250c66645cde44cdb8b867", size = 240318, upload-time = "2026-09-16T00:14:57.674Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/2b35e91455209b85ee98f7859583e0814fab57d3af0f2381aaee34c37304/propcache-0.5.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db3ae52ccc150dbc84704e9d642743897f3e1c54742ff34cacb661e52e3818a9", size = 246649, upload-time = "2026-09-16T00:14:59.352Z" }, + { url = "https://files.pythonhosted.org/packages/ed/74/08e6c1faf26ee2732023a3828787ba535557122774f4a386b1f715cbd8e0/propcache-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f85915e00dcb1cd9f2f890ead064ed40a27df06f0db65be427b29482ae357572", size = 234316, upload-time = "2026-09-16T00:15:00.696Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/08385733c9321c9bb78039d3ff31045e4fca962d9665023c4eb70f998819/propcache-0.5.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c2ba30a89035b57b73e00475de948521602f543d79ce01db10b04b36c4c76fc8", size = 204666, upload-time = "2026-09-16T00:15:02.019Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f4/e87bc7629af9a14a752b218764a78742d73c2c563ac58315da6841f0cbe4/propcache-0.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ae58f361bd5dae942717c65d3413b478c70aea9c462599e7b9adad3731db3894", size = 225900, upload-time = "2026-09-16T00:15:03.394Z" }, + { url = "https://files.pythonhosted.org/packages/d9/6d/11014938d3fe9bea2ea2dcf930f26ed565bfb2f5be3c756362ea48c92636/propcache-0.5.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:96f7c5c15656040ddcbc51e56dc59b58aa25999d743c126abd425b9766ab43e9", size = 219988, upload-time = "2026-09-16T00:15:04.811Z" }, + { url = "https://files.pythonhosted.org/packages/dc/72/fbf17c589f92c0b3bbf6709a425661f8ef2ed0d46b38985a7d7b5a0f6b91/propcache-0.5.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7cc528e760a8af06f2b13e9b9f362cd90c7c718ea61228a96dbd31ba16ed7f47", size = 233611, upload-time = "2026-09-16T00:15:06.498Z" }, + { url = "https://files.pythonhosted.org/packages/55/7e/dbd637572a279692e5518d117274a9331bf5faac59f191d30e82521a3ec7/propcache-0.5.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:425f8cc86ab5018b4b8d4a23bc8e74d964bd3d757c3702e301aa79be76c53f6c", size = 204333, upload-time = "2026-09-16T00:15:07.961Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5a/f99c92068f1e0f5c886899ce0e4a619db376ca98c5279d93f95bd86906af/propcache-0.5.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a5793c7698a53f56f4a1889a4737c7eeb1b7ad0842fa6b1abca22913ff79c8c1", size = 235177, upload-time = "2026-09-16T00:15:09.334Z" }, + { url = "https://files.pythonhosted.org/packages/ee/28/95456fabd2daf6be89049a13fbf03341756014d2959c83d12957d4c49694/propcache-0.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c02c0e570c5c7e077b0181a9f3cdb7d4c3617d1cda6b5c95bd5d34022923d82c", size = 228982, upload-time = "2026-09-16T00:15:10.729Z" }, + { url = "https://files.pythonhosted.org/packages/b1/bb/df90f62c9cf7c93ea235f6f9405143bba802914607317266dd81fc8d737e/propcache-0.5.4-cp313-cp313-win32.whl", hash = "sha256:3e413d7a4a9b4866b7a761d6060d434b64d23cd35122eda3b026a0bbe8196b25", size = 42611, upload-time = "2026-09-16T00:15:12.111Z" }, + { url = "https://files.pythonhosted.org/packages/01/bc/e0a7b84af04ec02d73a48aa71f091e1e4a2107e3074b7ce12195b66901f4/propcache-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:0c889f6fa84957bc7e8b4eab71fd16a0455068d5045e3aa40c733071d2b2fd77", size = 45342, upload-time = "2026-09-16T00:15:13.519Z" }, + { url = "https://files.pythonhosted.org/packages/9a/70/50b031cafe72a5c1878b903ee87303f71313345566bf3d6ec202e5ddc9ec/propcache-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:69fc35c0779522da366c563e5faf203ffc1f8ff0021d5b1337fa4efa5be73177", size = 42408, upload-time = "2026-09-16T00:15:14.788Z" }, + { url = "https://files.pythonhosted.org/packages/33/c9/07e227b930c8ae513b8ef1aae3793499be097bffcdf7aee4fb8b33db4cd1/propcache-0.5.4-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e6720ba44ad7e72174314d0e1fb0172494cff5c73a3a8a2159c3d2402ff15565", size = 85933, upload-time = "2026-09-16T00:15:16.073Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e1/6710bb44510c4e4a8e0f004bbaf3cecfd048141309c77bae56d4e5a6ebc1/propcache-0.5.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4cfe0a92ae30151869e67a4b5f5e105e4e03ad30b3f38e5211b5bf77d0881993", size = 50179, upload-time = "2026-09-16T00:15:17.377Z" }, + { url = "https://files.pythonhosted.org/packages/e2/22/b533b493d7025456f44518b33e53e000021a20fe7c27b88cf3d341df7186/propcache-0.5.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d759d05634f1b038fb625a66662a8c85e5a8fec912da381b5149ddac107482b", size = 51942, upload-time = "2026-09-16T00:15:18.589Z" }, + { url = "https://files.pythonhosted.org/packages/f1/74/70ac8430e28f21e442c7bcb964eb46c4363f6881ade4aa0e978bfd8d503a/propcache-0.5.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:251c63dd46a0659bb875cb254dc4c1e79ee91a847c737cd62373295afc2235dc", size = 232647, upload-time = "2026-09-16T00:15:19.905Z" }, + { url = "https://files.pythonhosted.org/packages/72/95/f222f13b6fe623310be0eb61a673bf26df439ce27e563ca8e422d0818777/propcache-0.5.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7a8d5ff04eb1f85698a78d20c62a14676e7b960dcafde09a388d60ad377d355d", size = 241541, upload-time = "2026-09-16T00:15:21.3Z" }, + { url = "https://files.pythonhosted.org/packages/a2/3e/763e370340db16115c5e63ad46e21ef0770a7f06928b3d3b62d8f8edfca4/propcache-0.5.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b9100a93b372418d8688f3f2a3e5b45c64d70ca4d6176e121aca1e3bfc1e32f", size = 245332, upload-time = "2026-09-16T00:15:22.802Z" }, + { url = "https://files.pythonhosted.org/packages/96/d3/e97cd6f5de2176bd90ed4076c7a9b5e09d0f0b9687d00a576507988bb62c/propcache-0.5.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc07876cfb079b6f6f36d21ce75784ad6c2c6b563eeac0ed26c2fa2669b85df9", size = 232757, upload-time = "2026-09-16T00:15:24.374Z" }, + { url = "https://files.pythonhosted.org/packages/f9/4c/6766e5f60bcda26d244333aa71d0a702c1c9b21b251d543c7af5953d1eee/propcache-0.5.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0951315a6b3142ee2167404d707743f0157c110091342b1aa0accac5cf0e4acf", size = 204389, upload-time = "2026-09-16T00:15:25.667Z" }, + { url = "https://files.pythonhosted.org/packages/b8/5e/ec4bb09a70b26ea99d76a8292c3383b960b296de2b347ac9986678f1761c/propcache-0.5.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bee7d3aed13d56f54e681df38c3a23031bc9e3863f687d9d598825c9146acd7d", size = 228217, upload-time = "2026-09-16T00:15:27.11Z" }, + { url = "https://files.pythonhosted.org/packages/e1/7d/b53922ba7d9e5bf797324e63aa05906ec240871899f779628df068743e2d/propcache-0.5.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4e985382be6d15da8d0c2710a6fa7b9070fc9ecdeefb7f580e88373984ec8be3", size = 216947, upload-time = "2026-09-16T00:15:28.532Z" }, + { url = "https://files.pythonhosted.org/packages/ff/39/b62eee45e5ea4de094a258cbb3b01c1e856ca51ddfd95b43135c5effd1eb/propcache-0.5.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9e9ab13760aa8b6d0881ae7cb04fd891d8d490cd2554ea8e79bb278399169bcc", size = 233457, upload-time = "2026-09-16T00:15:29.977Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a9/feec61ed296d993db9dd097e0f6723e3f576a647722367547495e4c5b05c/propcache-0.5.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1b2f3bec4261a94019575481c726c29850f72e27907773c75b1de421e20e9f9d", size = 204131, upload-time = "2026-09-16T00:15:31.74Z" }, + { url = "https://files.pythonhosted.org/packages/92/4d/411ef380cddad28dc001f1c6d75ec72c76cd3817030f68ec1ccfba0ec6c1/propcache-0.5.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:720cf832eb2d0b0dfee129cb3335a26f6ce3cc45ee1187e8f0731758caa16792", size = 234820, upload-time = "2026-09-16T00:15:33.087Z" }, + { url = "https://files.pythonhosted.org/packages/15/37/c988229753629ef1cfd5198337a83e624780ea2b3787efe9e747c05aad2d/propcache-0.5.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fb0a5be8d9aa213150e8d8148a42aca4984b285bcad1e69587dc4298edd929b", size = 228350, upload-time = "2026-09-16T00:15:34.533Z" }, + { url = "https://files.pythonhosted.org/packages/12/49/5ef1c5cf98591da3c5b952b39e6a298084cc1ce353bc70f85e82397a5036/propcache-0.5.4-cp314-cp314-win32.whl", hash = "sha256:30cc1cebaf9aef49db06357a50398323ae04d70460c0491837d026ab7d6452ea", size = 43578, upload-time = "2026-09-16T00:15:35.957Z" }, + { url = "https://files.pythonhosted.org/packages/1e/9e/a0ac821a2229186af5e2e3c3635a78abb23cfddca57f38513ab5d70420f3/propcache-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a095db8e15a6020db149ecbed6461939fe74f6acaa3ae8b702a1fe8c38cd983", size = 46304, upload-time = "2026-09-16T00:15:37.655Z" }, + { url = "https://files.pythonhosted.org/packages/a1/19/c8d0d36a9d16cba5dcee67d389c9333b988c8986a653a61c00a451817a46/propcache-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:45488d1a5f9ab5bd90aaa1ca20f50fe1922b8ffad71a2009d2adf41355897aac", size = 43440, upload-time = "2026-09-16T00:15:39.091Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e9/42f1da77cacfc184e6ec929557ef653b7961bbf6f1da460b9221273948b3/propcache-0.5.4-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:53eaa697c4d0422ff4cb714d00231b43352064d97b944033b30c1d57cc506ec0", size = 90672, upload-time = "2026-09-16T00:15:40.306Z" }, + { url = "https://files.pythonhosted.org/packages/cf/2f/4b79940908c6ab8c795097c102999d7bc1f7e0b8604dfd1c232f9d99d67a/propcache-0.5.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:886b59c4d28ca97dd23b025fdfc50a0356be934efbbbca89ad26230067f86fe5", size = 52586, upload-time = "2026-09-16T00:15:41.575Z" }, + { url = "https://files.pythonhosted.org/packages/eb/07/02196ae6320c110235bb343f90dbd34be41f8b8964a3ee30db84ec12579e/propcache-0.5.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fa15757fea1dfcd5b7745cad9f4638929605531bd4018ab2adff7955f1a403d", size = 54335, upload-time = "2026-09-16T00:15:43.027Z" }, + { url = "https://files.pythonhosted.org/packages/6f/44/f48b9a131985659924df5fa5093f68fe72c7ee375329802989ba3126efc6/propcache-0.5.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f0093ac3e9daada202c2082439d414a625c57184727a46e112a3fb2a81cb788", size = 297567, upload-time = "2026-09-16T00:15:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/04/a1/418d956d2735139f77fc35262179f1f52c23aa666de5a8ab3819c1ae7854/propcache-0.5.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3cd3a7edb6b95b9b33998135ebfa18d709da82290fb8f27c858970b5a12c8b56", size = 297477, upload-time = "2026-09-16T00:15:46.048Z" }, + { url = "https://files.pythonhosted.org/packages/69/fd/ff811fdb6d3d3e67fd9bbfb75881675d34a42d0ef29a45d33e3e233dde07/propcache-0.5.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c174bfd1c48a1b51a3078e95586dde718374bac79719ab3541ec9e74aec40574", size = 302669, upload-time = "2026-09-16T00:15:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/fc/57/527910c455b5ec62f6871bef45d4f79fea16cb8c966ba0d4a07f0339ddc4/propcache-0.5.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a219f0ac59817a9114dd2aa57c13180f993e819ba658c7ddab4b66ed1ee0d370", size = 287908, upload-time = "2026-09-16T00:15:48.99Z" }, + { url = "https://files.pythonhosted.org/packages/1d/86/f69ab82707534a0cb2057bdca04f9200a71214c7551800f9d34d6ac39e4f/propcache-0.5.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:17a7400cec0256f0a71ae71f9da398f9894c956ff6668a1c9d317b3367316320", size = 249804, upload-time = "2026-09-16T00:15:50.486Z" }, + { url = "https://files.pythonhosted.org/packages/27/19/60677af50d93be4256213de7cd487f056944c048b9c0b6f2e45b3a30f666/propcache-0.5.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:978f28401afbc76cdc3df9e1717b4229a06b626a1dcc75db4e1f2beb3884c3e9", size = 282344, upload-time = "2026-09-16T00:15:52.029Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f7/a0057808a91fb3b6a5f3602b528f0cdcb3d53e0ff8315d73fabdfdf8fec4/propcache-0.5.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4a1f4f5ffa55dce6307631f3cb2948e117e665966ea512e0d502b16c24f567e7", size = 270167, upload-time = "2026-09-16T00:15:53.466Z" }, + { url = "https://files.pythonhosted.org/packages/83/c8/f4a865490df0dc0c8531d4e59ac411cb6dc24bb255d2396a6f1c60a368f4/propcache-0.5.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:213bb68d9ced5cf2bf717b1071bf2b09b4b04c426256f9fe6d054c60318424c4", size = 286551, upload-time = "2026-09-16T00:15:54.995Z" }, + { url = "https://files.pythonhosted.org/packages/b0/67/b4faebde9da4e8173d0e5a30e8cd31335914af7ef350b988f27fec588cfd/propcache-0.5.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:286867fb156488c251a3721766e380ac4495e4fd6b51aaa1403d89ce7f4359d9", size = 249595, upload-time = "2026-09-16T00:15:56.505Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/52e1dd5636e9f5a27f6b5a4b4e2f33c322fd72afe956c397d82523ec4a80/propcache-0.5.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:445ee3bfb46e85838387fb3c536a73cc0b994dc192b004e40e170adc54aa2a7e", size = 286700, upload-time = "2026-09-16T00:15:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0e/30b2b324b93ff31a0bab539c102aae59e84e444031b2742150a7646aa1bb/propcache-0.5.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:48cb48c5346a97de792254af77715aa2529c2a1ebc5f586aa0aae44a02f1fe57", size = 280500, upload-time = "2026-09-16T00:15:59.487Z" }, + { url = "https://files.pythonhosted.org/packages/64/36/721bb59f682ff060d0c8df64274fca8cd0521b1a54506c2eedaef795b7f5/propcache-0.5.4-cp314-cp314t-win32.whl", hash = "sha256:03b229037d25b801e7af53fd52b9fc49d9439b036fca1e087e02780631adfa97", size = 46121, upload-time = "2026-09-16T00:16:01.349Z" }, + { url = "https://files.pythonhosted.org/packages/c1/86/0b1b80fa1ac3a0aac44e2922a6964fbe9cd52af5eab8fa933bf9e90b030c/propcache-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:8a1fc236528c457cd739c88abe823da851b7ab645d72792f88658114cc340c12", size = 49154, upload-time = "2026-09-16T00:16:02.901Z" }, + { url = "https://files.pythonhosted.org/packages/69/4f/9fe6f05a47cb550c823155052116f710064b6be5c6e8ec4e9faae7e18115/propcache-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:135036c5cfc93864affb0f9af9a27e5d7a71cb7bd745e7b6dbfc2d56cc30e827", size = 46005, upload-time = "2026-09-16T00:16:04.266Z" }, + { url = "https://files.pythonhosted.org/packages/58/25/895a11d1e4c5c2acc6d816e2bece34e02d9dc92f2182ae276cd819e9e804/propcache-0.5.4-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:45bf2e730ab8905d0527fe05a86500f406e64305c34cc81ebe64b4617cab9760", size = 85634, upload-time = "2026-09-16T00:16:05.599Z" }, + { url = "https://files.pythonhosted.org/packages/58/41/c0acd69271de7a1cf439e77d5d60c18575fd09bad56e798b95fa23458ea4/propcache-0.5.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:31eb43ba2edc704ab2ec27815315dd8a19def0fb16215be4cfe8d32fe78ffd51", size = 50084, upload-time = "2026-09-16T00:16:07.384Z" }, + { url = "https://files.pythonhosted.org/packages/a5/1a/ad561f99f90884089e6403b76c220610809429ba868a81a2e7ce115d32e0/propcache-0.5.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:174507f82d3594622acb1dd2dafecf2d899d6d506335494e7107767bf05f3aae", size = 51692, upload-time = "2026-09-16T00:16:08.956Z" }, + { url = "https://files.pythonhosted.org/packages/e9/07/057bdd3a9609ffad59b06239cceee784b047f6c720247bfaa36d2103e138/propcache-0.5.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e337653721d20ead710da33bf44487fbe8a0db8782714b60306481e9f95b51", size = 232947, upload-time = "2026-09-16T00:16:10.466Z" }, + { url = "https://files.pythonhosted.org/packages/fa/dd/d36ad35986718530498a65e45e3713f9f0e6a580f192ef02d2ef7cae9b52/propcache-0.5.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d21d0d2c82bbfeb1677a9711f38df968f9837576102bb4add1bd449d28d88f1", size = 241250, upload-time = "2026-09-16T00:16:12.056Z" }, + { url = "https://files.pythonhosted.org/packages/fb/81/f1459415cdb6c10d46942779de39bb59a77b38e5a76bb1def9227962eb45/propcache-0.5.4-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccf4f7a79e26bb7efb06ecd50c177833b71df05cbc748701372325e6bcc17f6f", size = 245150, upload-time = "2026-09-16T00:16:13.596Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4e/58b9b1460afc97a4c0b17ee89af701c4011d4d7f46470eba3aaff76a8069/propcache-0.5.4-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23278f808cd81d5ada7184a76606b925fb3389c60e1077b2cd7da7b1fcf0553c", size = 232166, upload-time = "2026-09-16T00:16:15.126Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c6/5a79e0eda3e7b6987d03d8c622ff6d52a42165a12e8418eb37694b9cc4b4/propcache-0.5.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e738ab81179510ce79b2eac9a6ecf47feffd9e76d1c72e403005dddb6e36c06c", size = 206085, upload-time = "2026-09-16T00:16:16.713Z" }, + { url = "https://files.pythonhosted.org/packages/4e/72/940aed42c73f9da345ca2de0f6e835c726498159abca5f1ef14fb0a2af8a/propcache-0.5.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:a419ee85e654927baabda3929c03c0cc1112bf472ff0dfd6142f4e3a81ca4162", size = 228460, upload-time = "2026-09-16T00:16:18.352Z" }, + { url = "https://files.pythonhosted.org/packages/85/71/3f54e1535c8f323d91ba566044d7c2b39ff6f6a2f1d0bd9071779d07b9b3/propcache-0.5.4-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:b61805357d966680acf68b3b6d49772631ed9df44ebece10ff1460e117a7da8a", size = 218350, upload-time = "2026-09-16T00:16:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f4/025890cc389ac3ec485ecec607d4a7ca47e15bfa2a465746ab98af602536/propcache-0.5.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:58134228927cee6c047d626c08e60a81be604a20578a12ce752cc5c9a84d4826", size = 233156, upload-time = "2026-09-16T00:16:21.624Z" }, + { url = "https://files.pythonhosted.org/packages/04/29/b39cae08c87c140d3d274f0a2c058cb5588e836175c3309e260b230ab07d/propcache-0.5.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:350b272b2279f4135a64fc0c304a5d08e28a137c9573442c606152446638a831", size = 206206, upload-time = "2026-09-16T00:16:23.204Z" }, + { url = "https://files.pythonhosted.org/packages/18/61/e16462ef18a87247dc9ebbd5c606f46d5ce67e708bd9cc734dd0d9222564/propcache-0.5.4-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:45bebbe252550fec975ba3b62bc6f931643cfd3b5464ef47619cf3fef154e01c", size = 234469, upload-time = "2026-09-16T00:16:24.841Z" }, + { url = "https://files.pythonhosted.org/packages/9f/84/b6a1490922427204fc47df920ed002eec709621de6b79b11592bf45c623a/propcache-0.5.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:ada748108a43d29b7c328ba7db3755327cd94f028bcc1a7ee3f0addcfacd9c38", size = 227311, upload-time = "2026-09-16T00:16:26.549Z" }, + { url = "https://files.pythonhosted.org/packages/ff/5c/5a59527582e9bcb694b2f08b9894134b65a0f5f79dbff174f054f5f74ed0/propcache-0.5.4-cp315-cp315-win32.whl", hash = "sha256:ee19113bce2f3acd46432050688b70f61acd6857d75abb9ec96341b7e9ced123", size = 43512, upload-time = "2026-09-16T00:16:28.313Z" }, + { url = "https://files.pythonhosted.org/packages/26/07/93cf699ed363681e754d7c3fad587fb09ef6b65618ee193332ad16a68d7b/propcache-0.5.4-cp315-cp315-win_amd64.whl", hash = "sha256:ceb3e879afac028f93d272c957814695dc5569e4904262dbee92f6c41bd5e4a3", size = 46264, upload-time = "2026-09-16T00:16:29.751Z" }, + { url = "https://files.pythonhosted.org/packages/65/10/fef04fbdcd44a4a163cb5ff5674599c6d6fdefd64a5a459438f9ad2ba042/propcache-0.5.4-cp315-cp315-win_arm64.whl", hash = "sha256:c83acbce9f2b5e3f5f5eda9e53d2001fed22fcdfef81274a9e02d8fd53b70a30", size = 43395, upload-time = "2026-09-16T00:16:31.5Z" }, + { url = "https://files.pythonhosted.org/packages/70/f6/7e2f4dab0b92ab46111bd48cee9ee1e5f519514c44e3779ede5358d7ada0/propcache-0.5.4-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:a5e8ef588c109725dc713ba69aadcac00a1ef90c2ce9c0a8c7075128f569f47f", size = 89825, upload-time = "2026-09-16T00:16:43.115Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8b/dfeff925cb6ced97ede701d5c6a99998da963c6f2e06abbf879c9dac5b54/propcache-0.5.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:4d86476a935c88963d9b8e1a9a0d38188790e9622169bfbafa173046846709d3", size = 52159, upload-time = "2026-09-16T00:16:44.754Z" }, + { url = "https://files.pythonhosted.org/packages/24/6c/924c810be5b7cf218ef47e707cf06d34adb4e3f3a31e3c24c55c6d945a88/propcache-0.5.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:f5470694918830da62fac9e69133b53d23b736d7070e587b27a4a2be37e08e68", size = 53956, upload-time = "2026-09-16T00:16:46.762Z" }, + { url = "https://files.pythonhosted.org/packages/3f/b6/9ed0a5c939b58b6bed740a05b5d0f919f0b318d03284b4b6d81a0fe8a29a/propcache-0.5.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10ef33a68a61ce317e095fd2e202a592ea92392b90944a78c993f0d9a73ab06c", size = 295235, upload-time = "2026-09-16T00:16:48.577Z" }, + { url = "https://files.pythonhosted.org/packages/5a/eb/5ce886e902a2e781dddf110993d5329458a9b1a8626b876c65e5e25bf413/propcache-0.5.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5cacf3c9efd09df409dc33654dd077e1c245ba8fb747b0f0236ef41b7c49b589", size = 294463, upload-time = "2026-09-16T00:16:50.539Z" }, + { url = "https://files.pythonhosted.org/packages/f2/88/c98f49183ecd3e5b204a556f0ca47baa02c2206a500fe8c7ec1726297b0a/propcache-0.5.4-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:770e8209d018175fc0063936fa9583b6d27e88c5ad31543f3383d66080efdd62", size = 300081, upload-time = "2026-09-16T00:16:52.423Z" }, + { url = "https://files.pythonhosted.org/packages/27/0d/c5090f9e6f67cbc30a2b744c7bb0f8006dcba5ec1b0d82f866ae1cc7c5c4/propcache-0.5.4-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03969626faf0783a592dfa17e28eac06018bd0b44dafae6943d53b92421a7f72", size = 285360, upload-time = "2026-09-16T00:16:54.141Z" }, + { url = "https://files.pythonhosted.org/packages/ac/9c/34a55396910583ed07926669ab309dde2213a2dec05a7e946bb90ad66908/propcache-0.5.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef3b928d9c984322b5c44e6964d8dbc653da87d2d8ee1647fa6da43072e650a9", size = 248014, upload-time = "2026-09-16T00:16:56.062Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b5/c0a142b656093ca397039dd3fe166cbb87c945712b534546514a24cd2611/propcache-0.5.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7177c43eddf10a0893c4fec52ebb408fdcd7f7d63962caace9180d8f81b14ece", size = 280662, upload-time = "2026-09-16T00:16:58.044Z" }, + { url = "https://files.pythonhosted.org/packages/54/28/fab2809c2e337fe26becea9648e84d5cef46075c91b826acb13e4f9dd04e/propcache-0.5.4-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:420162a77f94eb1cf5ef7893f500016dabd548e73de956785a1dd899cc73006a", size = 266149, upload-time = "2026-09-16T00:16:59.702Z" }, + { url = "https://files.pythonhosted.org/packages/3a/11/7ddf336288b2678a5f054f8da2e2bd1a719f5d4b7de714d9c6bd588a2313/propcache-0.5.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:3eb2e820e8e2101407da93f17c57cbb7d225461955fc60105daaba14cd421ee2", size = 283097, upload-time = "2026-09-16T00:17:01.459Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ae/351b1a5225f5473c411d9a612a229ae147cf0cf65c72ad838b87219ea8e8/propcache-0.5.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:13e52b6e0bde97dee98ab66552dbff2931649c96f1ac432eac299fe689ec373b", size = 248160, upload-time = "2026-09-16T00:17:03.298Z" }, + { url = "https://files.pythonhosted.org/packages/3f/d0/7f79f061e30d135bb615c9782c94a74652033d00b49254edbbf35a9165a8/propcache-0.5.4-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:12682126712ddc19b70ff819debbd279e58adf1f0c8f8f8138c18ade2044b284", size = 283036, upload-time = "2026-09-16T00:17:05.238Z" }, + { url = "https://files.pythonhosted.org/packages/53/3c/016f1cad8bf4c428d748cf399b2bac603026fbfd6966e6a5579b5c5b6956/propcache-0.5.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:3af0c8642b2da4815d86e631232ac8286e17644fad907c19508aa8e7cb4ba8ad", size = 279350, upload-time = "2026-09-16T00:17:06.881Z" }, + { url = "https://files.pythonhosted.org/packages/ea/60/d8f72cb24b412487ed4c397f539117d3b74c3c33dd32020e91fe00a958a8/propcache-0.5.4-cp315-cp315t-win32.whl", hash = "sha256:1df8d8561b21465c5dd56110a01caf897e026d065b4b84e98a488209094272ec", size = 45874, upload-time = "2026-09-16T00:17:08.567Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ef/8bae0a316d406644450522f2f3d44a4e19632f5f3bb60d1d0e6c53842616/propcache-0.5.4-cp315-cp315t-win_amd64.whl", hash = "sha256:02c0a34f16889cf800f10f0247a564d8ce6eeab6ffcd7c87198f769067eb8432", size = 48574, upload-time = "2026-09-16T00:17:10.077Z" }, + { url = "https://files.pythonhosted.org/packages/57/be/bcc053f66a97355683884b448198e79580fae8e8fa4d96b9bb01614e9913/propcache-0.5.4-cp315-cp315t-win_arm64.whl", hash = "sha256:dc4242ca653c9b30ab51c5f8193323e7bc0928f897ee9103201e59a43abcb72e", size = 45625, upload-time = "2026-09-16T00:17:11.377Z" }, + { url = "https://files.pythonhosted.org/packages/f5/cd/785c64ed382f3f04201870267b02783f63b4678c2acfddc177a3ebcc2727/propcache-0.5.4-py3-none-any.whl", hash = "sha256:62c60aec739ed00124573cce1178138fd690c7676352d67a37328c1cf51d7468", size = 16338, upload-time = "2026-09-16T00:17:13.106Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/9f/abfd2959e9261dd5217ca8551d4de211ca6ab26fe9b72cf44731ff6c4442/pure_eval-0.2.4.tar.gz", hash = "sha256:260c2774686e651b79f8b8e7fc9d80b3599ea6a66334b47d5f4abb69fc2c0ea1", size = 20563, upload-time = "2026-09-10T21:41:22.836Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl", hash = "sha256:96cae060a313cfaad51bb761278bfb0e62dc0248d9315a81173752dc546cd37a", size = 11893, upload-time = "2026-09-10T21:41:21.532Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", size = 845750, upload-time = "2026-08-28T14:04:00.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", size = 472589, upload-time = "2026-08-28T14:03:59.136Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", size = 472262, upload-time = "2026-08-28T10:01:31.677Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/6b/8f79692844269427abb3e4dd9e68edfcbe65ae25527d99183214de716c59/pydantic_core-2.46.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:657b40d6240c0a7b6a64b30f22d1e3aa631c7e846c621b0c0f6d1d75e2e15ea6", size = 2076533, upload-time = "2026-08-28T09:57:35.421Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d0/c787604c71c2bdcda1a5656942fc822cd0f9cd879b9484bb84fc42172703/pydantic_core-2.46.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ecb42011e12ee19cafbc312887cbf3546959fe02fbad44f272d4be5baa997615", size = 1924650, upload-time = "2026-08-28T09:57:37.944Z" }, + { url = "https://files.pythonhosted.org/packages/4a/77/ca2f8e997d9bfdb32205297aff38f210f398822d895b1af1b59fd9df9c13/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dedce55295becb61921e386b99d4f2706045306e7fa52249a33004c837379fb", size = 1951261, upload-time = "2026-08-28T09:57:39.339Z" }, + { url = "https://files.pythonhosted.org/packages/a0/53/bd12e1a9255df4edee00353778e2614b5346265d51e1567ab72153e803a2/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f47b8a949e60f027f0aa0a6f6c7b7e9c55cbf4380d10b344e282fa4e7ab1e1b", size = 2021808, upload-time = "2026-08-28T09:57:40.69Z" }, + { url = "https://files.pythonhosted.org/packages/d7/41/f7f312751ebc6d6767da91964a9c7954c18e226a1720ab234e3dfb9d6c17/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:200aa3dc9f8d54f0754f43247c0bad0999fdcfbfd2488384dd44f37279271fe6", size = 2196184, upload-time = "2026-08-28T09:57:42.275Z" }, + { url = "https://files.pythonhosted.org/packages/3d/93/ce93aa030ab6bac4683ba8861e7baad89dd24b02e66b8801a0e4f6a00311/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d30e1a4f138b8951063e9a394752a9179b51da288ffa507b1e659222f4c1793", size = 2238212, upload-time = "2026-08-28T09:57:44.122Z" }, + { url = "https://files.pythonhosted.org/packages/34/a1/c8e6b66f499f510752c07a092dfe27621f9c255635e59d38704b5681c35a/pydantic_core-2.46.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:850a08d167dde16db8702c274f320c7be9d7da6f6dff2b58b18f9e815bd94f5b", size = 2064073, upload-time = "2026-08-28T09:57:45.613Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/605e2b127ee30dbf4b1da9da4843587cf2b2d16486c241cc7a5be2d2c1bd/pydantic_core-2.46.5-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:c3471e5c4a949c26ec00a77f01df59096aa9495877de76fd60a980f8ee6be461", size = 2093102, upload-time = "2026-08-28T09:57:46.953Z" }, + { url = "https://files.pythonhosted.org/packages/4a/f7/1ab28093c09032ddce7c92c7a55d503b6ecd70f42c32492946c1cb5477b1/pydantic_core-2.46.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a3e26b6a8274211bddee2d0e4d0d42778f17a34510f49d2ec44b58abfc41736", size = 2133452, upload-time = "2026-08-28T09:57:48.362Z" }, + { url = "https://files.pythonhosted.org/packages/30/c8/47c79b756f12f85e8b0fbdb2b495f6b6eb32e6c98a4beae7a570a0b7c63c/pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fc5d783bd4a2387e97b8a2d5ec781cfb92b3d893bf82370548e99db5915935d3", size = 2146477, upload-time = "2026-08-28T09:57:49.74Z" }, + { url = "https://files.pythonhosted.org/packages/13/5c/79fc00cb8f651d6061991de8d7cedf1c78c73cbd4862c42ef418f03b8bfa/pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:356c8368cbc321050b169595683a2e1d63413b1e0e2868b330af9fc14c616d3f", size = 2300832, upload-time = "2026-08-28T09:57:51.639Z" }, + { url = "https://files.pythonhosted.org/packages/b4/72/dd1a29853cf6d22a1ebd9e3baf0239cbc57d2d16caff36a89e38eb9b1db3/pydantic_core-2.46.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eb7d8d0e5886a89a55d2eef490e272fa965a9d57c6b29a5b5088a7997ec2cad1", size = 2320505, upload-time = "2026-08-28T09:57:53.236Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d1/ba4a8e06a9ddad0b4caf69cfaeecc0fbfcec20473bd808f5127fd16491c4/pydantic_core-2.46.5-cp310-cp310-win32.whl", hash = "sha256:4d44cf99ddebf875f9b68cc267aa684c99b7b44fe63ee1cac4ec163807290069", size = 1956853, upload-time = "2026-08-28T09:57:54.592Z" }, + { url = "https://files.pythonhosted.org/packages/f2/94/205ed9d7ddaf44acd489889708ea124a3f41bdb42c141c8684d528ad0e7a/pydantic_core-2.46.5-cp310-cp310-win_amd64.whl", hash = "sha256:1e5aad1220a1192c42341c8fd4a8686657e73ab2a920c970bdc4de334fe3193d", size = 2042551, upload-time = "2026-08-28T09:57:56.017Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b6/81d2d19ea0be2c03664381b59f65fa72fc7969decedae00bc2c4ad835708/pydantic_core-2.46.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a1dee1b804ff4d11c663636cf15d2ea47e9f79cd56c033fb1cbf08924842a48f", size = 2074737, upload-time = "2026-08-28T09:57:57.711Z" }, + { url = "https://files.pythonhosted.org/packages/0c/18/b70da8300e292df4099684ea11b1958043580d2f50d2dc8bf7e542bdd84a/pydantic_core-2.46.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d625a186a65201c23a9e3b8ed9c47e90a026e03256608cc91851c6709096844f", size = 1921751, upload-time = "2026-08-28T09:57:59.265Z" }, + { url = "https://files.pythonhosted.org/packages/e7/1a/0d590341b6ffa4b4aca83508e6b8db4761aaeacfc15a25ca3815876d4797/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8507560a9284e1370bb048ed4282012fbef4e8d109875b95e884d228552061", size = 1948231, upload-time = "2026-08-28T09:58:00.678Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/02eb35761c51f2f7b1b042d6ab4cda6600f0c8c88a2243b3f734376201e5/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f93c5fe914d75fbec9a49209b00da5f08e9e467d69da2b1510c81940cfd10be", size = 2020708, upload-time = "2026-08-28T09:58:02.267Z" }, + { url = "https://files.pythonhosted.org/packages/4a/ea/f86073830e35d508cc8ddf9c3d9e6e6840fcb88d34bf726b0b4710186f27/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c767f552b21b10f774aeac128e828eafb796adfa1b666a18bf6321453c3a", size = 2194914, upload-time = "2026-08-28T09:58:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d7/fc36240d7791ce90939e51608568c33bfdae26202016f9770c229a487d86/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:701b2e04b560eeb4bddf7a25ab8ca476176e34fdbd9a0e18196f0d12d4685f0b", size = 2235622, upload-time = "2026-08-28T09:58:05.516Z" }, + { url = "https://files.pythonhosted.org/packages/cf/bc/3fa2d76b83162820a17da7f645b28d1cba99fc8e1e5fc6517067ec450fa1/pydantic_core-2.46.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49776eab08766a08dfff7012f8b422dcd7e25e43b316eedf0477c24fcfa84b7c", size = 2062091, upload-time = "2026-08-28T09:58:07.135Z" }, + { url = "https://files.pythonhosted.org/packages/ab/9a/095d557bb492c90cd8a70a6dd048bf793d433d03d86c81c11e912e4cd049/pydantic_core-2.46.5-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:a2468d93d181667a7abd66e1b64bb9f76f361b0fef8faddf687456453576f5ee", size = 2089904, upload-time = "2026-08-28T09:58:08.814Z" }, + { url = "https://files.pythonhosted.org/packages/24/98/7b76b1ad10a19a617a52aaa1d80e159115af939b095e86f8e756fd52e0df/pydantic_core-2.46.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53feb344243bb9510a9dec7bf3cf1b64d88a98af5dc7872a5160465f8b198c8e", size = 2132244, upload-time = "2026-08-28T09:58:10.435Z" }, + { url = "https://files.pythonhosted.org/packages/20/32/7d6ca365fadba186a0c8f85de1a701663bce81efd309d9479be58687622f/pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cd5214352ae68f3b5e9af7768bdc5253695ee069675db3480518420b3be881f2", size = 2143901, upload-time = "2026-08-28T09:58:12.033Z" }, + { url = "https://files.pythonhosted.org/packages/f8/09/eb9a6aa57f22fd1541a9c0aa2a1f3aeef3ec65347d33e10a6da2f43e0ee9/pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:9432f3598db432cb51c5b37fdbf29a60fcccc79e30d37a05022776a6bc4ab689", size = 2299425, upload-time = "2026-08-28T09:58:13.614Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f9/548a5bb9d4ba8cd26e26daf48052236f6b38bb61e7b7241fbc3c995719eb/pydantic_core-2.46.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8feeac04b5794e513e710af2f9c87d49f31a6dc47967bb264a1fed61a8989bec", size = 2318566, upload-time = "2026-08-28T09:58:15.199Z" }, + { url = "https://files.pythonhosted.org/packages/4a/20/06454d18834c02c406c9133f1a3b485305fd9ee984f9636c2f730bef6a9d/pydantic_core-2.46.5-cp311-cp311-win32.whl", hash = "sha256:892a881d5f68c2b9ea304b7a6c2c60d9343df578a311b0f86b94bc8f1ffe8129", size = 1954258, upload-time = "2026-08-28T09:58:16.813Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c2/718b9deb4b72453b5d8c7447a3b14cb77bef36917ef5f514e0948a4096a0/pydantic_core-2.46.5-cp311-cp311-win_amd64.whl", hash = "sha256:40375c2d05acec10323e45dfe2077ac44bc74659008614af5069034e2cfc781c", size = 2041030, upload-time = "2026-08-28T09:58:18.288Z" }, + { url = "https://files.pythonhosted.org/packages/67/ea/c1d1a5b72d6e1ff7f377a4d9199f6591f095beb5b409a8a5d89f7238d939/pydantic_core-2.46.5-cp311-cp311-win_arm64.whl", hash = "sha256:28a6a556cd3b6066bea827857f9d9cce027c96f776e512f544a581f9e42161f8", size = 2009234, upload-time = "2026-08-28T09:58:19.929Z" }, + { url = "https://files.pythonhosted.org/packages/82/3f/76358795aa7a8c6d4f36e2cb828ad1c90ee118e1393a9281664f5aade9d4/pydantic_core-2.46.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d", size = 2076516, upload-time = "2026-08-28T09:58:21.576Z" }, + { url = "https://files.pythonhosted.org/packages/db/50/26b091836076ce4cb2fac264186936acc069e0595772cfd02a563bc4761a/pydantic_core-2.46.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e", size = 1922874, upload-time = "2026-08-28T09:58:23.766Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/2a8ce3849e299d44e2d2c196b6082643a3235565a735cb51db7a6261f614/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29", size = 1951772, upload-time = "2026-08-28T09:58:25.435Z" }, + { url = "https://files.pythonhosted.org/packages/87/46/ac0dc8bdd9e6048183a14eb127764e7ad9240021c17513074a4711b0e31e/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4", size = 2031832, upload-time = "2026-08-28T09:58:27.102Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/339de5bef7be36301a2231eaa52e62163742c2281f11b5f4892bc79785cd/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a", size = 2208645, upload-time = "2026-08-28T09:58:28.948Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a0/9ff22b797724262da14427abaed4dd1d864a139693fc5e7809114376a716/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62", size = 2265935, upload-time = "2026-08-28T09:58:30.625Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a4/eb9409ec0736e50aa70a412f16c204ed149516846912f7e6724d4c73ee53/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2", size = 2066284, upload-time = "2026-08-28T09:58:32.289Z" }, + { url = "https://files.pythonhosted.org/packages/c0/02/7f6156ffc926857f1c37c07d9a388682865a81830ab6a1b637082c25e399/pydantic_core-2.46.5-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869", size = 2105889, upload-time = "2026-08-28T09:58:33.986Z" }, + { url = "https://files.pythonhosted.org/packages/92/b1/e781d357ebe09fc929f995700f1b3503e8897f1cece183ecb1300d4d67e9/pydantic_core-2.46.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5", size = 2158006, upload-time = "2026-08-28T09:58:35.647Z" }, + { url = "https://files.pythonhosted.org/packages/70/0a/644597d84ab400e50609c192120b85c9681c22d3a20461b9060a79be0a7a/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3", size = 2158408, upload-time = "2026-08-28T09:58:37.38Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ee/ca3b7b3a4b3769ffe9ce9432a7c9be755de9593a46d3b0d54d0409323e44/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b", size = 2309609, upload-time = "2026-08-28T09:58:39.22Z" }, + { url = "https://files.pythonhosted.org/packages/ce/52/39fa1f451486019524ca685020390e7ca351832fd874530ba30c8628e6dc/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0", size = 2342618, upload-time = "2026-08-28T09:58:40.89Z" }, + { url = "https://files.pythonhosted.org/packages/81/5e/468fc630568c61dcef3cd47ad32ffbeed9af643f49208d1ea86ab4f890c4/pydantic_core-2.46.5-cp312-cp312-win32.whl", hash = "sha256:5cb482e9e84c851f4e623fe4acc1ced89168cf1fe18f7089db4548c8f5bbb65b", size = 1939475, upload-time = "2026-08-28T09:58:42.591Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c9/4c19f41b84cf6b622a72fbeed7665b25d47a187d68d47d0d430c07f23268/pydantic_core-2.46.5-cp312-cp312-win_amd64.whl", hash = "sha256:5e81740c09e310f5aa5cbd3e434a01c154d4bef93241c7877b39f211d2b78ba8", size = 2043140, upload-time = "2026-08-28T09:58:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/af/dd/0c1a050299147c746e5256db16d645ab5efd4f78c59937d581a0524e74a2/pydantic_core-2.46.5-cp312-cp312-win_arm64.whl", hash = "sha256:f7b0ec93a2893de856652154d73b7ba622f26fa97726487dcac373de5f4c6084", size = 1997729, upload-time = "2026-08-28T09:58:46.13Z" }, + { url = "https://files.pythonhosted.org/packages/f5/37/5abe39a8372a61d3dc3c1338fc504281c01b32fdb3169cd7187153b56d3e/pydantic_core-2.46.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0", size = 2075885, upload-time = "2026-08-28T09:58:47.856Z" }, + { url = "https://files.pythonhosted.org/packages/21/43/6323b1f8b217780454c61304bcd2b38ae4762f50754414124603ccc90bb2/pydantic_core-2.46.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff", size = 1922768, upload-time = "2026-08-28T09:58:49.58Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a3/c05ca796e1197618a774b01e596aeedfefc2f7d8c01ae3054e910b120e8a/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:193375f3548919d3f0b60936ca113ada3e38f264f91b9b8e0508efaad57be931", size = 1951241, upload-time = "2026-08-28T09:58:51.511Z" }, + { url = "https://files.pythonhosted.org/packages/68/32/33bc39ac705c52cffc908e8389f9754fdb208aea5c69cceddf4eb3ce99af/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79bdfa52f843137045b2d081cc05c120ba6665d29b7559c2c47690906f39279f", size = 2031975, upload-time = "2026-08-28T09:58:53.166Z" }, + { url = "https://files.pythonhosted.org/packages/b0/70/2333e885c0f6a67bc105c5916965dac9b57f2718ee20d81d1a06a4ebdc13/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24922243639cbdac66c75fcb6fd6495a9cb52b213d62f9a0d16f0310b1ff8038", size = 2208542, upload-time = "2026-08-28T09:58:55.017Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ea/296debfb4264207bbda5936133892e027c0a58875ad53ebd512fba8ec3a2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76fe65e607be28c7fd4d56fc3c42b1583aa058ce3408b7ad0fd540171d31f9f", size = 2264692, upload-time = "2026-08-28T09:58:56.767Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/9e4de77a6271e07a76d2d58b11c091a979c191ed2939bf80067568b369d2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f7b393a8b3da82f5c1fc0751e6d01ac6c55b93c18226a60bdfba4a724efafd1", size = 2066633, upload-time = "2026-08-28T09:58:58.531Z" }, + { url = "https://files.pythonhosted.org/packages/8d/db/f9e9d0c97445987b2084823d5c240de88087338f04fc2cfaa2df186b8049/pydantic_core-2.46.5-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:7ac031912d54f3d83ef3b3eb98dfabc1608802e2202263d25957eeed40b94761", size = 2105235, upload-time = "2026-08-28T09:59:00.421Z" }, + { url = "https://files.pythonhosted.org/packages/07/c5/79169b047b3b2c3e99e04bc76372af9637e0bf6db638274fa927df96369e/pydantic_core-2.46.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:837b396ca3d7b74091ca623f6cbd8351bd42d670a79c2683e79fb089f06a2de5", size = 2157367, upload-time = "2026-08-28T09:59:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/26/b5/ba6057afb7c291bd449f51b867f95aef2072941c4ce4e5c31d6ffd132d3b/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:5ee239d575f80b08eca11f6e20f90c4c695de7825c67eefe6091fbf20dda648e", size = 2158420, upload-time = "2026-08-28T09:59:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/6e/28/2057abecaafdc22912afa819603a51f0a62d40643b7c4871c51721fea9be/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e80675d75ae2cd14372cb65cad5400d9347a3d3f6c13000183f22dfd027283ed", size = 2309588, upload-time = "2026-08-28T09:59:06.048Z" }, + { url = "https://files.pythonhosted.org/packages/71/9d/881156dc404e27479c4246128d73538464cab4a239bec61995e227644c30/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c4b71f10dd532fb7a5cbc8f58707779e64f03a258c2bf8bfbaecfcd9970b519", size = 2341866, upload-time = "2026-08-28T09:59:08.539Z" }, + { url = "https://files.pythonhosted.org/packages/5a/38/d66f443a259f84d13babdceae568e572b0ed26da17ca5d0a649ebb110a67/pydantic_core-2.46.5-cp313-cp313-win32.whl", hash = "sha256:97bf8de4d541598c94a59344eeb988a94c08ff76b5723c41f6567ec18c7892ea", size = 1938580, upload-time = "2026-08-28T09:59:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1e/1d5371213f4cc9a7ed70c0bfcc7911de22311ee99a662a56077d7292d2ac/pydantic_core-2.46.5-cp313-cp313-win_amd64.whl", hash = "sha256:15f4a94963c95accac15b7b657bb177d3ad82bb90b0d0526d9a9b85079925db5", size = 2041980, upload-time = "2026-08-28T09:59:12.396Z" }, + { url = "https://files.pythonhosted.org/packages/5a/48/4222d90b1c67568bace4dec6dca6271449c66de3595d72b6d098f5fde597/pydantic_core-2.46.5-cp313-cp313-win_arm64.whl", hash = "sha256:d22a945598fb91236b4dd793a6e42e4f3dd7740bb5aace5ebd7d4c08d13bb575", size = 1997213, upload-time = "2026-08-28T09:59:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8a/14596f2a8367da50cf7cbac48169ee5d9c8e11d486a3b527082384630c72/pydantic_core-2.46.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c1c43ad4339643d70ebb8124e1305a7dab423001eff58bb41a0f731adbc98355", size = 2074081, upload-time = "2026-08-28T09:59:16.141Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d5/d8a4eb6d6c7f66b91dd37c576d76e9e60fba900caf5372c17bcf949febc2/pydantic_core-2.46.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1a353f84de772f423b5ffb11d7ae352fbbef0f446f3c0b0af0f8236d7233606e", size = 1920497, upload-time = "2026-08-28T09:59:18.065Z" }, + { url = "https://files.pythonhosted.org/packages/8e/26/092079428f86e927e030b2c0ced87df69dbb1c875cdeaa67bf42ea2be746/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5086029a57366b8cf81b130a43908738095c270c21a8d7f0e8bdfdb89718e2f3", size = 1952130, upload-time = "2026-08-28T09:59:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/8ec0e290a9ebaebd64047bf5fda94be835c6b1551b02437e4b76778fbcd7/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46c25dda9d092a06c08db76ffe0a197107904d0dfac653f7d5306bbcd6d6119c", size = 2026371, upload-time = "2026-08-28T09:59:22.227Z" }, + { url = "https://files.pythonhosted.org/packages/01/72/4fd20ad520fb8da0157f95b27a7eb05a72790ef08138e7701ac972c342ea/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37ea7b83c935e5b0d68c9449b82651accf78a10828b2c02b2f2d9e9496446c21", size = 2202822, upload-time = "2026-08-28T09:59:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/31/b0/d16e0771206b29314f0d52198b720be21e8a99ab2bf11e3bc0d7c9cebdff/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64e88d5585bea9ce95861079de72006c7fa6d3df4e3a3b65ba31eb979c15c9f", size = 2262756, upload-time = "2026-08-28T09:59:26.608Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9b/59634b7ac631c63b2a37760eb6943af3e29573d6b59a4abc5e7f019d4cee/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d510bac3ee52247af28ed4bb18a1e799f040ac60fd2bf5ccd4c92f1fbe786f", size = 2068352, upload-time = "2026-08-28T09:59:29.044Z" }, + { url = "https://files.pythonhosted.org/packages/08/7c/570abb1ad2155348dc754ea91be22e5aaa18eb6d69a6068f7c6f2679a6ed/pydantic_core-2.46.5-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a2a5e1d0ff29adddc9f6d6821a66302e4493f8ca898b715b6b1182c2c201ea0a", size = 2104777, upload-time = "2026-08-28T09:59:30.95Z" }, + { url = "https://files.pythonhosted.org/packages/8e/25/5bf74adc65a1ac5b7be3f6cb0bcb5433615c1598a801c19d830d84c98ded/pydantic_core-2.46.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03b9666e41e35d8909852ba191a0607520f81b74eaf12ccf8737005dbb313821", size = 2156312, upload-time = "2026-08-28T09:59:32.604Z" }, + { url = "https://files.pythonhosted.org/packages/90/6a/2ef38830675e050121040618135564ed56b860b45433b02d9b4ebece46f3/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:a91c17edf6eea2402cb5457b4c89e99bc5ed1004aa34c4adf1d4258c1a5c22c2", size = 2150067, upload-time = "2026-08-28T09:59:34.453Z" }, + { url = "https://files.pythonhosted.org/packages/90/ef/a7dbb03a14a64c2a4621f989c615ed9a892535a6cad938fc27079f919d80/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b49924c73a235e969511bf2aabdff3beebf9820931f646c80274d5d780010c47", size = 2304516, upload-time = "2026-08-28T09:59:36.194Z" }, + { url = "https://files.pythonhosted.org/packages/68/f8/6bb4c4b80e8a6fde1904c64a51c62a1d04fcdfa3ea521a66b2ddefa1d885/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:2cbd9a5eff05e51c447c34dfa4632145b26b09120cf04bd0c871e44c1a5e1c9a", size = 2335223, upload-time = "2026-08-28T09:59:37.931Z" }, + { url = "https://files.pythonhosted.org/packages/2a/80/f46b8c681195190b2c1f1c7c0a81abce60663e987613e09ef64d433dd96b/pydantic_core-2.46.5-cp314-cp314-win32.whl", hash = "sha256:2d5d76654becf5efd62c9e51c3756c67b49498b0c9a40884934c40807adbd074", size = 1934827, upload-time = "2026-08-28T09:59:39.836Z" }, + { url = "https://files.pythonhosted.org/packages/f7/3c/60674207246bc0a4009d2391b7c7251c7159f279c8d2ab8aae8ef46f3dee/pydantic_core-2.46.5-cp314-cp314-win_amd64.whl", hash = "sha256:fa10ef4112775900e7a0661068635eb67b2ab824fbde764de6e0e21982a93db0", size = 2042648, upload-time = "2026-08-28T09:59:41.792Z" }, + { url = "https://files.pythonhosted.org/packages/69/0c/117c562c7c1babdf44576b72a5e496906506c93690387ecfbca7c729ae2e/pydantic_core-2.46.5-cp314-cp314-win_arm64.whl", hash = "sha256:045ab3b6d308439e32b81cc173bba5b9018bc6ed896afd0c65b3b009b1699af5", size = 1989652, upload-time = "2026-08-28T09:59:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/e8/66/9336ae58f9eb68c41d121894e52c4c89eccb07eb8f602a04ee9c3f37736a/pydantic_core-2.46.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8816f3d218beb4b787de5c9759c259b8fa61f9dec42dc7811f320a33771778b7", size = 2065829, upload-time = "2026-08-28T09:59:45.364Z" }, + { url = "https://files.pythonhosted.org/packages/c5/02/bc19b47a96c2d3109760711acf22369e56bd7e405ca52f7ade164d2ead57/pydantic_core-2.46.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bce57638e08ac148e5778cce7feb968307a727d66f8e2274a543d0cf0c9ad6a3", size = 1905716, upload-time = "2026-08-28T09:59:47.18Z" }, + { url = "https://files.pythonhosted.org/packages/52/a4/70b47c0509923dd98ccfed04fb3e32ea3849c82a0ff2205bb41009b43c00/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976e1128455aa595ea04c79ccfedff1aaeab96ee013fcc916bed120c4f0ad94f", size = 1934216, upload-time = "2026-08-28T09:59:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/52/ab/aa03b65f7bb198585edf806b906c3223ecf1795543e39e23aec4cce27ad2/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b891faeedeafba41b2983e5001a81b6a915b69544c7e7570d1989ce1c36ac7", size = 2010635, upload-time = "2026-08-28T09:59:51.692Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8b/0da06343f30b84ec549aafd309c6456223d5dc8bd36af504c573faad561d/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f194189415698233dd1114a093a9b56e61e2c57e11b469be3b0506f46f0771c", size = 2209369, upload-time = "2026-08-28T09:59:53.582Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5b/844c4defaa34a3df66eb9257087d121d70c201298b96abdf9f492fc2f1bf/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82a36973cf8a2ef5406f4fe2edbf8ed0c99629535d959e0b100c76a32535a111", size = 2253238, upload-time = "2026-08-28T09:59:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/f4/64/a4e536cb16d7f61a7fd3120b46c577fc7fa7325992f69c4f52bc786d77d8/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbb78909f52b981d3b2d56b97328d71eb0b974c36bd77c920123a7ebb192829", size = 2065740, upload-time = "2026-08-28T09:59:58.038Z" }, + { url = "https://files.pythonhosted.org/packages/5f/75/aaa38c6bc2d085f6605b34eabdc6a8a4e0b2e61fc9c8e6e52b28e97b3125/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:52e24eacdb536cade636aa90fb851835222becff8484b7001fdc78cb0290f2aa", size = 2087425, upload-time = "2026-08-28T09:59:59.898Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/fcab4cfc39aba3689e1d20c8b5250ad280957022c09af2ed9cd585602a5e/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37ae34309d7bd8c0d61ab839668058f2a7962ea1fc51d105d2db228fe0618034", size = 2139306, upload-time = "2026-08-28T10:00:03.057Z" }, + { url = "https://files.pythonhosted.org/packages/2d/f4/f1d03a4bc9d9acbc62f4d742b8a319af52f71885079868b2ff8e48a651ee/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:0cdbada856a1c69a7624a64d3d9aefe79300bd6ef827b43a4f265010b9b55184", size = 2144589, upload-time = "2026-08-28T10:00:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/83/f3/7a53bb1356de514a4cd295f25b6ac39237895620c0462d2592b76c16e114/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:545f26c504b27c3758439a5e6d9349931f0a04f855668d5fe323c89e82300a38", size = 2288882, upload-time = "2026-08-28T10:00:07.931Z" }, + { url = "https://files.pythonhosted.org/packages/cd/94/5a81583660c175c59d49ffb09f4b3a44debeaf86a19fca664ae1cdd9ee32/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9", size = 2335210, upload-time = "2026-08-28T10:00:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9f/5d685c2693b972d1a59c998586e8823712b66603aeff47ee60a4bdaafd37/pydantic_core-2.46.5-cp314-cp314t-win32.whl", hash = "sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9", size = 1921180, upload-time = "2026-08-28T10:00:12.35Z" }, + { url = "https://files.pythonhosted.org/packages/70/12/5c94ee16d65a37a15f9e869f5e6256df111154491173801a4c5e800ab548/pydantic_core-2.46.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290", size = 2020515, upload-time = "2026-08-28T10:00:14.774Z" }, + { url = "https://files.pythonhosted.org/packages/63/19/67830dda664e6bdf9285ee2e40f355d0d7d6b92aa0c42e8d217bb8d33d36/pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f", size = 1989276, upload-time = "2026-08-28T10:00:16.984Z" }, + { url = "https://files.pythonhosted.org/packages/af/1e/ecca01fce348f7e8afa9572441ff6f7d1cc70d21e4859f33944d10877e1e/pydantic_core-2.46.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:c14ad3bdc85ee7f318742c457ca3968a92126d144b15721c759033bfb06296c2", size = 2075342, upload-time = "2026-08-28T10:00:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4c/af80c7a8032dfc897040ad5cb772bebde529a381186499e6e29987f23f8c/pydantic_core-2.46.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0bddb4020d8f04175865ccd17eff3040874fc11fb593f424edb452653b4b947c", size = 1907219, upload-time = "2026-08-28T10:00:53.438Z" }, + { url = "https://files.pythonhosted.org/packages/be/3e/54d89e2b092e778716bf6153634ef479e955f48c261090be23aa1e0fb0b5/pydantic_core-2.46.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2471fd51c61c610e1dcf7de44d7299283661654d11264ab4802b303368d69c47", size = 1953393, upload-time = "2026-08-28T10:00:55.58Z" }, + { url = "https://files.pythonhosted.org/packages/ea/89/828ee90cda28ce17bdefaa3a6eaf74fe430e113295a10e6126beca559d6c/pydantic_core-2.46.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10ec717381bdbfafef34607824db4c91de69ff085e4fca3b2af91b4fa17e68a", size = 2099024, upload-time = "2026-08-28T10:00:57.794Z" }, + { url = "https://files.pythonhosted.org/packages/df/dd/053c2e4303f791f3b8f8a14ab0b22008e8eb21d868c0c90b4f9be705b76a/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:013d6f3483d81e02e7c328831808f336c8596ee33b4bd4026b9ffb1e960b8942", size = 2062540, upload-time = "2026-08-28T10:01:00.318Z" }, + { url = "https://files.pythonhosted.org/packages/d7/dd/a18df751a5e37dd51bfad7f68e766999125bebe68c9e1d10a493ad01bd63/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:e9c134bb666dd54b778b9fc0d2b50cbb7f979b9e3716f26a88c9ab3b6fc1dd0f", size = 1902040, upload-time = "2026-08-28T10:01:02.529Z" }, + { url = "https://files.pythonhosted.org/packages/b7/13/01d40f9d07ce8a779fd6e0bd8ad4fba91309500dd67b869e2e219d261a6d/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:347ec774390c87326a2e4929d58d3f7e8763a104d5d35f4cd595a4c952366433", size = 1967479, upload-time = "2026-08-28T10:01:05.004Z" }, + { url = "https://files.pythonhosted.org/packages/fa/04/c81d4841331c2178b6fb09ae225425e110ed72d990c9fe556c4ec03d1013/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e24d8f05fa2d28513d94e877e9c75ad66175376209b3977f916e240e623193c", size = 2111034, upload-time = "2026-08-28T10:01:07.345Z" }, + { url = "https://files.pythonhosted.org/packages/20/21/22102e9950b3049526d20e811b95396508377d87651edd2b80d2b3d28659/pydantic_core-2.46.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ab4b66edffb32d9e951efb3814bd104b8367a7501b81b955cacb5726d897389f", size = 2071333, upload-time = "2026-08-28T10:01:09.636Z" }, + { url = "https://files.pythonhosted.org/packages/d8/18/87aefa427d191e6d3ab1447f1efc1cdcac86af1069239b133e8a0fd7f7c9/pydantic_core-2.46.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:337639ba62a11acde6ef3aeb08c8ea755f8ef1fe5e513356c0f36a2b0d7568b0", size = 1912713, upload-time = "2026-08-28T10:01:12.285Z" }, + { url = "https://files.pythonhosted.org/packages/1f/93/fd89e9ad49b1805ca94d24ce1088b7d305f05c35ffafcedb9819d03588a0/pydantic_core-2.46.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:413a717a410d0c817ef5b786a059415550b3794e1d0c2abffd9efb93a3d9f7b4", size = 2090926, upload-time = "2026-08-28T10:01:15.19Z" }, + { url = "https://files.pythonhosted.org/packages/6f/45/8e59dab6acf8d35f02f0a958980074f31038968bdb2c983fcae9d1efee03/pydantic_core-2.46.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e449def1945a462c464331254e5a44fca7c3b4f9aedf59ec2f50f8066dd8e25", size = 2131303, upload-time = "2026-08-28T10:01:17.937Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a5/e1d4dc5180dd887a9522efc1f8716b8692b7606b1d3273d7862eaf66be44/pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a445486499897b88a7d6c310c88ed64dd37b1b59bfd7ae9107490bbb362f47d6", size = 2145128, upload-time = "2026-08-28T10:01:20.694Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/ad493864a7fb21c0c4df98f965e2db430cb25a9d7369b5778d5016c09fd9/pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2d330aaba8621b1edcec8ae2c4050f63b84ccf6d98723a8f212e9684713abf0e", size = 2294560, upload-time = "2026-08-28T10:01:23.495Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/b41c84c913f29973a268e6c2b5bbf13c95adb9956c126d10da11ba3b2bef/pydantic_core-2.46.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b6acfb46a814762367fb7ba0828b0a17d441b92ce249a0e007474c9072662dda", size = 2317531, upload-time = "2026-08-28T10:01:26.334Z" }, + { url = "https://files.pythonhosted.org/packages/db/1d/068464f23075f66a8f1b806935e9cd9363ee446636ea70d2c22ee8659dbf/pydantic_core-2.46.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d0a24b40877af2de4950252be9d21eaf7fb07660f3c2cae1f56c6b599ada5266", size = 2140686, upload-time = "2026-08-28T10:01:28.947Z" }, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "snowballstemmer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/d5385ca59fd065e3c6a5fe19f9bc9d5ea7f2509fa8c9c22fb6b2031dd953/pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1", size = 36796, upload-time = "2023-01-17T20:29:19.838Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/ea/99ddefac41971acad68f14114f38261c1f27dac0b3ec529824ebc739bdaa/pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019", size = 38038, upload-time = "2023-01-17T20:29:18.094Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/c3/8a3b59c25070cc61dc517fbdfa5dc0904670c96f605cc69759dc09166b99/pyjwt-2.14.0.tar.gz", hash = "sha256:77283c83fb56ecf566a886c757a714bc83668e38156de2cce8263302f42e0b86", size = 113177, upload-time = "2026-09-11T13:11:54.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/97/672cb32ce0dfea44b740cb7b4f97038463b9cf7c0ead1aacf595572851d6/pyjwt-2.14.0-py3-none-any.whl", hash = "sha256:ad0cef71c756a56e74863c2919cf0985f72decbcfcb550ee2f422e7c62b5eedc", size = 32896, upload-time = "2026-09-11T13:11:53.409Z" }, +] + +[[package]] +name = "pylint" +version = "3.3.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/9d/81c84a312d1fa8133b0db0c76148542a98349298a01747ab122f9314b04e/pylint-3.3.9.tar.gz", hash = "sha256:d312737d7b25ccf6b01cc4ac629b5dcd14a0fcf3ec392735ac70f137a9d5f83a", size = 1525946, upload-time = "2025-10-05T18:41:43.786Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/a7/69460c4a6af7575449e615144aa2205b89408dc2969b87bc3df2f262ad0b/pylint-3.3.9-py3-none-any.whl", hash = "sha256:01f9b0462c7730f94786c283f3e52a1fbdf0494bbe0971a78d7277ef46a751e7", size = 523465, upload-time = "2025-10-05T18:41:41.766Z" }, +] + +[[package]] +name = "pyproject-api" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/2d/7b6837335aab9c00bcd18adde43c78948db6a2fe26dfa79a6bfcdefd3c5d/pyproject_api-1.11.2.tar.gz", hash = "sha256:7bff8690a101f5f0bac3221475d214018f196ae5eea3af9c9879f4d682a3fd60", size = 24445, upload-time = "2026-09-18T01:11:37.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/7e/234694c81ad7476fddb7bb3420c051fab33d09120a34e5d42ec1327c4a82/pyproject_api-1.11.2-py3-none-any.whl", hash = "sha256:69040bb421a3d502217bf9d5f92a94e65e1db3fe25b641d6064b2b5b9880428b", size = 13327, upload-time = "2026-09-18T01:11:36.228Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, +] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/15/da3df99fd551507694a9b01f512a2f6cf1254f33601605843c3775f39460/pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", size = 63245, upload-time = "2023-05-24T18:44:56.845Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/4b/8b78d126e275efa2379b1c2e09dc52cf70df16fc3b90613ef82531499d73/pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a", size = 21949, upload-time = "2023-05-24T18:44:54.079Z" }, +] + +[[package]] +name = "pytest-freezer" +version = "0.4.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "freezegun" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/f0/98dcbc5324064360b19850b14c84cea9ca50785d921741dbfc442346e925/pytest_freezer-0.4.9.tar.gz", hash = "sha256:21bf16bc9cc46bf98f94382c4b5c3c389be7056ff0be33029111ae11b3f1c82a", size = 3177, upload-time = "2024-12-12T08:53:08.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/e9/30252bc05bcf67200a17f4f0b4cc7598f0a68df4fa9fa356193aa899f145/pytest_freezer-0.4.9-py3-none-any.whl", hash = "sha256:8b6c50523b7d4aec4590b52bfa5ff766d772ce506e2bf4846c88041ea9ccae59", size = 3192, upload-time = "2024-12-12T08:53:07.641Z" }, +] + +[[package]] +name = "pytest-mock" +version = "3.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, +] + +[[package]] +name = "pytest-order" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/d8/82a5dc2aad3392f66c9741960fcbeeb45dd4d62eedb218aed6c52eea36eb/pytest_order-1.5.0.tar.gz", hash = "sha256:96acd7587b5a2855dcaa4a898288103d202894a61afd813adbc9b77aab04d90d", size = 54136, upload-time = "2026-06-13T05:39:41.636Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/5c/bd6a85b44beb8bd74c0e86ab699ef0e096e260dc90458b84fc54c28e4335/pytest_order-1.5.0-py3-none-any.whl", hash = "sha256:667ba2b7303fe2c529848663e0a41dfb0cc4d64f09f87272e4a9a1751efb52b2", size = 16634, upload-time = "2026-06-13T05:39:40.431Z" }, +] + +[[package]] +name = "pytest-recorder" +version = "0.6.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "black" }, + { name = "cryptography" }, + { name = "idna" }, + { name = "mypy" }, + { name = "pylint" }, + { name = "pytest" }, + { name = "pytest-mock" }, + { name = "time-machine" }, + { name = "vcrpy", marker = "platform_python_implementation == 'CPython'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/50/af42c0dd030259de336a4b93495e0021e66a107afd9c5ed87fc57f594971/pytest_recorder-0.6.6.tar.gz", hash = "sha256:1005b7a9d993a9d6c6fdc7cf18b48d687fb5324ab81da82fab65034362ffddb2", size = 16990, upload-time = "2026-06-24T17:02:45.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/06/07ca6377a619bb97c0debc7b97a5fc16b74ca632746eb1f0b6f3e8cb9194/pytest_recorder-0.6.6-py3-none-any.whl", hash = "sha256:66516eaeca0200a687f884e426bcdb6733ea0495c5e2d8bb4e3e2b0e8c595571", size = 22021, upload-time = "2026-06-24T17:02:46.445Z" }, +] + +[[package]] +name = "pytest-subtests" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/d9/20097971a8d315e011e055d512fa120fd6be3bdb8f4b3aa3e3c6bf77bebc/pytest_subtests-0.15.0.tar.gz", hash = "sha256:cb495bde05551b784b8f0b8adfaa27edb4131469a27c339b80fd8d6ba33f887c", size = 18525, upload-time = "2025-10-20T16:26:18.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/64/bba465299b37448b4c1b84c7a04178399ac22d47b3dc5db1874fe55a2bd3/pytest_subtests-0.15.0-py3-none-any.whl", hash = "sha256:da2d0ce348e1f8d831d5a40d81e3aeac439fec50bd5251cbb7791402696a9493", size = 9185, upload-time = "2025-10-20T16:26:17.239Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/57/250bd238b966cece44328235eb85290045d059265fdaf7527a3a958123db/python_discovery-1.6.1.tar.gz", hash = "sha256:cf87d3627dfb4412437fdd5b13eae402607722998d21567993aedbc59b23c15e", size = 84338, upload-time = "2026-09-18T01:31:53.971Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/7d/e9ffbadfbf89c93848412d04594135c4ae8c1d37d9e053b9c3ed718fabc4/python_discovery-1.6.1-py3-none-any.whl", hash = "sha256:d43fcdef879fe795352bd13ccf8d185ba5a9f86f36cfcd00529f596e737442b3", size = 38664, upload-time = "2026-09-18T01:31:52.448Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/53/ed9d74092561d4b01a2ef1349d52cdbc135e526c245f366b089cfca6de49/python_dotenv-1.2.3.tar.gz", hash = "sha256:a20a594dabeaa385725aa239d5244871c143ecb356add8a20fcf23773a6c3a35", size = 58945, upload-time = "2026-08-16T16:54:54.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/17/c5c6b53ddc18f297992099b3d9ec16c855c0ccc83263a21fe4d1c625ec6c/python_dotenv-1.2.3-py3-none-any.whl", hash = "sha256:904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9", size = 22780, upload-time = "2026-08-16T16:54:52.473Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, + { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + +[[package]] +name = "pytz" +version = "2026.3.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/48/fb042503b6ca6cd271261dc559fd6432f7d8c713153e9ec5c591af4dfc1c/pytz-2026.3.post1.tar.gz", hash = "sha256:2211d3fcf9a797d3405cac96ac7f61d80e6a644f72a3309607282fe8a2010c5d", size = 319745, upload-time = "2026-07-25T15:12:07.385Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/7b/39c34ca613b0b198cb866466651b26b045e2009864c5183c979a3b83f383/pytz-2026.3.post1-py2.py3-none-any.whl", hash = "sha256:dd95840dd199baea12d9cc096a1d452caa6596a1c1e4b5f3dbd1541855d5e815", size = 508283, upload-time = "2026-07-25T15:12:05.782Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "pyzmq" +version = "27.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/8d/5b3d5631c2f4b4b8862f64cd0c9eb777b5710eeb5125b4be8dd0a200a4c0/pyzmq-27.2.0.tar.gz", hash = "sha256:54d4259d1bfae24ecdb5ca79f7acc2eac6c286a02d6a0ae617797cb45f0726d3", size = 292316, upload-time = "2026-08-20T19:08:21.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/de/c9d653d686ec686bac6ae5f953c784ffc4d2e4a33f9c20d0326aff549ca8/pyzmq-27.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:480dba27b145373b5e103890f17969d891bc9e86746d6b8b29dd70b0d4addc62", size = 1458528, upload-time = "2026-08-20T19:06:11.455Z" }, + { url = "https://files.pythonhosted.org/packages/13/3a/e3ae8e56fdb87cefbef655dc1d808d5ac76eacf40af850c5d78fd81a7faf/pyzmq-27.2.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:722f0a6940be1a483c81029a271d950e04dc2ff113a42e21b3d2b7a0d8e59638", size = 985810, upload-time = "2026-08-20T19:06:13.409Z" }, + { url = "https://files.pythonhosted.org/packages/03/ee/0ace0abf6315f3f481388826f1e9218802d9b5bb4a01bae5ce42018c3b4e/pyzmq-27.2.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ee556ed1cf836f96de9d5e545563116426d4a94f21b8041fdc79408eff18ebb", size = 713840, upload-time = "2026-08-20T19:06:14.85Z" }, + { url = "https://files.pythonhosted.org/packages/3b/fd/aee8c87f4854a012e232a6557b1167b6cd207506f59f378954b2d8bb6ed1/pyzmq-27.2.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d64da42cae09e6b0c61368b4cc8ca80f23ce3af17584d08053f3dc957433d5ed", size = 888214, upload-time = "2026-08-20T19:06:16.295Z" }, + { url = "https://files.pythonhosted.org/packages/94/2f/9be34eab874a26aa91315be0b27f94d43d35dad544cbdf28bcd41691afb2/pyzmq-27.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:376981d106598beb70be384f44d8f589832fd0051d184d38d10043da3cc3b080", size = 1705252, upload-time = "2026-08-20T19:06:17.89Z" }, + { url = "https://files.pythonhosted.org/packages/7f/26/362344e337d6b5905d65d0c205e91090b39b3b5a59ec9b84dbe88a5dd60c/pyzmq-27.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:40d96cb7a8f6a43aa9617c00215c2b73e1b5e4a1d6cbc9f5860ed7ac682599f0", size = 2075298, upload-time = "2026-08-20T19:06:19.27Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ee/3b76b91e2bb8c5c12f8ae9ee553eb6254ba0bb048c2f0dea7d945420a0d3/pyzmq-27.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4ebc7889b31bc11c72e9f17ba3ebb0a8b0911cce413f41b498e55383a94819a3", size = 1926812, upload-time = "2026-08-20T19:06:20.621Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ae/83d0740f125a6ef91d5ed29e560679e4c5beeaa3ab1fc87e5fe1128c296d/pyzmq-27.2.0-cp310-cp310-win32.whl", hash = "sha256:650c6cd7cb39a069e7048261efe66fce8bf2e0052c831a7a099b7a0f2ea860d7", size = 571057, upload-time = "2026-08-20T19:06:22.078Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b3/3e99a7af1c25e84f68c6c7b16c51b664dd51e12d0bc3d252a48d298e9a08/pyzmq-27.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:82a09aa67871d4f2fcafd47bf670fb93210b232a7c2d4b8a54676314edf04033", size = 643232, upload-time = "2026-08-20T19:06:23.431Z" }, + { url = "https://files.pythonhosted.org/packages/06/cb/785d002a08e630807141b102fd2740cc11623c366bb4606465450bd12e03/pyzmq-27.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bad4813f270592cedf56977e31ac1fc374fb0f6f67ea5134a5e37c19cb429a8e", size = 567590, upload-time = "2026-08-20T19:06:24.664Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2e/8897afa4538707d86645f51cc50e66b2b84900edb1be9dc9af2c2fc04e5d/pyzmq-27.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:9216132843d139a123f243c07fe70f7487dce5041093dd77040f9adb5dc91872", size = 1457109, upload-time = "2026-08-20T19:06:26.022Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bc/dbce7bc1654fa25b1e68b9bad9e547906f581ce919c186a88ed951cb794c/pyzmq-27.2.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:d41ebb260b69329b7d4a2936d44c872c86dd785355b51366c8b14e07ed7e9373", size = 985701, upload-time = "2026-08-20T19:06:27.481Z" }, + { url = "https://files.pythonhosted.org/packages/95/cf/6981738b57c83fef33f356141ad83bf51e92f2f70c9d5767affd1a699f07/pyzmq-27.2.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:468139ddb2e494d06e586bd3a6835077e8b3764560c8db552fe685c5867fc24e", size = 714285, upload-time = "2026-08-20T19:06:28.962Z" }, + { url = "https://files.pythonhosted.org/packages/50/b5/13657961a845e29c28a4e7ac4202999ec90b3bba1890a5469ce2ae90359d/pyzmq-27.2.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39755dc4a923021bd0677990ffdbc21cff0e1ee1cf07fe3817acea153ef4cb67", size = 888465, upload-time = "2026-08-20T19:06:30.4Z" }, + { url = "https://files.pythonhosted.org/packages/58/5a/ca7ee7a767413d4ba858e93748b95e30b35b8c139849fba94de4433ea2e5/pyzmq-27.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:714f8cbd66c7e405338d668f79d2fe83fe923defe348e843be998603cf92eeff", size = 1705731, upload-time = "2026-08-20T19:06:31.819Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8b/083f6184e4eba566c9a3cc9974b1b0fe327b7093788135ba8133edaa67a6/pyzmq-27.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1132805970045adb9f5f05dd57040978286a8e21a5475f2c2ddf1bc983b9a2c7", size = 2074243, upload-time = "2026-08-20T19:06:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/57/f5/249362b664ae725d534c8843214fa9fd7fccd74532a19e24603954a88a7d/pyzmq-27.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b26f2d0493b79ce3c3112c8a12649418915582ba4707b8ed9f44febf2be71f42", size = 1926859, upload-time = "2026-08-20T19:06:34.796Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cc/23c613c15f06d879f13364d14c17e5e4e8304049411e96c1410e6e56c3ea/pyzmq-27.2.0-cp311-cp311-win32.whl", hash = "sha256:44f261eca7dfb9904ea2b56428f59ab693bbe2715c0413a701f17b067ebf877c", size = 570747, upload-time = "2026-08-20T19:06:36.337Z" }, + { url = "https://files.pythonhosted.org/packages/dc/bc/bbbcf89003c93f18e33665c26e3c48d75e3915c3dd22887f3a7aea2c5e26/pyzmq-27.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:8b86e04f55af0f4d8cd8ecf14c0b8b81ebc8fd66fa20126b753514628ecadc7e", size = 644845, upload-time = "2026-08-20T19:06:37.711Z" }, + { url = "https://files.pythonhosted.org/packages/14/c5/4635d0ba2b8493edf6d5541fff0b07fa1d986fdfc29c596a53a21e20f9af/pyzmq-27.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:917d601e9540098f580d2723d0ce6402cdb6f02bc8dc2de74e0dca6e13bffd1b", size = 567495, upload-time = "2026-08-20T19:06:39.246Z" }, + { url = "https://files.pythonhosted.org/packages/57/8a/153532fa53db30e116118164f3af269a1f3966b3e2ba32c89b12fe864bd8/pyzmq-27.2.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:591c8de5851c5ea372194469fe97587b97c3b641e9a70f31bb3474acbfde0241", size = 1431074, upload-time = "2026-08-20T19:06:40.601Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ef/c08b91248bb90a9efa81fa00ba81b69c157c74d0c5efbb2c319d91babb62/pyzmq-27.2.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:00e73942ef12cecbc7951c4a9104bb8ffaed742abb13af2da6833d90dd368cef", size = 973915, upload-time = "2026-08-20T19:06:42.037Z" }, + { url = "https://files.pythonhosted.org/packages/b4/78/a3a3a86c2b00fadb92ece1ca4f8f028d62b2ce9ac3526097239ab2d6fba9/pyzmq-27.2.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f8079d0521fe94bbb401fe9407578b28f3701627c8be2c9f7e0c5b77dcb0109", size = 697722, upload-time = "2026-08-20T19:06:43.325Z" }, + { url = "https://files.pythonhosted.org/packages/62/2c/d5828306f795e8d34676d266823b74e2101e0ad3760d12083de3e02abbb2/pyzmq-27.2.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dea74fd65f1fc5f7fe167916a473ebe6ed6174e5e5d9de11ea6583661be6cf43", size = 872258, upload-time = "2026-08-20T19:06:44.627Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/51253b78fd8739293e283407eeecb14215c02c71b6519af21f6eed8e69cd/pyzmq-27.2.0-cp312-abi3-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcc99ca132b667a4ed750afd42db4ea73288f18425a9b2e3c0af095665c491f5", size = 739591, upload-time = "2026-08-20T19:06:46.214Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3e/142c85b67a4c9678629b0cf6d5125b29663d75be69bfaa57a3cac344d780/pyzmq-27.2.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b8d5f66e4a8246cf77f7b8f7902af64f00553368fa0373c89d99b78f0ad79394", size = 1689031, upload-time = "2026-08-20T19:06:47.612Z" }, + { url = "https://files.pythonhosted.org/packages/0e/ee/0776fb0f98ed1eb74d77240087fef0ab045b6ad15cb09555c6c5134c98ad/pyzmq-27.2.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:d1526b42a2e725b84ed226f37becedc250c6347594e5ed304e4e9aff68c9aec3", size = 2059547, upload-time = "2026-08-20T19:06:49.064Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0e/ec77f691a4aebe29ab6329f996fb0e0270c876a3016086e3ca6ef733bcae/pyzmq-27.2.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f707bcf2c1d007d14d70531d4dd7b41060881c73efa845580bf6faaf9ea24d42", size = 1910457, upload-time = "2026-08-20T19:06:50.783Z" }, + { url = "https://files.pythonhosted.org/packages/30/97/1f5530ff4fc271b4597048371d5af972c2baab51be132ba15874e0327a6a/pyzmq-27.2.0-cp312-abi3-win32.whl", hash = "sha256:fdaaa4ea3242f6ad298eb5177eb042aea5c73c30e76d20caee7b15af20d24ec2", size = 563450, upload-time = "2026-08-20T19:06:52.307Z" }, + { url = "https://files.pythonhosted.org/packages/02/8b/b83f7780dad22e0878e4c7bd9158ebd24ed12bc3d5e3a471cd0576f77ded/pyzmq-27.2.0-cp312-abi3-win_amd64.whl", hash = "sha256:2c218c6ab8bc447ba62054b581fd30209689d199c6ecb253f79615ca74a38e12", size = 628633, upload-time = "2026-08-20T19:06:53.809Z" }, + { url = "https://files.pythonhosted.org/packages/52/aa/3918b5ac7f9987bd9c421b065074fd7409ded88f856f2c704a24341877ec/pyzmq-27.2.0-cp312-abi3-win_arm64.whl", hash = "sha256:348d6fd3e4b81ae4580622ea8c2ea60224e84b2ac1b3be4482e6edc7de06e7a3", size = 556006, upload-time = "2026-08-20T19:06:55.242Z" }, + { url = "https://files.pythonhosted.org/packages/83/5e/d0541596b48c5a19f85dcbea83d6673d8e91681cdf853eb194c31fc9766e/pyzmq-27.2.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:c551b9e2f86dc625fcb1a032c0d68042678caf96a8dd7c28796766b673bd5b52", size = 1127193, upload-time = "2026-08-20T19:06:56.545Z" }, + { url = "https://files.pythonhosted.org/packages/50/9f/8c7411bb283982d46e6d56dca6a095678c87eb0398daead12776d9881ac2/pyzmq-27.2.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:288cc790da0e3064a14a38ddc56ba169dada8c8af4cb86518db2bcbd380eedbb", size = 1166833, upload-time = "2026-08-20T19:06:58.011Z" }, + { url = "https://files.pythonhosted.org/packages/f9/84/a849161ff88b2de9b991cc8ab332218824741122fdc4fdf222a5b822ac8c/pyzmq-27.2.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:3d45189c0c3c99f817b7fefff0d32eeef684cf33e1e3c0fc4281515357c54702", size = 1134452, upload-time = "2026-08-20T19:06:59.898Z" }, + { url = "https://files.pythonhosted.org/packages/3c/34/ff4aaff0cfba2a4d7ad1a16ffedc52c6deb89fcf673d455085446b23f215/pyzmq-27.2.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d61910b52be5b2cd8b248dbcbe3a1b0275556a7d99fb613fc43323b546e273b8", size = 1167520, upload-time = "2026-08-20T19:07:01.283Z" }, + { url = "https://files.pythonhosted.org/packages/b6/07/42111e9dc1041d78b4443d6eb1b82b027f1a58178dc8a38385effbc72ad5/pyzmq-27.2.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:3ab6eb88590e510ab16715c32dbba12000da9bee989fdadd9ee19a234c492eb7", size = 1466289, upload-time = "2026-08-20T19:07:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b4/def7a478458da78665840564161772e7e938600c32a89f28e8b221b54d2d/pyzmq-27.2.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1ecbdd131b9669f62d3a45afee5527c7ae9f141e4301267f21714c90bd21725f", size = 975868, upload-time = "2026-08-20T19:07:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/38/d5/e3e85f7fea37153097aaff49db9e33093909cc2a7b22c1ac4ebe546600fc/pyzmq-27.2.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3146385b94a760236c5eceff468a66a296a716ca98a2e0f9217b1518118466b1", size = 706054, upload-time = "2026-08-20T19:07:05.623Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ef/3b7d9449b223183222bf517245e1e53d5f1ab8c10be8b45f6a301b2f994a/pyzmq-27.2.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9846e881620dd62566ca76a53e384c3f37490faf4b9240aebc7498810dfca853", size = 878984, upload-time = "2026-08-20T19:07:07.153Z" }, + { url = "https://files.pythonhosted.org/packages/be/a5/8b49dbd494f6dcfda69dc4cade322a4b02706ef4e3d30cc366d4e369899f/pyzmq-27.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d9527e3dbaef1edaeeb2446fa7379446814a43ade8adc7c4a5ebe69437815ddd", size = 1697489, upload-time = "2026-08-20T19:07:08.945Z" }, + { url = "https://files.pythonhosted.org/packages/da/5a/4bb8280901130c26ea25f0cbb4a6d39d94250860c6b3dbd912f1cf48fca7/pyzmq-27.2.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:56b48fa9d478a3af7254f397697a62f5ad3e1bb677e200b2701f0c290d97e5af", size = 2064236, upload-time = "2026-08-20T19:07:10.384Z" }, + { url = "https://files.pythonhosted.org/packages/de/38/f433af66922554adb2b5f79e897018c8e19a90b9eaeb49c4814f8355ebe4/pyzmq-27.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bf0b6e4ce1bb089751c504c5493d6b0557eabd02dd21b76e9086cf964234b103", size = 1917424, upload-time = "2026-08-20T19:07:11.909Z" }, + { url = "https://files.pythonhosted.org/packages/36/81/ea1c1ae3f801d96ba2c269e056761ebcfe023476e651d3af2a7817962051/pyzmq-27.2.0-cp314-cp314t-win32.whl", hash = "sha256:fba8afcf265c6e9fbe1594cb045d4765c6c9a7d607653a8196067ef23566b843", size = 591103, upload-time = "2026-08-20T19:07:13.451Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/149a627707e780fa9f2c1ede3590c14fa6b18b5576d15744342622299a50/pyzmq-27.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d1bc1d380a91d954ed5fc9f12915dba014eed0978d2de05ee7ca688bdaac144a", size = 670215, upload-time = "2026-08-20T19:07:15.069Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/f9c3c1536c41ef3dbf765ea04218990e2056e558f98184ecd883767fc501/pyzmq-27.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c7cfb75caa83f5153c687e9d2107f64b5ef0ef0d6edd260d3ff920baaaa69101", size = 582252, upload-time = "2026-08-20T19:07:16.582Z" }, + { url = "https://files.pythonhosted.org/packages/fa/00/78fe097a304a408275747ce43f20428789130b059c5649956277c20f30cf/pyzmq-27.2.0-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:c5129a8fe43ecc49b99eb75616603d483a3c2fcaef504988fafe8ea392aea98b", size = 1134295, upload-time = "2026-08-20T19:07:17.94Z" }, + { url = "https://files.pythonhosted.org/packages/f2/83/1c36270658d2ee56e23a3f9ef5fbcb94cbd2f9fe966a6641f2f38e697162/pyzmq-27.2.0-cp315-cp315-android_24_x86_64.whl", hash = "sha256:baa2ce3485145653194d6c8c5beedd1e9f0bf46a0919c9fa2fe2204fc35b74d9", size = 1167492, upload-time = "2026-08-20T19:07:19.476Z" }, + { url = "https://files.pythonhosted.org/packages/58/b2/f0ae223438d7faa991f6feefdc823815f11cc604f898738376b59fd96515/pyzmq-27.2.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:e1ed46048d1920cabc96d952a0d5cfe4127ad8db572c335aae4e3c57b9278d7f", size = 1465992, upload-time = "2026-08-20T19:07:20.941Z" }, + { url = "https://files.pythonhosted.org/packages/59/46/fb56f3f37a6a0937b0e1d2885e808b5eedc171320bac85573cfae78fa9bc/pyzmq-27.2.0-cp315-cp315t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e0fa0bc6b1a184aee59b32efcd1b7f0e6d5b8f9387799e4c16a4cb66a86747d6", size = 976118, upload-time = "2026-08-20T19:07:22.577Z" }, + { url = "https://files.pythonhosted.org/packages/21/82/a2c9bfd7c4d34eea1278493cd041bc000d41acb4463c89ceaad29dc813b6/pyzmq-27.2.0-cp315-cp315t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4bd6743e8bf854c3bfce892dd6578a514aabf128e37a4b2eafcf01856f7e44", size = 705968, upload-time = "2026-08-20T19:07:24.019Z" }, + { url = "https://files.pythonhosted.org/packages/d6/12/b906b269116b6591dc15c0acc5d04c043957c8a531d336999731f4b1d899/pyzmq-27.2.0-cp315-cp315t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95369ed6626afcfe2ac89832fb1b917c077fbeb905fbbe5d918349ce0222b89b", size = 879011, upload-time = "2026-08-20T19:07:25.428Z" }, + { url = "https://files.pythonhosted.org/packages/12/13/f96359534bfb77651c15f1fbfc4bfdd7ec3489d23f434706d39598dd0dcd/pyzmq-27.2.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:40124779c3a56ad5d91902df1ff89159cb414b6c1a0ee697abcc66cf5e6db62d", size = 1697496, upload-time = "2026-08-20T19:07:26.821Z" }, + { url = "https://files.pythonhosted.org/packages/21/b4/2c007ae5f2fe5eca86cbfbc874ed86b5135f2f7812615dfd78606d3c93f6/pyzmq-27.2.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:ec8a318dfc27c7d946651b3d9e8025d5734f30c168a822195601827207bac09b", size = 2064347, upload-time = "2026-08-20T19:07:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/9b/88/767af3a6630c15215f3a66700ec79598a375edd1fdc9d75a3ad522178c01/pyzmq-27.2.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:88c0fac061bac269076edeb3a209acefc96cd6167c239daf1c2b404ac48d7012", size = 1917360, upload-time = "2026-08-20T19:07:29.693Z" }, + { url = "https://files.pythonhosted.org/packages/35/c1/80dd2d20d6e57bc68e1dce1e84bf3e76c9577c1bf728199985c8b4ea0fd1/pyzmq-27.2.0-cp315-cp315t-win32.whl", hash = "sha256:ac126d48cf18aa955daabef43bf0009ff76ad4deee437d09ecf15388214b5beb", size = 591073, upload-time = "2026-08-20T19:07:31.341Z" }, + { url = "https://files.pythonhosted.org/packages/f8/b5/33b781666f3f52ae834bc9c8e38f4f0483a826c5a91cccc993292007bf10/pyzmq-27.2.0-cp315-cp315t-win_amd64.whl", hash = "sha256:edce90a1e588ec63adbf612cc0ad582de4169cd216c7ae53c15f42a2ee902f35", size = 670701, upload-time = "2026-08-20T19:07:32.895Z" }, + { url = "https://files.pythonhosted.org/packages/6e/97/bc4f0edefb992df4fdebcf9f0cc40f631cd4ed277e1ed59ef2cd99a5c8c5/pyzmq-27.2.0-cp315-cp315t-win_arm64.whl", hash = "sha256:a843094b4d3d633bc3623e47a2ff50742d6af02bc1f7606aa2e67e971e21878d", size = 581985, upload-time = "2026-08-20T19:07:34.19Z" }, + { url = "https://files.pythonhosted.org/packages/8c/26/1a7cd2d8e4e3c27d83a46960e22101b69f527843be140cb3375267aa8ca6/pyzmq-27.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:770a37f28ddfbe1d2c40a2e3ce37e5fd10831daa6ae9634105aa8a5d23507b00", size = 875015, upload-time = "2026-08-20T19:07:52.084Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ed/c8daf770ca31eb293bef40f801f05148e0a96ad538d55ec3bceb226c91ec/pyzmq-27.2.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b398c5fe102b41e1559f7ffdae760aabd5f432d73b047b4ae0eac4e01cb594d2", size = 774400, upload-time = "2026-08-20T19:07:53.596Z" }, + { url = "https://files.pythonhosted.org/packages/64/6f/b958b0785eb15ab7a78913e3df8de705dbf226ce1d0004998fa4872bdd59/pyzmq-27.2.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0e1af01858d6dc0c09cea57f9cb1ddf4601f04897b6bb1efc3a2038123c87d79", size = 877993, upload-time = "2026-08-20T19:07:55.192Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5a/7a070d0e9911441061402013f0c64e5772ee781704c5dae093e6b051c6f6/pyzmq-27.2.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:211350c3ccd4746bc5a85e8fe961bad1f7f2f274f67cf1f785fad7f96f562eea", size = 614493, upload-time = "2026-08-20T19:07:56.77Z" }, + { url = "https://files.pythonhosted.org/packages/b3/28/833485224e1bd8960cbf3539db465ab2bf23b50de885dc4225a637480539/pyzmq-27.2.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dde5e291548ca0f397623b5e523db5c90172b32aa4fd3ba464a79ea31a580b43", size = 780573, upload-time = "2026-08-20T19:07:58.18Z" }, + { url = "https://files.pythonhosted.org/packages/90/8e/52239b9b5fe4f9cf77e272e6a8c548c01ba407d08d3aeeb3220816669ef1/pyzmq-27.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:94242bd4de6af7e74665e14a88630bccd615057f6acfaf08a3a432551d604645", size = 553963, upload-time = "2026-08-20T19:08:00.015Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/7187a1f0bf2b8bf8dc6b91762438fb9b472f684f2dc4cb74a24bf8957943/pyzmq-27.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a7c1144dc61777938e932a2c9011b980b89fd8ff3733033b34c44c299187a6e1", size = 875015, upload-time = "2026-08-20T19:08:01.692Z" }, + { url = "https://files.pythonhosted.org/packages/92/71/09b71620ad52bad4eb68b1516978ecaf52ef623c3fa16e0732a03cf3274c/pyzmq-27.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:c218b816220d05acf6ab1bafca58926d95cbcc5fec5024724666030466308f0c", size = 774395, upload-time = "2026-08-20T19:08:03.108Z" }, + { url = "https://files.pythonhosted.org/packages/9c/cf/5c8eb9994a14ff5ee5b0cada339421748746c95aee0280c8b656741e8749/pyzmq-27.2.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ae6ebbc0bfe5a21ce21e32ba567bf73df2d93888109c65acbd42506cf9395759", size = 877994, upload-time = "2026-08-20T19:08:04.724Z" }, + { url = "https://files.pythonhosted.org/packages/97/64/e22094c5555e550b6450ecfcceb6a1205d893d9a18ae27764c8c45acfb16/pyzmq-27.2.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:679b5b1dde326a921ea2c9ec1f9ea3115bfe1b4735779bbc6eb0473a0ed93f71", size = 614492, upload-time = "2026-08-20T19:08:06.487Z" }, + { url = "https://files.pythonhosted.org/packages/d2/28/5b1042899caed18278c56d54a502f5254d463afe8aea1acbecc98e053391/pyzmq-27.2.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5c6d8744d10b5e1eadd90a7c58f8546acf6bf680ee463f7e6ada09ad6c9f802", size = 780574, upload-time = "2026-08-20T19:08:07.987Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/f134603a671c114c6b56eb912bba890f09e2d43b8a28d243be5a5507cf2f/pyzmq-27.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3ee8dd7031d5e23f632e0e7eee67183ca7d2536e0de35dc1e5d69f3471a791e8", size = 553961, upload-time = "2026-08-20T19:08:09.651Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/06/ae069393fc66e8ff33036d4b368003833bf6e88ccf182e17e7a2f1c754fd/ruff-0.15.22.tar.gz", hash = "sha256:3f15175b1fb580126f58285a5dae6b2ea89000136d980c64499211f116b54809", size = 4785063, upload-time = "2026-07-16T15:14:13.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/18/ee54b7ae1e121be7a28ea6da4b67564ebb0530e183a54415ab7e3bcd2c4e/ruff-0.15.22-py3-none-linux_armv6l.whl", hash = "sha256:44423e73493737f5e7c5b41d475483898ff37afcdae38bc3da5085e29af1c2d8", size = 10781258, upload-time = "2026-07-16T15:13:19.452Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d2/2520cb14761ddbeaf57642a76942fc36adcbdbe53b4532241995f6fc485c/ruff-0.15.22-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b82c6482946e9eda7ff2e091d25b8bad3f718684e1916d41bd56873cee05b697", size = 10999477, upload-time = "2026-07-16T15:13:23.318Z" }, + { url = "https://files.pythonhosted.org/packages/c9/10/74e53572aa758dfaa678c2a2646b5c5515d884b7ca56be4d2ce03ca4b560/ruff-0.15.22-py3-none-macosx_11_0_arm64.whl", hash = "sha256:11c1c715af53a09f714e011106bffc419751ec8232fcb5da42173284ea3fec6f", size = 10466716, upload-time = "2026-07-16T15:13:26.162Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cc/44eaaf0844e028182f2d0a8f2190d0f359159aed0a9e5ab861d892f1ae2a/ruff-0.15.22-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742a29cf29bddb7c8327895d6a10e0e6c5b38a96dd407af9b5d0857f809c0576", size = 10892644, upload-time = "2026-07-16T15:13:29.229Z" }, + { url = "https://files.pythonhosted.org/packages/9f/21/8edf559014d2b0f82beea19cfb713993ad802ccda16868769979c6090a84/ruff-0.15.22-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72af58b951b0ae395935ae79763dc349bc0eb706319d28f7a33ad2cfb3cfc178", size = 10576719, upload-time = "2026-07-16T15:13:32.35Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1e/3a13abd392a3b50b62e5938a831f9ab6e588358cacad5c18545b716d2182/ruff-0.15.22-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d425005c1835eb24e2ee4161cb90e8db263415f4a71c8c72c33abaa6c0c224", size = 11376494, upload-time = "2026-07-16T15:13:35.958Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3e/422d3d95bcf04dd78e1aeac22184d4f9a8fb2c01865d39d44618484a0317/ruff-0.15.22-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b9b3f8779a4f08c969defc3c8c35abffaa757e601ed5ae66d6d1db6519969a", size = 12208370, upload-time = "2026-07-16T15:13:39.185Z" }, + { url = "https://files.pythonhosted.org/packages/1e/91/5d065a0e0a02bf4813f5119ad278462eed081d2b832eb7c021ade0ec9e65/ruff-0.15.22-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e0dd1b2e4d3d585f897a0d137cbf4eaf6223bef4e8ce34d6bb12556c5f9249e", size = 11581098, upload-time = "2026-07-16T15:13:42.132Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/a0d4871d12fae702eb1f41b686caf05f1f8b124dc6db6f784f53d74918fa/ruff-0.15.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365523eb91d9224e1bcb03b022fbf0facb8f9e23792a2c53d9d4b3924bdbdebb", size = 11399422, upload-time = "2026-07-16T15:13:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/18/80/c843a5176cddbceb0b7e8dd41cf9993490796c1c469348d384f5a5c13c56/ruff-0.15.22-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fabfd168afdf29fee5be98b831efa9683c94d7c5a3b58b9ce5a2e38444589a74", size = 11381683, upload-time = "2026-07-16T15:13:48.46Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/8485de0ae92239438a36cfc51350db9b9e85c9ebdfaea91b18e422706662/ruff-0.15.22-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:225dbf095a87f1d9f90f5fd7924d2613ee452a75a4308c63a8f50f761787aa7c", size = 10850295, upload-time = "2026-07-16T15:13:51.655Z" }, + { url = "https://files.pythonhosted.org/packages/fa/91/24977ec2ec72eaf15e4394ace2959fdff2dd1e14f03e005e838023407169/ruff-0.15.22-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1877d63b9d24ed278744f1523fd11b85540566d54641f97c566d7d9dc5ca5296", size = 10579640, upload-time = "2026-07-16T15:13:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/9c/47/9b51216951974df1f263ac19da550d34252e0ed7218c25f10c5ef9ed7517/ruff-0.15.22-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1606c510bd7215680d32efab38965f7cdec3ef69f5170a3f4791404ffdd5262", size = 11105077, upload-time = "2026-07-16T15:13:57.915Z" }, + { url = "https://files.pythonhosted.org/packages/c2/47/20e9d4a3b8016778acea5fc32bb50d35d207500a17ddb529ffa6996feef8/ruff-0.15.22-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:630479b18625f5ffc373f77603a22a9f8ac0acd7ff0501178b5db28ec71e9c64", size = 11490980, upload-time = "2026-07-16T15:14:01.032Z" }, + { url = "https://files.pythonhosted.org/packages/4d/76/3f72d8fc38c1cb77b38c56a70da9d0c17700cc1cc50f9649c9d3c8f5ba71/ruff-0.15.22-py3-none-win32.whl", hash = "sha256:e5ba0e4a13fd14abbed2a77b517a3911290c6c6c59ef67784328d1668fab76cf", size = 10789165, upload-time = "2026-07-16T15:14:04.16Z" }, + { url = "https://files.pythonhosted.org/packages/cb/46/4965251734c2b6fcdca1b1b187d20bcac3af0ee5b083b89c910bb961ce3a/ruff-0.15.22-py3-none-win_amd64.whl", hash = "sha256:9be63ba1eb936acd2d1342fb8337c356353706fce233b2a15a09a97037e6acde", size = 11938297, upload-time = "2026-07-16T15:14:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/e69b1ff4c8b69093ef08b8919ab767af0569666865b39c30a8795d88d3c6/ruff-0.15.22-py3-none-win_arm64.whl", hash = "sha256:e1168075b72158510839f250027659cdd78476f40507dd517892304c41318661", size = 11298172, upload-time = "2026-07-16T15:14:10.51Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, +] + +[[package]] +name = "scipy" +version = "1.18.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz", hash = "sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size = 30781235, upload-time = "2026-08-21T23:28:50.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/f7/240c110c08693826b4513a52f5717d62ec7c7af72f2920821247c03b17b3/scipy-1.18.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:457fd7a2a8edeb044ab6ffbc0aa03ff6cd18491356e5e0c834d76ce621b916d1", size = 31111061, upload-time = "2026-08-21T23:23:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/05/4a/78c6285577c375e7cf27277ea8ee6961224327f1e1a0c44af5f17f23635c/scipy-1.18.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:e708533e8b2ae2497d65346538a7dcc92814410b25b81432eac66de0f2af8265", size = 28733332, upload-time = "2026-08-21T23:23:50.015Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f6/a5b82f8abbe14d134691b8b903696f701d25a081353a29dc655c364d9e62/scipy-1.18.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:7bbf207c4453ce1ad2e00b17313852b33310b83090c2311bdaf97f93c0380d12", size = 20475078, upload-time = "2026-08-21T23:23:54.138Z" }, + { url = "https://files.pythonhosted.org/packages/23/22/0858a0bbd6b3e825ceb8cd9baf9eaf3b2f2b1d77727eb6be40500bcdc92f/scipy-1.18.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:78c0665edead396b1abb4897c41a5c1d9bf090c8a637a4c20a61678e0a264e66", size = 23108904, upload-time = "2026-08-21T23:23:57.824Z" }, + { url = "https://files.pythonhosted.org/packages/75/9a/2e71719f31eaefe0e3a1706c4a1ded94e664bfd95ffca2b219a671faee01/scipy-1.18.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c085faa2cfa879c5141df483f836f4d691045a078224a670fa570fa01612d89", size = 34025113, upload-time = "2026-08-21T23:24:02.209Z" }, + { url = "https://files.pythonhosted.org/packages/df/64/ff35eb9e54894cf471ff4716abd3c81eb0a0626869217ce3e6ba4ccf17d7/scipy-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f55fa87b6c612ecd6b058f167c53231b1d14e412efe361d3d6e38b3631c73218", size = 35344199, upload-time = "2026-08-21T23:24:07.844Z" }, + { url = "https://files.pythonhosted.org/packages/d3/af/c5538be1792f7034c12c7db6ee67cace58253c7b87b122d68253eaf5de89/scipy-1.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c35d74ce0e193ff740c2f2be2ac913ddc232fe6c1ff40b26cfecb9c670c63314", size = 35639587, upload-time = "2026-08-21T23:24:13.05Z" }, + { url = "https://files.pythonhosted.org/packages/91/4c/075e4f66471bac101141ac739e9e135549be1bae584571bd03a530c056e1/scipy-1.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2924a03db38dc2e848bca2fe9f077dafb891480b91a00a0963a8cf86dfc31c1", size = 37480330, upload-time = "2026-08-21T23:24:19.608Z" }, + { url = "https://files.pythonhosted.org/packages/39/e7/979fd14e75008623df31ba70d6bb144700f68feadcea042021c06a05bf82/scipy-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:5e4d44984abc0020154ea81b247adeddcc3ac5527b975ff798bd1ba0adc513c2", size = 36658278, upload-time = "2026-08-21T23:24:25.463Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/e1525354ff9d7d5feb6d1b31af6d14072e5c91e9607b421fa1ec889660b3/scipy-1.18.1-cp312-cp312-win_arm64.whl", hash = "sha256:d65d448389b8436493abcf629cc94ad0cf32aecaf06e1acca1de53cc795f2f12", size = 24400588, upload-time = "2026-08-21T23:24:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/b6/55/4540ee0f9c42a9ad7109d0d1a8cc70de54c3572b01c6693a2b1c70e90ceb/scipy-1.18.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:3ab3523da44749156e1f68b464dc56af11ae4cbc5c739a49d05f32b982eca9f3", size = 31089958, upload-time = "2026-08-21T23:24:35.8Z" }, + { url = "https://files.pythonhosted.org/packages/2a/f5/769f36d14922b8071a43e95d24d18b6bdafad10d7f5cf647867e1ac052bc/scipy-1.18.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e6fb6a55cc0ba97b59a1f288fb86dc6fce8bdfc0fffcbfd015e3a954bf2a2d93", size = 28715106, upload-time = "2026-08-21T23:24:40.775Z" }, + { url = "https://files.pythonhosted.org/packages/9a/d7/21d890274f75ea37a8209d5519e72da3da90302e3b9fb8397a0918386a62/scipy-1.18.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ea324d9dd34c38bfb9bec8ca4d1b407db97dbb74029f566b8e322b1b6fe56fe6", size = 20456846, upload-time = "2026-08-21T23:24:45.066Z" }, + { url = "https://files.pythonhosted.org/packages/ec/01/798430ecea2e78ec7c02663d5f71c007bb6abeca931080debd40d7fa55ea/scipy-1.18.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:75b00eb8fb802090aa903f4ea1c7f5a584779f967361e68b7e98e531cc2d7174", size = 23087986, upload-time = "2026-08-21T23:24:49.539Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5f/4634e9d35c68496e4e34cb6946eafab044458e6cedab42b40b6588e475b6/scipy-1.18.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d416b16cccfd70fbf62400e84d0bb2f4e6af519a45557f1692c749b37f14b315", size = 33998146, upload-time = "2026-08-21T23:24:54.714Z" }, + { url = "https://files.pythonhosted.org/packages/41/48/6450ed9243315322bbc19ac57b9b70d66a20bf1d38d124c96bc4bf6af9ea/scipy-1.18.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fdaf5ea890a6183d0565f51a61799d67081bd5b1cf03c5f4b3fd3732108625c9", size = 35312578, upload-time = "2026-08-21T23:25:00.44Z" }, + { url = "https://files.pythonhosted.org/packages/00/bd/bf5a4be6a3525676499f6dff307991739ff6fdcad1481b1aeb6745339f58/scipy-1.18.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c825cef2f49e46753726a7181a8e199804a912b29519ada542c6ebc654951899", size = 35612621, upload-time = "2026-08-21T23:25:06.144Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4e/3c45c33e00a77996c4b1cb707929f833ba7b1d522ee29f882512c330676d/scipy-1.18.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e3b417bf8c2c7c16e8f58ad91db17783ec911ac16e7b50eb6eab6e809b4f5b07", size = 37457323, upload-time = "2026-08-21T23:25:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/93/0e/e0348fbc0dbab65c114cf78957e7dfeb49f8e8b556b4d930cc12ff195e18/scipy-1.18.1-cp313-cp313-win_amd64.whl", hash = "sha256:559ed65f60c1af5a03f3912605a1b5114f522c7c32fb23c3376ae8f03219fe28", size = 36622841, upload-time = "2026-08-21T23:25:18.722Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/6a77f5f267c555108f0a864b6db714363dab567a8266422a79a385f9232b/scipy-1.18.1-cp313-cp313-win_arm64.whl", hash = "sha256:cd479fc04dd9401e3b4f49e76518768ef99c4f517a98c284eb091fd725719adf", size = 24399315, upload-time = "2026-08-21T23:25:23.458Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/d8eb4e280ddb56a4ab2c6f02ee49b56b23f6e977cf0802fd6d68dbef14f5/scipy-1.18.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:83de5453a7799afc9048b4616bd085cef126e36412f0ea2f6370c36a2a3a51e7", size = 31090936, upload-time = "2026-08-21T23:25:28.686Z" }, + { url = "https://files.pythonhosted.org/packages/2a/49/59ea385dc3a62ff498ddf3cfff7c2b41b0f9f9d3c4122b3f1dcb6d6327fe/scipy-1.18.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:9554bcc6d715ee87a633a3cc8e7703c6628b100dd29cb8a2efc4c0533c7ff729", size = 28725221, upload-time = "2026-08-21T23:25:33.244Z" }, + { url = "https://files.pythonhosted.org/packages/70/e8/6b0c288c50942d78193696c9f15f9a0874f5178aa0ddf40f83d9924b3e8d/scipy-1.18.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:011413b7426b75012840e35649e00fe0a2c3bae89fed433876e3a99251572efc", size = 20466839, upload-time = "2026-08-21T23:25:37.516Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e0/54fd3793c729e3b936782f181b59cbb1205bf250ab605a16cb1ba61cdd5e/scipy-1.18.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:88f0e784020649f88ea48c9f5ddfa403bf9205820667c0914740b392035afb82", size = 23089121, upload-time = "2026-08-21T23:25:42.019Z" }, + { url = "https://files.pythonhosted.org/packages/0b/56/030af62bea3cf878e0028515dff78c123b01633606a879b63f42d2db99cc/scipy-1.18.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d3ab0e8c69a17dd3559eab8cbb88f258e285c94d572c2719033f90f83290c89", size = 34053851, upload-time = "2026-08-21T23:25:47.998Z" }, + { url = "https://files.pythonhosted.org/packages/6b/89/2a844506d49651e9aa1af6ef95b6bd8031cb1d5a4375edec6155037e04cf/scipy-1.18.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac0333bdf38309aa3dcbe7e3fa7ea29e7a2c37c6ea306a757b700ded8e4596ad", size = 35329183, upload-time = "2026-08-21T23:25:53.522Z" }, + { url = "https://files.pythonhosted.org/packages/eb/56/c7370c3640e92ac9613cbf26cb3f729f9b12ddf1727b55b94b53b24d6f48/scipy-1.18.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:911de823097db8b63f034299d12662db93344e6ffa0b881cbb57748974b70168", size = 35672551, upload-time = "2026-08-21T23:25:59.387Z" }, + { url = "https://files.pythonhosted.org/packages/24/16/ec8536f351421f8bf60a1120930638f83790f4710b8230446aca3d6159d4/scipy-1.18.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:95298364e251be3e60249facbeeca03631d3bb7584f85879516ec55ac717b81f", size = 37469416, upload-time = "2026-08-21T23:26:05.432Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/d73da0d28f16c45bb9b0a5691b91610b0275c5ef0eb5e43c87cf2dc1bf31/scipy-1.18.1-cp314-cp314-win_amd64.whl", hash = "sha256:78a0d7c918e74a232394117160e7e3db503377572a45bcef8826e4ab8a35feba", size = 37362755, upload-time = "2026-08-21T23:26:11.366Z" }, + { url = "https://files.pythonhosted.org/packages/89/25/e996e4dc74e10e227b1e14db5eaf6608bb6dd33884a64851c38f18dd4249/scipy-1.18.1-cp314-cp314-win_arm64.whl", hash = "sha256:cbf38d043c1aa4ab306e1ada6ab6eddacc3322a20b7af1b30bc93254b366fe09", size = 25036090, upload-time = "2026-08-21T23:26:15.887Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/c00213f92309d753b48903e6a451b87eb52ff5b7a16e789d1568bbf221c4/scipy-1.18.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0fcb3c93519f27bb4f0c4b0f7802cdcaca7fcf93267b75edda2e9f4e8a55cbd7", size = 31485550, upload-time = "2026-08-21T23:26:20.776Z" }, + { url = "https://files.pythonhosted.org/packages/74/b2/e3067c487982d4eeab2938928529410370c06fea84a4d3f4925e7d96647d/scipy-1.18.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:ddef79fb382df40104a19bb7151b3b23e57c1778fcf857c71ceecd9bd264513f", size = 29174642, upload-time = "2026-08-21T23:26:25.395Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ab/374c9fe2d1ec014e576c781a4b5d8e1ba340e8f6b4638c16f711d2b194f0/scipy-1.18.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0e82073ecc7acc6436fac4b31674109c7e1d3e596789767eda01258a8c9e8123", size = 20916357, upload-time = "2026-08-21T23:26:30.112Z" }, + { url = "https://files.pythonhosted.org/packages/90/38/223915c88a17317cafbf8ca2a42b11c265a9fb1e804aa665544132b5fe8a/scipy-1.18.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8bcf3c1ba5d6456e2effd30fcbd3459b044d683fcdac79a2e6830f0bdf7de487", size = 23482611, upload-time = "2026-08-21T23:26:34.846Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d1/db0948da8ca57a80b36520ef0a768b967d99f3af65f4b6f1bf6362ad4dd4/scipy-1.18.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cfbf154f2ba187f2ed6cce2639efff7d105f1140573642c0161615b6d91d6a87", size = 34143202, upload-time = "2026-08-21T23:26:40.4Z" }, + { url = "https://files.pythonhosted.org/packages/87/53/39d046cc7574ed6acacb6bd5723e220107ece80bff12faaf3efc4ddeede4/scipy-1.18.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1d33a7836f7ddc1993427966a0823468ec41bcbdb1a9f9942d1d7e57f803ba3", size = 35380876, upload-time = "2026-08-21T23:26:46.1Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/32e0e799d875a85ca57d9bde6c78148afcc0e38276df683d95854eadc8c3/scipy-1.18.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4b8bc363b6d65ee2152bec57568e3c52639bb34c46057b09857a307ed5e21d", size = 35770885, upload-time = "2026-08-21T23:26:51.533Z" }, + { url = "https://files.pythonhosted.org/packages/88/2e/f97a666d362fee68b18f41c9c30ed502ca5c98b549749bfcb52a8b74d1eb/scipy-1.18.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:11c423f1049c5755ad4409af52a9ada1cff96fe9b50795d4af3619f292901239", size = 37525424, upload-time = "2026-08-21T23:26:56.751Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d5/a9e765a84654ebba8479a1fd1b059ced1af72b168a3b2a3a46540ea38d20/scipy-1.18.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c24acac1e18912761c4700239bbc1fd32f615af690f1584d49b35859be51324d", size = 37416961, upload-time = "2026-08-21T23:27:01.546Z" }, + { url = "https://files.pythonhosted.org/packages/ee/16/e79e0d1c63ef698879d85439d37e9fb434e3b804e506a6991038d086ebd9/scipy-1.18.1-cp314-cp314t-win_arm64.whl", hash = "sha256:9f2897bf7737392ad0d5213ea7b6add72a4edf5679b3153106aeb88b6507b3b9", size = 25331848, upload-time = "2026-08-21T23:27:05.884Z" }, + { url = "https://files.pythonhosted.org/packages/be/4f/1bd37c883b67163e2ca1f60977a399500e6879c15defecac62831c8d078d/scipy-1.18.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:eb0dfcf4e28a99c12c999744a2ff67c9b06200e20401c7c88186e33552a46331", size = 31091484, upload-time = "2026-08-21T23:27:11.051Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c5/ba929d7feb9b2332f96827c12e0e924b61973b59b4dea383b603372c65ce/scipy-1.18.1-cp315-cp315-macosx_12_0_arm64.whl", hash = "sha256:30f464bee641fa8e282577c7dce027308403213c6ca8270bba73285c91024bc5", size = 28725057, upload-time = "2026-08-21T23:27:15.9Z" }, + { url = "https://files.pythonhosted.org/packages/a4/19/68f1c50f609d955d230e66d25d02bd3e1e167ec540232135354fb9a4b9e3/scipy-1.18.1-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:1bca3b943fc2567ea49cd02c99abde49da4d5178ec46f624bd8255cda8755beb", size = 20466734, upload-time = "2026-08-21T23:27:20.044Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6d/319fa29b73d1802fa80b32a6eaf3f5be456ef81526da2716a9493bcb5501/scipy-1.18.1-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:c9d18a33309122074ea483dd92dd444189166b8b2ec429fe9ed5ac73c7a0aa23", size = 23089664, upload-time = "2026-08-21T23:27:24.345Z" }, + { url = "https://files.pythonhosted.org/packages/b7/db/30992f9b51a63de671daf3888ffd18378b6cb9ec9f2c972264238ffa7fd6/scipy-1.18.1-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82f201b4c878551d48558337aab270d3c6cca5507b8737c8d8a608d234cccde0", size = 34054035, upload-time = "2026-08-21T23:27:29.409Z" }, + { url = "https://files.pythonhosted.org/packages/91/d4/bf3e735dc0b9d5a8ff45079d2540e17d3aff7a2f0048dd8f552ffd031d2b/scipy-1.18.1-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ac49ea97594532dd44b7136094d35f5440fa06e6d9c6384a74c01764df388c5", size = 35333883, upload-time = "2026-08-21T23:27:34.293Z" }, + { url = "https://files.pythonhosted.org/packages/19/93/12d78ce9f871fe945fca588d32644e6e63f553c2a35c564d73f3b22a3313/scipy-1.18.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:ceb30a00ce7c92d459819443d29ca486d882b83fb6738bdcbb2a1cce94ac5daa", size = 35673124, upload-time = "2026-08-21T23:27:39.059Z" }, + { url = "https://files.pythonhosted.org/packages/70/cd/886219313a1012a48e6ae0ec4f302c837151beb92e1ff0d709ef8fdfc488/scipy-1.18.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f29633129f9fa7e88a3f0fca835de2d030bfc9643f7799e1a0c46cee24d38fc7", size = 37470753, upload-time = "2026-08-21T23:27:44.435Z" }, + { url = "https://files.pythonhosted.org/packages/17/6c/a776888ce618bee54fbde26172f0f46ac1da70d27b63861797fe78e1904b/scipy-1.18.1-cp315-cp315-win_amd64.whl", hash = "sha256:92c14f5bdbfb6216315ce33e78080474082de8b3830122ba97809bfbe65f75c0", size = 37361483, upload-time = "2026-08-21T23:27:49.334Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/97b651691322ebee97999b017ffc18a15a0b815103844c97e8da9d469731/scipy-1.18.1-cp315-cp315-win_arm64.whl", hash = "sha256:e402cf31eb68f453dbb2d36fc6d722b33f24a55d68b2ae1d92fa6305ca71c298", size = 25035883, upload-time = "2026-08-21T23:27:53.596Z" }, + { url = "https://files.pythonhosted.org/packages/ed/0f/9ec20467bbabd0d44e2a77d0fd3d124f884b4d67df92af82c91d2d6a486f/scipy-1.18.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:2a0b02f9fc46f8520330c23d45e6560db7e3a0d927232139427637f98943e11d", size = 31474926, upload-time = "2026-08-21T23:27:57.993Z" }, + { url = "https://files.pythonhosted.org/packages/8a/58/dcb79161e56efbedc50079fcd2f5fe427a0ebb53022eb476aa73c015ad8f/scipy-1.18.1-cp315-cp315t-macosx_12_0_arm64.whl", hash = "sha256:1d73131e358976663dd969e1fb4ed1404b815cd977eaaedc3b3a133ba2d81c35", size = 29164940, upload-time = "2026-08-21T23:28:03.062Z" }, + { url = "https://files.pythonhosted.org/packages/71/d3/1eeea80c817fcb8ef7bd4a05a58824977a0e57a375cfc3d7ea7c911c01ad/scipy-1.18.1-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:bff0b729edd992766136b34e39cc76bc2fad905aa58897ee72a9cd000a6d8443", size = 20906742, upload-time = "2026-08-21T23:28:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/54/46/e59350428b6099301a20128108c995e2eb175a43f383af9a346e38824f9b/scipy-1.18.1-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:10ac20c69d880f77f375db44c22e3e6a644f9fefa291d4cd2fb9790a89fc99fd", size = 23472183, upload-time = "2026-08-21T23:28:12.109Z" }, + { url = "https://files.pythonhosted.org/packages/89/31/cc91623fa98f0621766a0f0aaaadb2c66de74a7ea7e3837164f6e4354260/scipy-1.18.1-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33a834464fdabc0f26a45508df31b3cc5d028e04dbf6c5ed398541418e0a12fe", size = 34130796, upload-time = "2026-08-21T23:28:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/fc/3e/8572ef536957ddb8aa81bb4090d9e25f257e3b4e05d97deb54319deb8a3a/scipy-1.18.1-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49023963c193dacee096301452f223ee24d86ec5807f8df93c0f7221d119e305", size = 35374253, upload-time = "2026-08-21T23:28:23.732Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c6/59fdeffb4f1435299f93d9dc8140b43ad2916e6cfc944be6c3041fcec86d/scipy-1.18.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:d84a09d0dad90ba6525d8ac1c2334b33e64bf3ccfe9e841f02feb867a22681e4", size = 35758543, upload-time = "2026-08-21T23:28:29.431Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d9/135be205d9de8783193aff9cc3bf483a03a38e4b29432c954e8cb66ac14e/scipy-1.18.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:179ce34a8d0fe273d8883ba59e17e052247d08973dfcb743ca52bb1cce2d60b0", size = 37521946, upload-time = "2026-08-21T23:28:35.245Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a2/5b7d5270621ab7cfa3f7766067bf95dc360b5efb6394694e8143b4156e2b/scipy-1.18.1-cp315-cp315t-win_amd64.whl", hash = "sha256:5632e3ae3d09197c446310cd5187de63e28448ce22f0f67b2b93d97503c0c230", size = 37408295, upload-time = "2026-08-21T23:28:40.724Z" }, + { url = "https://files.pythonhosted.org/packages/63/ad/741c19fcb66755ff953daf9243af8480e4bf3d7fbe57583c178c7d2b6b51/scipy-1.18.1-cp315-cp315t-win_arm64.whl", hash = "sha256:eda632a7981f69730d6281f451db9c1c370993a2c0d7ddb43e2a809a2862b83a", size = 25319710, upload-time = "2026-08-21T23:28:45.713Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "snowballstemmer" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/f8/0a71edf031f03c40db17503cb8ca78a69a171254e568e7db241b0ab57ea1/snowballstemmer-3.1.1.tar.gz", hash = "sha256:e07bbc54a0d798fe6010a12398422e62a8bfbba95c394fd0956ef58cb4d3e260", size = 123314, upload-time = "2026-06-03T00:56:40.194Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/07/2ebca9b11fb9be7340a818d8d6f63feaebb146be2c4afbd6061701d6df6e/snowballstemmer-3.1.1-py3-none-any.whl", hash = "sha256:7e207fa178741da09cdee59d3ecec3827ad5f92b1fc5c9ff3755b639f71f5752", size = 104164, upload-time = "2026-06-03T00:56:38.614Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "starlette" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/2b/3850dc6bf7ef71b088962eba31dafc6cffd2f96e577ebb0bb316df96da3e/starlette-1.7.0.tar.gz", hash = "sha256:c79f74ea63cff761804fbbfb182f1e0b440c2d07b164d24700c5a1bab5d6ff5d", size = 2736246, upload-time = "2026-09-23T07:30:26.35Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/d6/1ec1b290f9e0fb067899b61e1d37a30c923068bad260b216dbe37a7d2967/starlette-1.7.0-py3-none-any.whl", hash = "sha256:67f8e99895493dd2911a03f11314af6ceebeae4e704bb9f43dfc6a9db151c93e", size = 78980, upload-time = "2026-09-23T07:30:24.567Z" }, +] + +[[package]] +name = "statsmodels" +version = "0.14.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "packaging" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "patsy" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "scipy", version = "1.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/81/e8d74b34f85285f7335d30c5e3c2d7c0346997af9f3debf9a0a9a63de184/statsmodels-0.14.6.tar.gz", hash = "sha256:4d17873d3e607d398b85126cd4ed7aad89e4e9d89fc744cdab1af3189a996c2a", size = 20689085, upload-time = "2025-12-05T23:08:39.522Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/6d/9ec309a175956f88eb8420ac564297f37cf9b1f73f89db74da861052dc29/statsmodels-0.14.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4ff0649a2df674c7ffb6fa1a06bffdb82a6adf09a48e90e000a15a6aaa734b0", size = 10142419, upload-time = "2025-12-05T19:27:35.625Z" }, + { url = "https://files.pythonhosted.org/packages/86/8f/338c5568315ec5bf3ac7cd4b71e34b98cb3b0f834919c0c04a0762f878a1/statsmodels-0.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:109012088b3e370080846ab053c76d125268631410142daad2f8c10770e8e8d9", size = 10022819, upload-time = "2025-12-05T19:27:49.385Z" }, + { url = "https://files.pythonhosted.org/packages/b0/77/5fc4cbc2d608f9b483b0675f82704a8bcd672962c379fe4d82100d388dbf/statsmodels-0.14.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e93bd5d220f3cb6fc5fc1bffd5b094966cab8ee99f6c57c02e95710513d6ac3f", size = 10118927, upload-time = "2025-12-05T23:07:51.256Z" }, + { url = "https://files.pythonhosted.org/packages/94/55/b86c861c32186403fe121d9ab27bc16d05839b170d92a978beb33abb995e/statsmodels-0.14.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06eec42d682fdb09fe5d70a05930857efb141754ec5a5056a03304c1b5e32fd9", size = 10413015, upload-time = "2025-12-05T23:08:53.95Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/daf0dba729ccdc4176605f4a0fd5cfe71cdda671749dca10e74a732b8b1c/statsmodels-0.14.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0444e88557df735eda7db330806fe09d51c9f888bb1f5906cb3a61fb1a3ed4a8", size = 10441248, upload-time = "2025-12-05T23:09:09.353Z" }, + { url = "https://files.pythonhosted.org/packages/9a/1c/2e10b7c7cc44fa418272996bf0427b8016718fd62f995d9c1f7ab37adf35/statsmodels-0.14.6-cp310-cp310-win_amd64.whl", hash = "sha256:e83a9abe653835da3b37fb6ae04b45480c1de11b3134bd40b09717192a1456ea", size = 9583410, upload-time = "2025-12-05T19:28:02.086Z" }, + { url = "https://files.pythonhosted.org/packages/a9/4d/df4dd089b406accfc3bb5ee53ba29bb3bdf5ae61643f86f8f604baa57656/statsmodels-0.14.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ad5c2810fc6c684254a7792bf1cbaf1606cdee2a253f8bd259c43135d87cfb4", size = 10121514, upload-time = "2025-12-05T19:28:16.521Z" }, + { url = "https://files.pythonhosted.org/packages/82/af/ec48daa7f861f993b91a0dcc791d66e1cf56510a235c5cbd2ab991a31d5c/statsmodels-0.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:341fa68a7403e10a95c7b6e41134b0da3a7b835ecff1eb266294408535a06eb6", size = 10003346, upload-time = "2025-12-05T19:28:29.568Z" }, + { url = "https://files.pythonhosted.org/packages/a9/2c/c8f7aa24cd729970728f3f98822fb45149adc216f445a9301e441f7ac760/statsmodels-0.14.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdf1dfe2a3ca56f5529118baf33a13efed2783c528f4a36409b46bbd2d9d48eb", size = 10129872, upload-time = "2025-12-05T23:09:25.724Z" }, + { url = "https://files.pythonhosted.org/packages/40/c6/9ae8e9b0721e9b6eb5f340c3a0ce8cd7cce4f66e03dd81f80d60f111987f/statsmodels-0.14.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3764ba8195c9baf0925a96da0743ff218067a269f01d155ca3558deed2658ca", size = 10381964, upload-time = "2025-12-05T23:09:41.326Z" }, + { url = "https://files.pythonhosted.org/packages/28/8c/cf3d30c8c2da78e2ad1f50ade8b7fabec3ff4cdfc56fbc02e097c4577f90/statsmodels-0.14.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e8d2e519852adb1b420e018f5ac6e6684b2b877478adf7fda2cfdb58f5acb5d", size = 10409611, upload-time = "2025-12-05T23:09:57.131Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cc/018f14ecb58c6cb89de9d52695740b7d1f5a982aa9ea312483ea3c3d5f77/statsmodels-0.14.6-cp311-cp311-win_amd64.whl", hash = "sha256:2738a00fca51196f5a7d44b06970ace6b8b30289839e4808d656f8a98e35faa7", size = 9580385, upload-time = "2025-12-05T19:28:42.778Z" }, + { url = "https://files.pythonhosted.org/packages/25/ce/308e5e5da57515dd7cab3ec37ea2d5b8ff50bef1fcc8e6d31456f9fae08e/statsmodels-0.14.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe76140ae7adc5ff0e60a3f0d56f4fffef484efa803c3efebf2fcd734d72ecb5", size = 10091932, upload-time = "2025-12-05T19:28:55.446Z" }, + { url = "https://files.pythonhosted.org/packages/05/30/affbabf3c27fb501ec7b5808230c619d4d1a4525c07301074eb4bda92fa9/statsmodels-0.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26d4f0ed3b31f3c86f83a92f5c1f5cbe63fc992cd8915daf28ca49be14463a1c", size = 9997345, upload-time = "2025-12-05T19:29:10.278Z" }, + { url = "https://files.pythonhosted.org/packages/48/f5/3a73b51e6450c31652c53a8e12e24eac64e3824be816c0c2316e7dbdcb7d/statsmodels-0.14.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8c00a42863e4f4733ac9d078bbfad816249c01451740e6f5053ecc7db6d6368", size = 10058649, upload-time = "2025-12-05T23:10:12.775Z" }, + { url = "https://files.pythonhosted.org/packages/81/68/dddd76117df2ef14c943c6bbb6618be5c9401280046f4ddfc9fb4596a1b8/statsmodels-0.14.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b58cf7474aa9e7e3b0771a66537148b2df9b5884fbf156096c0e6c1ff0469d", size = 10339446, upload-time = "2025-12-05T23:10:28.503Z" }, + { url = "https://files.pythonhosted.org/packages/56/4a/dce451c74c4050535fac1ec0c14b80706d8fc134c9da22db3c8a0ec62c33/statsmodels-0.14.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81e7dcc5e9587f2567e52deaff5220b175bf2f648951549eae5fc9383b62bc37", size = 10368705, upload-time = "2025-12-05T23:10:44.339Z" }, + { url = "https://files.pythonhosted.org/packages/60/15/3daba2df40be8b8a9a027d7f54c8dedf24f0d81b96e54b52293f5f7e3418/statsmodels-0.14.6-cp312-cp312-win_amd64.whl", hash = "sha256:b5eb07acd115aa6208b4058211138393a7e6c2cf12b6f213ede10f658f6a714f", size = 9543991, upload-time = "2025-12-05T23:10:58.536Z" }, + { url = "https://files.pythonhosted.org/packages/81/59/a5aad5b0cc266f5be013db8cde563ac5d2a025e7efc0c328d83b50c72992/statsmodels-0.14.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47ee7af083623d2091954fa71c7549b8443168f41b7c5dce66510274c50fd73e", size = 10072009, upload-time = "2025-12-05T23:11:14.021Z" }, + { url = "https://files.pythonhosted.org/packages/53/dd/d8cfa7922fc6dc3c56fa6c59b348ea7de829a94cd73208c6f8202dd33f17/statsmodels-0.14.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa60d82e29fcd0a736e86feb63a11d2380322d77a9369a54be8b0965a3985f71", size = 9980018, upload-time = "2025-12-05T23:11:30.907Z" }, + { url = "https://files.pythonhosted.org/packages/ee/77/0ec96803eba444efd75dba32f2ef88765ae3e8f567d276805391ec2c98c6/statsmodels-0.14.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89ee7d595f5939cc20bf946faedcb5137d975f03ae080f300ebb4398f16a5bd4", size = 10060269, upload-time = "2025-12-05T23:11:46.338Z" }, + { url = "https://files.pythonhosted.org/packages/10/b9/fd41f1f6af13a1a1212a06bb377b17762feaa6d656947bf666f76300fc05/statsmodels-0.14.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:730f3297b26749b216a06e4327fe0be59b8d05f7d594fb6caff4287b69654589", size = 10324155, upload-time = "2025-12-05T23:12:01.805Z" }, + { url = "https://files.pythonhosted.org/packages/ee/0f/a6900e220abd2c69cd0a07e3ad26c71984be6061415a60e0f17b152ecf08/statsmodels-0.14.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f1c08befa85e93acc992b72a390ddb7bd876190f1360e61d10cf43833463bc9c", size = 10349765, upload-time = "2025-12-05T23:12:18.018Z" }, + { url = "https://files.pythonhosted.org/packages/98/08/b79f0c614f38e566eebbdcff90c0bcacf3c6ba7a5bbb12183c09c29ca400/statsmodels-0.14.6-cp313-cp313-win_amd64.whl", hash = "sha256:8021271a79f35b842c02a1794465a651a9d06ec2080f76ebc3b7adce77d08233", size = 9540043, upload-time = "2025-12-05T23:12:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/71/de/09540e870318e0c7b58316561d417be45eff731263b4234fdd2eee3511a8/statsmodels-0.14.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:00781869991f8f02ad3610da6627fd26ebe262210287beb59761982a8fa88cae", size = 10069403, upload-time = "2025-12-05T23:12:48.424Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f0/63c1bfda75dc53cee858006e1f46bd6d6f883853bea1b97949d0087766ca/statsmodels-0.14.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:73f305fbf31607b35ce919fae636ab8b80d175328ed38fdc6f354e813b86ee37", size = 9989253, upload-time = "2025-12-05T23:13:05.274Z" }, + { url = "https://files.pythonhosted.org/packages/c1/98/b0dfb4f542b2033a3341aa5f1bdd97024230a4ad3670c5b0839d54e3dcab/statsmodels-0.14.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e443e7077a6e2d3faeea72f5a92c9f12c63722686eb80bb40a0f04e4a7e267ad", size = 10090802, upload-time = "2025-12-05T23:13:20.653Z" }, + { url = "https://files.pythonhosted.org/packages/34/0e/2408735aca9e764643196212f9069912100151414dd617d39ffc72d77eee/statsmodels-0.14.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3414e40c073d725007a6603a18247ab7af3467e1af4a5e5a24e4c27bc26673b4", size = 10337587, upload-time = "2025-12-05T23:13:37.597Z" }, + { url = "https://files.pythonhosted.org/packages/0f/36/4d44f7035ab3c0b2b6a4c4ebb98dedf36246ccbc1b3e2f51ebcd7ac83abb/statsmodels-0.14.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a518d3f9889ef920116f9fa56d0338069e110f823926356946dae83bc9e33e19", size = 10363350, upload-time = "2025-12-05T23:13:53.08Z" }, + { url = "https://files.pythonhosted.org/packages/26/33/f1652d0c59fa51de18492ee2345b65372550501ad061daa38f950be390b6/statsmodels-0.14.6-cp314-cp314-win_amd64.whl", hash = "sha256:151b73e29f01fe619dbce7f66d61a356e9d1fe5e906529b78807df9189c37721", size = 9588010, upload-time = "2025-12-05T23:14:07.28Z" }, +] + +[[package]] +name = "time-machine" +version = "2.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/1b5fdd165f61b67f445fac2a7feb0c655118edef429cd09ff5a8067f7f1d/time_machine-2.19.0.tar.gz", hash = "sha256:7c5065a8b3f2bbb449422c66ef71d114d3f909c276a6469642ecfffb6a0fcd29", size = 14576, upload-time = "2025-08-19T17:22:08.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/8f/19125611ebbcb3a14da14cd982b9eb4573e2733db60c9f1fbf6a39534f40/time_machine-2.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b5169018ef47206997b46086ce01881cd3a4666fd2998c9d76a87858ca3e49e9", size = 19659, upload-time = "2025-08-19T17:20:30.062Z" }, + { url = "https://files.pythonhosted.org/packages/74/da/9b0a928321e7822a3ff96dbd1eae089883848e30e9e1b149b85fb96ba56b/time_machine-2.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85bb7ed440fccf6f6d0c8f7d68d849e7c3d1f771d5e0b2cdf871fa6561da569f", size = 15157, upload-time = "2025-08-19T17:20:31.931Z" }, + { url = "https://files.pythonhosted.org/packages/36/ff/d7e943422038f5f2161fe2c2d791e64a45be691ef946020b20f3a6efc4d4/time_machine-2.19.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a3b12028af1cdc09ccd595be2168b7b26f206c1e190090b048598fbe278beb8e", size = 32860, upload-time = "2025-08-19T17:20:33.241Z" }, + { url = "https://files.pythonhosted.org/packages/fc/80/2b0f1070ed9808ee7da7a6da62a4a0b776957cb4d861578348f86446e778/time_machine-2.19.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c261f073086cf081d1443cbf7684148c662659d3d139d06b772bfe3fe7cc71a6", size = 34510, upload-time = "2025-08-19T17:20:34.221Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b4/48038691c8d89924b36c83335a73adeeb68c884f5a1da08a5b17b8a956f3/time_machine-2.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:011954d951230a9f1079f22b39ed1a3a9abb50ee297dfb8c557c46351659d94d", size = 36204, upload-time = "2025-08-19T17:20:35.163Z" }, + { url = "https://files.pythonhosted.org/packages/37/2e/60e8adb541df195e83cb74b720b2cfb1f22ed99c5a7f8abf2a9ab3442cb5/time_machine-2.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b0f83308b29c7872006803f2e77318874eb84d0654f2afe0e48e3822e7a2e39b", size = 34936, upload-time = "2025-08-19T17:20:36.61Z" }, + { url = "https://files.pythonhosted.org/packages/5e/72/e8cee59c6cd99dd3b25b8001a0253e779a286aa8f44d5b40777cbd66210b/time_machine-2.19.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:39733ef844e2984620ec9382a42d00cccc4757d75a5dd572be8c2572e86e50b9", size = 32932, upload-time = "2025-08-19T17:20:37.901Z" }, + { url = "https://files.pythonhosted.org/packages/2c/eb/83f300d93c1504965d944e03679f1c943a923bce2d0fdfadef0e2e22cc13/time_machine-2.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8db99f6334432e9ffbf00c215caf2ae9773f17cec08304d77e9e90febc3507b", size = 34010, upload-time = "2025-08-19T17:20:39.202Z" }, + { url = "https://files.pythonhosted.org/packages/e1/77/f35f2500e04daac5033a22fbfd17e68467822b8406ee77966bf222ccaa26/time_machine-2.19.0-cp310-cp310-win32.whl", hash = "sha256:72bf66cd19e27ffd26516b9cbe676d50c2e0b026153289765dfe0cf406708128", size = 17121, upload-time = "2025-08-19T17:20:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/db/df/32d3e0404be1760a64a44caab2af34b07e952bfe00a23134fea9ddba3e8a/time_machine-2.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:46f1c945934ce3d6b4f388b8e581fce7f87ec891ea90d7128e19520e434f96f0", size = 17957, upload-time = "2025-08-19T17:20:41.079Z" }, + { url = "https://files.pythonhosted.org/packages/66/df/598a71a1afb4b509a4587273b76590b16d9110a3e9106f01eedc68d02bb2/time_machine-2.19.0-cp310-cp310-win_arm64.whl", hash = "sha256:fb4897c7a5120a4fd03f0670f332d83b7e55645886cd8864a71944c4c2e5b35b", size = 16821, upload-time = "2025-08-19T17:20:41.967Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ed/4815ebcc9b6c14273f692b9be38a9b09eae52a7e532407cc61a51912b121/time_machine-2.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5ee91664880434d98e41585c3446dac7180ec408c786347451ddfca110d19296", size = 19342, upload-time = "2025-08-19T17:20:43.207Z" }, + { url = "https://files.pythonhosted.org/packages/ee/08/154cce8b11b60d8238b0b751b8901d369999f4e8f7c3a5f917caa5d95b0b/time_machine-2.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed3732b83a893d1c7b8cabde762968b4dc5680ee0d305b3ecca9bb516f4e3862", size = 14978, upload-time = "2025-08-19T17:20:44.134Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b7/b689d8c8eeca7af375cfcd64973e49e83aa817cc00f80f98548d42c0eb50/time_machine-2.19.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6ba0303e9cc9f7f947e344f501e26bedfb68fab521e3c2729d370f4f332d2d55", size = 30964, upload-time = "2025-08-19T17:20:45.366Z" }, + { url = "https://files.pythonhosted.org/packages/80/91/38bf9c79674e95ce32e23c267055f281dff651eec77ed32a677db3dc011a/time_machine-2.19.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2851825b524a988ee459c37c1c26bdfaa7eff78194efb2b562ea497a6f375b0a", size = 32606, upload-time = "2025-08-19T17:20:46.693Z" }, + { url = "https://files.pythonhosted.org/packages/19/4a/e9222d85d4de68975a5e799f539a9d32f3a134a9101fca0a61fa6aa33d68/time_machine-2.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68d32b09ecfd7fef59255c091e8e7c24dd117f882c4880b5c7ab8c5c32a98f89", size = 34405, upload-time = "2025-08-19T17:20:48.032Z" }, + { url = "https://files.pythonhosted.org/packages/14/e2/09480d608d42d6876f9ff74593cfc9197a7eb2c31381a74fb2b145575b65/time_machine-2.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60c46ab527bf2fa144b530f639cc9e12803524c9e1f111dc8c8f493bb6586eeb", size = 33181, upload-time = "2025-08-19T17:20:48.937Z" }, + { url = "https://files.pythonhosted.org/packages/84/64/f9359e000fad32d9066305c48abc527241d608bcdf77c19d67d66e268455/time_machine-2.19.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:56f26ab9f0201c453d18fe76bb7d1cf05fe58c1b9d9cb0c7d243d05132e01292", size = 31036, upload-time = "2025-08-19T17:20:50.276Z" }, + { url = "https://files.pythonhosted.org/packages/71/0d/fab2aacec71e3e482bd7fce0589381f9414a4a97f8766bddad04ad047b7b/time_machine-2.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6c806cf3c1185baa1d807b7f51bed0db7a6506832c961d5d1b4c94c775749bc0", size = 32145, upload-time = "2025-08-19T17:20:51.449Z" }, + { url = "https://files.pythonhosted.org/packages/44/fb/faeba2405fb27553f7b28db441a500e2064ffdb2dcba001ee315fdd2c121/time_machine-2.19.0-cp311-cp311-win32.whl", hash = "sha256:b30039dfd89855c12138095bee39c540b4633cbc3684580d684ef67a99a91587", size = 17004, upload-time = "2025-08-19T17:20:52.38Z" }, + { url = "https://files.pythonhosted.org/packages/2f/84/87e483d660ca669426192969280366635c845c3154a9fe750be546ed3afc/time_machine-2.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:13ed8b34430f1de79905877f5600adffa626793ab4546a70a99fb72c6a3350d8", size = 17822, upload-time = "2025-08-19T17:20:53.348Z" }, + { url = "https://files.pythonhosted.org/packages/41/f4/ebf7bbf5047854a528adaf54a5e8780bc5f7f0104c298ab44566a3053bf8/time_machine-2.19.0-cp311-cp311-win_arm64.whl", hash = "sha256:cc29a50a0257d8750b08056b66d7225daab47606832dea1a69e8b017323bf511", size = 16680, upload-time = "2025-08-19T17:20:54.26Z" }, + { url = "https://files.pythonhosted.org/packages/9b/aa/7e00614d339e4d687f6e96e312a1566022528427d237ec639df66c4547bc/time_machine-2.19.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c85cf437dc3c07429456d8d6670ac90ecbd8241dcd0fbf03e8db2800576f91ff", size = 19308, upload-time = "2025-08-19T17:20:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/ab/3c/bde3c757394f5bca2fbc1528d4117960a26c38f9b160bf471b38d2378d8f/time_machine-2.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d9238897e8ef54acdf59f5dff16f59ca0720e7c02d820c56b4397c11db5d3eb9", size = 15019, upload-time = "2025-08-19T17:20:56.204Z" }, + { url = "https://files.pythonhosted.org/packages/c8/e0/8ca916dd918018352d377f1f5226ee071cfbeb7dbbde2b03d14a411ac2b1/time_machine-2.19.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e312c7d5d6bfffb96c6a7b39ff29e3046de100d7efaa3c01552654cfbd08f14c", size = 33079, upload-time = "2025-08-19T17:20:57.166Z" }, + { url = "https://files.pythonhosted.org/packages/48/69/184a0209f02dd0cb5e01e8d13cd4c97a5f389c4e3d09b95160dd676ad1e7/time_machine-2.19.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:714c40b2c90d1c57cc403382d5a9cf16e504cb525bfe9650095317da3c3d62b5", size = 34925, upload-time = "2025-08-19T17:20:58.117Z" }, + { url = "https://files.pythonhosted.org/packages/43/42/4bbf4309e8e57cea1086eb99052d97ff6ddecc1ab6a3b07aa4512f8bf963/time_machine-2.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2eaa1c675d500dc3ccae19e9fb1feff84458a68c132bbea47a80cc3dd2df7072", size = 36384, upload-time = "2025-08-19T17:20:59.108Z" }, + { url = "https://files.pythonhosted.org/packages/b1/af/9f510dc1719157348c1a2e87423aed406589070b54b503cb237d9bf3a4fe/time_machine-2.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e77a414e9597988af53b2b2e67242c9d2f409769df0d264b6d06fda8ca3360d4", size = 34881, upload-time = "2025-08-19T17:21:00.116Z" }, + { url = "https://files.pythonhosted.org/packages/ca/28/61764a635c70cc76c76ba582dfdc1a84834cddaeb96789023af5214426b2/time_machine-2.19.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cd93996970e11c382b04d4937c3cd0b0167adeef14725ece35aae88d8a01733c", size = 32931, upload-time = "2025-08-19T17:21:01.095Z" }, + { url = "https://files.pythonhosted.org/packages/b6/e0/f028d93b266e6ade8aca5851f76ebbc605b2905cdc29981a2943b43e1a6c/time_machine-2.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8e20a6d8d6e23174bd7e931e134d9610b136db460b249d07e84ecdad029ec352", size = 34241, upload-time = "2025-08-19T17:21:02.052Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a6/36d1950ed1d3f613158024cf1dcc73db1d9ef0b9117cf51ef2e37dc06499/time_machine-2.19.0-cp312-cp312-win32.whl", hash = "sha256:95afc9bc65228b27be80c2756799c20b8eb97c4ef382a9b762b6d7888bc84099", size = 17021, upload-time = "2025-08-19T17:21:03.374Z" }, + { url = "https://files.pythonhosted.org/packages/b1/0d/e2dce93355abda3cac69e77fe96566757e98b8fe7fdcbddce89c9ced3f5f/time_machine-2.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:e84909af950e2448f4e2562ea5759c946248c99ab380d2b47d79b62bd76fa236", size = 17857, upload-time = "2025-08-19T17:21:04.331Z" }, + { url = "https://files.pythonhosted.org/packages/eb/28/50ae6fb83b7feeeca7a461c0dc156cf7ef5e6ef594a600d06634fde6a2cb/time_machine-2.19.0-cp312-cp312-win_arm64.whl", hash = "sha256:0390a1ea9fa7e9d772a39b7c61b34fdcca80eb9ffac339cc0441c6c714c81470", size = 16677, upload-time = "2025-08-19T17:21:05.39Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b8/24ebce67aa531bae2cbe164bb3f4abc6467dc31f3aead35e77f5a075ea3e/time_machine-2.19.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5e172866753e6041d3b29f3037dc47c20525176a494a71bbd0998dfdc4f11f2f", size = 19373, upload-time = "2025-08-19T17:21:06.701Z" }, + { url = "https://files.pythonhosted.org/packages/53/a5/c9a5240fd2f845d3ff9fa26f8c8eaa29f7239af9d65007e61d212250f15b/time_machine-2.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f70f68379bd6f542ae6775cce9a4fa3dcc20bf7959c42eaef871c14469e18097", size = 15056, upload-time = "2025-08-19T17:21:07.667Z" }, + { url = "https://files.pythonhosted.org/packages/b9/92/66cce5d2fb2a5e68459aca85fd18a7e2d216f725988940cd83f96630f2f1/time_machine-2.19.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e69e0b0f694728a00e72891ef8dd00c7542952cb1c87237db594b6b27d504a96", size = 33172, upload-time = "2025-08-19T17:21:08.619Z" }, + { url = "https://files.pythonhosted.org/packages/ae/20/b499e9ab4364cd466016c33dcdf4f56629ca4c20b865bd4196d229f31d92/time_machine-2.19.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3ae0a8b869574301ec5637e32c270c7384cca5cd6e230f07af9d29271a7fa293", size = 35042, upload-time = "2025-08-19T17:21:09.622Z" }, + { url = "https://files.pythonhosted.org/packages/41/32/b252d3d32791eb16c07d553c820dbc33d9c7fa771de3d1c602190bded2b7/time_machine-2.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:554e4317de90e2f7605ff80d153c8bb56b38c0d0c0279feb17e799521e987b8c", size = 36535, upload-time = "2025-08-19T17:21:10.571Z" }, + { url = "https://files.pythonhosted.org/packages/98/cf/4d0470062b9742e1b040ab81bad04d1a5d1de09806507bb6188989cfa1a7/time_machine-2.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6567a5ec5538ed550539ac29be11b3cb36af1f9894e2a72940cba0292cc7c3c9", size = 34945, upload-time = "2025-08-19T17:21:11.538Z" }, + { url = "https://files.pythonhosted.org/packages/24/71/2f741b29d98b1c18f6777a32236497c3d3264b6077e431cea4695684c8a1/time_machine-2.19.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82e9ffe8dfff07b0d810a2ad015a82cd78c6a237f6c7cf185fa7f747a3256f8a", size = 33014, upload-time = "2025-08-19T17:21:12.858Z" }, + { url = "https://files.pythonhosted.org/packages/e8/83/ca8dba6106562843fd99f672e5aaf95badbc10f4f13f7cfe8d8640a7019d/time_machine-2.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e1c4e578cdd69b3531d8dd3fbcb92a0cd879dadb912ee37af99c3a9e3c0d285", size = 34350, upload-time = "2025-08-19T17:21:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/21/7f/34fe540450e18d0a993240100e4b86e8d03d831b92af8bb6ddb2662dc6fc/time_machine-2.19.0-cp313-cp313-win32.whl", hash = "sha256:72dbd4cbc3d96dec9dd281ddfbb513982102776b63e4e039f83afb244802a9e5", size = 17047, upload-time = "2025-08-19T17:21:14.874Z" }, + { url = "https://files.pythonhosted.org/packages/bf/5d/c8be73df82c7ebe7cd133279670e89b8b110af3ce1412c551caa9d08e625/time_machine-2.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:e17e3e089ac95f9a145ce07ff615e3c85674f7de36f2d92aaf588493a23ffb4b", size = 17868, upload-time = "2025-08-19T17:21:15.819Z" }, + { url = "https://files.pythonhosted.org/packages/92/13/2dfd3b8fb285308f61cd7aa9bfa96f46ddf916e3549a0f0afd094c556599/time_machine-2.19.0-cp313-cp313-win_arm64.whl", hash = "sha256:149072aff8e3690e14f4916103d898ea0d5d9c95531b6aa0995251c299533f7b", size = 16710, upload-time = "2025-08-19T17:21:16.748Z" }, + { url = "https://files.pythonhosted.org/packages/05/c1/deebb361727d2c5790f9d4d874be1b19afd41f4375581df465e6718b46a2/time_machine-2.19.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f3589fee1ed0ab6ee424a55b0ea1ec694c4ba64cc26895bcd7d99f3d1bc6a28a", size = 20053, upload-time = "2025-08-19T17:21:17.704Z" }, + { url = "https://files.pythonhosted.org/packages/45/e8/fe3376951e6118d8ec1d1f94066a169b791424fe4a26c7dfc069b153ee08/time_machine-2.19.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7887e85275c4975fe54df03dcdd5f38bd36be973adc68a8c77e17441c3b443d6", size = 15423, upload-time = "2025-08-19T17:21:18.668Z" }, + { url = "https://files.pythonhosted.org/packages/9c/c7/f88d95cd1a87c650cf3749b4d64afdaf580297aa18ad7f4b44ec9d252dfc/time_machine-2.19.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ce0be294c209928563fcce1c587963e60ec803436cf1e181acd5bc1e425d554b", size = 39630, upload-time = "2025-08-19T17:21:19.645Z" }, + { url = "https://files.pythonhosted.org/packages/cc/5d/65a5c48a65357e56ec6f032972e4abd1c02d4fca4b0717a3aaefd19014d4/time_machine-2.19.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a62fd1ab380012c86f4c042010418ed45eb31604f4bf4453e17c9fa60bc56a29", size = 41242, upload-time = "2025-08-19T17:21:20.979Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/fe5209e1615fde0a8cad6c4e857157b150333ed1fe31a7632b08cfe0ebdd/time_machine-2.19.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b25ec853a4530a5800731257f93206b12cbdee85ede964ebf8011b66086a7914", size = 44278, upload-time = "2025-08-19T17:21:21.984Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3a/a5e5fe9c5d614cde0a9387ff35e8dfd12c5ef6384e4c1a21b04e6e0b905d/time_machine-2.19.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a430e4d0e0556f021a9c78e9b9f68e5e8910bdace4aa34ed4d1a73e239ed9384", size = 42321, upload-time = "2025-08-19T17:21:23.755Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c5/56eca774e9162bc1ce59111d2bd69140dc8908c9478c92ec7bd15d547600/time_machine-2.19.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2415b7495ec4364c8067071e964fbadfe746dd4cdb43983f2f0bd6ebed13315c", size = 39270, upload-time = "2025-08-19T17:21:26.009Z" }, + { url = "https://files.pythonhosted.org/packages/9b/69/5dd0c420667578169a12acc8c8fd7452e8cfb181e41c9b4ac7e88fa36686/time_machine-2.19.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbfc6b90c10f288594e1bf89a728a98cc0030791fd73541bbdc6b090aff83143", size = 40193, upload-time = "2025-08-19T17:21:27.054Z" }, + { url = "https://files.pythonhosted.org/packages/75/a7/de974d421bd55c9355583427c2a38fb0237bb5fd6614af492ba89dacb2f9/time_machine-2.19.0-cp313-cp313t-win32.whl", hash = "sha256:16f5d81f650c0a4d117ab08036dc30b5f8b262e11a4a0becc458e7f1c011b228", size = 17542, upload-time = "2025-08-19T17:21:28.674Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/aa0d05becd5d06ae8d3f16d657dc8cc9400c8d79aef80299de196467ff12/time_machine-2.19.0-cp313-cp313t-win_amd64.whl", hash = "sha256:645699616ec14e147094f601e6ab9553ff6cea37fad9c42720a6d7ed04bcd5dc", size = 18703, upload-time = "2025-08-19T17:21:29.663Z" }, + { url = "https://files.pythonhosted.org/packages/1f/c0/f785a4c7c73aa176510f7c48b84b49c26be84af0d534deb222e0327f750e/time_machine-2.19.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b32daa965d13237536ea3afaa5ad61ade2b2d9314bc3a20196a0d2e1d7b57c6a", size = 17020, upload-time = "2025-08-19T17:21:30.653Z" }, + { url = "https://files.pythonhosted.org/packages/ed/97/c5fb51def06c0b2b6735332ad118ab35b4d9b85368792e5b638e99b1b686/time_machine-2.19.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:31cb43c8fd2d961f31bed0ff4e0026964d2b35e5de9e0fabbfecf756906d3612", size = 19360, upload-time = "2025-08-19T17:21:31.94Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4e/2d795f7d6b7f5205ffe737a05bb1cf19d8038233b797062b2ef412b8512b/time_machine-2.19.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:bdf481a75afc6bff3e520db594501975b652f7def21cd1de6aa971d35ba644e6", size = 15033, upload-time = "2025-08-19T17:21:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/dd/32/9bad501e360b4e758c58fae616ca5f8c7ad974b343f2463a15b2bf77a366/time_machine-2.19.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:00bee4bb950ac6a08d62af78e4da0cf2b4fc2abf0de2320d0431bf610db06e7c", size = 33379, upload-time = "2025-08-19T17:21:33.925Z" }, + { url = "https://files.pythonhosted.org/packages/a3/45/eda0ca4d793dfd162478d6163759b1c6ce7f6e61daa7fd7d62b31f21f87f/time_machine-2.19.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9f02199490906582302ce09edd32394fb393271674c75d7aa76c7a3245f16003", size = 35123, upload-time = "2025-08-19T17:21:34.945Z" }, + { url = "https://files.pythonhosted.org/packages/f0/5a/97e16325442ae5731fcaac794f0a1ef9980eff8a5491e58201d7eb814a34/time_machine-2.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e35726c7ba625f844c13b1fc0d4f81f394eefaee1d3a094a9093251521f2ef15", size = 36588, upload-time = "2025-08-19T17:21:35.975Z" }, + { url = "https://files.pythonhosted.org/packages/e8/9d/bf0b2ccc930cc4a316f26f1c78d3f313cd0fa13bb7480369b730a8f129db/time_machine-2.19.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:304315023999cd401ff02698870932b893369e1cfeb2248d09f6490507a92e97", size = 35013, upload-time = "2025-08-19T17:21:37.017Z" }, + { url = "https://files.pythonhosted.org/packages/f0/5a/39ac6a3078174f9715d88364871348b249631f12e76de1b862433b3f8862/time_machine-2.19.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9765d4f003f263ea8bfd90d2d15447ca4b3dfa181922cf6cf808923b02ac180a", size = 33303, upload-time = "2025-08-19T17:21:38.352Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ac/d8646baf9f95f2e792a6d7a7b35e92fca253c4a992afff801beafae0e5c2/time_machine-2.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7837ef3fd5911eb9b480909bb93d922737b6bdecea99dfcedb0a03807de9b2d3", size = 34440, upload-time = "2025-08-19T17:21:39.382Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8b/8b6568c5ae966d80ead03ab537be3c6acf2af06fb501c2d466a3162c6295/time_machine-2.19.0-cp314-cp314-win32.whl", hash = "sha256:4bb5bd43b1bdfac3007b920b51d8e761f024ed465cfeec63ac4296922a4ec428", size = 17162, upload-time = "2025-08-19T17:21:40.381Z" }, + { url = "https://files.pythonhosted.org/packages/46/a5/211c1ab4566eba5308b2dc001b6349e3a032e3f6afa67ca2f27ea6b27af5/time_machine-2.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:f583bbd0aa8ab4a7c45a684bf636d9e042d466e30bcbae1d13e7541e2cbe7207", size = 18040, upload-time = "2025-08-19T17:21:41.363Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fc/4c2fb705f6371cb83824da45a8b967514a922fc092a0ef53979334d97a70/time_machine-2.19.0-cp314-cp314-win_arm64.whl", hash = "sha256:f379c6f8a6575a8284592179cf528ce89373f060301323edcc44f1fa1d37be12", size = 16752, upload-time = "2025-08-19T17:21:42.336Z" }, + { url = "https://files.pythonhosted.org/packages/79/ab/6437d18f31c666b5116c97572a282ac2590a82a0a9867746a6647eaf4613/time_machine-2.19.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a3b8981f9c663b0906b05ab4d0ca211fae4b63b47c6ec26de5374fe56c836162", size = 20057, upload-time = "2025-08-19T17:21:43.35Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a2/e03639ec2ba7200328bbcad8a2b2b1d5fccca9cceb9481b164a1cabdcb33/time_machine-2.19.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8e9c6363893e7f52c226afbebb23e825259222d100e67dfd24c8a6d35f1a1907", size = 15430, upload-time = "2025-08-19T17:21:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ff/39e63a48e840f3e36ce24846ee51dd99c6dba635659b1750a2993771e88e/time_machine-2.19.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:206fcd6c9a6f00cac83db446ad1effc530a8cec244d2780af62db3a2d0a9871b", size = 39622, upload-time = "2025-08-19T17:21:45.821Z" }, + { url = "https://files.pythonhosted.org/packages/9a/2e/ee5ac79c4954768705801e54817c7d58e07e25a0bb227e775f501f3e2122/time_machine-2.19.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf33016a1403c123373ffaeff25e26e69d63bf2c63b6163932efed94160db7ef", size = 41235, upload-time = "2025-08-19T17:21:46.783Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3e/9af5f39525e779185c77285b8bbae15340eeeaa0afb33d458bc8b47d459b/time_machine-2.19.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9247c4bb9bbd3ff584ef4efbdec8efd9f37aa08bcfc4728bde1e489c2cb445bd", size = 44276, upload-time = "2025-08-19T17:21:47.759Z" }, + { url = "https://files.pythonhosted.org/packages/59/fe/572c7443cc27140bbeae3947279bbd4a120f9e8622253a20637f260b7813/time_machine-2.19.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:77f9bb0b86758d1f2d9352642c874946ad5815df53ef4ca22eb9d532179fe50d", size = 42330, upload-time = "2025-08-19T17:21:48.881Z" }, + { url = "https://files.pythonhosted.org/packages/cf/24/1a81c2e08ee7dae13ec8ceed27a29afa980c3d63852e42f1e023bf0faa03/time_machine-2.19.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0b529e262df3b9c449f427385f4d98250828c879168c2e00eec844439f40b370", size = 39281, upload-time = "2025-08-19T17:21:49.907Z" }, + { url = "https://files.pythonhosted.org/packages/d2/60/6f0d6e5108978ca1a2a4ffb4d1c7e176d9199bb109fd44efe2680c60b52a/time_machine-2.19.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9199246e31cdc810e5d89cb71d09144c4d745960fdb0824da4994d152aca3303", size = 40201, upload-time = "2025-08-19T17:21:50.953Z" }, + { url = "https://files.pythonhosted.org/packages/73/b9/3ea4951e8293b0643feb98c0b9a176fa822154f1810835db3f282968ab10/time_machine-2.19.0-cp314-cp314t-win32.whl", hash = "sha256:0fe81bae55b7aefc2c2a34eb552aa82e6c61a86b3353a3c70df79b9698cb02ca", size = 17743, upload-time = "2025-08-19T17:21:51.948Z" }, + { url = "https://files.pythonhosted.org/packages/e4/8b/cd802884ca8a98e2b6cdc2397d57dd12ff8a7d1481e06fc3fad3d4e7e5ff/time_machine-2.19.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7253791b8d7e7399fbeed7a8193cb01bc004242864306288797056badbdaf80b", size = 18956, upload-time = "2025-08-19T17:21:52.997Z" }, + { url = "https://files.pythonhosted.org/packages/c6/49/cabb1593896082fd55e34768029b8b0ca23c9be8b2dc127e0fc14796d33e/time_machine-2.19.0-cp314-cp314t-win_arm64.whl", hash = "sha256:536bd1ac31ab06a1522e7bf287602188f502dc19d122b1502c4f60b1e8efac79", size = 17068, upload-time = "2025-08-19T17:21:54.064Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, +] + +[[package]] +name = "tornado" +version = "6.5.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/61/53d562a57b28c08eda40b258c0f975e360541943ad7c7bef897a40caafda/tornado-6.5.10.tar.gz", hash = "sha256:a6b1ccd08c04b4a06fb5aeb381be99de5ad1e5375c1785e31d78c880feb57687", size = 537910, upload-time = "2026-09-15T13:47:48.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/5b/ff5fc58fa2427c30dea74c90053f4fc5eda1e7f3833ed3ecc7147fe2b311/tornado-6.5.10-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9261783640e23258694a9ff0795df430a5a7b0a651d3dd53dd0969ad6be16da7", size = 465883, upload-time = "2026-09-15T13:47:35.463Z" }, + { url = "https://files.pythonhosted.org/packages/ad/f5/cd7be26c34a3315532f3aef5f092465da8f59c334dd439d3c14aaef16461/tornado-6.5.10-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:83e6cf438b106c6b3852d70960967bb1b70c87438050dca0981e4b9aa751a4c1", size = 464046, upload-time = "2026-09-15T13:47:37.178Z" }, + { url = "https://files.pythonhosted.org/packages/60/33/df6d7d04854a58619f8349a51e3edb138324130a7562b0bb21f115bb940f/tornado-6.5.10-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bdf942448169e5336451d0494d7e3d81cfa726d5aa312affdc4682dd62a62f6d", size = 467096, upload-time = "2026-09-15T13:47:38.559Z" }, + { url = "https://files.pythonhosted.org/packages/29/17/cc35dff68272d685cffd8600ffafbd8067e7d05e7348d9f80caddffbbd5f/tornado-6.5.10-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69acca6501eed74582b76dbbceee2a91613f54728e3e418346000d7103101676", size = 468067, upload-time = "2026-09-15T13:47:40.085Z" }, + { url = "https://files.pythonhosted.org/packages/c3/01/6e5349b4e1a53a4b4972a6716785e1fe7407f312063c3972690af8ff301b/tornado-6.5.10-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:66aaa3f57d30c6e6becee83ff28055d5930ac724214bde99393eefda83d5e015", size = 467901, upload-time = "2026-09-15T13:47:41.576Z" }, + { url = "https://files.pythonhosted.org/packages/28/5e/b4facf94370dba006819c8d304376f8b9fbec6b935b5e51bf45823a9790b/tornado-6.5.10-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4bd192b959f9128fb99b8898148070ba4574c9589b78bce42d1851131fe85828", size = 467308, upload-time = "2026-09-15T13:47:43.145Z" }, + { url = "https://files.pythonhosted.org/packages/56/ae/047938e828cafc8eca4c908fafb6588fee944e3af39a0af9d7b602499ae5/tornado-6.5.10-cp39-abi3-win32.whl", hash = "sha256:302eb1e0e3e159314eb591920529fdea80acca92df5510a2cec5bbd4f099ec72", size = 468387, upload-time = "2026-09-15T13:47:44.556Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d4/5901517f05affd752490f6a654ba31b7474664e8dd80bd045a00c220bd88/tornado-6.5.10-cp39-abi3-win_amd64.whl", hash = "sha256:37ae8f150cecfdbf747fc4e12f5e9a97ecd8cf1d4cdb3f119e2de84b11196918", size = 468828, upload-time = "2026-09-15T13:47:45.961Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/fd497f3a7f7b74bb04f4b94536b5c9f80742b5d50501fd27977652ddec16/tornado-6.5.10-cp39-abi3-win_arm64.whl", hash = "sha256:ce045d3c298fddd30e89a2777f97039d1b641eb9518ac7b26a4721903539c694", size = 467847, upload-time = "2026-09-15T13:47:47.283Z" }, +] + +[[package]] +name = "tox" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "colorama" }, + { name = "filelock" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "pluggy" }, + { name = "pyproject-api" }, + { name = "python-discovery" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomli-w" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/93/ce52def83be73dcb86c0c5985f40808917f09828c1616d4018206c3dbba3/tox-4.63.0.tar.gz", hash = "sha256:fb7105ef125b371e31797cfa615e5a160de3d8ef29b86dca7fcbd9048da3a9d8", size = 310965, upload-time = "2026-09-19T16:45:35.275Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/4d/d0e37d254b4ad5492d63ea60981365a5122261b5954a9d952d8b15283e26/tox-4.63.0-py3-none-any.whl", hash = "sha256:e8c560a9124e127c5e0547912d2ae1ef948dbba2fdea0b8d506e609a126b2e47", size = 229324, upload-time = "2026-09-19T16:45:33.829Z" }, +] + +[[package]] +name = "traitlets" +version = "5.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/2e/a7fbfe268c8a3b32546930c0297c101d65a4a14c304ad5790a9f478f0e4e/traitlets-5.16.1.tar.gz", hash = "sha256:ed900c2b631aa3a112811139fa97b8d2c3bad5e989656bba4b7e52c7852c18c1", size = 166137, upload-time = "2026-08-03T08:32:36.848Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl", hash = "sha256:f775618166caa0396c8e337099240f2bd3e5e917d203b2e6fbe21a58d3cb1f6b", size = 86211, upload-time = "2026-08-03T08:32:34.48Z" }, +] + +[[package]] +name = "truststore" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" }, +] + +[[package]] +name = "ty" +version = "0.0.83" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/7d/2fd9575bce2d14e2281bec82d0713dee68329b0dc944e9c06c2e36b761fe/ty-0.0.83.tar.gz", hash = "sha256:db118de73c05ac476faceb4d42d59782feaeddfc4d721fd3ae8b807a0a2ae4e4", size = 7373076, upload-time = "2026-09-21T22:47:29.207Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/85/7259431bade2317d8b82fa27220ae4618c21396acf0ca28a7bd75e92afd7/ty-0.0.83-py3-none-linux_armv6l.whl", hash = "sha256:836bc08bad96ecc7e38baf1a6ddaec15aa8c8b46ae99a96365397b5f6481b7a5", size = 13905669, upload-time = "2026-09-21T22:46:36.677Z" }, + { url = "https://files.pythonhosted.org/packages/d2/30/9527386ade8dd71fc52f65cde3c094411af7ad0d505f9063ef0841998702/ty-0.0.83-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0f1fb029fe076c7dca404d98e38fdb5fdbc2038a09407f89d43be1fdd9b2c459", size = 13499505, upload-time = "2026-09-21T22:46:40.114Z" }, + { url = "https://files.pythonhosted.org/packages/a0/54/63080d8cfd59a53810499a69a2ebc995724be27618b70d7f099e73ace317/ty-0.0.83-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3515776d01ee3b1be974dafa73d449d0bc711afbc12ba341f82f70bc641ff4ee", size = 13498980, upload-time = "2026-09-21T22:46:43.197Z" }, + { url = "https://files.pythonhosted.org/packages/49/78/e647f190bd9513d09db30d45309e9b5df070b5caa44af2de3290fab59fbe/ty-0.0.83-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5b80d66c7f9c8d60f25ccfd40c6eaebe8fd7c9005074904e0e164dd89c13d03", size = 13494705, upload-time = "2026-09-21T22:46:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/2e/12/aef645b13c5bd15e3e5993537dbc23f7d65475f3cf1755d07b4d3e56850d/ty-0.0.83-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57eae4aa55dab1af5d1423227c6517ccab1e658fe265c8e5a58caa584ca2876b", size = 13661466, upload-time = "2026-09-21T22:46:49.392Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c4/11872e823653c568468ef7f0c3b23aed7ad2d33652e20144d28d5bfad58a/ty-0.0.83-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dea3680911340ecf93dff5786dbeefd69c9399df685a22751e1fbd27e6007b50", size = 14479677, upload-time = "2026-09-21T22:46:52.277Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/5b0d0d24d16d24d781f29ee842ee718903e453dc4068acb02bef860d5540/ty-0.0.83-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8453786c274f48e2a89b78e8a505a4f449323f357f2ce26a9b90a4bd68176f67", size = 14972208, upload-time = "2026-09-21T22:46:55.242Z" }, + { url = "https://files.pythonhosted.org/packages/54/04/0077a53bcf6ceb41365a45d8091b52094c91a5f4b071c2a6d705ed8bb65b/ty-0.0.83-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7701bdae3b52271cf0835abe4202aa3b23b735425d8c5a3e6a8ca1c68d39ead8", size = 14660550, upload-time = "2026-09-21T22:46:58.256Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ff/b6c879f1c3121387edd19ebdb1fb02af30691f88f632278a5d467fb5f348/ty-0.0.83-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa95b4376447e5309fe3ccc98ee75916a9082714e6a029b99427ca62021dc94a", size = 14147376, upload-time = "2026-09-21T22:47:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bc/82e730247d53cb02acd5a91237c0c62f215b2e0379689a550176d307ed89/ty-0.0.83-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2a4550a08fc1899a33e4a08787a73ec9daa99d292268df4f0a5f1c24c63854c3", size = 14567502, upload-time = "2026-09-21T22:47:04.344Z" }, + { url = "https://files.pythonhosted.org/packages/67/13/bb6f57923231f51b626931625ada71e219d64541f8fadf96065bc5034358/ty-0.0.83-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:feb72080f0835fa2b27485e3b11fa50dc57c30e0ba73808a3f7d64b56db83f6f", size = 13443051, upload-time = "2026-09-21T22:47:07.316Z" }, + { url = "https://files.pythonhosted.org/packages/50/20/9862431662d4453527cd52cf0f30d5975cd1801aa39e570bf45fb299be77/ty-0.0.83-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3e5206def2d6e1df93c75ed8dc763255233ccc3153647509d125bada10ad770a", size = 13678908, upload-time = "2026-09-21T22:47:10.714Z" }, + { url = "https://files.pythonhosted.org/packages/d7/e3/94ebbe1573b76660fa09f2dbce96da6d0bb69a2d29c7b6ad538b452979d4/ty-0.0.83-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bdf5dfa157f5c127ba096fe0561b47877e6e79562c7fc2b35906ba5e9f6bb723", size = 13937248, upload-time = "2026-09-21T22:47:14.133Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c0/3ea45c2ec3a82fddb7137e457ccb2a452446905430d225adaa4990cdc338/ty-0.0.83-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:626d6ad00f05a5d9b7ea80de44b12931d0c0fb50e64eab20b7fcc13e9aff2a68", size = 14290826, upload-time = "2026-09-21T22:47:16.874Z" }, + { url = "https://files.pythonhosted.org/packages/d8/b2/c941ce83a59e46ef31dd04bdf6067530e7ec77d5f0cdd1e223fbc6fc351b/ty-0.0.83-py3-none-win32.whl", hash = "sha256:0391017dac6ba3e452e0322301f50bba12fbccadac07d7fd888c46951ad37638", size = 13105291, upload-time = "2026-09-21T22:47:19.807Z" }, + { url = "https://files.pythonhosted.org/packages/b2/8a/5eef9458505698b144643cbd2062f465c5d860d8cf86a8710d74505ad9a0/ty-0.0.83-py3-none-win_amd64.whl", hash = "sha256:1b67d86f5bbad4076ae1a2ff23a6a4493219885d72d4b148cb0193e090390060", size = 13901557, upload-time = "2026-09-21T22:47:23.429Z" }, + { url = "https://files.pythonhosted.org/packages/9f/27/a06061513f1de25569c2e8898018bd552ab7b31afa7cfe818703269b3350/ty-0.0.83-py3-none-win_arm64.whl", hash = "sha256:a916a3d7d68fe30b7aacdb9fe16d461fa944d722e59e96ea0fa9d206a1b6625f", size = 13627960, upload-time = "2026-09-21T22:47:26.305Z" }, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20260807" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/4e/b3fa538f9cb38dfece0d6ccf6d3d0d925bdedb144fb9c8129dfc007cd003/types_python_dateutil-2.9.0.20260807.tar.gz", hash = "sha256:e0b8a90d464c8684c66b7b8e4556d9074afdddcc56ca45323f0987134f9e7034", size = 17618, upload-time = "2026-08-07T04:17:13.491Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/5e/3715867caea2f4cea56ccb04c851cde23ed063449c3b004c7a047f20dd48/types_python_dateutil-2.9.0.20260807-py3-none-any.whl", hash = "sha256:54aa3707350ed7a9cc0776fd2f6739679d6967d11b40150985e81edcb86df4db", size = 18486, upload-time = "2026-08-07T04:17:12.504Z" }, +] + +[[package]] +name = "types-requests" +version = "2.33.0.20260906" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/18/4c2c0290953f8b3b9612adfcb07b57f144ade3ad32a76764fca42b77c5f3/types_requests-2.33.0.20260906.tar.gz", hash = "sha256:76ab8a0fb736744a0c3deee7aa57b2927e301f078d9e61f5391b3e92002416b9", size = 25263, upload-time = "2026-09-06T06:35:47.707Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/4c/51ec821d22a45b4162fa3f3e9e94ea4c0f8c49e82363b10955baa5438391/types_requests-2.33.0.20260906-py3-none-any.whl", hash = "sha256:9f53622652bd921ead7a54d665be1b8d518165c8b51cc9d68d944cd6b6bfa8fb", size = 21461, upload-time = "2026-09-06T06:35:45.856Z" }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20260518" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/11/6ece999e91f2ccb848ab4420f3f4816e78ac0541f739e6864affdaaa5737/types_toml-0.10.8.20260518.tar.gz", hash = "sha256:80e10facd24fdeda9d5c672187d72be3ac284843788d67f5aae59e3e016db6fe", size = 9419, upload-time = "2026-05-18T06:02:16.719Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/25/489751806bf5c95e4007f8e17409199c54d31e49ffbea07c5729b1286c8e/types_toml-0.10.8.20260518-py3-none-any.whl", hash = "sha256:0e564ab05f6fde62a315b3b5a9b6624fda569399795d30a37e64705a70459303", size = 9669, upload-time = "2026-05-18T06:02:15.86Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/31/3d74fa778a63b98b7374323befcc0be5ab3bd94afd4096a0124e7379152c/tzdata-2026.4.tar.gz", hash = "sha256:f1b8bd365d8d210c55353f4d7f8d6d8561c0ba50d704b700d195a9424bba0d79", size = 199350, upload-time = "2026-09-12T12:56:03.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/bc/8737e8d54cf51106118039b83f485a4783112fab49ea9d044b234978a46e/tzdata-2026.4-py2.py3-none-any.whl", hash = "sha256:c2169a8b0a7a5e9674da5a135ccdfb2b3e671b333ed9fed17b41f73c34476e81", size = 347494, upload-time = "2026-09-12T12:56:01.67Z" }, +] + +[[package]] +name = "url-normalize" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/26/b60cce0211e94bb130e88dbcba87583f61c6ddf386fa6adc10a167461f6a/url_normalize-3.0.1.tar.gz", hash = "sha256:1655cd214159d9d47dc37aa6ce993c2149da44fa35cac6bafd90036a4eda3ac3", size = 28198, upload-time = "2026-09-22T22:20:54.513Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/bf/98209a164859c81d9eec311ee2b35cd1e5b33c7be8d3665c08850557abe1/url_normalize-3.0.1-py3-none-any.whl", hash = "sha256:97ea68fc543b1fc9f270f34c90cf164453e7d490da2ec653dcd8ebd4e3ac1faf", size = 18296, upload-time = "2026-09-22T22:20:53.342Z" }, +] + +[[package]] +name = "urllib3" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" }, +] + +[[package]] +name = "uuid7" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/19/7472bd526591e2192926247109dbf78692e709d3e56775792fec877a7720/uuid7-0.1.0.tar.gz", hash = "sha256:8c57aa32ee7456d3cc68c95c4530bc571646defac01895cfc73545449894a63c", size = 14052, upload-time = "2021-12-29T01:38:21.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/77/8852f89a91453956582a85024d80ad96f30a41fed4c2b3dce0c9f12ecc7e/uuid7-0.1.0-py2.py3-none-any.whl", hash = "sha256:5e259bb63c8cb4aded5927ff41b444a80d0c7124e8a0ced7cf44efa1f5cccf61", size = 7477, upload-time = "2021-12-29T01:38:20.418Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/ad/04bbb797c84fc1f26cb171f7394716f4865ffb8d8c5e1eef42565c2dfa6b/uvicorn-0.53.0.tar.gz", hash = "sha256:a9356f0cb89b3b8621529c5d5eebd69bfe154f4c3f68b4cf2de47e45fa855c2e", size = 110881, upload-time = "2026-09-14T07:44:23.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/18/0eea75741ee812e9f598b687619ce2454f6c3a1c5cd21ea990ec6bd26f45/uvicorn-0.53.0-py3-none-any.whl", hash = "sha256:e8dca71ec86dce5f04e333f0d56cdedf942446e6643b9cea1af0d6d3a02cb03e", size = 87081, upload-time = "2026-09-14T07:44:22.179Z" }, +] + +[[package]] +name = "vcrpy" +version = "8.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/d5/8a1f8eb603e2d35fbb0ecd1e309d0c5c18a0ecfc8c0a8f04088bbc8f833b/vcrpy-8.3.0.tar.gz", hash = "sha256:46d64e77e8d95e5c76c7d9a94ff05d8b38b2ae4e1d4869eb0235024b6fcb5212", size = 96117, upload-time = "2026-07-04T14:27:01.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/77/cb4219be91508399cbcb6143bad89462cfb16f6c638458f454a5d46ac95a/vcrpy-8.3.0-py3-none-any.whl", hash = "sha256:bd66e6143746778157f00e2a922527a8d96b2fdc350be8988a45a29c843815b9", size = 46530, upload-time = "2026-07-04T14:27:00.546Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/87/d29c3edf5dae96555aa69499e0bfaf48dd41b889a05a04d41884bcdb5c5c/virtualenv-21.11.1.tar.gz", hash = "sha256:6266bdc327ed2a760f628ecbdadc776745fe08c3cac6139417bc2437e4d061dd", size = 5395254, upload-time = "2026-09-23T15:01:07.362Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/7b/7ca6956dd5fd8298682186782de4a33c507552bce206adf6b0f2a3596353/virtualenv-21.11.1-py3-none-any.whl", hash = "sha256:00596e6238363a1a69fd899a6e67365830be0b0bff9a9c83bdcad6e9aee3c419", size = 5485002, upload-time = "2026-09-23T15:01:04.935Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/be/0553582644877cc0a46ab505266055c8af0032c9ad5cfd7e35398c8af2a6/wcwidth-0.9.0.tar.gz", hash = "sha256:1e9eb9dec86e14e4aa4b877bcf6763f245803c9b08d10f2d72ffae1aa06bbf16", size = 952732, upload-time = "2026-09-23T15:45:02.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/82/e59847cba534fe8d6c1bf539a30a91140c19b05f56ce1e701ada56dcbb5a/wcwidth-0.9.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:d5fd98bd30785ecdb6a9a37d632c2f557e37dec7b7b3b76397842e03e891d3fb", size = 599248, upload-time = "2026-09-23T15:44:34.245Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/1c60d2c047f0e9ca207deb25547ca57c746ea6b92c83d1d61224227cfa3c/wcwidth-0.9.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:1bd6e3ea20382288c4d1eac079d775742a788f16bdecd92d957ecdb941b40870", size = 606184, upload-time = "2026-09-23T15:44:37.009Z" }, + { url = "https://files.pythonhosted.org/packages/d3/fc/0a410777214f640ec0d68b74596e47b0edcac79c137acf03754d5a41e97a/wcwidth-0.9.0-cp310-abi3-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5a20a031f744881e4f895e0ea9494e41c0d4a7320e6405fa7dce5e062d1f0545", size = 749579, upload-time = "2026-09-23T15:44:44.558Z" }, + { url = "https://files.pythonhosted.org/packages/2e/5e/c806c5b41ee2701fda27ad73e9d717d0b767de353d9d4a75714ccea4b090/wcwidth-0.9.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532eba871fb5c93ae492e5bd5ed3cab0d4805fd3a3e9d60a7aa92892f814a958", size = 758562, upload-time = "2026-09-23T15:44:39.939Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8d/e293019bfd94b1373421aedcb7be55089c19b5bd5a0a0fb3476d5a4e336e/wcwidth-0.9.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d095384f4d99261c8de74f025a2b7b13fde0188cbb3f83807e9015825e914f42", size = 755540, upload-time = "2026-09-23T15:44:45.974Z" }, + { url = "https://files.pythonhosted.org/packages/95/7b/8087dc19bb6bf2909a29c07568cb115af762346c07760d736b5cd74880b1/wcwidth-0.9.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e2e3425dcb46b0f29f65ed0236e8c64d9ba53473a9b895d9435d64a9a2b70af1", size = 768333, upload-time = "2026-09-23T15:44:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/fa/91/e288a2b54dd7125db8da70e85c2bf9bb07c81dc8c48aaee8c1b81089a52e/wcwidth-0.9.0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:326cbf3503ec676d4c4d413cf8cc96d8b2591c23dc860c273ef1f8740e36befc", size = 769430, upload-time = "2026-09-23T15:44:47.795Z" }, + { url = "https://files.pythonhosted.org/packages/17/0f/914ab82c26483b6cbb4bab27a5f7f0111193edcf803642d27056cd9f33d2/wcwidth-0.9.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03e2980286d59b61c247b6810333d596cbf21751d0f8e475c88664c6ae4ada04", size = 770862, upload-time = "2026-09-23T15:44:49.119Z" }, + { url = "https://files.pythonhosted.org/packages/91/aa/0b6247ce4b36c56bf30e1d23685ea0be37adfefab32b05fa1056a9180001/wcwidth-0.9.0-cp310-abi3-win32.whl", hash = "sha256:94b752e19a47f8ad79bda46665c7d6bcef4ef23f7da8b4544f39d9f9256f8634", size = 589900, upload-time = "2026-09-23T15:44:56.921Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/21e9748185f29e2c72d83e72a96dbc98134f9ad0bab3d3ad22e44e8f5ccd/wcwidth-0.9.0-cp310-abi3-win_amd64.whl", hash = "sha256:e0f8ffa0c912e2a103777f6978433a9991fe1346f6731b7a2e5e4128b4a44463", size = 593743, upload-time = "2026-09-23T15:44:58.457Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d9/4fbe5ea37702011a8ab940687c92f7412307ab75a212c59bf2bca9f842c9/wcwidth-0.9.0-cp310-abi3-win_arm64.whl", hash = "sha256:489a121550bcedeec6c9e5a414768ad5719cae957bbd882833841026e3209672", size = 591770, upload-time = "2026-09-23T15:44:53.976Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d3/05962a2d2195c231d1c5b820a6023b61e7be55c7a8eb32c91f2a721caa5e/wcwidth-0.9.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c9ad6463bd15ee57c4178849fdaa29acb828052e3971d1e86028cc5dc1d58c72", size = 599614, upload-time = "2026-09-23T15:44:35.687Z" }, + { url = "https://files.pythonhosted.org/packages/87/64/32a8cf2aeef513bd813d4a86318fb221c6f16984e176e36e8c9b47056239/wcwidth-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4a7f533babe32007c2781e438973213a45bd07370f6cb272ee18f56135dc5703", size = 606366, upload-time = "2026-09-23T15:44:38.544Z" }, + { url = "https://files.pythonhosted.org/packages/aa/98/9ed38b76b5013dcd6f0ba60946c5f84d9b1c70f566cabc7bebcab18a9ce7/wcwidth-0.9.0-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4bb4a6ada96121e8e1898a64d991be39518c18c832f8c9bd941a63623ad58a20", size = 755139, upload-time = "2026-09-23T15:44:50.532Z" }, + { url = "https://files.pythonhosted.org/packages/4f/f2/216542761f8f2be07d398c58c5ba72d802219b803eb46c0dcfd6a7514b96/wcwidth-0.9.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ae8503058d68483b37e533f54f964510bbf73daa30637fbf60bb224084254f7c", size = 762651, upload-time = "2026-09-23T15:44:43.166Z" }, + { url = "https://files.pythonhosted.org/packages/f0/dd/595c9a49cf7da0edfe271eca474e1caae532bd982c791c59692961ff498a/wcwidth-0.9.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3aefeb02106a0ea2ed2ed8541ac243260849a07e97fbc083b4df4f568307c257", size = 761338, upload-time = "2026-09-23T15:44:52.268Z" }, + { url = "https://files.pythonhosted.org/packages/03/6b/f812194f3f3d06fdaabfa506c75f1acf259ccfb174201388de659f6547b7/wcwidth-0.9.0-cp314-cp314t-win32.whl", hash = "sha256:bb4ef3d5a9a92931cf4eb6d7a4b17736576591748f43dfe2f53bc63ff2163192", size = 597363, upload-time = "2026-09-23T15:45:00.1Z" }, + { url = "https://files.pythonhosted.org/packages/76/28/dfdeca9ed1d1001b01d0e3f02f303efa8a458475d06af77837e1b7f048d3/wcwidth-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:26c2daaa6bf53bdf135a96fd9a01a3a60d3c18628d86637aa8e1f6c3392874a5", size = 600395, upload-time = "2026-09-23T15:45:01.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/96/afa804b2bb647d130cbfee1e178b73a45b124eca6f00a920727291abaabf/wcwidth-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0885ab30f862bda202f5cd4e0e01dfe86b6d18e7cb2619d1fcb2b52c2d3c", size = 598868, upload-time = "2026-09-23T15:44:55.187Z" }, + { url = "https://files.pythonhosted.org/packages/88/50/c979c311f1ecd497fc7459cd6df86a8ad5c6e9517a01f5985542e272711b/wcwidth-0.9.0-py3-none-any.whl", hash = "sha256:8ccbb76076a4e6db0b4bf3a9a19181ed2f777340966638a49267989f93acc242", size = 300656, upload-time = "2026-09-23T15:44:32.836Z" }, +] + +[[package]] +name = "websockets" +version = "16.1.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/21/f7/bc3a25c5ec26ce62ce487690becc2f3710bbc7b33338f005ad390db0b986/websockets-16.1.1.tar.gz", hash = "sha256:db234eda965dcce15df96bb9709f587cd87d4d52aaf0e80e2f34ec04c7670c57", size = 182204, upload-time = "2026-07-17T22:51:05.858Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/e7/d1671fb984f9dd844e1da5288070c7c23c9eaba3082d3871aae19c3ab8b9/websockets-16.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:49ae99bdfcae803a885c926bf14f886196e84925395bb3f568fef5c0f0979d7d", size = 179570, upload-time = "2026-07-17T22:48:24.032Z" }, + { url = "https://files.pythonhosted.org/packages/99/f5/70df723bf571f5e0b1b845e0a4ff1c966eeb84f667599fc251caa37d15a3/websockets-16.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5bfd1ac19b1b9986a9c95a82d5e23a391ebb09e12c34d7be6094b86efcc35731", size = 177252, upload-time = "2026-07-17T22:48:25.775Z" }, + { url = "https://files.pythonhosted.org/packages/90/72/2f14b2e167170b8bf1c8bb7f9b0d78000f470d41a2085a91f33e3917b6c9/websockets-16.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9246a0d063cfcbcc85f2359dd6876d681213f4790832272aa16641b4ed5d64d4", size = 177530, upload-time = "2026-07-17T22:48:27.337Z" }, + { url = "https://files.pythonhosted.org/packages/f3/18/a17e2f0cde02dc10154c808deed7e1d8528afff93612f70d3f0a5b19b011/websockets-16.1.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1214e673c404684b9bf7154f5cf43b45025b1a6160fac3a9e438e9c1a97e22cb", size = 186038, upload-time = "2026-07-17T22:48:28.756Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b0/41de283899cf5929d637b72a508cdbc9aa40dc0f317c6b77613fd1000488/websockets-16.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90001d893bc368e302ef168d82130b4e4fdd27b85fa094682df9b667c2d48838", size = 187278, upload-time = "2026-07-17T22:48:30.328Z" }, + { url = "https://files.pythonhosted.org/packages/50/61/874aab5257e027f9f61b5004cec65e592babca7942b1bc09f38e72b7f1fd/websockets-16.1.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:130937b167a52af203c8d58e78d67705874e82759862e3b9671a452fec4abc87", size = 189936, upload-time = "2026-07-17T22:48:31.896Z" }, + { url = "https://files.pythonhosted.org/packages/a6/1a/42173913ac5519607220849ed417c864d77384e4119f06dbba964a50f096/websockets-16.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c9f23004a3d40e89c01a7955d186a6cc83418d93b749701944ce2de3e95a1f3", size = 187796, upload-time = "2026-07-17T22:48:33.344Z" }, + { url = "https://files.pythonhosted.org/packages/1b/f4/37c1840bd89b529479aec41470b97b7c683b107ca90b6399ac5afb99dedf/websockets-16.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f55f0b01956a094c8587146d9558c91937e78789c333860ffaf35931a6e5dbc4", size = 186481, upload-time = "2026-07-17T22:48:34.843Z" }, + { url = "https://files.pythonhosted.org/packages/9e/70/652d9b964adcfbeb056f42e0ca6bece34d108fe75534e74df20643cae199/websockets-16.1.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6aaface73b9c71974c6497366d8b9628357f6c9749e09c4ea3610176c63f2ae3", size = 184351, upload-time = "2026-07-17T22:48:36.307Z" }, + { url = "https://files.pythonhosted.org/packages/13/f1/af3850e5d48d482921985be72ebcb169c6180b3a77b57bd612deebcee23b/websockets-16.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dc0fad4933f427acd5b1cec210f3ea6dce7089e1724e4b9ec6ef47c6c04d1b3b", size = 186791, upload-time = "2026-07-17T22:48:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/1d/40/1a4e3ed4969ec378dcad337e5f1472c5e292cb3e733bc392f0dc2e230abd/websockets-16.1.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f2769a0344a09e9ccf5b3cce538bc75a51b53eff3275d3896310c8552049195d", size = 185413, upload-time = "2026-07-17T22:48:39.127Z" }, + { url = "https://files.pythonhosted.org/packages/aa/3e/4e3fa1afe8f1a6a780434cd9ba8eb422632b044eff3dd73f6af67523c147/websockets-16.1.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f70541f3104339f59f830522d94ebadb1bf47426287381623443d8bb1cdbf33d", size = 187178, upload-time = "2026-07-17T22:48:40.676Z" }, + { url = "https://files.pythonhosted.org/packages/71/ab/dd742766aa5dda7f349be0de49e4d565b84cf6f7f7fa02e07692f0f2bdd9/websockets-16.1.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:dc385593a42e31cd6fb60c19f0ecb015b386603818fc2c6c274fb42bd2bb4165", size = 185051, upload-time = "2026-07-17T22:48:42.098Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f5/76438c6560f416f1c0a7f587679fb97cc6e99ed336011d43ce2002dd27c1/websockets-16.1.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:387e8e4aa5df2f90b198fa3cad3478822a89cf905b6a6d6c97dc3664689640cc", size = 185846, upload-time = "2026-07-17T22:48:43.472Z" }, + { url = "https://files.pythonhosted.org/packages/62/12/5c0320f2127823d27b2d56d611d31b0b284ad4edcb41364d66bf4c92b537/websockets-16.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fd46fff7eb62c24804d234f0051c7a8ea81285ad63e0337d3dcf33ca82aee58a", size = 186066, upload-time = "2026-07-17T22:48:44.884Z" }, + { url = "https://files.pythonhosted.org/packages/a2/97/875986b857b955c3f9dd192cb8a1af81254dfb2ea22cc9590f0a1e020b8b/websockets-16.1.1-cp310-cp310-win32.whl", hash = "sha256:7883388947767080f094950b342b30d35a2a06b849cd967c422fa0db72b40ea9", size = 179940, upload-time = "2026-07-17T22:48:46.481Z" }, + { url = "https://files.pythonhosted.org/packages/54/82/1013a5fe7ddae8e102bc3b4b39db81d8d28fd02100a324ce6ede8cd832b1/websockets-16.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d57685547e0060cc6fd90ee6a28405d6bd395e525545f13c8d7cd99c78afd79f", size = 180239, upload-time = "2026-07-17T22:48:48.043Z" }, + { url = "https://files.pythonhosted.org/packages/2b/03/47debfe28e9d6d354be5d777b67fd44c359b9eb299a5d103500bd7cc3e37/websockets-16.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d0fcf657e9f13ff4b177960ab2200237b12994232dfb6df16f1cfe1d4339f93c", size = 179566, upload-time = "2026-07-17T22:48:49.596Z" }, + { url = "https://files.pythonhosted.org/packages/72/93/31efa1ed78c17e5cfc229fd449e3966e1b9cc15753204cd585cc8dd01f4a/websockets-16.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b852788aa51764e2d8e4cf5493d559326bcae5e38d16ba25ffa322b034df272a", size = 177250, upload-time = "2026-07-17T22:48:50.942Z" }, + { url = "https://files.pythonhosted.org/packages/01/4a/542378ab3972b0c1cf1df3df3eff9591cea0d30c58c3aa3c4ddbc244e787/websockets-16.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1427fb4cf0d72f66333e2cacc3ff5f575bf2d7008166ce991a4a470b21d51a22", size = 177528, upload-time = "2026-07-17T22:48:52.59Z" }, + { url = "https://files.pythonhosted.org/packages/33/d9/162321f63c7eed558e9e1798ed7a1e34a4f6dab51f35419e4ed7a4907979/websockets-16.1.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:da4ca1a9d72f9030b3146b8d7022719a9f3d478f61efe6f7dd51d243f61c51b2", size = 186859, upload-time = "2026-07-17T22:48:53.915Z" }, + { url = "https://files.pythonhosted.org/packages/de/09/87df740f7430ce564bd52402e9c9458d4d0459cc7d2ee29e530c8204851b/websockets-16.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86d7f0f8bdb25d2c632b72527325e4776430fd5bc61b9118de4e2b8ddb5f5b01", size = 188095, upload-time = "2026-07-17T22:48:55.384Z" }, + { url = "https://files.pythonhosted.org/packages/d2/12/3d2703af7cc095f3c81904c92208cc1ae79affbc67376944b50ee9301f73/websockets-16.1.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7dfcad78ea1492ee3a9ec765cb7f51bbc17d477107aaf6b22abf7b2558d1c5a0", size = 191385, upload-time = "2026-07-17T22:48:56.742Z" }, + { url = "https://files.pythonhosted.org/packages/1d/69/986aa0234a964a00f5149cfc46e136e96c8faad1c783474550f40d31aef4/websockets-16.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fb9a0a6dc3d1b3986cb88091b6899f0396651e0f74e2c9766ab8d6ffc3842e29", size = 188653, upload-time = "2026-07-17T22:48:58.134Z" }, + { url = "https://files.pythonhosted.org/packages/35/6b/10f9d03e3970a69ba67bd3b46b87a929b586d0300fadbfe14f57c1f85490/websockets-16.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29dfa8114c4a620c69591c5973860f768eac29d3fd6904f37f34266cb219c512", size = 187426, upload-time = "2026-07-17T22:48:59.515Z" }, + { url = "https://files.pythonhosted.org/packages/56/db/bb3aad62bf63d8bb3f0634b2eabffcfb3677a34bd19492110ff6869cf703/websockets-16.1.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ff9417c0ada4d0f7d212f928303e5579bdf3ace4c802fa4afabb30995da58c3", size = 184882, upload-time = "2026-07-17T22:49:00.916Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4c/c09a2ea9bfbeccce52fdc383e5f28af4bc8843338aabac28c81489af6120/websockets-16.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fe0b50da2d84535fb4f7b4bfa951280f97ce3d558a0443b541166d609e67b57", size = 187584, upload-time = "2026-07-17T22:49:02.283Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8b/31bb4eb4d9eaacf1fdd39d115772a8aeaedfc19b5dc262e57ffbc8a9d42c/websockets-16.1.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:34420aaa64440ebd51ac72ca8a45ef4626429438c9b02e633ae412ed43f925d3", size = 186174, upload-time = "2026-07-17T22:49:03.973Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e4/dc02d725610a1ad49e193ef91a548194d71bdc6cdf27da83067dd1f73995/websockets-16.1.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a6a61aff018180c9c50b7b0da33bfd29d378af3497429c95006c589a23a11648", size = 187986, upload-time = "2026-07-17T22:49:05.553Z" }, + { url = "https://files.pythonhosted.org/packages/e0/73/30ed84c8bfd14c73d4af29d5ed9323c3073b48e0b7b23b67070f4e7fd59b/websockets-16.1.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:04fd29a0e2fe9414a95b00e92c67ae51bf900c50c0f8a4b2dafdad621f49ea1d", size = 185565, upload-time = "2026-07-17T22:49:06.959Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d3/4be8d4959f51e31b4f8fc0ece12b45bd3b6c0d15ea23b9990d9c11fc805f/websockets-16.1.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5c31aa7e39ee3e8a358573257f1c0bb5c52430d1b637030dd9c8cc2c282926be", size = 186598, upload-time = "2026-07-17T22:49:08.293Z" }, + { url = "https://files.pythonhosted.org/packages/26/fa/abb38597a52d84ed9cfacadc7a0c6f2db282c0ab23cdf72b58a666a21227/websockets-16.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d14bfb217eb4701e850f1525c9d29d79c44794cdf1c299ead25f39f8c78dea81", size = 186834, upload-time = "2026-07-17T22:49:09.766Z" }, + { url = "https://files.pythonhosted.org/packages/59/80/1119ad08a228b90c4eb77fbe48df7836731a605f5f881ba701ca826a4a65/websockets-16.1.1-cp311-cp311-win32.whl", hash = "sha256:2e28e602bb13da44fbe518c1781a88e3b9d4c3d48d02c9bad83e546164336f57", size = 179940, upload-time = "2026-07-17T22:49:11.196Z" }, + { url = "https://files.pythonhosted.org/packages/71/b2/e511c1c6f64a95c2f3fc54bffda0e14eaa7e9442be605c29270f7589b918/websockets-16.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:7421fad442de870a8cbf2287d1cad7e706ece0dbfeba5e911df132cbdc1cb56a", size = 180239, upload-time = "2026-07-17T22:49:12.519Z" }, + { url = "https://files.pythonhosted.org/packages/17/9d/681cda21c9eee743203a6cb79b9d3d05adad9aa60ec660c6c9bf4dd619ca/websockets-16.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cc97814dfb786a83b6e2dc2e79351e1b83e6d715647d6887fcabd83026417a00", size = 179600, upload-time = "2026-07-17T22:49:13.92Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8d/6195a88b45e8d2a8f745fc2046e36f885a3c9763e6767d2c46229bf9510c/websockets-16.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e047dc87ef7ca50f4d309bf775ad4a71711c58556d75d7bd0604b2317f43e94b", size = 177272, upload-time = "2026-07-17T22:49:15.453Z" }, + { url = "https://files.pythonhosted.org/packages/73/e3/fe2d498c64dea0095c9a9f9a351af4cd6eef31b618395582bc1f38ba45ff/websockets-16.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01fbdcbac298efe19360b94bc0039c8f746f0220ba570f327577bfee81059175", size = 177542, upload-time = "2026-07-17T22:49:16.875Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ed/f1831681fce0e3242346e5458486003c5f124ed69e5e0b847fd029db4973/websockets-16.1.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f62863e8a00a6d33c3d6566ec0b89f23787b747ffe0c3bc71ec0e76b82c94b1", size = 187137, upload-time = "2026-07-17T22:49:18.323Z" }, + { url = "https://files.pythonhosted.org/packages/6f/79/4ff9dcc1bb46f6b4c536936dde1fd60f9b564f3304307274db97f4c9496d/websockets-16.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8087e82f842609734c9b5a1330464f8e94e346ba0e18c832c08bafa4b0d63c15", size = 188374, upload-time = "2026-07-17T22:49:19.65Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/5c49b6efb36cab733d23773f6de575e1dba65736ead17d5d2b2a1daef779/websockets-16.1.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2bb5d041a8307d2e18782e7ce777f6fdb1e8c2f5d09291484b18c294b789d9aa", size = 191155, upload-time = "2026-07-17T22:49:21.331Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f6/56ccceda3a4838d18f1d40821480da4775397e8b1eecf4031e20c50e2e90/websockets-16.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1db4de4a0e95673f7545d393c49eeb0c2f18ac1ef93073218c79d5cdb2ee75ab", size = 189011, upload-time = "2026-07-17T22:49:22.889Z" }, + { url = "https://files.pythonhosted.org/packages/86/d6/ad5286241a2bce1107e2798d3bfbd62cf79aee167bdb654f8cb1e9dbf949/websockets-16.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f17dbe07eb3ea7f99e4df9b7e0efefe80fbf30d37a8cc4d561a0aed310bc8847", size = 187766, upload-time = "2026-07-17T22:49:24.339Z" }, + { url = "https://files.pythonhosted.org/packages/bc/67/d65c970b7e347fdca69479beb7811c2060529956730a7a4e3ae7c66b0e31/websockets-16.1.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b57693728576d84ede0a77987ab16881b783d2cd9f1dc180a8fbbc3f79c4428", size = 185173, upload-time = "2026-07-17T22:49:25.743Z" }, + { url = "https://files.pythonhosted.org/packages/1d/5b/14af3cd4ee69d8ea9baca58f3dc3cfb1ba78332a347fd478cb096549d60e/websockets-16.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2a636ff1e7a5c4edf71ef0e79adae7f25dba93b4fcbe3dc958733477ffeb0eaf", size = 187809, upload-time = "2026-07-17T22:49:27.147Z" }, + { url = "https://files.pythonhosted.org/packages/7b/11/be301710d70de97e3e7b3586e6d492c9c06d6a61bf1c2202c36cf0c75607/websockets-16.1.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d6bec75c290fe484a8ba4cacdf838501e17c06ecfbbf31eede81a9e431bd7751", size = 186412, upload-time = "2026-07-17T22:49:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/db/07/fe1435bf6fe738a3d3b54dbe0c18dabf12cba4d909ac8b58b539ce27c1f4/websockets-16.1.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:54509b8e92fee4453e152b7558ddef37ce9705a044922f2095a6105e3f80c96f", size = 188290, upload-time = "2026-07-17T22:49:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0a/81f394aff8efcbb01208c1ced77df0a3c7fcce584a88c7273663697946c2/websockets-16.1.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f0aa4aad3b1b69ad3fd85a0fd0952ec64331c762bd77ec51cc814170873890b2", size = 185844, upload-time = "2026-07-17T22:49:31.447Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/dd485b995473f415510251fe9bd708f2d24458f439fce958daf8d66dc7c6/websockets-16.1.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:42290eb6db4ccaca7012656738214f8514082fb6fa40cdeb61bb9a471b52e383", size = 186823, upload-time = "2026-07-17T22:49:33.104Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0b/f78de76ff446f1e66af12b43c48a35f31744de93cfdec2f4ea67d5d7bbf1/websockets-16.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:53260c8930da5771cec89439bff99c20c8cb03ddb9588b980697355a83cd4bd3", size = 187102, upload-time = "2026-07-17T22:49:34.616Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/4cf892007778eaf84ad162bfc98046e0ed89b63ac55949e3236626b2a23f/websockets-16.1.1-cp312-cp312-win32.whl", hash = "sha256:1d27fa8462ad6a1cb36206a3d0640b2333340def181fae11ed7f9adeaa5c0747", size = 179943, upload-time = "2026-07-17T22:49:36.213Z" }, + { url = "https://files.pythonhosted.org/packages/d9/de/6abe251d28c3a3f217096575400b27750b18e0b1d2fff3a2a239960fea07/websockets-16.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:b436f6ec4fc3a6b4237c84d3f83170ed2b40bb584222f0ac47a0c8a5921980c7", size = 180243, upload-time = "2026-07-17T22:49:37.626Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fd/6ec6c6d2850aea25b1b2aa9901a016980bb87d01e89b3eb00470b1b5d471/websockets-16.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ab59169ace05dcb49a1d4118f0bde139557adf45091bd85747e36bf5de984dd1", size = 179587, upload-time = "2026-07-17T22:49:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d8/1d299d2dd34087db39831a34cc645ef8a6f89d78efada6983093513cd81c/websockets-16.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5e3b7d601f6f84156b08cc4a5e541c2b50ad7b36cfc302b657a12477c904a5df", size = 177272, upload-time = "2026-07-17T22:49:40.293Z" }, + { url = "https://files.pythonhosted.org/packages/3d/86/0a70d3ae2f0f2256bb41302d9804dbca65d4360281e7feb3e1f94102ac46/websockets-16.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cd2ca96a082a36964aca83e992f72abeb61b7306c1a6cba4c7d06a7b93750cac", size = 177530, upload-time = "2026-07-17T22:49:41.786Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c2/c676c69444d9db448b3f0a55a98dcc534affce0bce961d9d2f0b8499b10a/websockets-16.1.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f5d497865f05bb222cab7016c6034542e84e5f29f49c6fd3f4939cda7197b5b8", size = 187197, upload-time = "2026-07-17T22:49:43.658Z" }, + { url = "https://files.pythonhosted.org/packages/0b/13/88137fbaf726ebe29d62c1117fa11fa2bbb6209dc79d4ad738efbe36a2aa/websockets-16.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bae954c382e013d5ea5b190d2830526bfa45ad121c326da0049b8c769f185db6", size = 188433, upload-time = "2026-07-17T22:49:45.147Z" }, + { url = "https://files.pythonhosted.org/packages/01/6d/46c2f2ce6751cb26f39293e1ecbf8544cb01321397cd476c2756b98c216d/websockets-16.1.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e09f753a169951eb4f28c2c774f71069304f66e7277e0f5a2892423599cfa854", size = 189868, upload-time = "2026-07-17T22:49:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/29/2b/170a9e8097636cfde4dc3c592b6e00b18a44a2f5407606d96ca542dd5838/websockets-16.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:024193f8551a2b0eafbdd160911012c4e6c228c28430c84433253299a9e42d6a", size = 189059, upload-time = "2026-07-17T22:49:47.972Z" }, + { url = "https://files.pythonhosted.org/packages/a7/48/f0d4ebc9ab4b473b8861b9e20fdb663d515d42f7befdf62cdb60fee7a1ec/websockets-16.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:aabe464bfd13bd25f4821faf111da6fefdc389f870265a53105580e45b0a2e49", size = 187814, upload-time = "2026-07-17T22:49:49.344Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ba/39a41d3ae8e72696a9492581900611c5a91e2b07563b0bcd2523adea9854/websockets-16.1.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a28fcbc9b6baf54a2e23f8655f308e4ccc6afdd7266f8fe7954f320dcda0f785", size = 185229, upload-time = "2026-07-17T22:49:50.787Z" }, + { url = "https://files.pythonhosted.org/packages/3c/36/ac15b604f850d1907f0a85ed721cefe47cd45034b3620069b829746cccbe/websockets-16.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79eace538c6a97e96d0d03d4f9d314f9677f5ed85a8a984992ffd90b13cb8a56", size = 187874, upload-time = "2026-07-17T22:49:52.228Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f3/3fbd5d71d59299c3770faa5884d4f45070236ca5a35ab3a61830812c409a/websockets-16.1.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:496af849a472b531f758dbd4d61338f5000538cb1a7b3d20d9d32a264517f509", size = 186469, upload-time = "2026-07-17T22:49:53.776Z" }, + { url = "https://files.pythonhosted.org/packages/b4/fc/dd90349bba58af2a53ef2ddd9c32716c81eb6d59a0687939fff561860878/websockets-16.1.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5283810d2646741a0d8da2aa733d6aefa0545809afccb2a5d105a26bc45125f1", size = 188347, upload-time = "2026-07-17T22:49:55.202Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f3/f73ba86427682da59b78c11d77ba56d5b801c32e84afe79b274bbd6a9bb2/websockets-16.1.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4e3b680b1e0a27457e727a0d572fd81dffa87b6dbf8b228ab57da64f7d85aead", size = 185903, upload-time = "2026-07-17T22:49:56.75Z" }, + { url = "https://files.pythonhosted.org/packages/34/7c/f95eb20e80104173b3a0a092291f89ea4047ef6e608e0a57ca06eb14eecb/websockets-16.1.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:69159730a823dde3ea8d08783e8d47ef135a6d7e8d44eb127e32b321c9db8e3e", size = 186855, upload-time = "2026-07-17T22:49:58.467Z" }, + { url = "https://files.pythonhosted.org/packages/b0/35/dd875b3e050ff232d60fa377707f890e369f74d134f1be32e8f68879747c/websockets-16.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ed5bb271084b46530ee2ddc0410537a9961152c5ccba2fc98c5276d992ccba87", size = 187140, upload-time = "2026-07-17T22:50:00.016Z" }, + { url = "https://files.pythonhosted.org/packages/e8/dc/5cbfcb41824502f6af93b8f3943a4d06c67c23c7d2e31eb18748c4a5b2a7/websockets-16.1.1-cp313-cp313-win32.whl", hash = "sha256:cfb70b4eb56cac4da0a83588f3ad50d46beb0690391082f3d4e2d488c70b68ea", size = 179928, upload-time = "2026-07-17T22:50:01.685Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c1/71e5deb5b7f8f226997ab64908c184ac3105c0155ce2d486f318e5dd08a8/websockets-16.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9531d9cbeac99af6f038fb1bc351403531f7d634a2c2e10e2f7c854c6ed5b68", size = 180242, upload-time = "2026-07-17T22:50:03.117Z" }, + { url = "https://files.pythonhosted.org/packages/73/a2/ba78a164eeea4620df4a4df4bd2ed6017438c4655cc0f36f2c0bc0432355/websockets-16.1.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:443aefe96b7fdb132e2a70806cca1f2af49bb3f28e47abcd7c2e9dcf4d8fa1b8", size = 179635, upload-time = "2026-07-17T22:50:05.001Z" }, + { url = "https://files.pythonhosted.org/packages/b9/08/d26d7a7628cd4ac34cbbdb63ac80914ca842ed8e42938c40a53567806df3/websockets-16.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6456ff333092d509127d75a638cb411afae8ff17f092635015d1902efec8a293", size = 177320, upload-time = "2026-07-17T22:50:06.427Z" }, + { url = "https://files.pythonhosted.org/packages/0f/45/ebec83e6269536aa5932533c67b0af5c781f3e73fdbcd68672dcf43f4f44/websockets-16.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fce6c48559c86d1ac3632ccb1bebc7d5442fbe79bd9bb0e40379ee54be2a4051", size = 177544, upload-time = "2026-07-17T22:50:07.834Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d5/abc614d2297f6c1c3e01e61260364457a47c25cc1cf6a879038902bc6aa8/websockets-16.1.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:92b820d345f7a3fc7b8163949ee92df910f290c3fc517b3d5301c78065adafe1", size = 187270, upload-time = "2026-07-17T22:50:09.275Z" }, + { url = "https://files.pythonhosted.org/packages/52/71/4c99af3b87dff1b2927981f6876607d4acb45338c665242168d3982f7758/websockets-16.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a606d9c24035242a3e256e9d5b77ed9cd6bccfcb7cf993e5ca3c0f6f68fb6a7", size = 188509, upload-time = "2026-07-17T22:50:10.722Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b4/5c8ca14b0df7eb84ed0524165c5359150210140817a3312aee57bf62a1cf/websockets-16.1.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:414e596c75f74e0994084694189d7dc9229fb278e33064d6784b73ffbba3ca31", size = 189882, upload-time = "2026-07-17T22:50:12.293Z" }, + { url = "https://files.pythonhosted.org/packages/25/c1/bedfba9e70557129cb8083748d167bdcc01483dedf0f0df143676df05cbe/websockets-16.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:536676848fc5961aca9d20389951f59169508f765637a172403dc5434d722fa0", size = 189114, upload-time = "2026-07-17T22:50:13.789Z" }, + { url = "https://files.pythonhosted.org/packages/df/09/aa835b2787835aebd839114be5de51b797cb480b63ba42b26d34dfe147cb/websockets-16.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:97fd3a0e8b53efa41970ac1dff3d8cf0d2884cadeb4caaf95db7ad1526926ee3", size = 187861, upload-time = "2026-07-17T22:50:15.179Z" }, + { url = "https://files.pythonhosted.org/packages/20/26/f6408330694dbc9830857d9d23bc14ac4f6875127a480cfdda8d5ca21198/websockets-16.1.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7b1b19636af86a3c7995d4d028dbe376f39b4bf31541146f9c123582a6c94562", size = 185286, upload-time = "2026-07-17T22:50:16.741Z" }, + { url = "https://files.pythonhosted.org/packages/17/9a/e0675e70dd8a80762cf35bb18799d3f290a4890ffe6439bc51d222796083/websockets-16.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:41c8e77f17294c0ac18008a7309b99b34ee72247ef10b6dff4c3f8b5ac29896b", size = 187935, upload-time = "2026-07-17T22:50:18.213Z" }, + { url = "https://files.pythonhosted.org/packages/33/c1/3234cfb86afde01b81e9bddcc6e534c440975d60a13991259e833069ab3e/websockets-16.1.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9f63bcef7f4b02b06b35fc01c93b96c43b5e88e1e8868676caacf493d5a31f3a", size = 186444, upload-time = "2026-07-17T22:50:19.67Z" }, + { url = "https://files.pythonhosted.org/packages/89/87/9c15206e1d778923d8daa9657de07aa62ea815e13448319c98458c37b281/websockets-16.1.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dab9eb87869da2d6ed3af3f3adf28414baae6ec9d4df355ffc18889132f3436c", size = 188409, upload-time = "2026-07-17T22:50:21.28Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/cf5de5c67676de2d3eef8b2a518f168f6796595447a5b7161ba0d012915c/websockets-16.1.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:43e3a9fdd7cbf7ba6040c31fae0faf84ca1474fef777c4e37912f1540f854499", size = 185958, upload-time = "2026-07-17T22:50:22.719Z" }, + { url = "https://files.pythonhosted.org/packages/62/c0/731b6ddede2e4136912ec4cff2cffbda35af73546be4762c3d7bd3bd79af/websockets-16.1.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:056ae37939ed7e9974f364f5864e76e49182622d8f9751ac1903c0d09b013985", size = 186911, upload-time = "2026-07-17T22:50:24.108Z" }, + { url = "https://files.pythonhosted.org/packages/8c/7f/39c634472c4469a24a7c09cecddffb08fac6d0e74f73881a94ee8a40a196/websockets-16.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a0eadbbf2c30f01efa58e1f110eb6fa293261f6b0b1aa38f7f48707107690af9", size = 187204, upload-time = "2026-07-17T22:50:25.548Z" }, + { url = "https://files.pythonhosted.org/packages/26/89/9667c256c256dafcc62d21328ce7a40067da857969b68ee9af375b0aaf72/websockets-16.1.1-cp314-cp314-win32.whl", hash = "sha256:195c978b065fa40910582464f99d6b15c8b314c68e0546549a55ed83f4735328", size = 179603, upload-time = "2026-07-17T22:50:27.086Z" }, + { url = "https://files.pythonhosted.org/packages/bd/dd/1c099d6c0fc5deb6b46ccdbb6981fdb4b12c917869cb3952408409dc18db/websockets-16.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:4e8d01cc3bcae7bbf8167f944aeafefed590fae5693552bba9794a9df68371cc", size = 179948, upload-time = "2026-07-17T22:50:28.521Z" }, + { url = "https://files.pythonhosted.org/packages/35/25/9956b2d5e0529d5d23924f21bba1440d4c5c88a562e4f08550871ffa97a7/websockets-16.1.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0ffd3031ea8bda8d61762e84220186105ba3b748b3c8da2ae4f7816fac03e573", size = 179963, upload-time = "2026-07-17T22:50:29.982Z" }, + { url = "https://files.pythonhosted.org/packages/17/06/55ffc976c488b6aee9ea05761ff7c4e88e7c1fd82818c8ca7b556ad2f90c/websockets-16.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:84a2cef8deffbd9ab8ee0ea546a2a6a7030c28f44e6cdd4547dbfeb489eb8999", size = 177497, upload-time = "2026-07-17T22:50:31.396Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/f7dac2e980bacc92bdc26cebae4ae4d50cae5380732c50980598fc0bbae4/websockets-16.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3df13f73af9b3b38ab1195eb299ecb67a4330c911c97ae04043ff74085728abe", size = 177698, upload-time = "2026-07-17T22:50:32.829Z" }, + { url = "https://files.pythonhosted.org/packages/b2/39/26762f734113e22da2b942c3aca85798e0c0405d64c256549540ff31e5a1/websockets-16.1.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:23253dd5bcae3f9aaee0a1d30967a8dbd52e5d3cff93a2e5b84df57b77d4750d", size = 187561, upload-time = "2026-07-17T22:50:34.24Z" }, + { url = "https://files.pythonhosted.org/packages/11/94/c3f330851806b9b02138b774d593478323e73c99238681b4b93efe64e02d/websockets-16.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1c5705e314449e3308872fe084b8571ce078ee4fc55a98a769bdefe5917392", size = 188732, upload-time = "2026-07-17T22:50:36.088Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f2/eb2c450f052de334ae33cf200ece6e87b0e14d186807074e4eb1cd2cdea2/websockets-16.1.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:69e52d175a0a7d1e13b4b67ad41c560b7d98e8c6f6126eb0bda496c784faf8c7", size = 190872, upload-time = "2026-07-17T22:50:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/70/31/2ac8cecf3a74f7fed9132129fc3d90b3998a1554570c11a69b2a8c20332d/websockets-16.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1f79c89b5eb034d1722938a891916582f8f7f503f58ca22518a63c3f2cd18499", size = 189305, upload-time = "2026-07-17T22:50:39.53Z" }, + { url = "https://files.pythonhosted.org/packages/6a/cf/8ab19650d3c0d4562c92e70ab47c257c4aa5c6a713ed87fe63766b31fefc/websockets-16.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:39f2a024af5c345ffe8fcf1ee18c049c024c94df393bb09b044a6917c77bde43", size = 188033, upload-time = "2026-07-17T22:50:40.912Z" }, + { url = "https://files.pythonhosted.org/packages/66/d7/a49a38a6127a4acb134fb1912b215d900cc657605cff32445bf519f3acc4/websockets-16.1.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:952303a7318d4cbe1011400839bb2051c9f84fa0a35923267f5daba34b15d458", size = 185748, upload-time = "2026-07-17T22:50:42.559Z" }, + { url = "https://files.pythonhosted.org/packages/95/3e/ad1fa40388c7f2e0bb2c7930d0090b6c5498594bd1cdaec18864df3d9e97/websockets-16.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:249116b4a76063d930a46391ad56e135c286e4562a18309029fc2c73f4ed4c62", size = 188285, upload-time = "2026-07-17T22:50:43.974Z" }, + { url = "https://files.pythonhosted.org/packages/35/b8/d5db28ca264b9104f82196f92dc8843e35fd391f763d42e4ad358f5bc97e/websockets-16.1.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:61922544a0587a13fd3f53e4c0e5e606510c7b0d9d22c8444e5fae22a06b38cb", size = 186777, upload-time = "2026-07-17T22:50:45.474Z" }, + { url = "https://files.pythonhosted.org/packages/42/9c/726cb39d0cc43ae848dce4aa2acb04eecc6738b1264ec6d700bf6bcfb9f8/websockets-16.1.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:46dcaa042cd1de6c59e7d9269fa63ff7572b6df40510600b678f0826b3c7af51", size = 188682, upload-time = "2026-07-17T22:50:46.973Z" }, + { url = "https://files.pythonhosted.org/packages/be/c7/1168704de8c2dd483edabe4a22cbe4465dd8be8dd95561d214f9fe092871/websockets-16.1.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:38565aca3e01ea8734e578fb2118dade0ecb0250533f29e22b8d1a7a196cf4d0", size = 186377, upload-time = "2026-07-17T22:50:48.413Z" }, + { url = "https://files.pythonhosted.org/packages/ca/40/f9ff2d630ffce4e7dfea0b2288e1caf9ebbf9ff8a9ec9396136ce8b94935/websockets-16.1.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:42f599f4d48c7e1a3338fdaac3acd075be3b3cf02d4b274f3bf2767aedd3d217", size = 187148, upload-time = "2026-07-17T22:50:49.845Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/e177c8299f78d7cbe2d14df228643c10c70c0e86e108e092056bbcc16e46/websockets-16.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dcc04fedf83effaeb9cce98abc9469bb1b42ef85f03e01c8c1f4438ef7555737", size = 187578, upload-time = "2026-07-17T22:50:51.619Z" }, + { url = "https://files.pythonhosted.org/packages/49/b2/b6987faf330f5af5c787a2610124c2e8403d51724f9001ec4fff6311fe7a/websockets-16.1.1-cp314-cp314t-win32.whl", hash = "sha256:8483c2096363120eea8b07c06ae7304d520f686665fffd4811fad423930a65d7", size = 179729, upload-time = "2026-07-17T22:50:53.269Z" }, + { url = "https://files.pythonhosted.org/packages/a2/6e/fbac6ed878dd362fbad7d415fa4f84d38e3e33fed8cde45c64e783acf826/websockets-16.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:bcce07e23e5769375158f5efdcdafa8d5cd014b93c6683865b840ed65b96f231", size = 180072, upload-time = "2026-07-17T22:50:54.969Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ed/71fea6e141590cafc40b14dc5943b0845606bee87bdb52a21b6a73eb4311/websockets-16.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:820fb8450edddae3812fd58cbc08e2bf22812cb248ecb5f06dbb82119a56e869", size = 177185, upload-time = "2026-07-17T22:50:56.665Z" }, + { url = "https://files.pythonhosted.org/packages/01/ec/00e7eeca200facf9266a83e4cbbf1bed0e67fba1d4d45031d3e5b3d81b5c/websockets-16.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:125f22dbefaf1554fea66fc83851490edb284ce4f501d37ffed2752f418332d9", size = 177459, upload-time = "2026-07-17T22:50:58.197Z" }, + { url = "https://files.pythonhosted.org/packages/75/fd/5774c4b33f7c0d8f0c51809c8b3a93456c48e3543579262cfa64eb5f522e/websockets-16.1.1-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30bbe120437b5648a77d3519b7024ea09530e0b5b18d3698c5a0ae536fe0cc2e", size = 178294, upload-time = "2026-07-17T22:50:59.641Z" }, + { url = "https://files.pythonhosted.org/packages/37/c3/48e2c03d2bd79bb45948841c592d24156312dd5f58cdf8f549febe652fb6/websockets-16.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b6b9dadbef0cccd9f4c4ee96b08898afa73e26803bbe0f6aeb5bb12b0074206d", size = 179190, upload-time = "2026-07-17T22:51:01.129Z" }, + { url = "https://files.pythonhosted.org/packages/2d/3f/73e511ecf2496ceac57dd4ed8388efe2bcf0769338a2dbf242c8366ae87e/websockets-16.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56cd5fc4f10a9ea8aa0804bddb7b42506cf9e136046f3b4c27de8fec9e2ecba5", size = 180330, upload-time = "2026-07-17T22:51:02.603Z" }, + { url = "https://files.pythonhosted.org/packages/be/4d/2d0d67834092e354d2b0498f014a41249a89556bc406cf86f3e1557bb463/websockets-16.1.1-py3-none-any.whl", hash = "sha256:6abbd3e82c731c8e531714466acd5d87b5e88ac3243465337ba71d68e23ae7e3", size = 173814, upload-time = "2026-07-17T22:51:04.184Z" }, +] + +[[package]] +name = "websockets" +version = "17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/18/72/fba934cb3dff7a85d811820efffcd141ddd52b5a2a01637f64551373ff4d/websockets-17.1.tar.gz", hash = "sha256:acfea4c20bf54384883ea33b1240fc1db4f52e190823a4e2b334bc3e8bfca96a", size = 187520, upload-time = "2026-08-26T17:25:33.063Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/ad/66a74d42fb537bd44056483eae6cbb7ebb10b742c300a0bf8cee427556d4/websockets-17.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:88b882764ef65147a7a5ae13168dedbe225a04e2ff4858fe543f2c402f093e9c", size = 216984, upload-time = "2026-08-26T14:55:20.747Z" }, + { url = "https://files.pythonhosted.org/packages/70/1b/344ab22cea729e872f759b926441f7b822ab6cd106db527736afc066927f/websockets-17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:98a5b2589a56a4b4f098b0a958099a4356ab904a7844f1da3841efca469af7e9", size = 214667, upload-time = "2026-08-26T14:55:22.298Z" }, + { url = "https://files.pythonhosted.org/packages/2e/42/bace574b6ae80e1a8d6935b8c5f03fb67236233ec572e976fe826ff719cf/websockets-17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:020e271205f8ab3406d7a59cd00de6dec722315924411c421bd00642f18bad86", size = 214944, upload-time = "2026-08-26T14:55:23.618Z" }, + { url = "https://files.pythonhosted.org/packages/ee/87/08e35ca4a0ffafb500a16ff461bf9561ad2b755362adb5d077d4dba9affc/websockets-17.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:65be6bda2b537fefa4b3a5ccd6ab386533ce39dd8fe62433ec90901fdc81752d", size = 224004, upload-time = "2026-08-26T14:55:24.748Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/00ae2e147eaa086fe8bdddd36f57216ce72b9a9dfc0b17c717005ebdacaf/websockets-17.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c84bdef916556cbe1d5a43b423398be4dd3cba6522b463e53d848578b920695", size = 224278, upload-time = "2026-08-26T14:55:26.016Z" }, + { url = "https://files.pythonhosted.org/packages/98/e2/7aeb4e00defa68826f449392922a382ce7fdf542fe52190558dc1714e284/websockets-17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47a62d6045c6eaa0d8f97bc2fb68b8cf90077a0cbfd4e83d6f2d2145611ee134", size = 225511, upload-time = "2026-08-26T14:55:27.183Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e4/655be3d93c3edbe1a51606073b5454ea6b1b32d87aa26253a6df952417b7/websockets-17.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34879e19bb0a3c44f9317679435aea5327fac993933a704cbf353bf1234b10c7", size = 228802, upload-time = "2026-08-26T14:55:28.431Z" }, + { url = "https://files.pythonhosted.org/packages/e4/33/98549a2afa9d68fe1b5a8e0a61cd461a43ea1ab7209bce675eea67c79190/websockets-17.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2d72879819f5145a342d0030c418702496c65a4b913ef81f5ae944dd91dd50f6", size = 226075, upload-time = "2026-08-26T14:55:29.695Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6b/cbc27e014d6c292b9b2709cfd32781a2b61eb30cd4c9130e7c57e41a204a/websockets-17.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f25e099fdfe3b09f953d84698f729a1f7d1e99101b2787d7a28ed77b323750", size = 224846, upload-time = "2026-08-26T14:55:30.964Z" }, + { url = "https://files.pythonhosted.org/packages/10/a6/e57925f7a423d90f24559e85bac21a7f0b44c0cf4a5c0babc0759ca54bab/websockets-17.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469355ab1af100b9380f1afb09985019f4a4b94fa1dd0e9396db4361626d7ab8", size = 222136, upload-time = "2026-08-26T14:55:32.376Z" }, + { url = "https://files.pythonhosted.org/packages/32/07/b9de0400addb542ba7c819022abc3afa46cd7e518068881bceadac69d995/websockets-17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:00679b7468b4c2b12b0757118174e8eabac56bb2f579a928a104d9554a56e098", size = 225000, upload-time = "2026-08-26T14:55:33.527Z" }, + { url = "https://files.pythonhosted.org/packages/4c/bb/af2828a1d7f2beb792af6ba56d7b02d56262070266b95d2af9ef391fbfb0/websockets-17.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:a9fe648abd1d9b89aebfa30407bfdd08a0271ec5dc7d44a4c6ccd1ce22cf562a", size = 223592, upload-time = "2026-08-26T14:55:34.636Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/7379f254730c1dc7d8e4dd8d686868d8f7be55bdc94c6d3a44538840f639/websockets-17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f47aafd92aa28b941180e6da8a42b0f711851b14b81a5b6bb28dbbb1fa35152c", size = 224360, upload-time = "2026-08-26T14:55:35.777Z" }, + { url = "https://files.pythonhosted.org/packages/88/c6/fcd91320dd71dda7046df9bc60f60c70ee15c052dee21e31ed6221dc8b5d/websockets-17.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c89406fa3dcd4aa8662c6406cc5c0de1790e9614d2c3aaf03ca53a8a8ccf3405", size = 225404, upload-time = "2026-08-26T14:55:36.85Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b2/655a4f939388079f80f1b3f8a1b9d40783e70a376e7423988cc9a590a09f/websockets-17.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b3b451fd2723ad3191a209afe6f3f4bc86c83e9a85bdc255353b91803ee6aa66", size = 222982, upload-time = "2026-08-26T14:55:38.006Z" }, + { url = "https://files.pythonhosted.org/packages/87/75/37c84c4371c6aa668910d7841036c4397ea10554c07030603ccd5e44b02a/websockets-17.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:054c28db2dcec0e857e3b705d8c28012613e555b38c765d6a4f75340a4fc06a0", size = 224017, upload-time = "2026-08-26T14:55:39.39Z" }, + { url = "https://files.pythonhosted.org/packages/5e/20/8a9a94323bfcfe03bde3f9d98926bea4855856359702fe3ca0d07051ef5d/websockets-17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f8e822efd54137d8cc8310eb64635ab827a4a6c72ff08691f38aa624776d8ecb", size = 224252, upload-time = "2026-08-26T14:55:40.498Z" }, + { url = "https://files.pythonhosted.org/packages/3d/dd/aa66e6500188cd40306abeb92c9a738ca6dd7029d8d8532c538055ab5daf/websockets-17.1-cp311-cp311-win32.whl", hash = "sha256:dcb8d5f7edef7a399d322cf28d4c4e6f98dab64d301c8f50581a1080e5198142", size = 217484, upload-time = "2026-08-26T14:55:41.763Z" }, + { url = "https://files.pythonhosted.org/packages/01/a2/cdf3b551f0b9177023afd3a45d3b431a0d4064951008c4321a8b42ac2288/websockets-17.1-cp311-cp311-win_amd64.whl", hash = "sha256:b1bc819c6db90e8f91a38250a1ab4c058261871aa52d2fe36382eddedf146dee", size = 217779, upload-time = "2026-08-26T14:55:42.942Z" }, + { url = "https://files.pythonhosted.org/packages/e0/13/51253dbed7d16a4bb87b05110ad3bf12165f410e915f6da1edd4186d8dc1/websockets-17.1-cp311-cp311-win_arm64.whl", hash = "sha256:edadce7a22052056fd4384543019856b34850363c9d387929f677ae01d79709c", size = 217710, upload-time = "2026-08-26T14:55:44.016Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0d/098f23c4c858e5de9459ffc554fa07d5493fbcfca7f040b5800cf1cecc35/websockets-17.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:76dd004f59115087c7b700474cb18f01325e37250032e19396c08ae41448e4b3", size = 217015, upload-time = "2026-08-26T14:55:45.194Z" }, + { url = "https://files.pythonhosted.org/packages/13/86/bc1317b1a4d8c4688e2a7e564b5e004dab44c2534d7ca05de6ae9a863fca/websockets-17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:581fa678ef46f4277cc8491312468e582f8ad609dbab907ba6096a08c6a0ff98", size = 214692, upload-time = "2026-08-26T14:55:46.366Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e7/df821761772beaa48c211ee0e234930b35c1473778470773823f56d3911b/websockets-17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87f0d5e77548b0c40c8464cdb6108792e7e53f487c6400028a4ec28a8afbe5ab", size = 214959, upload-time = "2026-08-26T14:55:47.885Z" }, + { url = "https://files.pythonhosted.org/packages/3e/92/c3fb72f11764812fc648bf3838d224972427b348e8b3989d9e0a9df87da3/websockets-17.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:882af300d2c6a092b93767d5de03c7bb56dfb06314140c8e872d3f48e09f7b74", size = 224278, upload-time = "2026-08-26T14:55:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/fb/05/9f82d090c8d2d861604147ef6dfb938a90b039f9358d5193f1df62558593/websockets-17.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c863507ada5805517ca6dff1c524dcd42942efe6304dacf06700878398d21a6", size = 224557, upload-time = "2026-08-26T14:55:50.348Z" }, + { url = "https://files.pythonhosted.org/packages/8a/50/5cbf677b865290fe36819ff00615826e7edc1df38786f770123ff39a933d/websockets-17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d41ef69d5416fbc1d98cf96c37be6192d10fd101c3e0f8b3ddc36e09432b3c08", size = 225791, upload-time = "2026-08-26T14:55:51.75Z" }, + { url = "https://files.pythonhosted.org/packages/c1/1c/eb8a032285243381b09a221ae384c972d5000453ad136add4d1595cec798/websockets-17.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5aefe78e6a3077fe22b5e64b04666a85a3eb8b934d40e8595a693adcbceb6f11", size = 228574, upload-time = "2026-08-26T14:55:52.922Z" }, + { url = "https://files.pythonhosted.org/packages/69/85/413736251cb3ac04ce84cbd90e893d9a36a9698d4820b323aff3aa187e50/websockets-17.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f64e001bb7fa89b9f32cfa600bf8e9ac8ca26759d9b92ae01453ee303d9cd7b4", size = 226428, upload-time = "2026-08-26T14:55:54.263Z" }, + { url = "https://files.pythonhosted.org/packages/d2/2b/a08bcc7fa1ca81a10f84ba32b6e6edd73a913f4b0c2640eed1fd626efacd/websockets-17.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:677014a073bcb1fbaa7e21144786864f16c08f856d66834f611eceb9006cbab8", size = 225184, upload-time = "2026-08-26T14:55:55.943Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8a/3bd2d0cf6b148c8c866d5d9fdcde30c04bfd81fdfac86813e69377eb4448/websockets-17.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0de501b7f2db11e83739ac20e2d33d46da4604b829f506c24be80e7def069391", size = 222430, upload-time = "2026-08-26T14:55:57.103Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c9/8e891ae342668735eabbbc669895e15195e4b45f24a4beeb58af76f414c7/websockets-17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f62114a54117e4948a1e414e89521f7fe1e3c2f83f2a571a06a4fc6718b0900a", size = 225227, upload-time = "2026-08-26T14:55:58.375Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6f/c816f332dca11425e9bda7c07f7573eb5c5f8a735849d02b0d81e8ee20fa/websockets-17.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:eec113a5b41d124ef42ff56b0d74a6da3fd986400038eab9e58ee42a4024e837", size = 223831, upload-time = "2026-08-26T14:55:59.664Z" }, + { url = "https://files.pythonhosted.org/packages/53/67/5e91d5308ce24fc1ec74f56536c12f4888bad45ff5ea50f3180f8c518c57/websockets-17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5f051f8030a51815dc00e24bd2e5f1435af095c1cc111d747ac6e2a3620d7641", size = 224600, upload-time = "2026-08-26T14:56:00.873Z" }, + { url = "https://files.pythonhosted.org/packages/bb/96/faa298ecf2570d35b0eb37caddf4992178d907e108ed74bfffb6bc092c29/websockets-17.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:655a8e28010f09fd6fa317e857afab3af7647f33e41dee88fa421e92086d1090", size = 225707, upload-time = "2026-08-26T14:56:02.001Z" }, + { url = "https://files.pythonhosted.org/packages/0b/12/5710d2482ca5061c1eec5eb46f6313837c760d4115b1795c85b6c08be4e3/websockets-17.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dc2b79afc074d2f3e64b26539350f697fe1b85ea1c49ea24eb588f247b053ce1", size = 223263, upload-time = "2026-08-26T14:56:03.092Z" }, + { url = "https://files.pythonhosted.org/packages/27/47/0c30f4eebfd1d93fae779d268f678d48847fb98516f5200849574eee8820/websockets-17.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e4bd7eacb87d8cf3ed70d6392c770a0d92441f05d7d2a3efafb5bc171d5e3067", size = 224244, upload-time = "2026-08-26T14:56:04.321Z" }, + { url = "https://files.pythonhosted.org/packages/41/33/46c256195a1255079ae23d1b1267b2e1843dc5f46a67f973cdf2a3523dff/websockets-17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ccbf3f4a9890d50b3a08ee04029fde30a03bfdeffaa19977628bf17251764e60", size = 224520, upload-time = "2026-08-26T14:56:05.521Z" }, + { url = "https://files.pythonhosted.org/packages/06/9a/aef0792731df4352e5f417369b532b3325fe434765ca90c193f594ae1e67/websockets-17.1-cp312-cp312-win32.whl", hash = "sha256:7e724f843fa6a0614aece65a7c73e51d0f4412ca41dccac13c3caf98e69536bb", size = 217485, upload-time = "2026-08-26T14:56:06.715Z" }, + { url = "https://files.pythonhosted.org/packages/50/23/493ecfdaf32898e5ea24dc900e33e5e317f9662d5d9ab2d44b2e111b4e1c/websockets-17.1-cp312-cp312-win_amd64.whl", hash = "sha256:617243e19a0992095956f406ee9cd3bc4ba92862d83cb1d83bb59ce574412bec", size = 217786, upload-time = "2026-08-26T14:56:08.055Z" }, + { url = "https://files.pythonhosted.org/packages/97/3d/91954e2f7876f74ce1213e9b92c65a63b559cc4b942a931ebeb351cd9932/websockets-17.1-cp312-cp312-win_arm64.whl", hash = "sha256:9f4a08ff7cb68c27b18e09223cc6304e01d0f82d5a240d251266dfd2e6e44729", size = 217711, upload-time = "2026-08-26T14:56:09.267Z" }, + { url = "https://files.pythonhosted.org/packages/1d/31/5f6450a7879f4f063ef08897cc385ea3ce3f1fe17f08b11e3fd959abdf27/websockets-17.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a0162a6372110a5601cb5c9fd826635cedf69f3e110c545dd19774e040b970e", size = 217006, upload-time = "2026-08-26T14:56:10.509Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2a/c1b006fc861695d2aa4e35327b842015ce1d98cf8f99241829b3d6460bfc/websockets-17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:829dba1bc049779de9b332088c1a6a9858e96bd67e50b6b644a95e02b67836bc", size = 214690, upload-time = "2026-08-26T14:56:11.681Z" }, + { url = "https://files.pythonhosted.org/packages/46/69/66e5b7d01445e0eeb1d4ab419c30315f2c90cf7a8a8cd4ecc47f894dba54/websockets-17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd8f47dbf2e8adb15c847215f83436de3fdb120b51fdae0fbbdf69fd97a3ad80", size = 214947, upload-time = "2026-08-26T14:56:12.923Z" }, + { url = "https://files.pythonhosted.org/packages/07/ce/033cafe2d2538562efa876b9149a2c7a0f7787870a4b1bb6e28adc9ceb6b/websockets-17.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9f4c0377a83e163a303514fdfab501dbe379bdc13e5b9312a91d112658b29dce", size = 224329, upload-time = "2026-08-26T14:56:14.212Z" }, + { url = "https://files.pythonhosted.org/packages/34/c7/e1c2e8a67f6cc0aa43abe0046fb3b7a020980649e6a843751dc7ce9eb170/websockets-17.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c3241d684a76eaaef8b2dc789afde4343cd3aad55ea81e4e8ab3605b529bae51", size = 224611, upload-time = "2026-08-26T14:56:15.702Z" }, + { url = "https://files.pythonhosted.org/packages/be/de/07c6d48eb3d2069709410c851e7de10ab83d752c4bd09862899627c2729b/websockets-17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e5f5c7a893507d0e83a80b88aefd6522f7e882cd53f9722c6f23f5a020c9557c", size = 225848, upload-time = "2026-08-26T14:56:16.962Z" }, + { url = "https://files.pythonhosted.org/packages/f3/dd/3c68572d20509648cc2fb6f50ccf3deeb4b87270f2c8966e99476e278ea3/websockets-17.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:00bf34b64501e3477e81fc281532ff3cbf4da26633c10b63979d5085d46602d3", size = 227290, upload-time = "2026-08-26T14:56:18.204Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4a/8f6651c8a22093539c9215af0c5bbf217b87b382c99d2112039b92d593c2/websockets-17.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ce0305b702b20d1e1d60a9aaace6bc89970e1753565543f310d549eab22c2435", size = 226476, upload-time = "2026-08-26T14:56:19.459Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/f6fc33cea86b1127fd1297b18c107e81580ab55a73a39f9a934441ef321f/websockets-17.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29176d8b429cfa0fa443c473878d37a5c06cfd0cb36b71ba4314accc71e05906", size = 225233, upload-time = "2026-08-26T14:56:20.939Z" }, + { url = "https://files.pythonhosted.org/packages/cb/83/65edaf05f7c9b1dea82f4d252fdc37706a84571646f06119a27b0a16fe19/websockets-17.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3709a1ab30b4b922027d22f68d2b61a0656a91680ac894a537624e6be7dd7f7c", size = 222488, upload-time = "2026-08-26T14:56:22.208Z" }, + { url = "https://files.pythonhosted.org/packages/07/42/d1169c2f7f1f0032b0d4b0c00f0711a070cd7c735de37bfeb876bc0f9606/websockets-17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:43bd0c1ceb924d67f5c1a5254d8361dd9d94246e6331a726064dfa2917880780", size = 225295, upload-time = "2026-08-26T14:56:23.445Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f4/64e2a386c3899b917c2933225c9b47887874229d159797f3bf1a11c20d51/websockets-17.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:1fce0f43e0d41422e0b2cad6561e1970df22f212f4c7e884967df7cf591b031c", size = 223891, upload-time = "2026-08-26T14:56:24.647Z" }, + { url = "https://files.pythonhosted.org/packages/26/b3/dfb5c482f7e310a3432fdbb045ddfe6d34114680e89a233d4ff900a32961/websockets-17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4031152769179ab8dcdeafc7b0e58052a49117560a28671700b47b2c7b717aad", size = 224661, upload-time = "2026-08-26T14:56:26.027Z" }, + { url = "https://files.pythonhosted.org/packages/a4/cf/94865130a336029f46412adc127c4fbe380f46172b90ce251369e35c4302/websockets-17.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a06f3b5085176763182449559e20391d7ce616a8972a9f7a33deda87ea6d4f3c", size = 225766, upload-time = "2026-08-26T14:56:27.455Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/eb8c658f86dfe562ed49a887a27424bfe9e618c26ea6f865b093d075d3a6/websockets-17.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:77b37cceca17291897c3c73bd30a7c7c7909593554b5da574ec852af83c1742a", size = 223323, upload-time = "2026-08-26T14:56:28.807Z" }, + { url = "https://files.pythonhosted.org/packages/1b/7e/2629609652ece5ca0c7ac235927dd4511b08131e3a5d53439b798fddf002/websockets-17.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d8e83333385cac6030a5167fd18bf96cc6c58b914c308e683f05b0cf94bc8dd0", size = 224276, upload-time = "2026-08-26T14:56:29.991Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/8525737fe840b38e5f40956c198fb586a4fac1e07144d41a5b949b989cf8/websockets-17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:073c5c3f7e127041fa9d34a9e29ceefee8c3cafbd267ed2927318f425144380d", size = 224558, upload-time = "2026-08-26T14:56:31.184Z" }, + { url = "https://files.pythonhosted.org/packages/74/ab/3a958c6cbcf74b118f601c20a80ac8bd5e8dfec0bcf7345116feaeefb121/websockets-17.1-cp313-cp313-win32.whl", hash = "sha256:2afb58c7ba48b329d56769f8dfd89f394efe587b65ef806bae810a484d6d3608", size = 217475, upload-time = "2026-08-26T14:56:32.431Z" }, + { url = "https://files.pythonhosted.org/packages/22/36/fb521f0f2994c25509651f169efe5582dddd8713d57a0757ba87859372ef/websockets-17.1-cp313-cp313-win_amd64.whl", hash = "sha256:0340bbef6bfbe16da888b3983d666a4db4954ac3253c38f13bc7aba0c7db5a2f", size = 217784, upload-time = "2026-08-26T14:56:33.608Z" }, + { url = "https://files.pythonhosted.org/packages/68/92/9b8419584681a12a7534b746dfb2737c466efe2455483e2fbf8b941a04ec/websockets-17.1-cp313-cp313-win_arm64.whl", hash = "sha256:7a72efa3bf4fa3a6669a54420a472ad056da3973d827f10e3a536da463f926c2", size = 217715, upload-time = "2026-08-26T14:56:34.865Z" }, + { url = "https://files.pythonhosted.org/packages/90/0d/500cf5daea09d4669dff3a7d67159094a0bd6c4ef130381404f6edd3eb5f/websockets-17.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0c9982938980e086da59f70d05f9418cd143401a601a0faac10fa48f7bb1cd3e", size = 217048, upload-time = "2026-08-26T14:56:36.03Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/5b12c6168aa269cffbfd24d177cd492b130120403a418c7e89462e27b4ac/websockets-17.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:57b39dc8541cf7ed3f639da82bf7451060483967f9e733da1f8173e4095f0642", size = 214737, upload-time = "2026-08-26T14:56:37.43Z" }, + { url = "https://files.pythonhosted.org/packages/0c/36/e453e5106e4e2416f008ac222837c2f1637a063b08008afcd1088889b631/websockets-17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:96abdecbaae746851b87c3a36cb4a661df93ca3d92f114270f79228bf1d00de6", size = 214955, upload-time = "2026-08-26T14:56:38.71Z" }, + { url = "https://files.pythonhosted.org/packages/dd/30/0204bb86176db02cdfc678ce65ed808a66fab87d250ce61a8790800a60b0/websockets-17.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d9fc873e239c5abeb150bc24dbd1a7af23a9254526383ce0a077f5e20adbeb19", size = 224331, upload-time = "2026-08-26T14:56:39.924Z" }, + { url = "https://files.pythonhosted.org/packages/46/c8/d8372256e00c4e3cab1115c45075d1eeedb642a3f2b42bd70c4deae03f06/websockets-17.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f42912fa9eb4cb7c7ec9fde9b3332ba339eb8a8811981043d4029599f3d950b", size = 224685, upload-time = "2026-08-26T14:56:41.169Z" }, + { url = "https://files.pythonhosted.org/packages/12/7d/650355b8f67f908ff99603351d4458d1a0b787d627950a47c38db7e25308/websockets-17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f98bf378d7a5be047a044a1a27c987a8f355e10e3b5754617dbe756248cbc5ce", size = 225927, upload-time = "2026-08-26T14:56:42.359Z" }, + { url = "https://files.pythonhosted.org/packages/34/6c/a9ffa5b903579eed76017870f055d75ecc73988d9d0c9b65a92ba0bf2a27/websockets-17.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d334d11398086bb5559606cb42d51c013ea7c061c7db701521392373d3c087f5", size = 227300, upload-time = "2026-08-26T14:56:43.538Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5d/4551c2269066af7481ee44605a0813770961615b5b5da3e87a8f5cb859ea/websockets-17.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c27336b1a0ac56569493e858497870347854372395f50483725f8cdacc5a45c", size = 226533, upload-time = "2026-08-26T14:56:44.669Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/237a99233e5c445759a613831b3a92e91905afc064dc3bd0ad33c35fd1e2/websockets-17.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:67258b00302a5aaf0b267771c7014b13429abd7ea17eebc4c55bd935ff101555", size = 225280, upload-time = "2026-08-26T14:56:45.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b5/e9407a91613d1d1cd932414143a1012096b26674a782fc55a0bd23217ee4/websockets-17.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:455ffeea0879d313205df1e745e5883e1feb7f31ecd26be882f5f0babd3db04f", size = 222540, upload-time = "2026-08-26T14:56:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/db/d2/db76628db0577b783205d9779f64d8e373416b04c62d1546be4b75dc8540/websockets-17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f7233eaf441a345a5943a929fd4b5ea3278f11aed35a9ed0f3106b8cb3ca846a", size = 225354, upload-time = "2026-08-26T14:56:48.32Z" }, + { url = "https://files.pythonhosted.org/packages/a9/4c/2174181c067b89a74ae18e2650c2ac29959f4b796afe876ab3f4d30d642c/websockets-17.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c65da239a5ad553619804c1f9d65c1a0b3005381c6158ee14da2c7444cbd0c78", size = 223867, upload-time = "2026-08-26T14:56:49.579Z" }, + { url = "https://files.pythonhosted.org/packages/df/75/274decb9a8253561b5be3261e02a6676fc8ecdf31e95b722e53d5bfb8fd2/websockets-17.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fa1ffa08c81a4f809cdab6129f8e55bee4650b9d6d3461019dda73aacd146b6", size = 224652, upload-time = "2026-08-26T14:56:50.885Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e6/49824f1fb4db7656d2f7492b1d8be16147b759d909490e32f4776843ee64/websockets-17.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:406b8107943a43ef4649b1e0cb0cdc052bbf08fe1c8905a623c4af9586e5cebb", size = 225822, upload-time = "2026-08-26T14:56:52.356Z" }, + { url = "https://files.pythonhosted.org/packages/b8/6a/5dc43838c0b02a95f42c47a0de33c5ddd7767a9feeb4d0d8777ac1cfefe4/websockets-17.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:4e8ffcb486c8490a34a4cef5e4409d8da5a1cb1681e5bf7d786ce5e84aa8540d", size = 223379, upload-time = "2026-08-26T14:56:53.699Z" }, + { url = "https://files.pythonhosted.org/packages/c2/62/585637cf06d6b321232f79c55dc14d65518d12cf87c94c44f5864068810e/websockets-17.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fb88076df585b69c5761c387c0081aa87d7b9eb1b205a6535ca4777e25650d81", size = 224330, upload-time = "2026-08-26T14:56:55.184Z" }, + { url = "https://files.pythonhosted.org/packages/de/68/c3b234a6a1366b6ab5bbfaa4434a1b946e1dc4e8ddd6824bfd93a8835b7f/websockets-17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5d4724255fb8398acd9e583b97eb2279cec20e0bd0f9a94bf75f6056ef9f13da", size = 224622, upload-time = "2026-08-26T14:56:56.393Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d4/84cf3d1376f5d8207f55f43c1c818babd6b89447f5dcd01f18a6d5526796/websockets-17.1-cp314-cp314-win32.whl", hash = "sha256:be3f0129c5654517b2abf07dcb75bb1d9479759a4ccfb569e8293579e9fc029a", size = 217036, upload-time = "2026-08-26T14:56:57.652Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0f/9e7ac63c5d7cb642952200814f584318e65146df008b7d375d5d9c6b2c97/websockets-17.1-cp314-cp314-win_amd64.whl", hash = "sha256:2a4dc6ef83f4559e0d05f313a375cb38f63c986096a9da99fe94fdd779d313e5", size = 217382, upload-time = "2026-08-26T14:56:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/1ae6b91f7f3ac05f5c9f14a72dc2181c115ff370bcd8a7f10f02c174adfd/websockets-17.1-cp314-cp314-win_arm64.whl", hash = "sha256:46c0331c9eaaf73a559f3a9e388466be0df96eb83d40f06f1ca6ab6613b35c82", size = 217268, upload-time = "2026-08-26T14:57:00.654Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f0/f65644d0e0b2b90918a8c41503841cc4072a58f2bf76c09bc36e751fc0dd/websockets-17.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d411ea5ca18ac1b12c0c94be88b60c18ca641ac43bcdfdf1c9f79d46cdbe1603", size = 217379, upload-time = "2026-08-26T14:57:02.181Z" }, + { url = "https://files.pythonhosted.org/packages/ff/35/4c46d1f620ac1a30f92b6eae78ee40a772a93f568647ca7ccdc5ea283cf8/websockets-17.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:07fa3e7c30e2c577928d359b56bf872a3e0cbcc15553eaa0907c1ee86344b56f", size = 214911, upload-time = "2026-08-26T14:57:03.478Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/4587e8406d7c1188e97b9cf466c081e93399380d447f885bfce81626cd37/websockets-17.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6de9acef07e3a78e9567fcd26c29011a4da8f050b13004bbf880a0fd82a6eea5", size = 215115, upload-time = "2026-08-26T14:57:04.692Z" }, + { url = "https://files.pythonhosted.org/packages/ec/06/1381c8fff525041025909eb80ace32489194a00ba22a0a8d428030afcc84/websockets-17.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ea0ed9373b880115911d9d39634bccc95b8ce590c9c42e8589f5cacc3ef3cee2", size = 224696, upload-time = "2026-08-26T14:57:05.899Z" }, + { url = "https://files.pythonhosted.org/packages/36/9d/9034e867dc85340be058619751742b895f722326e83100d110063461ca07/websockets-17.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50903d335bfda026c2fa11dd9aed09d8cbee0c451e3a85122a9acb041b7dc69b", size = 224975, upload-time = "2026-08-26T14:57:07.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/eb/ed03aa3cae748ebf6397e5d44028f433f746bad09dc568ff754fda3a3c9b/websockets-17.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a74531ce81af587f906ab42f194032388fcff8fc7938402e5917c9147a39441", size = 226151, upload-time = "2026-08-26T14:57:08.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c9/cc1964a096d16f3b73cb1ee5f14f277f5a3bcac07c6e8f9a1dcded99f4c8/websockets-17.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8fbf28e639544503b7d1c96452a5e5e043e4108d89b1f3fa02910603622d19db", size = 228292, upload-time = "2026-08-26T14:57:09.846Z" }, + { url = "https://files.pythonhosted.org/packages/1a/26/46da6dd0363c2db2e4876fd59a40fd40c1943a82d7018d0a33afbce47d52/websockets-17.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f612dc57f00c07cf4aa2673f7cbceabd654ad2457b7e639f061b794d6e11f9fd", size = 226722, upload-time = "2026-08-26T14:57:11.118Z" }, + { url = "https://files.pythonhosted.org/packages/78/98/ecd8f5e1c5d0e54c08ebc5c66852271112166db68107cb0e17ca1bf25009/websockets-17.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c7ac77401227212dc6e849182feee50d57cf456ec6329ffd6979c94bb136c5c", size = 225451, upload-time = "2026-08-26T14:57:12.601Z" }, + { url = "https://files.pythonhosted.org/packages/65/4d/da8d2760db53e17aae763738b6ba834b1fcf16813d3632f3edb6951e1ec8/websockets-17.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32a2a68d989d6e5b74a9d5095415c51189ebae29fceb7cf2b64a1c0318a81256", size = 223003, upload-time = "2026-08-26T14:57:13.875Z" }, + { url = "https://files.pythonhosted.org/packages/a4/40/ea401c141a79c5b1d0021a0dab9d0df2051c108f1620fbb39a6e7c714c3b/websockets-17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:aec00f018d34c67500ff0438dc314b40277be4a1b983cbacbf53ccf7db63e257", size = 225704, upload-time = "2026-08-26T14:57:15.091Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8e/07ab3f44215d89840d5385fdcaaab1fed8caeffa67c6899e15062957c12c/websockets-17.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0014eaff8ad5b3b43feda2279f9d34bf2eaae040720b9fbbb55944b10f40b14d", size = 224192, upload-time = "2026-08-26T14:57:16.3Z" }, + { url = "https://files.pythonhosted.org/packages/58/93/ccf1af0a23e5748d4e22292a377d78d15cf294d7e707bbb11a8990ae6bd5/websockets-17.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:db9d7ee47f3ba531e278be539af39e2c7c7d28fb94897b6cd1120d63b0ef5922", size = 225082, upload-time = "2026-08-26T14:57:17.531Z" }, + { url = "https://files.pythonhosted.org/packages/e2/db/e32200f99ce282e728d2929f2c429db353cf3282db7d0eba99eb32c9fec1/websockets-17.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:ff3e2ba7a9f0a110b0555452e9b5a03a34e11662544e01beea15f144b48ba7b7", size = 226101, upload-time = "2026-08-26T14:57:18.802Z" }, + { url = "https://files.pythonhosted.org/packages/28/3d/e7a6e9777b29433620167c98f3caaff0d6b08b1239a273ef7f7fd1393349/websockets-17.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6da17fc94bd270f5987b10bee113461ac36a36a98b0481ddcc98056e5a90001a", size = 223794, upload-time = "2026-08-26T14:57:20.313Z" }, + { url = "https://files.pythonhosted.org/packages/48/05/ac569090726dedd6656f3ee28b0c02dfb1ba76e898dceaccc2987a237cef/websockets-17.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:e8dc3fa6d6b7ead3f9de57895f41b116a28787548e066365d9d90f7356bcaad2", size = 224567, upload-time = "2026-08-26T14:57:21.634Z" }, + { url = "https://files.pythonhosted.org/packages/14/50/4ef62941111db6b31193f4fabbb65f845a5177579040cb8fe0d774d25034/websockets-17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b65d5fe48219dc2d5e158de9e6514e75600f379cc7e37108d35f31764c155566", size = 224993, upload-time = "2026-08-26T14:57:22.86Z" }, + { url = "https://files.pythonhosted.org/packages/28/42/2b95ada4ea19bf3a2072b68669ce4f4afb212690b727d31640576287fd68/websockets-17.1-cp314-cp314t-win32.whl", hash = "sha256:2cce251f3e2469b99b6802b55435bcdd07123b41870f54c87b336183af9d7e68", size = 217168, upload-time = "2026-08-26T14:57:24.466Z" }, + { url = "https://files.pythonhosted.org/packages/32/0a/67d5ee08dd8060a37d612fd40a625b5376ad19ae48fe1c8ad428c278b817/websockets-17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f6c38cdcaf98a911d7acc25577f2f9e710f3a2fc2bde1563556784320196b51", size = 217508, upload-time = "2026-08-26T14:57:25.983Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/822005d0c674451d2411027b878cdc128a2b7ea5a30d337d9e279da22eba/websockets-17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:d1e2f5fa2b6d01f0d85b4f223fea7ed1d504be282a02a81bd2be4817ef7a2f03", size = 217425, upload-time = "2026-08-26T14:57:27.324Z" }, + { url = "https://files.pythonhosted.org/packages/de/d5/99a6c6a1eb5d5ae9f45f59a3c97f4e3b21f310eb404a547fb3e7d2fc054c/websockets-17.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:88381602e379165b66244b2ebc29f9b23ea0851fbe63ae157f91ca324f072d6f", size = 216970, upload-time = "2026-08-26T14:57:28.575Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0e/1e7f6e833728193958d3ed3d67b5d57c3c7cfa948abf94d4bc553257c954/websockets-17.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:88bc5138e53903a85c354e59df7ba73ce306f7b09724cef74dba121e60a88ce2", size = 214699, upload-time = "2026-08-26T14:57:29.862Z" }, + { url = "https://files.pythonhosted.org/packages/07/00/95d39549f86e34425a0412bcbe61708dd1fc46af654e2134a6c4389102ad/websockets-17.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:3546ef55b3a074494106508bc6505c73825970d2d9505f7bf53882b3e88b0d1e", size = 214927, upload-time = "2026-08-26T14:57:31.148Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ff/b442415fc4f7f9943b0fc8e8eebaa13923ca73361e167c439ba634eecbd9/websockets-17.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9ae55d24241fc055f22aea3ac924559069848bd0ad4ea065fdd72d2194685fe8", size = 224373, upload-time = "2026-08-26T14:57:32.833Z" }, + { url = "https://files.pythonhosted.org/packages/a8/dd/b83537aae4cf61615b9d8b2dbb235c0030ba85457a6d934798273814600f/websockets-17.1-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d7b349265fad6244013eecd99df8d83c12bf3013943e431f4fadd5bffc37db42", size = 224801, upload-time = "2026-08-26T14:57:34.041Z" }, + { url = "https://files.pythonhosted.org/packages/76/83/5ab0abed58454909e8dbab45086ac68ee4556d7a8ada26735addc909b903/websockets-17.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc5789e5ea182b77a38881383ada5347202a6c66f4857d054e075290e80b604b", size = 225967, upload-time = "2026-08-26T14:57:35.292Z" }, + { url = "https://files.pythonhosted.org/packages/4b/26/e2412f2b998a8c1dfc00c0709ff6ee0c634dd0b0b4f92bdfe9667876b71c/websockets-17.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce13c7d233239e739600a57d4a73c1192ad8259e655a4d55aa1a454242bc809d", size = 227664, upload-time = "2026-08-26T14:57:36.493Z" }, + { url = "https://files.pythonhosted.org/packages/ec/25/0dd4495df3c0e02f6db705312ba85ab9b2dd42257dc23eb0da10066e4844/websockets-17.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1036189bd34b0bc1b10a4679321e2c7968af317efe6e8e4c1c5141c4254fb5bb", size = 226447, upload-time = "2026-08-26T14:57:37.781Z" }, + { url = "https://files.pythonhosted.org/packages/be/67/6df3f63ffc48f08126ed0cd2fd2a41092967c3e364f8ec100deae90b6d77/websockets-17.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e78fd4b7b2c5086a38671c9c882c1e643385eccea360b5b1fda4a105e590087e", size = 225343, upload-time = "2026-08-26T14:57:39.133Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8d/a8479bbb09ff054907d141123d8f52fb6ae5ac39c6dbe39e6a02a8408309/websockets-17.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:46e7a10bf04318c7b0c0273791925ae5e1cbe4a11e34aa934d2ef27862058a80", size = 222748, upload-time = "2026-08-26T14:57:40.478Z" }, + { url = "https://files.pythonhosted.org/packages/40/fb/4c3d2a3269cde3f3087916de9c3d9fc5d7196b46846d8c3a9ae59ad0a884/websockets-17.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:33e45c7ea38428e740a7f233555d71df0b875cef7fc080acebc9654475e35335", size = 225453, upload-time = "2026-08-26T14:57:41.859Z" }, + { url = "https://files.pythonhosted.org/packages/7f/1c/6467b401d19408f34e1c7389c222c2c7e1dfdf08c551190269b5eabc726c/websockets-17.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:6e63c01803be425ff062b7f7fc201a74def1d49fc94a2410dd17375df75936e9", size = 224112, upload-time = "2026-08-26T14:57:43.136Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5f/744e032ac80e11039a7447657ebabb46e9b5c2dbcec83be571335212932f/websockets-17.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:722ec21717eec6477bce582147a28acdfe034e604239466a6a95daedb863e774", size = 224646, upload-time = "2026-08-26T14:57:44.871Z" }, + { url = "https://files.pythonhosted.org/packages/9f/47/bcb9128d9afc4d0934d9192e2a24897ca2f7a63df2654904915349c6c46d/websockets-17.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:e74e41f0ad12ff1e8983e349daef79d37cc8280c743ce9d134d6c74c18dab5d6", size = 225797, upload-time = "2026-08-26T14:57:46.338Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e0/b058047b7cf565e1105b10ef6b6b24a6ebe3575678c7dc75a645334705a7/websockets-17.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:12fe8984a32dbfd084e0603f1a8d740c0180cb85b3174585c54a80d2455a8394", size = 223605, upload-time = "2026-08-26T14:57:48.175Z" }, + { url = "https://files.pythonhosted.org/packages/b9/69/fc1555bff884de363f1bf9eebf2836dbeb29fa7e4f957debb7bbcf43abba/websockets-17.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:01dcb47deebc40b38fd4a493b9b9f4d0a704b7bec6f35e4d34085b329abce71a", size = 224508, upload-time = "2026-08-26T14:57:49.407Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/648d4e68621688b19093b06f7b497d520952e68cdea1c1b54371fe9491de/websockets-17.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f4c45ee2512d3757b5e6c67c5a34e435143f2ecb7df3324f9fd888688c45c0f4", size = 224767, upload-time = "2026-08-26T14:57:50.799Z" }, + { url = "https://files.pythonhosted.org/packages/58/93/f8342b55864f71df13eb8e9ef7dce691b87a87f04f75bb8a1385b3336e7c/websockets-17.1-cp315-cp315-win32.whl", hash = "sha256:0f4f50dfe2cc810fc4e2de979b35e83bf8bb4bccdc6fe472d93762ea7b1d5927", size = 217003, upload-time = "2026-08-26T14:57:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f0/7b5fdb774c245e0b6217009e2a24d2105c1a64923949f33be41aa7959302/websockets-17.1-cp315-cp315-win_amd64.whl", hash = "sha256:4af784f3e436f65b355c117c6497320f2b5cf6a559295cb1c4c7338e335d45cc", size = 217300, upload-time = "2026-08-26T14:57:53.492Z" }, + { url = "https://files.pythonhosted.org/packages/76/33/1fe6ed1b5087516115ca451b2c240314b010647071f8fc3bd78a21e4dddb/websockets-17.1-cp315-cp315-win_arm64.whl", hash = "sha256:d58159af7835fde09c462394293c0d7aaf8fb4557d8f8e5699f5e722ccae013d", size = 217214, upload-time = "2026-08-26T14:57:54.88Z" }, + { url = "https://files.pythonhosted.org/packages/94/ca/ed02e75996a266d76c5fcb5dd9b930db4cf2b388ca5fa3d2a72086f81568/websockets-17.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:1a5cf4e7bbe3ca499e6a289206cb4fcb7444b09919e129bd517f57d5fa192c13", size = 217282, upload-time = "2026-08-26T14:57:56.108Z" }, + { url = "https://files.pythonhosted.org/packages/bd/7d/d536f5bc89ea5b52fd1c1727c59fabafee6bc41f5ce92c3bd2f83047908c/websockets-17.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:416b4bc8789a1865a3ff643ec4ee073a5f52402d0dbeafd27b1798d5dd6b6a51", size = 214863, upload-time = "2026-08-26T14:57:57.355Z" }, + { url = "https://files.pythonhosted.org/packages/37/37/944cf17bad668e9be1247e6314f88a48b9faf7c250e383410db8b38af0b9/websockets-17.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:259f45358c76d3b18489e3e80636cdbe807e05ecf1b10fdf1a779106d23d0c8e", size = 215073, upload-time = "2026-08-26T14:57:58.719Z" }, + { url = "https://files.pythonhosted.org/packages/74/bf/3267966cc1bbc2b8fa62fd329651b0af502df1f5d1c0eed027ff339d6aa8/websockets-17.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d9d01e8ede41fea4f5a847dad9d628355f74905f437a5b6856d67aa66d193800", size = 225229, upload-time = "2026-08-26T14:58:00.235Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d8/85ea722f483510abb39fc71aafb4465d17cf9051a275ab036874ff3c300c/websockets-17.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7b35181a14cbfcae163b4de545d22abfd07d06c2c41ca69cfcd99251d6888ab", size = 225500, upload-time = "2026-08-26T14:58:01.994Z" }, + { url = "https://files.pythonhosted.org/packages/50/ce/64c7d00005bd0d15ecb5c5fcb7fb2597b6b92ddd16c4fa6bbc3d2835ad63/websockets-17.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a8e768a048c2220697477ce2e67e4345dc9f693d0ee6af53945b5e30227c6a7", size = 226829, upload-time = "2026-08-26T14:58:03.327Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dc/096c67940fb957e667ca3c542818150434eb0388c6fdc90b3a502f3c3e96/websockets-17.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:880069d21cc33a558dcf180924a546d1ecf8ada5be3e4e70acee87019d706a24", size = 228457, upload-time = "2026-08-26T14:58:04.78Z" }, + { url = "https://files.pythonhosted.org/packages/51/fe/f2331b6b7ccc67589891da354fa46a5cb79e95f83b9fd0e734d77f1f2140/websockets-17.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cec1bb8f22abccc8d20f8ca63df9be41600c26c190f4b97ee86c675fd4a863a6", size = 227265, upload-time = "2026-08-26T14:58:06.102Z" }, + { url = "https://files.pythonhosted.org/packages/47/a5/fb1642302f8ec77ca922203074f155a9831a5128ad75e725059a476d1227/websockets-17.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f3a1d577e081667dda7f8e5b4796e6e32f9713c93e2a3d930669519840a3c623", size = 226143, upload-time = "2026-08-26T14:58:07.464Z" }, + { url = "https://files.pythonhosted.org/packages/d7/41/7133fcfb63f5562750b269d6a845c689dde6a2c6407286da395beea19ddd/websockets-17.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc053f9e95a76213c5eb7ed95779f7daf0d2bf0e4e03073629ebfa43a033f151", size = 223501, upload-time = "2026-08-26T14:58:08.766Z" }, + { url = "https://files.pythonhosted.org/packages/64/b1/82b36bfabc79ff2d383a1fc043cee6a13f794ef4f6bf1b4810ad6988cf6f/websockets-17.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:bb0efe019480a1c93e168ce96479273aaebd672fc8c350d5eed1e507ababb1b8", size = 226330, upload-time = "2026-08-26T14:58:09.987Z" }, + { url = "https://files.pythonhosted.org/packages/41/7d/5b511b9bf6e9ad331e6ff902fcbcc71c3794d10ef3b5efe80ccb8f0a7861/websockets-17.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:615746b12b26a3fd4077bc6fbeb277a1c192a45dd57b531d07ad9ed5c52a9a7a", size = 224980, upload-time = "2026-08-26T14:58:11.303Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/aed08f25301f8eef23be903ff9319fcf35630ca2bdec9d226f7d804dd5b3/websockets-17.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:1a20136d61f9ca3a31493732762661fafc2c20e8861930214e21afc6a8a692a2", size = 225478, upload-time = "2026-08-26T14:58:12.543Z" }, + { url = "https://files.pythonhosted.org/packages/3e/47/0d63d4168536b4682c9d19b7399443b1176f25dbb68878374fa716670230/websockets-17.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:2786cbd273ab69c22612db8a41229ddf2c158060b17b5928884bf388d07887f3", size = 226588, upload-time = "2026-08-26T14:58:14.457Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/844bd0b6386fc81ed6a55f4b6dd26f01c6987eda205afa10175ea12b2164/websockets-17.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:b1c323fc3be1dc3f87f6c59458cb7d9e13dcbbf971d6c3f3e2bbaf58d3bfcdfe", size = 224336, upload-time = "2026-08-26T14:58:15.778Z" }, + { url = "https://files.pythonhosted.org/packages/96/18/03709c84bc88ec4dcea68d4be4ccd07d611073dec111203a5bf45af8809d/websockets-17.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:12c8e2b25df59755954a04dfa09c990b96691025aaf7eafd19ed6da24b09c18d", size = 225197, upload-time = "2026-08-26T14:58:17.141Z" }, + { url = "https://files.pythonhosted.org/packages/27/cf/0d1c694b6466c89e875b85b32b51312c472cf6708eee91914866f5087dde/websockets-17.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f58f58b4b29bbea2a3635e2c56eff4a3adab011fe383802a9e542e31b97085fc", size = 225493, upload-time = "2026-08-26T14:58:18.521Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f5/99857c3dd9676749f33e3668665a34ad6099505fb8d75eb084f49f7807a9/websockets-17.1-cp315-cp315t-win32.whl", hash = "sha256:f78a3ffb1994304db2c0c4588e4d1a518079b557054fa3bb985a6f5e50ff49a3", size = 217130, upload-time = "2026-08-26T14:58:20.037Z" }, + { url = "https://files.pythonhosted.org/packages/2c/84/77599922ab441bfe61508f97dab2c71f8e114d31793993ea54011db16199/websockets-17.1-cp315-cp315t-win_amd64.whl", hash = "sha256:ad68c28a27246fed109a4409393d677b7e1388345cbbd2f5aee5c182d8506110", size = 217448, upload-time = "2026-08-26T14:58:21.382Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3c/8b9a225b523f06a9389be81f1b0ab07c49bec6014742e6aa359c1f920f1f/websockets-17.1-cp315-cp315t-win_arm64.whl", hash = "sha256:e552e0037230ac16e5f568de7012041344d1b18c9feed30ec2891b8eba55af81", size = 217372, upload-time = "2026-08-26T14:58:22.807Z" }, + { url = "https://files.pythonhosted.org/packages/e7/e4/af4abbcf07eac6a725ec6f865611526b2b0c23d482723de551bec667880d/websockets-17.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10ecb38ffc05e1841b619d99c725307a223ef9ad58e7b1ed33311d472dc43918", size = 214602, upload-time = "2026-08-26T14:58:25.211Z" }, + { url = "https://files.pythonhosted.org/packages/4d/fe/819fba7ba35f92b639333da7355041c07dd50048f9c76fba0b8e292a6483/websockets-17.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17aa424ab61620aad21b36b2240efc87b500cc496e7d0e999a5c2ae99395e886", size = 214874, upload-time = "2026-08-26T14:58:26.689Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a7/d370ab794f47fbeea648d17ad08caf0bb50131d6c04b7ad83e6af63c405a/websockets-17.1-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:764cf7bfa149365f32b7a0fd9fed32debdac29dd06295d5635cde1745b446cd8", size = 215821, upload-time = "2026-08-26T17:25:23.616Z" }, + { url = "https://files.pythonhosted.org/packages/9b/6b/251b00fe634e2a9c2cb5d6390e0e97cec55e3d18dd09b4b976620eed5d7b/websockets-17.1-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d1b108bd8f5f6a8b90801f6db3b3858d5deca889acfdb8ac497bbb24e4b0edf", size = 215714, upload-time = "2026-08-26T17:25:26.295Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b1/37fe0c96c206b4208a072c3a74add6a72af4b8228be3f5435163c5a6d099/websockets-17.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a62d8c424383c9dc769ff3672018df822603117e32686e567d452ed035b6fb2e", size = 216608, upload-time = "2026-08-26T17:25:28.134Z" }, + { url = "https://files.pythonhosted.org/packages/be/7e/75a0a491b512412e08333b9f8412757af6186fe1c598186261002de1a793/websockets-17.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8196d217eeca52b9235ee1f8a684a09885a5f953d5a31e80ef915bf2c5c94f9d", size = 217870, upload-time = "2026-08-26T17:25:29.745Z" }, + { url = "https://files.pythonhosted.org/packages/41/63/23572870e01836a98346075b9e17a8bc24a6ddd9800a3204ceee58677f3c/websockets-17.1-py3-none-any.whl", hash = "sha256:f221081107b8c48184d99f7019604486376e7ef826037e70aad6b02540732c23", size = 211134, upload-time = "2026-08-26T17:25:31.397Z" }, +] + +[[package]] +name = "wrapt" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/42/a6/6375d56c44d590ef24acf0f8f5bf7ed768ff7a510b959306ec412611e90f/wrapt-2.4.1.tar.gz", hash = "sha256:fd6390aab9e8aa40c52eff3c180f098e8d9f5894b1fd4c4fd2c207067b33ed16", size = 164597, upload-time = "2026-09-10T23:12:16.811Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/45/fe4524dea5a15a6f64522745a96618fb2f874c11c4060848abacc4f85232/wrapt-2.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7aaff952dd6930fc87b52d44bf72ac8426e45e90058fe0f2da446889738eff44", size = 97739, upload-time = "2026-09-10T23:09:36.872Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cb/08025a4402bd6f34ac7c07e4d5c75e4f32e595cd7424d697c6fce1663bc3/wrapt-2.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8d67b916f2f777a6d31067e842598e059f1a0e29022c86454564fc9efb9a0c19", size = 98237, upload-time = "2026-09-10T23:09:38.82Z" }, + { url = "https://files.pythonhosted.org/packages/3c/e9/2c1a482052cfabd6906887096993099afa2db738e7597c30e4f2b9922718/wrapt-2.4.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2945f4bce1518e09eb25458dbd09800f7aa3fee81a5a5cefe66c4e82fe8cb69c", size = 217028, upload-time = "2026-09-10T23:09:40.259Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0d/867f5a1955de8e04edfbdf32891376443629d94c3e31ae2ea2ba101d6d31/wrapt-2.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7ce62ee24eddb76c317ec790c44f4fbe9f34ee0a5dae37b30c65a2b8f3e3604", size = 219323, upload-time = "2026-09-10T23:09:41.701Z" }, + { url = "https://files.pythonhosted.org/packages/42/21/af01e7d55ce47df290e60cc4c0acccd439ccb95f30be63e46ba013e4f212/wrapt-2.4.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cbee8d00840ac89846f6567c75506260a15fbb4dc74de180678fafe09458d47", size = 208024, upload-time = "2026-09-10T23:09:43.318Z" }, + { url = "https://files.pythonhosted.org/packages/89/24/9ae249d4922408cb28bc31e8dec86e918350261cf61e38fd8bbfff8bfd66/wrapt-2.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b78dd8d058468156003ee8c212dab334b6eea18cd9c5db3b81f6c90a9408afcc", size = 217712, upload-time = "2026-09-10T23:09:44.739Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/34b1e65e490ae886b057ac813e86e16399b7dae91166b5bde5fc9f86c2ee/wrapt-2.4.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4d48f1720569b4f6e2df97783fd270554abc4c6ebd74b2e5f15e5110283aaf78", size = 206245, upload-time = "2026-09-10T23:09:46.07Z" }, + { url = "https://files.pythonhosted.org/packages/bb/e5/8b5af45e8e7dc102b78a40b077f071466a0f584898fe57ce181bbca2572b/wrapt-2.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:de5d2d7f12557c9e994b4037330efa5700fccb9f2b9df0bd4ccf384eaf73c254", size = 206937, upload-time = "2026-09-10T23:09:47.328Z" }, + { url = "https://files.pythonhosted.org/packages/b7/47/06e483462e90a50250f51da3ffa1f1c67e457910078b36aad55805202886/wrapt-2.4.1-cp310-cp310-win32.whl", hash = "sha256:5c0217b8c12952bf4d137a19ff2c61cc9b626f52c09c43f0289769c9f5f808aa", size = 93405, upload-time = "2026-09-10T23:09:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/01/c200bbabb0c46a431c9d965fff78e6bf08c1598fa970bdf105362aeba969/wrapt-2.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:0aacc1af512e040fb0b3c4593a8bded9aaa04235025f08a54eda9d8c1103d3c6", size = 98454, upload-time = "2026-09-10T23:09:49.771Z" }, + { url = "https://files.pythonhosted.org/packages/e3/04/c7eace579b4c397aa3651c550c216b05d3561cff56bcbeb726c8ab3faf4b/wrapt-2.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:6f32a45d0e883918387aabf5384952e5a93ae40be0a6fb5f1eddc8281af4c714", size = 95247, upload-time = "2026-09-10T23:09:51.119Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c6/5e08ef1e8ad5ff518354db77b7a303666c7e0652e2a569116b5ccb72b690/wrapt-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b6ea396edcfb305698f44ef0a47b9adaca15d30386ce093ca606844f776d7236", size = 97984, upload-time = "2026-09-10T23:09:52.569Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ec/e77ba77fdce732f39522382a6a9b968e5abf3471f352d51670120534e271/wrapt-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b86bfe741840cec6d8d5858b1eb53a325f564f08d7e6fb5694981961edcec230", size = 98241, upload-time = "2026-09-10T23:09:53.72Z" }, + { url = "https://files.pythonhosted.org/packages/7b/af/c54bd0bf32ef3c5f6d2d50af6bd9d88c040ef53d9f8e113e065337d76788/wrapt-2.4.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ec7aeb92e956810eedc268fe75f8c11f05d6c3d864d0995a53eb6623b3b6a51", size = 226167, upload-time = "2026-09-10T23:09:54.946Z" }, + { url = "https://files.pythonhosted.org/packages/00/3c/934e13c7e27680760fa74e25e4b291b213fd607aa42333d883f732de6d73/wrapt-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba66eccadd4b857a845e309c08457bb71fa8300000e508e3a86cd695bf9c7515", size = 228439, upload-time = "2026-09-10T23:09:56.272Z" }, + { url = "https://files.pythonhosted.org/packages/cf/5e/7593f3fe527260ff3e8eb09ed26ff8f0bf51cda5702a75fb5969b689b4a2/wrapt-2.4.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3d5c8f3b4eae814213d4097f134be03aeb23816e6358213454ec5e62e8449fb6", size = 213495, upload-time = "2026-09-10T23:09:57.622Z" }, + { url = "https://files.pythonhosted.org/packages/42/a8/d4b4d6ac44cb2c7d4a882499786fd96758c97feb6b1539ba0ed5c823849a/wrapt-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3bc7ab495e564449f39db2f54c1033d7190c944736d525f3bd06cefaf0134df0", size = 226108, upload-time = "2026-09-10T23:09:59.132Z" }, + { url = "https://files.pythonhosted.org/packages/05/b3/aedc046bbd8ff2333c2ce9d5267db42117221d4192ac13c66e871b528d9f/wrapt-2.4.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2e2e694057bcfd46f43e28f7b50fad8f5fec7113cc6445fdf251219539a08f28", size = 211144, upload-time = "2026-09-10T23:10:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/96/60/c01efb0e8049a9df379b04ebc589eeef995d6cf184b1d8be9f1b84b4d5f7/wrapt-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5577fdc702427698c81c8c9c3b8479cbecb3dfd0f697be8c1d09c89f5838cc", size = 215496, upload-time = "2026-09-10T23:10:02.778Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b5/26214472915eb447725982e2735c593131c5bae0c3fd582ad61e7b965907/wrapt-2.4.1-cp311-cp311-win32.whl", hash = "sha256:e78c0f6d66a564bab245faf170d55595df6fc237117b99d97fd838678f0906f0", size = 93493, upload-time = "2026-09-10T23:10:04.196Z" }, + { url = "https://files.pythonhosted.org/packages/d9/10/5f7d2dd40c97c5e572dce771b8d4c1c78d46a90cfc24c7b25f158253215a/wrapt-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:1f1851be0e593d65e68d7c1bc9d3b971e19fefa219f617e490d75e80e467ee14", size = 98679, upload-time = "2026-09-10T23:10:05.413Z" }, + { url = "https://files.pythonhosted.org/packages/a5/6a/f7565da9ab1323b2aa5fb4a83b919b6519cc0f4ea4b91ec8f2d25f5bcbab/wrapt-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:7ff549316d94c404999f96bb0a6993222566231ff4bede1b6f68908d1958a08f", size = 95071, upload-time = "2026-09-10T23:10:06.679Z" }, + { url = "https://files.pythonhosted.org/packages/9f/1f/a2f3225c5ecf522684c1d051aea8ce8253f240e55be75826b787777afd6c/wrapt-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7e86fbc2ac8a363ea04abf631fad82720e16b17a25020f32dbe9b24a2ed2b0e3", size = 98890, upload-time = "2026-09-10T23:10:07.876Z" }, + { url = "https://files.pythonhosted.org/packages/68/6c/eb45660fd4d92cce11ec923f55bb2e647a6c18d30e53734eb07a3c530e31/wrapt-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24389748f0b9d5b67e478fad4fc8b3f1108422ef80716e48eead6cebcebbff08", size = 98742, upload-time = "2026-09-10T23:10:09.236Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4f/17a89a580cb0082e61b8375074d5c9e5d38e4aa83ee19b6174ee472d17c2/wrapt-2.4.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30d11c289b013bf384ff1a1a6553f150d0b855901708a9bef667a5680f8247c9", size = 236301, upload-time = "2026-09-10T23:10:11.039Z" }, + { url = "https://files.pythonhosted.org/packages/01/ca/4700eb008a34bf02de328806ddde15fc84c8d1e65d3dcafb92a935a50319/wrapt-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9356dbb59199a0e4709de35fa4a1ac1a88ef6da99711a397f5b009233faff326", size = 237805, upload-time = "2026-09-10T23:10:12.594Z" }, + { url = "https://files.pythonhosted.org/packages/75/5d/26c1740299b29e190d5f4b4a99eb001401042a9d9ab338e3f8e1ce140ecb/wrapt-2.4.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8342f332dada211f64b74609e332d727b13315e9a83177f7918bf68c59f815f2", size = 217037, upload-time = "2026-09-10T23:10:14.028Z" }, + { url = "https://files.pythonhosted.org/packages/3a/55/ec72991153a2ae8b40238bc44cec7c3ddf7706ef6e2d314b0c6f5c7febce/wrapt-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2f86e328c482bc5383b4eda5094be0bed3617fc3076aa9225ff1a9eb6372de9b", size = 234659, upload-time = "2026-09-10T23:10:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/7cfa1e070dcda76ea56a3e252341cba1cd1e9412baf23cd7adfebf1114e2/wrapt-2.4.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4dc92697444ee380544fbb43c86612d8486529aadf917c22b5524141d4af074c", size = 214590, upload-time = "2026-09-10T23:10:16.744Z" }, + { url = "https://files.pythonhosted.org/packages/79/10/248841cb30107f6f32c53a02662e1c3e0c7c06bea0b8ebfaaee94885dcee/wrapt-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edd03758a7578526642508b8833d43496fdfba0f64e0025dfca153a7c1777735", size = 225103, upload-time = "2026-09-10T23:10:18.346Z" }, + { url = "https://files.pythonhosted.org/packages/90/02/5b2bf7b35b008a39939a2908e85dba3b867596e535fc9f12ddf3ba1fcaf6/wrapt-2.4.1-cp312-cp312-win32.whl", hash = "sha256:5d83e412665aeb1e854eefbf1564d0d67872d9994b502a0bce96e6ff7f4970b7", size = 93441, upload-time = "2026-09-10T23:10:19.81Z" }, + { url = "https://files.pythonhosted.org/packages/d4/2a/47be56772bfb07ef242d6e924049688e38384af2b2fc99b0f31180988448/wrapt-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:b4e7efdd476ac631a0181551fd9aace844765ea3ce2b5133b194fae4421e8ad0", size = 98808, upload-time = "2026-09-10T23:10:21.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ec/40e4c9735626afd1dc0eeb310310278361f192800503cda0f5b3d8d24db4/wrapt-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:38819761401baa2d11916d7265b82f23265f8fe5a31c431dd7c24a8863c65f88", size = 95240, upload-time = "2026-09-10T23:10:22.39Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1a/9b5aa3c2391aa6d00fffa085c2219e8fc91cd4d2b9b080d2b4e4b6b93f42/wrapt-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55f36bb1461f93beaf18d818e568b5de343dd8fade7456773d201a38ee723bd3", size = 98597, upload-time = "2026-09-10T23:10:23.723Z" }, + { url = "https://files.pythonhosted.org/packages/fe/07/dc98150c2f9ee5b5fcbd841765e178ea6cc6c43733ab8d1f5181a4fb9f3d/wrapt-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:53e15cd74bd6b84d7fa90b93dda7334d85f4641fae97632de8aa61d268dfd145", size = 98842, upload-time = "2026-09-10T23:10:25.109Z" }, + { url = "https://files.pythonhosted.org/packages/08/c2/0e772a570e8d75c1b3ec45930a3023492fa68223f8f5e3485af4844c10c3/wrapt-2.4.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:be6cdd7121adc89a6f52e3c2f4e26a2d4dcdc1c0fde47e3156234db8939e4cdc", size = 234642, upload-time = "2026-09-10T23:10:26.644Z" }, + { url = "https://files.pythonhosted.org/packages/76/25/4ce4d02dd95ff9ed972a2fc396d04fec636daa5a4ac18cc73a3dc20aa6c3/wrapt-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03b5598edd435373278731d0d53449ce7a9626bc48d5548e4a71124ee3e526a1", size = 235484, upload-time = "2026-09-10T23:10:28.098Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/436ef1df620ef8edd9ef057b4d55a0cd704435ff66852a0ef26cbaa02a58/wrapt-2.4.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fdc997819a6df4c65bdb0a1c5601c98b479ee34cc2f94ffa98a767034ad6366d", size = 214371, upload-time = "2026-09-10T23:10:29.841Z" }, + { url = "https://files.pythonhosted.org/packages/6a/2c/cc5b7503843399087db3106a7cf60d60d0900f69539e96148b9fff116291/wrapt-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3bfc6907ebed560d2d3f677c3b17bf6199679163b6c6e475035c9dca497d1697", size = 232347, upload-time = "2026-09-10T23:10:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/86/fb/17668b1ca572c44b458c09d76064d35f5f57d5ac7629248871604bef816c/wrapt-2.4.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a0c3b217332cf0c4df085fec41c126bf7507d6c1ca0efb3ba8d2b3fd234d4e73", size = 212792, upload-time = "2026-09-10T23:10:32.968Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2b/0c5de10d7259e07c478c44bf2fbe179c6878727d09edf0632b97884801db/wrapt-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a23b89621cfeb3329b1a290596bf402e61d7d5a647a65ca8ea735e48771b71d4", size = 223585, upload-time = "2026-09-10T23:10:34.43Z" }, + { url = "https://files.pythonhosted.org/packages/aa/36/013dce1c687f8f1c87b1e78f403d04c80ae9b2ae77de4ddba16069a3e7d1/wrapt-2.4.1-cp313-cp313-win32.whl", hash = "sha256:bc67d4872af5ab2dc1b88904097b92ac00e7658fdf010dee36807807fe882ac4", size = 93425, upload-time = "2026-09-10T23:10:35.901Z" }, + { url = "https://files.pythonhosted.org/packages/b7/1d/d53bcf5910209a45191c8bc173ff0e00830416547d8038772dc444932ee0/wrapt-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:1fe758b9c2d49138231ec3efabd106fec665f86fec50b3d02d7edd75f08a69ca", size = 98569, upload-time = "2026-09-10T23:10:37.316Z" }, + { url = "https://files.pythonhosted.org/packages/e8/1f/759d0c522918f9dfb620136622d8e1ce619570adda0de8fa2cfbb55cbb86/wrapt-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c974c36e8205255a3947dad9c2fe000431b57dd522946e56c192174a7f92a0f", size = 95272, upload-time = "2026-09-10T23:10:38.657Z" }, + { url = "https://files.pythonhosted.org/packages/08/05/ed5aa8991c5e9969e2ca17f0e44eb324a9757f44fa982bfc13844cfe9e1d/wrapt-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c6c35541cc729964c65c2b9b1f9cf317811039abc2da13b4557f20f21cdc292b", size = 98876, upload-time = "2026-09-10T23:10:40.078Z" }, + { url = "https://files.pythonhosted.org/packages/f1/5f/f8bf07c3b9a2ad01d28d5ca419e28819bb16937d129167d97c446c05875d/wrapt-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6b9df84f0a96763159cccb8e3b0ed83cc950c7c8bf82d6e45428372a805b3224", size = 99054, upload-time = "2026-09-10T23:10:41.484Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7a/518e5f3ebd18472652e5332bf37728cd6634ff81a0aa568969d05cc66099/wrapt-2.4.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78af62413095d0a57077606654ce85e273deda8e2bfa28fdb04257b962d94ef2", size = 237430, upload-time = "2026-09-10T23:10:43.007Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2b/503835d183c1ca99268e0b5a98af8f487d528ebabb124f4003cd96e73b1f/wrapt-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d60702ebc914d0bb01aa48f5c1785ceaaaa505e0a841b444a69d6ceb5de4097e", size = 237988, upload-time = "2026-09-10T23:10:44.466Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9d/a11a3e1eb18ae1d9adef1c39f8e8e36fd5b86db33938bebf8e6bc5fd9b06/wrapt-2.4.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8c4b44e4be7680fc496816e824b6ed134d781c4da296e44b75399196e5c248f1", size = 218554, upload-time = "2026-09-10T23:10:45.969Z" }, + { url = "https://files.pythonhosted.org/packages/e1/61/61a6f983737ba81008561ab634373bba25b986761d3a0f0aded8352ae3e8/wrapt-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fb5b3f94258bcf71db902795f4a71151c7a74d9fe21fffa00752d2bd286866f8", size = 235476, upload-time = "2026-09-10T23:10:47.528Z" }, + { url = "https://files.pythonhosted.org/packages/42/68/acb7cba46e0f662eaae421487807d2fed28ba52df9a00a3f04db16675e38/wrapt-2.4.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f1556a96b20d5bfdc9cb5dcd0a3ec65cbbac8a4eebcd3cd34efdc91c9519f660", size = 216454, upload-time = "2026-09-10T23:10:49.126Z" }, + { url = "https://files.pythonhosted.org/packages/8b/52/3a4dab8d4803df595de82f19775535d28c036746340c1b498fc8ecba39aa/wrapt-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:094a606d0bf1c4b847b3a743d22fc69cb164015b8c70cf6f20a534d30889ed47", size = 225346, upload-time = "2026-09-10T23:10:51.052Z" }, + { url = "https://files.pythonhosted.org/packages/5a/48/d72d051757fb7955021d8174014679f951fd5067187d2faed95520e1f8d6/wrapt-2.4.1-cp314-cp314-win32.whl", hash = "sha256:b0ee076be124406a7f97ca663c4a3ba32b6bcdd9102ca82497feaca79e9cb33c", size = 93870, upload-time = "2026-09-10T23:10:52.495Z" }, + { url = "https://files.pythonhosted.org/packages/a1/00/1a1bf4d8b60b9e7ac009969595438549ae6e33b2e3e174b78d26a833aad3/wrapt-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:d2d6f9abaa52de05090a2b4a4c1d0e858a268c4de69eec277506af9fbcccff91", size = 98910, upload-time = "2026-09-10T23:10:53.878Z" }, + { url = "https://files.pythonhosted.org/packages/1d/59/b3169c3bcdcff70a6d02ed2b6366097b083d31391d616c407efe9a943a82/wrapt-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:3152b2e94d733a9bd70dbd1c95f148266f079a70e9ffca2a9c5dc421ade1b4e6", size = 96013, upload-time = "2026-09-10T23:10:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/b1/91/d29a063c2ca6e779305cd444dd5acaac60833d73b2d94c7b1d0820c27e3e/wrapt-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:37ff91b390400463ecd4d080ea823314510b6843ac53e6e35cc09bba1805d466", size = 102069, upload-time = "2026-09-10T23:10:57.179Z" }, + { url = "https://files.pythonhosted.org/packages/72/26/7435de516099b528e2232770b459d6706002736f41d8f092bbb11ea01575/wrapt-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e0a518cac3e789443af54fc77f23e66f17d80192c281b160b42058f11fabccc5", size = 102647, upload-time = "2026-09-10T23:10:58.852Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a7/cca7fe0f26aa11cbbf09b2e6f4f774db167feeca43ad22fa1772afdccbad/wrapt-2.4.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ecdeb8a1ec13397421e6f925412186bd87ab86d63517e6825b6d7bccf781f26", size = 275614, upload-time = "2026-09-10T23:11:00.464Z" }, + { url = "https://files.pythonhosted.org/packages/77/22/f3b5f4428ae679b1e2c0d4833519045d8983a9df0ad24df96b195d44dd1b/wrapt-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c15c4ede3fde08723cabab0a892d4b75d35b5a6f0a51c84e6542ac0bdc0507aa", size = 287064, upload-time = "2026-09-10T23:11:02.111Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b8/24274ec01b6ab6d0672f4312bfa29b5f1ffd9d56f16c8da67d5683883956/wrapt-2.4.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e32c5951c36fed88b6c603dc0bab209a62dc3217bd8762413634725fe28f2f3c", size = 255440, upload-time = "2026-09-10T23:11:03.746Z" }, + { url = "https://files.pythonhosted.org/packages/8b/df/c63a36f0f03d70c7b92f60ec1ad3757088167914e1a262f5dcc7df233d51/wrapt-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:898513db90d55a4ed3009312d41c12932b8163edbae3535280046d47aaff774f", size = 281681, upload-time = "2026-09-10T23:11:05.41Z" }, + { url = "https://files.pythonhosted.org/packages/bc/c7/4930b6a369d9b3da59f5ada4ae9e659e2d4a68fe72c5d1ce15e5bead2d24/wrapt-2.4.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:42d01574bd4bcafc3476e77a95c6c0dd6167101991401325fc5591e2db21a91d", size = 252409, upload-time = "2026-09-10T23:11:06.867Z" }, + { url = "https://files.pythonhosted.org/packages/68/d7/f6bc5703061598c4a2ad4f7a2efce696782be6e9d5afed714a59f5902ead/wrapt-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:68a403adbeb4dd2654d6d108e43e69f0f90e6d34cb7588e0e6589109f6985e67", size = 270559, upload-time = "2026-09-10T23:11:08.385Z" }, + { url = "https://files.pythonhosted.org/packages/68/a3/952337a153e634f530079334c47dcc935b20f8ea22f364729fc2a8cf1821/wrapt-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:7cb3035b332bc9d21478600ba82c7c71e2d074ed9b4e76364297f54598792227", size = 96241, upload-time = "2026-09-10T23:11:09.862Z" }, + { url = "https://files.pythonhosted.org/packages/09/28/48f0651547a125dd84e312b86268d4150c565a29f81ff9ca6fd81eba4be4/wrapt-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:707d2bef68deddd0fc74a81103d91b286a69ac5a9ff0f0a6dad66f8787e86697", size = 102655, upload-time = "2026-09-10T23:11:11.388Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/977441bed5e5afbffe7a789067ac2f5fd2e9f848884122a23db9ccae6a70/wrapt-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:28cb1c2713b4377bf03ddcb3e76d8216e4bb334199066c6122be7eed2f72de8c", size = 98495, upload-time = "2026-09-10T23:11:12.952Z" }, + { url = "https://files.pythonhosted.org/packages/7c/1c/03bcbc3dbf6ac11231f0434f2494aa7b80657b130be1a1a639192a3e44c9/wrapt-2.4.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:96a5023fa63ca2f095f7776c8bfaa1694547577f762646c375abafd8f8ae649b", size = 98878, upload-time = "2026-09-10T23:11:14.506Z" }, + { url = "https://files.pythonhosted.org/packages/87/26/5ad9bd421b8cdce0b2227a61f09586423765da3c284f8bbf0f69fc149fdf/wrapt-2.4.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b21924949dedc3ac63725e09b9b0a130e771975f03432789344ed3422c46008", size = 99090, upload-time = "2026-09-10T23:11:16.101Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c7/fa768261966d587650004ac37bef541540014b1fe23fbf4919c497ebc98c/wrapt-2.4.1-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2f725af353bb3319c528ee69dbff838599426974288b281a3258d5397b18cb63", size = 237803, upload-time = "2026-09-10T23:11:17.738Z" }, + { url = "https://files.pythonhosted.org/packages/0f/37/9c1c876bd88fef8d065b3e8bf125cad9291d70ed3695cedb41b36b2453ec/wrapt-2.4.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20327e162ef7953fae56b31a43ca457f94ed1fc7a206d01e3d5c8412d1b91572", size = 238338, upload-time = "2026-09-10T23:11:19.742Z" }, + { url = "https://files.pythonhosted.org/packages/91/5b/47b2f2f08b90d9d3b4e1790ccae0d4e4de40e73f6614c9a52465a04f02a5/wrapt-2.4.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2cedb743dbdfb9b6d4f11acd8cb6329460264429777e81ea2e86b1b72ea503af", size = 220668, upload-time = "2026-09-10T23:11:21.422Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/da7844c7a3f7c01f3496e690e15ce9d01c0949278c64d4287ec472c90f72/wrapt-2.4.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:5a54744b1193505f19016194979b773ee3754a199e9ad90db18f2e9a18fffa16", size = 235812, upload-time = "2026-09-10T23:11:23.211Z" }, + { url = "https://files.pythonhosted.org/packages/69/af/ac4aa9f795721a54a21bac329201f7df1b199b71ef064187a266d4d29836/wrapt-2.4.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:b0d38d9cc23e9781e6584f303e5c5a9f0d45b85de92ad45678c4dde8d49d9535", size = 218195, upload-time = "2026-09-10T23:11:24.983Z" }, + { url = "https://files.pythonhosted.org/packages/78/aa/cff7670ec1cfa75b951171cfb41d45c7bdb94e928620a1599ec2b00cd2dd/wrapt-2.4.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:a524ca32f0bcdde2b728d9f81f9527d4dd24b13f15d180f05d08cdc01d1cebe0", size = 225713, upload-time = "2026-09-10T23:11:26.576Z" }, + { url = "https://files.pythonhosted.org/packages/77/c6/64f138aef50b5ea1dfacca2a023b32ba3e41ef087bbd92e8997a95f4a0b8/wrapt-2.4.1-cp315-cp315-win32.whl", hash = "sha256:47c267617551e906de72f6e7265aa3bce84c63d44513d2d2e735e943422aa0a2", size = 93880, upload-time = "2026-09-10T23:11:28.253Z" }, + { url = "https://files.pythonhosted.org/packages/8d/aa/afe7484a0f7232e87f2ebb65286c1025d43cbe1de4e4051b127b024a92f7/wrapt-2.4.1-cp315-cp315-win_amd64.whl", hash = "sha256:ee437bd7fd050823ef731aad20e968ca8fe670b95e1841f5d201bfdbad4a4e96", size = 98919, upload-time = "2026-09-10T23:11:29.832Z" }, + { url = "https://files.pythonhosted.org/packages/83/21/6865f976c6a1082ca61e2792bc6a2d6a0ed03cd750a46f837753127191f8/wrapt-2.4.1-cp315-cp315-win_arm64.whl", hash = "sha256:ebf3b703752b53366fd02b7fbd8c447428e0349c9af3fc17c7a05076dbdd749a", size = 96021, upload-time = "2026-09-10T23:11:31.448Z" }, + { url = "https://files.pythonhosted.org/packages/8d/48/baf784cc9f1fe554f96daf15af06c7c466d76469d5540e357cf564755606/wrapt-2.4.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:0f5990f5db090f069fcd577cc61cc8d463db73cde132abba9033b0a264fc06d0", size = 102028, upload-time = "2026-09-10T23:11:33.102Z" }, + { url = "https://files.pythonhosted.org/packages/82/35/fb809c95bf5512196aeda4c640aabf4e92f607340b8b2556555690a52b7a/wrapt-2.4.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:ef19a2590b195ac294deff8ee350a027a479afdd2ef2170ce3900af92406e110", size = 102640, upload-time = "2026-09-10T23:11:35.145Z" }, + { url = "https://files.pythonhosted.org/packages/a1/3f/bd462010ccbbe298479f320bf2bb02996706003e9c20a15790b65a186f8b/wrapt-2.4.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:432f402f9b6014403cacf9fd18a6bf089c77964330eae951a155131ab0414f5d", size = 275999, upload-time = "2026-09-10T23:11:36.863Z" }, + { url = "https://files.pythonhosted.org/packages/56/1e/bd043b2bad2943336e090583ff6cea2f3e8776bde02f073ccf760b16eee1/wrapt-2.4.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb7c0f8bdd21e954bad89d554cfb7431c2f8179842853f199841ab90cbebe914", size = 287576, upload-time = "2026-09-10T23:11:38.79Z" }, + { url = "https://files.pythonhosted.org/packages/1c/d9/9937bbd61ca4e7f58a7e8a38f5ff3562334f741de51b87f0e031df441254/wrapt-2.4.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8e82a1669d63b79a2b53041bbb6ea096b5763cab3ca698ed7ac244b3acb7cd51", size = 256763, upload-time = "2026-09-10T23:11:40.668Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a8/0f78cc7e7fc6313cdba32df06f4b29a45a4a2aed3499040d6c1628d3f339/wrapt-2.4.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b19e71c914c435d2c5652caea386c9bf2807979e957183f5bb06d5ed5fa8b55e", size = 282244, upload-time = "2026-09-10T23:11:42.483Z" }, + { url = "https://files.pythonhosted.org/packages/be/0d/30317d738b9898a7fbedc193657f151a468b0fa7235f4abd58a646a99cd8/wrapt-2.4.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:a1e4870d3368c6c918f38e308d5dcea970ea5b908ce889c21412f3a517ebf0c3", size = 254153, upload-time = "2026-09-10T23:11:44.357Z" }, + { url = "https://files.pythonhosted.org/packages/12/53/fe1f251a89f471ca7c5e17f2edbd8efd48fea6f1c08430dcf60986ea4183/wrapt-2.4.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:2286e8e4a937966706463d1bcea30dfefee529e6e73e097a18e9e066a07ae6c2", size = 271234, upload-time = "2026-09-10T23:11:46.132Z" }, + { url = "https://files.pythonhosted.org/packages/bd/7c/d63758cd7fa0d18c415a87312a07cdd9f0102eb824aaab5c7450b3ec08e5/wrapt-2.4.1-cp315-cp315t-win32.whl", hash = "sha256:80afa3b7010e82899044a2a189c468a0cd8c89980398a59ba3b96b451a6dc5e9", size = 96236, upload-time = "2026-09-10T23:11:47.942Z" }, + { url = "https://files.pythonhosted.org/packages/f7/2e/1785e6d2696db08b07c053b1f4ccb2b5e8f9d12e9bf2c7497693be6b4504/wrapt-2.4.1-cp315-cp315t-win_amd64.whl", hash = "sha256:3593b43fabab6b59fe77e38f7e40974aae120c30cea3fd1ceaea6621ee96be00", size = 102662, upload-time = "2026-09-10T23:11:49.831Z" }, + { url = "https://files.pythonhosted.org/packages/2f/fd/3f4ac5c4754948355e050f952ae7a64cdca94891e49fe9927c3f90a73334/wrapt-2.4.1-cp315-cp315t-win_arm64.whl", hash = "sha256:ac1939ccf3e1c33f463706fbf52db5075f5eefb6042bcc1f9cf48c2c20ef478c", size = 98500, upload-time = "2026-09-10T23:11:51.501Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a2/edcfc8d9a30375791b775715f8501364588f65b494ec4f6930568a19e765/wrapt-2.4.1-py3-none-any.whl", hash = "sha256:1e84ec5d89a0a07a0ef6bcd343f5c8ecdc95601d71de3058cdc63274e86c193c", size = 75317, upload-time = "2026-09-10T23:12:14.82Z" }, +] + +[[package]] +name = "yarl" +version = "1.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/16/e8be8e2fb175bbf41a0680381a319f1199fae256588241a2ac8677eafb49/yarl-1.25.1.tar.gz", hash = "sha256:03dd38de09bc213e9a8b29761eec33ee1d5318dac0e49d8af36e4d27830e23a7", size = 246245, upload-time = "2026-09-15T19:35:02.264Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/92/55fa9ee84cb8ec9930a910b5753936a926f918d8cc8965bdac571479c095/yarl-1.25.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:142c06c4d6a35ee3ec5da08499805e879cb3ca7c1fbfbecb0140fe72403818d6", size = 144695, upload-time = "2026-09-15T19:29:53.114Z" }, + { url = "https://files.pythonhosted.org/packages/16/b4/9edc8e605b16b2eb5bd0c2ccc73e2b15aab583862460767fb43f91f11839/yarl-1.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:24ce942011a61953e7d313438038f4d32ff21387b775f58a957f7a07dd55ef95", size = 104332, upload-time = "2026-09-15T19:29:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/ae/de/4204e6646e278b1cd4a9cf73ebe8b7cfcf16693f1249b324eb273d67602a/yarl-1.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e23c82b63cd7652fc24d33ed6cc17099d607aa3b4fc4ddc75e95062f3d82df4", size = 104449, upload-time = "2026-09-15T19:29:57.239Z" }, + { url = "https://files.pythonhosted.org/packages/1d/fc/21d74789198ad68a23a209ab0d73fe5a52d39ebbd9945041e72827acdd77/yarl-1.25.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ee202350cf57abf0e9502a41601841019c25d3db7ff52d980aaf31446254059", size = 116828, upload-time = "2026-09-15T19:29:59.031Z" }, + { url = "https://files.pythonhosted.org/packages/8f/31/90891ddb61848c067ebd1fb505ab5902a6e7d4707c2f3823f67ff37883d3/yarl-1.25.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5df89f769cc8ff94c3d7e7603386fba309d25ce5240132d26c15baa8d0e96c4c", size = 107150, upload-time = "2026-09-15T19:30:00.868Z" }, + { url = "https://files.pythonhosted.org/packages/a1/15/f3d18743d3688b5f01aa83034b6c00665692ab20e1d0c89a29000d4979ea/yarl-1.25.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83e9f4a25085bd4b7214701a0794ff1f50fc633ffb8bdfebf07abdd81c2db126", size = 124704, upload-time = "2026-09-15T19:30:03.128Z" }, + { url = "https://files.pythonhosted.org/packages/99/37/718789d8004775d6a2db86b2afc72b1d156e0590b88a1b8634ddd4bdb8ee/yarl-1.25.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e636b64d24fd9c38053c5e389a1174c66361fa49dcfd220f4dd35b4abde7cb89", size = 129246, upload-time = "2026-09-15T19:30:05.014Z" }, + { url = "https://files.pythonhosted.org/packages/28/b9/7818ac6dec7fbcd16be2d19495807c01e2c38834e57d41c63356a75e786b/yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e5637ca8d0bd7fb72648a6c7934af4baaccb697657f7438c9d264fc2abb8b0b1", size = 118071, upload-time = "2026-09-15T19:30:06.977Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e3/8d098cafb30a64df7283b5a5dd0d32d22a71b18a1b9ec071d0776ffe5c2a/yarl-1.25.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:683e362b8ba453080f7489c66f4ea794e751c35b72e7eab3575ef784c2fbc7fb", size = 116180, upload-time = "2026-09-15T19:30:08.836Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/fbe9432478d87e00eef80242e15179639df162cb3d5d8d978fc56dcb6c51/yarl-1.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:df23df54b5114a17c2d0ef192433e2e5a9f0c5178c32375e90b7cfc965f349d0", size = 116578, upload-time = "2026-09-15T19:30:10.782Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8e/b3dfd03732236b86b0bcf06a72a37de412ff6f82e719a947c3c651d4dece/yarl-1.25.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f53dcd26694f148f738edc052b5a69234833e739f10f4c3287bdfd8ec0f7b326", size = 108886, upload-time = "2026-09-15T19:30:12.951Z" }, + { url = "https://files.pythonhosted.org/packages/83/e1/94dacb650d4963d5f844bc3df7ba70288cb7b68ed5a4d070da71acb19b14/yarl-1.25.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b8075fe90bc08e40b8b8a1874fab42ee4c7b56af05c5886e9cc841397f916908", size = 124098, upload-time = "2026-09-15T19:30:14.963Z" }, + { url = "https://files.pythonhosted.org/packages/d6/3f/c93f76a218258c7bfc60dd27fd56e9102bdbc625517f4a6ee673295fe4e4/yarl-1.25.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a8c2b841478068440d8b733005d13a5ef535b9928cbc05f17182d410f32ba449", size = 115768, upload-time = "2026-09-15T19:30:16.736Z" }, + { url = "https://files.pythonhosted.org/packages/a8/79/4d93f13c3b05cda3c962805dec28cbc255c50239b3457808abc5633a00c2/yarl-1.25.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ca32926d7d77bcc8838425c4c95e040a3ace1cb7dfdae599013458dcda2607ca", size = 122461, upload-time = "2026-09-15T19:30:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8e/e2ba83b3a9bfc1d3b882ad35fa8abc04a7af4d9356f2e329a23d0c8d889d/yarl-1.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:192a866877a49993949ef1975864ad8728bea28ee810f6abe1a0729c2b500426", size = 118295, upload-time = "2026-09-15T19:30:21.07Z" }, + { url = "https://files.pythonhosted.org/packages/42/67/cb5ea1baa0c0ac60bda44ce59f601133154af2a2e67722d3517f8793855d/yarl-1.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3f4d48a6112712973e676bd792121fee470e432d749177162d9949d5c9460a1b", size = 102929, upload-time = "2026-09-15T19:30:23.122Z" }, + { url = "https://files.pythonhosted.org/packages/2f/85/9a1e98de10fc0e738d517efd2a9984c3e7db6cf35fa72b2b373988a9e9b7/yarl-1.25.1-cp310-cp310-win_arm64.whl", hash = "sha256:48796ea00a303961507dc6c8437c4b325a6fc3f95f7c36c71b91ea9a8150963c", size = 98854, upload-time = "2026-09-15T19:30:25.102Z" }, + { url = "https://files.pythonhosted.org/packages/83/b3/2cea721d495ca57f8f414aa4867ee263f486b274e07463158cf52f02ba7f/yarl-1.25.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d693bf4bf534e9ba3ae2780cfd577f5135629f7b5ac653490859d0b77864865", size = 143794, upload-time = "2026-09-15T19:30:26.946Z" }, + { url = "https://files.pythonhosted.org/packages/55/e6/cd145cff8e5cf60b8b3c41fbecfa2a45028a8dec3fbc52bec03595ff3d3b/yarl-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ab2054c5531af2a9ba7b69b8ec91e4f884420e83a8c5e579b013084cb57e5e5d", size = 103993, upload-time = "2026-09-15T19:30:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7c/fbb40fe2d53747c40aa36a9e2bd2178a202f942bda0b670f3306d4aefbce/yarl-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:564fdc7085d2245ab84f88882fdb1d6ac0723124bff6ded35bfb1c00f812630d", size = 104010, upload-time = "2026-09-15T19:30:31.069Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0b/5a516f70641092283f57cf3670bdb75e7327bcc0dcb5038697e4dfbfd569/yarl-1.25.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acae6b45d1ace09b6ba3876da43b88366ef368f73b988c7f57e14231753d4420", size = 116444, upload-time = "2026-09-15T19:30:32.894Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/d1a627f827b0a404ae0f5647cab0534959081cde13b0756a783469fb3b5e/yarl-1.25.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1fb2a01ba8cd9c5d2c5dc1ec35e0fc951d04b4f037541d4ac090c993ce58b3d7", size = 107565, upload-time = "2026-09-15T19:30:35.188Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cf/c8e0aaec886840a6c4480fe44eaec7cd4319f79a563b79321473be79c56f/yarl-1.25.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e92b6bcc741b86d67606c40d3cb9c7cc8e6c737f81e31f4a94efc204456c92e3", size = 125006, upload-time = "2026-09-15T19:30:37.1Z" }, + { url = "https://files.pythonhosted.org/packages/50/26/0cce366d54a93cdc8342965dc7663385e4db161625cc1e8b18e786753d24/yarl-1.25.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:72c34ac7ad4314c19362d5ce27626dcc8429bd30bbf8c179f4234078851f9492", size = 128717, upload-time = "2026-09-15T19:30:38.904Z" }, + { url = "https://files.pythonhosted.org/packages/95/0c/a71501bbc1a674ff72c4d6c2b75f4d9a5af819f5244c3a7558080a8802c5/yarl-1.25.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5add7b4ca7afeea91d52e4d4e4db3b1fe9885b71f07054560d8c4296b7441a2", size = 117728, upload-time = "2026-09-15T19:30:40.809Z" }, + { url = "https://files.pythonhosted.org/packages/e7/6f/c3267ca01defeed9ed9c4ff9b17bd54915432c405945233265b707475d1c/yarl-1.25.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:def538065f9e4d4cf1ae164bd59aba00dfa84f03923e0de4c3788f252d6bcd17", size = 116224, upload-time = "2026-09-15T19:30:42.818Z" }, + { url = "https://files.pythonhosted.org/packages/8f/69/fad57ee52d648431718ee0f1f68966a99c1352c3924688ffbdcc9d3fe51a/yarl-1.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a191bfdb30a79b98e5d175d75285f9fcb78bf0e46ba5efda042e1c72071a0de", size = 116299, upload-time = "2026-09-15T19:30:44.966Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d4/6a8c1e29f33338687ca278cba0a8fbf6525a322c2c02a9a500ccbe041152/yarl-1.25.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:71f42c5b9a948c113bbdebfa544598321431d064ff959d32e99b1feb61d68345", size = 108625, upload-time = "2026-09-15T19:30:46.874Z" }, + { url = "https://files.pythonhosted.org/packages/e8/d2/3a35ae791c9cb6522c106923ff25c3230d999091e5e65511bca23bbd9914/yarl-1.25.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:72849d892954be4d09e569b8b831ac39ce58417fedc767d4308a0fe542018a40", size = 124515, upload-time = "2026-09-15T19:30:49.082Z" }, + { url = "https://files.pythonhosted.org/packages/be/fd/2b022109a6b4af0f7dc371cf7500af380b0d4f034010243e1b0ce218dc93/yarl-1.25.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:efb01a106f971cb3752856bca2318bbdf7f01bd8823779c461586cbe5ffd5258", size = 115711, upload-time = "2026-09-15T19:30:51.208Z" }, + { url = "https://files.pythonhosted.org/packages/18/59/f7586271136c3ddb0126bbfe661844699369b76b555fe38c4efe86870b2e/yarl-1.25.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a1daf47cd95a7c3a63456336bc5aaa8c86dd3a47d07ed3d0e76132ae4666a5a1", size = 122751, upload-time = "2026-09-15T19:30:53.535Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0b/07f7a2d881f7e16c385b47fc1753382600847cad305dcc7fa0c25828acf8/yarl-1.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9489e6abf47ba37f332075a91444c7cfedb03e6ce99fbb2f116bfe1ce810da3b", size = 117983, upload-time = "2026-09-15T19:30:55.277Z" }, + { url = "https://files.pythonhosted.org/packages/aa/9d/8cdceec66a9b940700cb45931741403f045b162afd79bcb93c41cadd0972/yarl-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7306dee25b8a0e737363f347362b875094b4dc4e367311470656ae420fdbf8e", size = 102894, upload-time = "2026-09-15T19:30:57.606Z" }, + { url = "https://files.pythonhosted.org/packages/17/f1/7ec357db1d3ad2863542d71e8fe64a126bbec17b134cd7d30951196809a5/yarl-1.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:abb1384477f5901d436b5d2e5465954de46ea6098f59163d243660b5c4461d35", size = 98609, upload-time = "2026-09-15T19:30:59.705Z" }, + { url = "https://files.pythonhosted.org/packages/75/b3/cd32ac66ae622b854c2df0ac52106dda220d361b65a64fde7d5b3684aa3f/yarl-1.25.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d7aa6debf92a1dd14cb5280b083a764169a13cfb23a452111160274ed989f4", size = 144798, upload-time = "2026-09-15T19:31:01.821Z" }, + { url = "https://files.pythonhosted.org/packages/61/fb/a2c52a8007c2051ba74662afb112ecf3d00346af4c25e33df9d80fd14fb8/yarl-1.25.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83d4a37e4b95da4d8bda930d6d35b75b4cdadbacbb4980cae290ea3100b5d51d", size = 104583, upload-time = "2026-09-15T19:31:04.05Z" }, + { url = "https://files.pythonhosted.org/packages/be/dd/ee38aec8e09fdf957e50d4085453fbe202f56c6c3b4cf07b81cdb4f09ee9/yarl-1.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e029648f9c951db30e98a7d7ec90835db88ec4b32820efe2a9bdc2287e032eb6", size = 104325, upload-time = "2026-09-15T19:31:06.338Z" }, + { url = "https://files.pythonhosted.org/packages/1e/b3/058dbfb1857b484c9cf9cc135659f50b85ce66e03c99e44dc2f7b6161f55/yarl-1.25.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d781294bb815ecb5ea57ff6bbf8038e0a31a95fdf3e1788f66e0dc100d64b58", size = 115358, upload-time = "2026-09-15T19:31:08.593Z" }, + { url = "https://files.pythonhosted.org/packages/db/39/29693446cf0cf6b15a0e2f75a5d40f93c56819b05b0622196f45e95b5cc0/yarl-1.25.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e12c538e00e7c1b286a07061046b90e8124e6a9793efae2c70db6a4aad07faad", size = 107658, upload-time = "2026-09-15T19:31:10.802Z" }, + { url = "https://files.pythonhosted.org/packages/86/b3/3c4dd7e1af43b931fba95e0a722737f2ea94a6d199c802585282831d7abd/yarl-1.25.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e4de3ac4adbad3d0bc7c6f4360a7dbff5de2f15e3b723be3198074e17fd9c40", size = 122660, upload-time = "2026-09-15T19:31:12.84Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b5/1b60dbc3cfc9c5712b15148c206748f2bc93953ffdbe25ea75b63dfc89c9/yarl-1.25.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:419f392a1da624877975709e3864dfe833af6cc7671b39318086d456e288380c", size = 126506, upload-time = "2026-09-15T19:31:15.088Z" }, + { url = "https://files.pythonhosted.org/packages/bc/7b/ca212cbe170ac8b96e45317ecbcf9c3c3ecf0cdec98d5b088a9c4088929b/yarl-1.25.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6f117789d22dce188e5754e8bc65b7e6ebf8cb73963b9fa761f672a5883769d", size = 117050, upload-time = "2026-09-15T19:31:17.241Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c3/72b4938cdbe619ad71ac156182faef4908846b84dc3ca4dbb4c4e6f84014/yarl-1.25.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80e47012e730da131c9f059c80936783f9659aae22dc31c03c0595590d11ed54", size = 114174, upload-time = "2026-09-15T19:31:19.294Z" }, + { url = "https://files.pythonhosted.org/packages/e8/43/268717870f9ba0cc9701a95181587f6dc8c5f387aab4aeecc83158f38a79/yarl-1.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e80f557716fd765439577131e526b8942ffc2c07bdbc5e39fa62f660ba1e963f", size = 114944, upload-time = "2026-09-15T19:31:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/da/84/baa5bf504d51fe062c4bcaf62936da97fffb43285978d0b39984824231fd/yarl-1.25.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f61964f235a43738bfac50da46fc4254943a7eea3051aeb0b6fc7c992c29fadc", size = 108263, upload-time = "2026-09-15T19:31:23.388Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/779a2ed9e0152a601a27039bed9aead3f0b79797a67e2c44bfa444622dd8/yarl-1.25.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e546fe1d4a93ebc2910f0d768baff19faa09843ab3f2036a67ed6e69fae4419d", size = 122184, upload-time = "2026-09-15T19:31:25.343Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1f/118e9e5b8f07694d63fd3222e801d7782270003f1a222aa798df3f8d5933/yarl-1.25.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cce0727fd5ac04d372fa9bbfde9febc2bcf209aadfcf0468e45dec72719895d1", size = 114001, upload-time = "2026-09-15T19:31:27.465Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/a4cf1cf372313734b17996d4007f9f73596e7a178b9485802e5494ecf484/yarl-1.25.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af4ea5b37403ef4e30f3927eaed540db942bde01d8d3ff083527c0704d1c9c68", size = 120565, upload-time = "2026-09-15T19:31:29.47Z" }, + { url = "https://files.pythonhosted.org/packages/05/79/ad94f93ca731bc9e44d321833ab96b82a4f9f5f63cf773f81a4aeea5ecc1/yarl-1.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:68782fdb4027b8d1eee25ec35e9a6db05e863b899eb0310b3a33b6c3fef55707", size = 117060, upload-time = "2026-09-15T19:31:31.367Z" }, + { url = "https://files.pythonhosted.org/packages/bb/cc/51a7b4abf4ac593b8e7eb3794b28e5a35ae26eed8bc04787628d215af82f/yarl-1.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:7d575b54cb3863ef9bc290ea4b009999d55dc237326131e4853cf33e888fee03", size = 102593, upload-time = "2026-09-15T19:31:33.329Z" }, + { url = "https://files.pythonhosted.org/packages/9d/21/0941a6b93a58b59a1ec75e5333bf06929b671309c43c0cd201c172d9c39f/yarl-1.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:bc3ac7bf569f6b64dad04dd7808c7872dae8a97df657856eac05e9b7e3614a85", size = 97697, upload-time = "2026-09-15T19:31:35.855Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ed/2f3129bbcc9a5c8ba12cc2b29d8060a3bab9c8043c456cfd4b5ca3188890/yarl-1.25.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:25868beca8b6765f8f7d0e11fe6dd7c66dd4b0793b9500286d20cc92352126a5", size = 143623, upload-time = "2026-09-15T19:31:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/f1bc3390fdca352826676b531d0712736f156919090206700421d46b2c37/yarl-1.25.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:10b2fd95332f0d716d5eee3c9fb2ce8eada19082de7fee83d32e37992fd75c26", size = 104011, upload-time = "2026-09-15T19:31:40.25Z" }, + { url = "https://files.pythonhosted.org/packages/a8/aa/50acc5c3e5da04172ae3c281c75405af4d2ca911e16120ab0563f4dffb66/yarl-1.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f12afda4eea8c8994a76d4df1875c765194f5fbe8a9d197929ea303caee29ec", size = 103677, upload-time = "2026-09-15T19:31:42.46Z" }, + { url = "https://files.pythonhosted.org/packages/30/d2/7d1e0ab9f8390e1fbcede5a6dbf70d23c96ad09b8c5567f3a514d1ddb0e2/yarl-1.25.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14b79a30a93a3ce2e8832603fd0ab780ada281b0ba5110b519a634f2d7d7d1fc", size = 115392, upload-time = "2026-09-15T19:31:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/71/e1/5ba1e3a2a22139213655e760919038e8ed7e2d4a99826d0bbddb3beb96e5/yarl-1.25.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bd6340d20ae2c7ca719b87b426e808e90743b676d05d4c26c4fb5ca71f41184", size = 107493, upload-time = "2026-09-15T19:31:46.273Z" }, + { url = "https://files.pythonhosted.org/packages/f5/53/780653d5e0f73831f467cf13548912e5eec97f21dc49fc8daf21da027df4/yarl-1.25.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:126a2533570c554719ca40a1288fdee1700b6bc82e7131aa69fa85252d92e651", size = 122537, upload-time = "2026-09-15T19:31:48.654Z" }, + { url = "https://files.pythonhosted.org/packages/03/92/d54fa70236c6036271c9c9c09fd978df5cbe3ef49ef6c46e9b833476d215/yarl-1.25.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a3faadac7d812ddac258feb57b9846b60c1b437c4f4b9ad42595c6f6fe4390df", size = 126170, upload-time = "2026-09-15T19:31:50.872Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b7/a82a49bf88340b837ef6972b508a1604ae377b9e6904b46b10cf5f1cf925/yarl-1.25.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be80550d9bfe83d9b62398a37081a90434e6df2d978ec345c3d2820de6beddab", size = 117012, upload-time = "2026-09-15T19:31:53.189Z" }, + { url = "https://files.pythonhosted.org/packages/ef/78/5d684b411e3f3602464ee9b538db48205038f8605872985f61efb809ced0/yarl-1.25.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e07595c7d6f4db270ceede356a1bd1c07a34f1c26f958d1ed0cd7b48e0d2bba3", size = 114950, upload-time = "2026-09-15T19:31:55.694Z" }, + { url = "https://files.pythonhosted.org/packages/2f/11/51d82b852c64f7fad0fc7a7ff3031517204887e874c722bbca839c0b23ac/yarl-1.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eb96ed1ae6c7d072d60840c0434aef07a2df611812810807fbc54263a6053e9a", size = 115428, upload-time = "2026-09-15T19:31:57.966Z" }, + { url = "https://files.pythonhosted.org/packages/e4/49/9d1978049bf646b9ea918313926453c6901b71c92f097467777d47d36a88/yarl-1.25.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3feb99222553a8cbedfa52c2f59dd84c3f50d5b582c728d522caf8d72769a54b", size = 108428, upload-time = "2026-09-15T19:32:00.048Z" }, + { url = "https://files.pythonhosted.org/packages/43/35/7b8f1ebb45d7ec3dda7d1909bf44f458de41ef91e2937f107733582a5166/yarl-1.25.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a2ed0ba415ccdf08f14bf544cb78346d0f76086707ffee24921a2c84dbf1305a", size = 121961, upload-time = "2026-09-15T19:32:02.436Z" }, + { url = "https://files.pythonhosted.org/packages/63/d6/d8b689ab7ca26edeb85f6ff28812aac7a25376eefc1780e303a7bfbaceff/yarl-1.25.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2b49375d22299b0a834c2bca72f39aaecc270d96fb24c30424899676f487b22a", size = 114961, upload-time = "2026-09-15T19:32:04.456Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d5/1a1798ea4dc6b7ee3260010a27907ebc697c95dae99817d817ed446d24aa/yarl-1.25.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ef74070ac553c59eb4f04258722066d6c6135b7baa03b2e9f2da65c096e96d98", size = 120036, upload-time = "2026-09-15T19:32:06.5Z" }, + { url = "https://files.pythonhosted.org/packages/91/8d/b1b35ed7903da6669b1d367cb2c09436acd4ff508029b4f39a0c0c2058fc/yarl-1.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0a66db89ea473abeac4b70523cafd94db3772380e565f9d28af7a179b7af71fa", size = 117276, upload-time = "2026-09-15T19:32:09.401Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8f/4db01cef62caff0d7a4593ed694fb8a41a27a11158cab80d290221f13e57/yarl-1.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:1f51020b2eb8a003c84925638ec63c21a750a4bddd3a22ec8eac6a742dadf1b9", size = 101945, upload-time = "2026-09-15T19:32:11.545Z" }, + { url = "https://files.pythonhosted.org/packages/c0/5e/3ce00497c5c0babb74d4130c10c3828ccd215b4819d12020c42429f991ac/yarl-1.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:b10dd0557ba422715b5206b3743192135a6022acca8baec51aa127d0a75db8fe", size = 97270, upload-time = "2026-09-15T19:32:14.127Z" }, + { url = "https://files.pythonhosted.org/packages/80/cf/54023edfab7aa773b860503db0c56e962ccab0922803ee97988c176ea090/yarl-1.25.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:a9ca696eb02e5c02a8afd872ada510eba9b7fe6e68b9572c2e9a9b1941e31e2e", size = 143975, upload-time = "2026-09-15T19:32:16.416Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a8/e6c1be0e6761d0f2d10bbf33a3e1e02b99dc83874d92945d7b461a72481e/yarl-1.25.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a5877f2255aab518ebe528289037699201d5dc5f045f2396cb30aa02db22f57f", size = 104018, upload-time = "2026-09-15T19:32:18.364Z" }, + { url = "https://files.pythonhosted.org/packages/6e/bb/dda344765ffd3430afe1a1c66c866a57fae67786537d4f14607df6505ac1/yarl-1.25.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7a5c3115595995779ee21f2567035793911c3802a43c74f3fbb0314929ec67ac", size = 104156, upload-time = "2026-09-15T19:32:20.459Z" }, + { url = "https://files.pythonhosted.org/packages/e5/5f/ed1538bcd06009fe990d6d283dd7667f639e62a81e35c6d8c6ef6c08fb3c/yarl-1.25.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77e5099b99b37f3cf79c246998ca9f7313a78054cd1809ec46bc1afad47e1c4c", size = 116025, upload-time = "2026-09-15T19:32:22.766Z" }, + { url = "https://files.pythonhosted.org/packages/a2/af/2185daf56b99830d3356ecfada46faaa49945de6626e842b7728088d4980/yarl-1.25.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6efaf45df6a849cef613a03a94c845647456662f85438c886bb67a9c027c8c2c", size = 106985, upload-time = "2026-09-15T19:32:24.749Z" }, + { url = "https://files.pythonhosted.org/packages/c1/65/bc1ae564fb4b04a30b6a8f250e787772581c57e4c3d5cf07ac3359de3103/yarl-1.25.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5f90e44653c4e0f78501ed9bb7d3fce835a8d62b7c6ed0cb16557534087e743", size = 123030, upload-time = "2026-09-15T19:32:27.084Z" }, + { url = "https://files.pythonhosted.org/packages/6a/3e/e2afcde10d74e53b3fa889960991efb3019beda2b1682a01de720a302056/yarl-1.25.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:632da579b2d879f6bad20f2cfa35ded1efe2f4f77f8abb26a6234a5b236acd2f", size = 126765, upload-time = "2026-09-15T19:32:29.332Z" }, + { url = "https://files.pythonhosted.org/packages/a2/be/415b00c0fe5a0615b062a456b26623d7ec91c2bee20faea1a14045aa0469/yarl-1.25.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30eec96e8a91bd588ce897c9543f6d5d8d34b28fbcba28a4dedf20ebeae9fe57", size = 117199, upload-time = "2026-09-15T19:32:31.49Z" }, + { url = "https://files.pythonhosted.org/packages/97/27/3d8c63ddd3e8bcfd033748ab93876678ce59bacd66e4cb1ed851c9c5b37e/yarl-1.25.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:12b6bc4906e11f5e1a1cdcb12296e7afbd366c783cc8073403cd2fb74334e453", size = 115187, upload-time = "2026-09-15T19:32:34.137Z" }, + { url = "https://files.pythonhosted.org/packages/39/b7/7a81d0be1a502a26a0d4326c6f2ecb736c824f570ea1c6529f2b0b227b50/yarl-1.25.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9d6ed3d17bccce4c05343e1ca8da13bc5c02c812a4e7282ddd05e8769322d3fc", size = 116085, upload-time = "2026-09-15T19:32:36.438Z" }, + { url = "https://files.pythonhosted.org/packages/f0/69/39fff459916aa0fab42215dc47b759586fd80f94aa56dfc4a7c15ba6e0dc/yarl-1.25.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:f38a70074041d3b7e138e452799f5174198bae5bd5ab2000917badf403908c5f", size = 107996, upload-time = "2026-09-15T19:32:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/c0/39/80b9a55a3335590451d9ecf3eb593a8c635351f4c905ef056d7e8a8fd9e7/yarl-1.25.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca89e4e21854ed27ec753297dde84b16c9f8e53b14a4866fb44457d643c19f8", size = 122549, upload-time = "2026-09-15T19:32:41.151Z" }, + { url = "https://files.pythonhosted.org/packages/42/7d/a179c6757818bb59372a4adafd09f7f26a3b4a0f04c3ae404b544c0b0c82/yarl-1.25.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:1ab7618921a93767387a4b83776f751588f5b5ae9bb5bc96620e2e2e00bca868", size = 115107, upload-time = "2026-09-15T19:32:43.072Z" }, + { url = "https://files.pythonhosted.org/packages/32/2b/a773ac867e4ab53a98ed98e5cefe3bae31e6f550252ca9d1de266f1a40c5/yarl-1.25.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0ae12ff2b805fa02c4dab838005caef735e39986322698c48588d3beacb65c62", size = 120666, upload-time = "2026-09-15T19:32:45.061Z" }, + { url = "https://files.pythonhosted.org/packages/bc/41/52be6505e85b0f76b4f85b01b5de7e06a0512201abc2c95e14e099549174/yarl-1.25.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90c30ed53546da833c700115c0064c22120d1b1560f474699fd31f22dd668233", size = 117505, upload-time = "2026-09-15T19:32:47.177Z" }, + { url = "https://files.pythonhosted.org/packages/f5/01/349c0386caedbbe488d519f252df54efac8a1459282d466c474bdd84a620/yarl-1.25.1-cp314-cp314-win_amd64.whl", hash = "sha256:acfa7e22aa6c6e7a5996a41d275bfa01efa7ea56ab890590280e9063e2cf5c1b", size = 103446, upload-time = "2026-09-15T19:32:49.615Z" }, + { url = "https://files.pythonhosted.org/packages/5c/f0/8ec63180f77912f0dc4e5a42760cb8c08d20da1d5ace3578a01b84d1f3d8/yarl-1.25.1-cp314-cp314-win_arm64.whl", hash = "sha256:8e7d98cdbb6d71e726f7d525952867096053d1f290dd4e3c50d7d313a136f414", size = 99159, upload-time = "2026-09-15T19:32:51.686Z" }, + { url = "https://files.pythonhosted.org/packages/47/7d/92d2220d6886b70ab1ed8579533ac2af2dfac716d5d929001daff7986df9/yarl-1.25.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d21f0fa80a02d05299207eeaafef345d812ace96d5306e4ef265e1d419a615fa", size = 150071, upload-time = "2026-09-15T19:32:53.911Z" }, + { url = "https://files.pythonhosted.org/packages/64/fc/b245e448124bcda9340df38e3553fa222b50260fca027a84095e9bd8642d/yarl-1.25.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17c9877a89fb6e2bca6f9087eb24cd7fb434653946ef5075e470d23d49b52287", size = 106780, upload-time = "2026-09-15T19:32:56.443Z" }, + { url = "https://files.pythonhosted.org/packages/51/e2/9a6ce2e334ebf218a30335ae76fb1696459430d42f733b8cb0d7d65b84d3/yarl-1.25.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:29273edf1530e397bd07cb784db1fbe0d2590b77569f2e24679a9c0a2d763b94", size = 107361, upload-time = "2026-09-15T19:32:58.827Z" }, + { url = "https://files.pythonhosted.org/packages/ed/70/66e8c76b569b450d16e190f15071c916c3df70b0e33927e415ac497cf0c2/yarl-1.25.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7abffdf37af1cec6a2ad69b827aa84320db5894791bc8ed932dc93fb274b7e9", size = 114396, upload-time = "2026-09-15T19:33:02.24Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/0d82838a05c57fdc05bc8b66e8c92dcc0df15e27463a5f163142d521c682/yarl-1.25.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2239a02249d9326655419e0168a28ca9008938eaab31dc29fc875c217927a6c0", size = 104882, upload-time = "2026-09-15T19:33:04.494Z" }, + { url = "https://files.pythonhosted.org/packages/86/d4/ea08615c4edaa6049a13a2f1128944d068d1893abda7d708d4d7ea01599a/yarl-1.25.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:664ec6a520b74a1df2810666eb67695fcb77fa663e6ea0a25aaf2e529cb24dfa", size = 119485, upload-time = "2026-09-15T19:33:06.583Z" }, + { url = "https://files.pythonhosted.org/packages/1a/82/0898bdce9b1ae403b308b9c733d0d24af4a3464270c2c081f457b16c3e0d/yarl-1.25.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4f1c91f5a5980a937ff8e238e98e6897e1ad74a4b1e2c0d68c73b5ffbb3f5c0b", size = 122490, upload-time = "2026-09-15T19:33:08.653Z" }, + { url = "https://files.pythonhosted.org/packages/d1/38/97d79b81c342b78246cfedb74809e68841f3198d21653e10d3232bd9c622/yarl-1.25.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c88edaec8c349ad4c5ad4c486a3defcc4b80ceb2f074436ffa0a87caf5e76a6", size = 115336, upload-time = "2026-09-15T19:33:11.056Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9d/2577896554cd310dc470adb6da0b7dd0b435cb63e2565204a7ac240e504c/yarl-1.25.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:35dcbea443fafb3eece757ad4e514560ddeb6c34cfae1582c620d7b293d7feee", size = 111825, upload-time = "2026-09-15T19:33:13.204Z" }, + { url = "https://files.pythonhosted.org/packages/29/6b/7ac49d8ba84a5c4bd73415a4c949d22c749cb3762579b3d50e48019a78aa/yarl-1.25.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:882569ff613758cac762a457a5d72d6e211b28d4bcfea89d1d71ea942b02eac0", size = 114655, upload-time = "2026-09-15T19:33:15.553Z" }, + { url = "https://files.pythonhosted.org/packages/e5/18/e5942a16723f5b72f9b1297fd5a85a54f6300cd15c0dcb5005b90cd89156/yarl-1.25.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d0f1489233a254bb3643d2f05de7d59019254d81daeca6b9162fe9edef57e0c7", size = 106395, upload-time = "2026-09-15T19:33:17.599Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2d/549fa46240781513ebc47ae7eb418df428a163a2a3d644cc9cbb3ecb7846/yarl-1.25.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f41753a76f4f63927d03a0d8ba8f5ce0f2083bec29a8cfaccc55371b1564b96b", size = 119277, upload-time = "2026-09-15T19:33:19.973Z" }, + { url = "https://files.pythonhosted.org/packages/76/16/4763f78dcdc0b3b9fb3842b04afe72b9320857c6a69300c62a0eab03d119/yarl-1.25.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fb0eb4955adf0579001581f2f71a126e8781ba61bcd120f127b0401163c6c2d", size = 112504, upload-time = "2026-09-15T19:33:22.464Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b4/974e3edfe0d188393ce1cb9de400111c63fe61f4eb3b772a500d84c970d1/yarl-1.25.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a1e32763e641a1566507d90a8d3b19bfc3cc04a9d4e5ae3e32189874ed4b58a3", size = 116243, upload-time = "2026-09-15T19:33:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/b0/aa/157b940428da80c104ca09666a740e51c94963df65d5b112e06b52e4d7a8/yarl-1.25.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65b5b2066651b7432d389e9799d979c703bcc6ef44266bb8153ef54e91e4aab3", size = 115822, upload-time = "2026-09-15T19:33:26.886Z" }, + { url = "https://files.pythonhosted.org/packages/7e/af/19fbdce41412e1b96825544cc52cd7029d3724655d0988237972f078bd29/yarl-1.25.1-cp314-cp314t-win_amd64.whl", hash = "sha256:734f6e5400352ac4254456003d462866c684703570929cff7a7bde015d0cb371", size = 107386, upload-time = "2026-09-15T19:33:29.009Z" }, + { url = "https://files.pythonhosted.org/packages/2a/99/f6431c8968e89be608d74b28ae2d024521b2953f27dd44e0dece5e04f67a/yarl-1.25.1-cp314-cp314t-win_arm64.whl", hash = "sha256:287e99ff5aa4dc1c7630bfc683ded6f106d756c99dec432a2d7f197a784f51c6", size = 102094, upload-time = "2026-09-15T19:33:31.151Z" }, + { url = "https://files.pythonhosted.org/packages/c7/3b/4f51eab40c2eabea6c3d5b121dff4b8988dc35087732ffede12d2be8b8dd/yarl-1.25.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9b1bdaae98bc016825dd3c9d8ee1832f829b3341f9cc6ebd1a1b0a7fef7367cc", size = 143875, upload-time = "2026-09-15T19:33:33.52Z" }, + { url = "https://files.pythonhosted.org/packages/41/05/bbd58fc063f5f299a883f810760b265ca26c8167c91cb9a494d0fe2387e1/yarl-1.25.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:e7011b8fb8c4054bf0c12e5edc6cd83778b0028e99ce59b18586ed036f92cfdc", size = 104028, upload-time = "2026-09-15T19:33:36.22Z" }, + { url = "https://files.pythonhosted.org/packages/12/ee/2fba0aecb52e7020e189f684148783aa0b9cfa3b3bfb0b400646eef70ad4/yarl-1.25.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:f074e8d4aa0a5798920ddb6de3d08b228c614ff3724c3e8bd7577f4bafea867b", size = 104041, upload-time = "2026-09-15T19:33:38.86Z" }, + { url = "https://files.pythonhosted.org/packages/38/35/884beab53ed88c7247d1671972b5ef116f7351fe0c7e6de8c3558372cb16/yarl-1.25.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d42e7e3ca399555578b4d617e3a6ecf13371b3743a115995fa010c7bf341459", size = 116018, upload-time = "2026-09-15T19:33:44.265Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/1a91b685afb55cc18608443ace95280e96e263a97565811732b6788d3269/yarl-1.25.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aa4ed3dd308548f9e707d9caaf005d2d7f8c1e7868f858dfeb47fe76e16b391d", size = 107033, upload-time = "2026-09-15T19:33:46.45Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c1/58b379fcb1d68d907b7fcf75200c44321896509b2a6a74abbb4b19d864d2/yarl-1.25.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42a66563d8cc056ee32e6191e05097a7b2b3bc302e0bc3133daf8710eb18bd26", size = 123257, upload-time = "2026-09-15T19:33:48.631Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2d/1fe96cf5c2aeab10095e48f38585cf5a8451fb7253234822398e52aa5336/yarl-1.25.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:98d370568f393215d605304cdb77b3d5539bd192c75b623c7304c42c8d6d8273", size = 126745, upload-time = "2026-09-15T19:33:50.999Z" }, + { url = "https://files.pythonhosted.org/packages/ad/60/8674394ce43f4dadae573a1d6f451716438e9eab7d7fe8d643c673a32d85/yarl-1.25.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23bf5b403c879a54964e0feac7285688e04bb220074878d737d331522da0a5bf", size = 117255, upload-time = "2026-09-15T19:33:53.456Z" }, + { url = "https://files.pythonhosted.org/packages/2f/72/0faa30e02605d56127d42bb987dcc97da3863b7bf70b9bfbf5f739c05e30/yarl-1.25.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d45673badd08456d0340e9364eddafe1c53a9d2896424294de4d7dd71ad3ee57", size = 115166, upload-time = "2026-09-15T19:33:55.669Z" }, + { url = "https://files.pythonhosted.org/packages/8c/90/9a46eac564c437e128285c5c1d7bb385d268394e9209d84f6bf4a14471ef/yarl-1.25.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:0136d640dfa9b0523853e411430a99f8a91eca85774c6420285a33b755bc6de3", size = 116082, upload-time = "2026-09-15T19:33:57.78Z" }, + { url = "https://files.pythonhosted.org/packages/ad/38/4b1a686a3758878f93d2f1ea943f5a165f3555769cd16e761cfd0efdca17/yarl-1.25.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:59ba3a6e1aa8cfe5adf4bd270fd965db21955401b7ca6f1696010c55ed4daec2", size = 108048, upload-time = "2026-09-15T19:34:00.25Z" }, + { url = "https://files.pythonhosted.org/packages/da/14/f348eb967f31a58348612a2b93bd8a2ab664548e2b5e879cac7f592f7201/yarl-1.25.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:87796fedc3ba97ec14fab55acb48584276e6c1e4c1e89c422bda62c838e754a9", size = 122775, upload-time = "2026-09-15T19:34:02.455Z" }, + { url = "https://files.pythonhosted.org/packages/ab/e9/4f7b79700f88cb9e8bb66f8b54f9bce1844c013a2c39fdc47112e9334c95/yarl-1.25.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:bd0912757081f89b107d6c00b2ff8a194401b0b87eadcf4481de2b865a8fd44f", size = 115096, upload-time = "2026-09-15T19:34:05.283Z" }, + { url = "https://files.pythonhosted.org/packages/cf/37/f9cb020331997d3eb887bd28d5410ecfd3d80bf163c23d7cec490d78dade/yarl-1.25.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:b51c159a9794633f5e0db7ecec7b2b6e3734eca1f5d17dc989ff3552a43ff78b", size = 120655, upload-time = "2026-09-15T19:34:07.382Z" }, + { url = "https://files.pythonhosted.org/packages/12/83/52fceb22891a41f168db7ec22fd1d81e06b6a0b8d9f70921bd3e785defd0/yarl-1.25.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:319e070a01db9920fb63761843f96a104c8e2b9427266731810dc1e22595b17c", size = 117488, upload-time = "2026-09-15T19:34:09.988Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5c/ce6c4ff1247fcbe4b33d462c23a097106d909b173fde7042bc52290466e2/yarl-1.25.1-cp315-cp315-win_amd64.whl", hash = "sha256:a2059a2d891bd156bc5184e7ab7a56e78a84dfcfdeac8c501b552533ad1c36ee", size = 103434, upload-time = "2026-09-15T19:34:12.56Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/766a906b0704fb26d52b19dc22bed48a8ba0544203b70dcf44da350e8194/yarl-1.25.1-cp315-cp315-win_arm64.whl", hash = "sha256:a78b50b4f7918a3de71105d5c0b93bbc57bb8339a4d03a9dfd449f9068e76f3d", size = 99155, upload-time = "2026-09-15T19:34:15.132Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d3/a1d09b32cb6ab14f66b44939f5b4255b8b9e747aef3974af1d5d80ccc2fd/yarl-1.25.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:b5402a340723fa7da00b5cff987ddab61276be6d11251ea71ae02bcac54890d8", size = 149284, upload-time = "2026-09-15T19:34:17.452Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3b/fe554d879692650bca70bfbc0df124e82e4d2bb7456f698c7756f1279a96/yarl-1.25.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:eda19ea5ee88742f47a2340816e6f2d40b53bed3ab5b69794769f36af9f35bb4", size = 106399, upload-time = "2026-09-15T19:34:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4b/e7af56177ac8d40094c82d7728224c0b8472157d50d362e5fb3b014b2bc8/yarl-1.25.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:75baa6cf9b6d1c52f3e111a130e202fd8cf0a5b3a066c3f73d615e885092e4ec", size = 106968, upload-time = "2026-09-15T19:34:23.619Z" }, + { url = "https://files.pythonhosted.org/packages/da/4f/2df41fd738d46f23ef829ae8b4468d94bb6070038fc6dfab6165ed44fea8/yarl-1.25.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dbcef5a9119ef653653132cccaf999b30a0af6f33bb0a4ba80bec30056868487", size = 114717, upload-time = "2026-09-15T19:34:25.879Z" }, + { url = "https://files.pythonhosted.org/packages/69/ea/002b66df53bbd1aed1c23358ff99c9bdc744fe3f4d2740e1b2fdd7192885/yarl-1.25.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7efc9f082dfed77c316edffa9deb52888e1bc6789171887cc1f68e06d65465c8", size = 105198, upload-time = "2026-09-15T19:34:28.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7c/95c8bc0c8f97d71e59c94525ad60d76f5c57d3f2820f08137ca8b9f0542a/yarl-1.25.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fe01645169a2112aa1d4ebc3e4c5f029c5c8f97adfc32e5d37c993b39a994d75", size = 120271, upload-time = "2026-09-15T19:34:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7a/6fe9da56ec77927baa669fd86c39c567ce6205bab53d581082c6744c8ae7/yarl-1.25.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1c557dfd5e3db046053a0bdc72261ade790ebe8e2c7a41b36b0ca1f14cb95f3", size = 123572, upload-time = "2026-09-15T19:34:32.73Z" }, + { url = "https://files.pythonhosted.org/packages/8b/83/35f222d17fa70a14c7c74fdf112ccf5515e0c2a87082b1f9b99f7693bf57/yarl-1.25.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ce4d6ccafb33d39bd78444612d14938ead674c25702ded2ee9c54a47735d225", size = 115228, upload-time = "2026-09-15T19:34:35.344Z" }, + { url = "https://files.pythonhosted.org/packages/da/6f/fbaaf619423578a7d898d0f226ae47bc1906c293865c416d83c17b828b0e/yarl-1.25.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80a063f8297fc796296f00f100be520f209b23dc98f93ce8eba6ee7122598209", size = 111618, upload-time = "2026-09-15T19:34:37.656Z" }, + { url = "https://files.pythonhosted.org/packages/ba/78/7383278f1b3cf8e0496bd95b3281a7b09b89217b6b428db24c6b99b3deca/yarl-1.25.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7cb414a73e21a7ab58254926073f2930cb22f5b4314ea4260a687e2b3fd4dce3", size = 114839, upload-time = "2026-09-15T19:34:40.099Z" }, + { url = "https://files.pythonhosted.org/packages/62/49/5506e5b6d29aab91bd845cc9016d88c3d3f81b81bc242b8100bdd5737825/yarl-1.25.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:85a18376073f8a39aa07be34f9fc77e2869aa72c55c441efdd2cf79a0407504d", size = 106212, upload-time = "2026-09-15T19:34:42.768Z" }, + { url = "https://files.pythonhosted.org/packages/46/8a/19877c193b7c5929f4b07118c18bbe390f3fadd0f59dd98c0cd12b31fa5c/yarl-1.25.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:77716e245c90f058466a05e6a465bb8600f767a8f4b18b4d40f3aff958e5f73c", size = 119985, upload-time = "2026-09-15T19:34:44.976Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6c/0a46fbbf9ecbcbd0cc20d2193394254b9e19817f22c814aab60f99847400/yarl-1.25.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:1e80dcf1446e1b080b1932b0d103c464a04112f5bc31f0f983ad418172063cde", size = 112081, upload-time = "2026-09-15T19:34:47.45Z" }, + { url = "https://files.pythonhosted.org/packages/ef/30/93f5d471230c74ccd06255d0842739f86551f937f9e63a5e947853c6244a/yarl-1.25.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:bdc8d8b8c22e9e43ac68316b5e6cf083dec537f4ec213cb4aa967b583bc3fa64", size = 116995, upload-time = "2026-09-15T19:34:49.972Z" }, + { url = "https://files.pythonhosted.org/packages/2b/80/c386593035ee3f9c6c6af0847b5578f2830c674794a9d7701b744a3ebd42/yarl-1.25.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbf531053a0935f2e871bcd4753f90313688772ff8c017f5ea402e315a78c1f", size = 115570, upload-time = "2026-09-15T19:34:52.628Z" }, + { url = "https://files.pythonhosted.org/packages/38/02/eef443559563ef8f2e10469387b8b1e97cb5efee95b288a56da60801f7ee/yarl-1.25.1-cp315-cp315t-win_amd64.whl", hash = "sha256:b13b88747769537f3d32e89e3a735da10c0a9e35d7322928c701b5f93d3afffd", size = 106811, upload-time = "2026-09-15T19:34:54.935Z" }, + { url = "https://files.pythonhosted.org/packages/88/91/41e284ca2cf5211e05dae031d126a3668aea88fa759df56e7e35c6ad25ba/yarl-1.25.1-cp315-cp315t-win_arm64.whl", hash = "sha256:783dd1467083f4d3f7722ad6a313f24c173e7571372738fcb7a6e6d1ba48df25", size = 101804, upload-time = "2026-09-15T19:34:57.231Z" }, + { url = "https://files.pythonhosted.org/packages/54/22/318c7980066769c6bcd9221ed2248294f5698811da099013098c670565ed/yarl-1.25.1-py3-none-any.whl", hash = "sha256:681c758b0490f9e96b78e5fa8e8dc6e648e9185bb6eaebe73183c33ea0c445f3", size = 63617, upload-time = "2026-09-15T19:34:59.616Z" }, +] + +[[package]] +name = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, +]