This commit is contained in:
Tom Kunc 2026-03-19 21:36:15 +11:00
commit 664ee27835
33 changed files with 1969 additions and 0 deletions

75
.gitignore vendored Normal file
View File

@ -0,0 +1,75 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
*.egg-info/
*.egg
*.manifest
*.spec
eggs/
wheels/
*.whl
# PyInstaller
*.manifest
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage / pytest
.pytest_cache/
.coverage
.coverage.*
htmlcov/
*.cover
.hypothesis/
.tox/
nosetests.xml
coverage.xml
*.log
# Virtual environments
.venv/
venv/
ENV/
env/
.env
.env.*
!.env.example
# uv
.uv/
.uv_cache/
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Protobuf generated (if generated in-tree)
*_pb2.py
*_pb2_grpc.py
# IDE / editor
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Project-specific
*.log

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}

122
README.md Normal file
View File

@ -0,0 +1,122 @@
# Optivex
You are building **Optivex**, Optiver's own financial exchange. Your task is to design and implement an exchange supporting the features defined in the protobuf contracts in the `proto/` directory.
Clients of your exchange will connect via TCP to the public endpoints of your **components**. Each component can implement one or more protocols, which are defined in the `proto/` directory. Internally, you can organize your solution in whatever way you see fit, as long as you implement all the protocols defined in the `proto/` directory. The one exception is the **order book**: it must be a dedicated component that does not require authentication (`authRequired: false`), as it is an internal service that other components connect to.
A pre-populated [user data file](#user-data-file) will be provided, which is recreated automatically each day. Do not rely on any other form of persistent storage; all other data should be kept in memory.
## Constraints
* A component is a TCP server listening to a single port. Each component may connect to other components via TCP.
* The original protocols may be extended with new messages and services, but they must remain compatible with the original version for the automated system tests to pass. The system tests will only exercise messages defined in the original `proto/` files — custom extensions will not be tested.
* The `request_id` field is used to match requests to responses. It must be set to the same value in the request and response messages.
* Every response message must contain an `error_message` field.
* Floating point numbers precision is 4 decimal places.
## Project Structure
```
├── proto/ # Protobuf contracts your components must implement
├── src/
│ ├── application/ # BaseApplication: config loading, logging, signal handling
│ │ ├── config_schema.json # JSON Schema for component runtime config
│ │ └── data_file_schema.json # JSON Schema for the user data file
│ ├── connection/ # TCP connectivity library
│ └── sample_app/ # Reference implementation — start here
├── deployment_config.json # Describes your components, protocols, and ports
├── deployment_config_schema.json # JSON Schema for the above
└── pyproject.toml # Project metadata, dependencies, and entry points
```
## Getting Started
1. Install dependencies and activate the virtual environment:
```
uv sync && source .venv/bin/activate
```
2. Run the sample app to see the framework in action:
```
sample-app -c src/sample_app/sample_app_config.json
```
See [`src/sample_app/README.md`](src/sample_app/README.md) for details.
3. Read [`proto/README.md`](proto/README.md) — it describes every message your components must implement, and is the best place to understand the full scope of the system.
## Key Concepts
### BaseApplication
`BaseApplication` (`src/application/application.py`) is the base class all your components should extend. It handles config loading and validation, logging setup, and graceful shutdown. Implement `_start()` in your subclass.
### Entry Points
Each component must be registered as a **console-script** entry point in `pyproject.toml` under `[project.scripts]`. For example:
```toml
[project.scripts]
sample-app = "sample_app.main:main"
my-order-book = "order_book.main:main"
```
The `packageName` field in `deployment_config.json` must match the console-script name exactly (e.g. `"my-order-book"`). The test runner uses this name to locate and launch the component binary.
### Component Configuration
Each component's runtime config is validated against `src/application/config_schema.json`. The key fields are:
| Field | Required | Description |
|---|---|---|
| `logLevel` | Yes | `DEBUG`, `INFO`, `WARN`, or `ERROR` |
| `logDirectory` | Yes | Directory where log files are written |
| `listenOn` | Yes | `{ host, port }` — address the component listens on |
| `connectTo` | No | Map of target component names to `{ host, port }` — keys must match the `name` field in [Deployment Config](#deployment-config) |
| `dataFilePath` | Yes | Path to the JSON user data file |
### User Data File
The user data file follows the schema in `src/application/data_file_schema.json`. It contains an array of users, each with `username`, `password`, and `full_name`. Components that require authentication should read this file to validate credentials.
### Deployment Config
`deployment_config.json` describes your full deployment. It has two top-level fields:
* **`components`** — an array of component configurations, each with:
* `name` — a unique identifier for the component.
* `packageName` — the console-script entry point name (see [Entry Points](#entry-points)).
* `protocols` — which protocols this component implements (`admin`, `order_book`, `info`, `execution`, `risk_limits`).
* `config` — the component's runtime config (see [Component Configuration](#component-configuration)).
* **`systemTests`** — which test suites to run (see [Incremental Testing](#incremental-testing)).
Full specification is available in `deployment_config_schema.json`.
## Deploying and Testing
Once ready to test, run `deploy.sh` from your project root to build and submit your implementation to the testing environment. This script builds the project using `uv build` and copies the results onto the UAT server, where automated system tests will be run against it. Test results will be copied back to your dev server under the `/tmp` directory.
### How Tests Run
The test runner reads your `deployment_config.json` to determine which components to start and which tests to run. For each test:
1. **Dependency resolution** — the required protocols are determined from the test's dependencies and the `connectTo` fields of your components.
2. **Start order** — components are started in dependency order (a component's `connectTo` targets are started first).
3. **Config overrides** — the test runner rewrites several fields in your component config at runtime: `listenOn` and `connectTo` ports are replaced with ephemeral ports, and `logDirectory` and `dataFilePath` are pointed to temporary paths. Do not hardcode any of these values in your component logic; always read them from the config.
4. **Startup** — each component must accept TCP connections on its assigned port within **2 seconds**, or the test will fail.
5. **Execution** — tests run with 4 parallel workers, so your components must handle concurrent connections. Each test simulates a single day of trading.
### Incremental Testing
The `systemTests` array controls which test suites are run. You can test incrementally by listing only the protocols you've implemented so far:
```json
"systemTests": ["order_book"]
```
As you implement more protocols, add them to the array. The available test suites and their protocol dependencies are:
| Test Suite | Requires Protocols |
|---|---|
| `order_book` | `admin` |
| `info` | `admin`, `order_book` |
| `execution` | `admin`, `order_book` |
| `risk_limits` | `admin`, `execution` |

9
build_proto.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# Generate Python files from proto files
mkdir -p src/proto
touch src/proto/.gitignore && echo "*" > src/proto/.gitignore
protoc --proto_path=proto --python_out=src/proto --mypy_out=src/proto proto/*.proto
# Adjust imports in generated Python files to use relative imports
find src/proto -name "*.py" -exec sed -i 's/^import \([^ ]*\)_pb2 as \([^ ]*\)$/from . import \1_pb2 as \2/' {} \;

50
deployment_config.json Normal file
View File

@ -0,0 +1,50 @@
{
"components": [
{
"name": "admin",
"packageName": "sample-app",
"protocols": ["admin"],
"authRequired": false,
"config": {
"logLevel": "DEBUG",
"logDirectory": "./logs",
"listenOn": {
"host": "localhost",
"port": 9001
},
"connectTo": {
"core": {
"host": "localhost",
"port": 9100
}
}
}
},
{
"name": "core",
"packageName": "sample-app",
"protocols": [
"execution",
"info",
"order_book",
"risk_limits"
],
"authRequired": true,
"config": {
"logLevel": "DEBUG",
"logDirectory": "./logs",
"dataFilePath": "./data.json",
"listenOn": {
"host": "localhost",
"port": 9100
}
}
}
],
"systemTests": [
"execution",
"info",
"order_book",
"risk_limits"
]
}

View File

@ -0,0 +1,118 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "/optiver/deployment_config.json",
"title": "DeploymentConfig",
"description": "Defines the set of components to deploy and, for each, which protocols it implements and its runtime configuration.",
"type": "object",
"properties": {
"components": {
"type": "array",
"description": "Ordered list of components to deploy. At least one component must be defined.",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/ComponentConfig"
}
},
"systemTests": {
"type": "array",
"description": "One or more system tests to run according to the protocols implemented by the components.",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/SystemTest"
}
}
},
"required": ["components", "systemTests"],
"additionalProperties": false,
"$defs": {
"Protocol": {
"type": "string",
"description": "A protocol defined in the proto/ directory that a component may implement.",
"oneOf": [
{
"const": "admin",
"description": "Internal instrument admin protocol (optiver.exchange.internal.admin)."
},
{
"const": "execution",
"description": "Client-facing execution protocol (optiver.exchange.exec)."
},
{
"const": "info",
"description": "Market-data / information protocol (optiver.exchange.info)."
},
{
"const": "order_book",
"description": "Order-book protocol (optiver.exchange.orderbook)."
},
{
"const": "risk_limits",
"description": "Risk-limits protocol (optiver.exchange.risk)."
}
]
},
"SystemTest": {
"type": "string",
"description": "A system test to run. The dependencies are the protocols that must be deployed for the test to run.",
"oneOf": [
{
"const": "order_book",
"description": "Tests the order book protocol.",
"dependencies": ["admin"]
},
{
"const": "info",
"description": "Tests the info protocol.",
"dependencies": ["order_book", "admin"]
},
{
"const": "execution",
"description": "Tests the execution protocol.",
"dependencies": ["order_book", "admin"]
},
{
"const": "risk_limits",
"description": "Tests the risk limits protocol.",
"dependencies": ["execution", "admin"]
}
]
},
"ComponentConfig": {
"type": "object",
"description": "Configuration for a single deployable component.",
"properties": {
"name": {
"type": "string",
"description": "Unique human-readable identifier for this component (e.g. 'gateway', 'order_book_engine').",
"minLength": 1
},
"packageName": {
"type": "string",
"description": "Console-script entry point installed by the wheel (e.g. 'my-exchange'). Used to locate the binary in the virtual environment.",
"minLength": 1
},
"protocols": {
"type": "array",
"description": "One or more protocols this component is responsible for implementing. Each value must correspond to a .proto file in the proto/ directory.",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/$defs/Protocol"
}
},
"authRequired": {
"type": "boolean",
"description": "Indicates whether authentication is required for this component.",
"default": true
},
"config": {
"$ref": "src/application/config_schema.json"
}
},
"required": ["name", "packageName", "protocols", "config"],
"additionalProperties": false
}
}
}

72
proto/README.md Normal file
View File

@ -0,0 +1,72 @@
# Proto Definitions
This directory contains the Protobuf definitions that describe the entire messaging contract of the exchange system. Every message your components must accept, respond to, or emit is defined here.
These files are the **contract your implementation must satisfy** — system tests will connect to your components over TCP and exchange exactly these messages.
## Where to start
Begin with **`common.proto`**. It contains the `MessageType` enum, which is the single authoritative index of every message in the system. The enum is grouped by module (Auth, Admin, Info, Order Book, Execution, Risk), giving you a map of the whole system before you dive into any individual file. It also defines the shared types that appear everywhere else: `Instrument`, `Side`, and the auth `LoginRequest`/`LoginResponse` pair.
## File-by-file overview
### `common.proto`
The foundation. Defines:
- `MessageType` — a numbered enum of every request/response/notification in the system, grouped by subsystem.
- `Instrument` — the data model for a tradeable instrument (symbol, description, currency, multiplier).
- `Side` — the `BUY`/`SELL` enum used across order-related messages.
- `LoginRequest` / `LoginResponse` — the authentication handshake your components must perform with every connecting client before serving any other message.
Read this first to orient yourself. Every other file imports it.
### `admin.proto`
The administration interface your exchange must expose to let the system be bootstrapped before trading begins.
- `CreateInstrumentRequest/Response` — your exchange must accept requests to list a new tradeable instrument, create an order book for it, and return the assigned `order_book_id`.
User information is provided via a static JSON data file (see `users_data_schema.json` in the project root) whose path is supplied in each component's config as `dataFilePath`. Components that require authentication must read this file at startup to validate login credentials.
### `info.proto`
The market data feed your exchange must maintain and push to subscribed clients.
- `OnInstrument` — your exchange must push this to every client on connection (for all existing instruments) and again whenever a new instrument is created.
- `OrderBookSubscribeRequest/Response` — clients may subscribe to a specific instrument's order book in one of two modes. Your exchange must honour the chosen type and push updates accordingly:
- `TOP_OF_BOOK` — push `OnTopOfBook` whenever the best bid or ask changes.
- `PRICE_DEPTH_BOOK` — push `OnPriceDepthBook` with the full visible depth on every change.
- `OnTrade` — your exchange must broadcast this to all subscribed clients whenever a trade occurs, showing price, quantity, and which side was the aggressor.
### `order_book.proto`
The core of the exchange: the matching engine. This component stores resting orders, matches them when prices cross, and is the authoritative source of order book state.
It has two responsibilities:
**State broadcast** — pushed to all connected clients upon login (current state) and on every subsequent change:
- `OnOrderInserted` — broadcast when a new order enters the book, including any trades it immediately triggered.
- `OnOrderCancelled` — broadcast when an order is removed from the book.
- `OnTrade` — broadcast when a match occurs, carrying both the buy and sell `order_id`s and full trade details.
**Request/response** — your order book component must accept these from other internal services:
- `InsertOrderRequest/Response` — place a limit order identified by `order_book_id`. Your component must assign an `order_id`, record the timestamp, attempt to match, and return the result.
- `CancelOrderRequest/Response` — cancel a resting order by `order_book_id` and `order_id`. Your component must confirm the cancellation timestamp and remaining quantity at the time of removal.
### `execution.proto`
The client-facing execution interface your exchange must expose to trading clients. It is a higher-level entry point into the order book: clients address orders by `instrument_symbol` (human-readable) rather than `order_book_id` (internal numeric ID), so this component is responsible for the translation.
- `InsertOrderRequest/Response` — accept a limit order from a client, resolve the instrument to its order book, forward it, and return the result (order ID, timestamp, fills).
- `CancelOrderRequest/Response` — accept a cancellation request from a client and forward it to the appropriate order book.
- `OnTrade` — your exchange must send this **privately** to the specific client whose order was involved in a trade, reporting their `order_id`, side, and whether they were the aggressor.
### `risk_limits.proto`
The risk management component your exchange must implement to enforce guardrails on all trading activity. Before an order reaches the order book, risk limits must be checked.
Limits operate at two scopes:
**User-level** (`UserRiskLimits`):
- `max_outstanding_quantity` — caps the total quantity a user may have resting across all instruments simultaneously.
- `message_rate_rolling_limit` — caps the number of order/cancellation messages a user may send within a rolling time window.
**Instrument-level** (`InstrumentRiskLimits`):
- `max_outstanding_quantity` / `max_outstanding_amount` — caps how much of a single instrument a user may have resting at one time.
- `order_quantity_rolling_limit` / `order_amount_rolling_limit` — rolling-window caps on order flow per instrument.
Each scope has a symmetric `Get.../Set...` request/response pair. Your component must persist these limits and enforce them on every incoming order.

18
proto/admin.proto Normal file
View File

@ -0,0 +1,18 @@
syntax = "proto3";
package optiver.exchange.admin;
import "common.proto";
message CreateInstrumentRequest {
int64 request_id = 1;
Instrument instrument = 2;
double tick_size = 3;
}
message CreateInstrumentResponse {
int64 request_id = 1;
string error_message = 2;
int64 created_timestamp = 3;
int64 order_book_id = 4;
}

72
proto/common.proto Normal file
View File

@ -0,0 +1,72 @@
syntax = "proto3";
package optiver.exchange;
enum MessageType {
// Auth
AUTH_LOGIN_REQUEST = 0;
AUTH_LOGIN_RESPONSE = 1;
// Instrument admin
ADMIN_CREATE_INSTRUMENT_REQUEST = 20;
ADMIN_CREATE_INSTRUMENT_RESPONSE = 21;
// Info
INFO_ON_INSTRUMENT = 30;
INFO_SUBSCRIBE_REQUEST = 31;
INFO_SUBSCRIBE_RESPONSE = 32;
INFO_ON_TOP_OF_BOOK = 33;
INFO_ON_PRICE_DEPTH_BOOK = 34;
INFO_ON_TRADE = 35;
// Order Book
ORDER_BOOK_ON_ORDER_BOOK_CREATED = 40;
ORDER_BOOK_ON_ORDER_INSERTED = 41;
ORDER_BOOK_ON_ORDER_CANCELLED = 42;
ORDER_BOOK_ON_TRADE = 43;
ORDER_BOOK_GET_ALL_REQUEST = 44;
ORDER_BOOK_GET_ALL_RESPONSE = 45;
ORDER_BOOK_INSERT_ORDER_REQUEST = 46;
ORDER_BOOK_INSERT_ORDER_RESPONSE = 47;
ORDER_BOOK_CANCEL_ORDER_REQUEST = 48;
ORDER_BOOK_CANCEL_ORDER_RESPONSE = 49;
// Execution
EXEC_INSERT_ORDER_REQUEST = 50;
EXEC_INSERT_ORDER_RESPONSE = 51;
EXEC_CANCEL_ORDER_REQUEST = 52;
EXEC_CANCEL_ORDER_RESPONSE = 53;
EXEC_ON_TRADE = 54;
// Risk
RISK_GET_USER_LIMITS_REQUEST = 60;
RISK_GET_USER_LIMITS_RESPONSE = 61;
RISK_SET_USER_LIMITS_REQUEST = 62;
RISK_SET_USER_LIMITS_RESPONSE = 63;
RISK_GET_INSTRUMENT_LIMITS_REQUEST = 64;
RISK_GET_INSTRUMENT_LIMITS_RESPONSE = 65;
RISK_SET_INSTRUMENT_LIMITS_REQUEST = 66;
RISK_SET_INSTRUMENT_LIMITS_RESPONSE = 67;
}
message LoginRequest {
int64 request_id = 1;
string username = 2;
string password = 3;
}
message LoginResponse {
int64 request_id = 1;
string error_message = 2;
}
enum Side {
BUY = 0;
SELL = 1;
}
message Instrument {
string symbol = 1;
string description = 2;
string currency = 3;
double multiplier = 4;
}

45
proto/execution.proto Normal file
View File

@ -0,0 +1,45 @@
syntax = "proto3";
package optiver.exchange.exec;
import "common.proto";
message InsertOrderRequest {
int64 request_id = 1;
string instrument_symbol = 2;
Side side = 3;
double price = 4;
int32 quantity = 5;
// TODO start with LIMIT order type only
}
message InsertOrderResponse {
int64 request_id = 1;
string error_message = 2;
int64 order_id = 3;
int64 timestamp = 4;
repeated int64 trade_ids = 5;
int32 traded_quantity = 6;
}
message CancelOrderRequest {
int64 request_id = 1;
string instrument_symbol = 2;
int64 order_id = 3;
}
message CancelOrderResponse {
int64 request_id = 1;
string error_message = 2;
}
message OnTrade {
int64 trade_id = 1;
string instrument_symbol = 2;
int64 timestamp = 3;
int64 order_id = 4;
Side side = 5;
double price = 6;
int32 quantity = 7;
bool is_aggressive = 8;
}

60
proto/info.proto Normal file
View File

@ -0,0 +1,60 @@
syntax = "proto3";
package optiver.exchange.info;
import "common.proto";
// All active instruments are sent to client on connection, then any new instrument is sent on creation
message OnInstrument {
Instrument instrument = 1;
int64 created_timestamp = 2;
double tick_size = 3;
int64 order_book_id = 4;
}
// ------------------------------------------------------------
// Subscription service to an instrument's order book
// ------------------------------------------------------------
enum SubscriptionType {
TOP_OF_BOOK = 0;
PRICE_DEPTH_BOOK = 1;
}
message OrderBookSubscribeRequest {
int64 request_id = 1;
string instrument_symbol = 2;
SubscriptionType subscription_type = 3;
}
message OrderBookSubscribeResponse {
int64 request_id = 1;
string error_message = 2;
}
message PriceLevel {
double price = 1;
int32 quantity = 2;
}
message OnTopOfBook {
string instrument_symbol = 1;
int64 timestamp = 2;
PriceLevel best_bid = 3;
PriceLevel best_ask = 4;
}
message OnPriceDepthBook {
string instrument_symbol = 1;
int64 timestamp = 2;
repeated PriceLevel bids = 3;
repeated PriceLevel asks = 4;
}
message OnTrade {
int64 trade_id = 1;
string instrument_symbol = 2;
int64 timestamp = 3;
double price = 4;
int32 quantity = 5;
Side aggressor_side = 6;
}

81
proto/order_book.proto Normal file
View File

@ -0,0 +1,81 @@
syntax = "proto3";
package optiver.exchange.orderbook;
import "common.proto";
// ------------------------------------------------------------
// Messages sent to all clients upon logging in with the current
// state of the order book, then upon any changes
// ------------------------------------------------------------
message OnOrderBookCreated {
int64 order_book_id = 1;
double tick_size = 2;
int64 created_timestamp = 3;
}
message OnOrderInserted {
int64 order_id = 1;
int64 order_book_id = 2;
int64 timestamp = 3;
Side side = 4;
double price = 5;
int32 quantity = 6;
string username = 7;
repeated int64 trade_ids = 10;
}
message OnOrderCancelled {
int64 order_id = 1;
int64 cancellation_timestamp = 2;
}
message OnTrade {
int64 trade_id = 1;
int64 order_book_id = 2;
int64 timestamp = 3;
int64 buy_order_id = 4;
int64 sell_order_id = 5;
double price = 6;
int32 quantity = 7;
Side aggressor_side = 8;
}
// ------------------------------------------------------------
// Requests accepted by this service
// ------------------------------------------------------------
message InsertOrderRequest {
int64 request_id = 1;
int64 order_book_id = 2;
Side side = 3;
double price = 4;
int32 quantity = 5;
string username = 6;
// TODO start with LIMIT order type only
}
message InsertOrderResponse {
int64 request_id = 1;
string error_message = 2;
int64 order_id = 3;
int64 timestamp = 4;
repeated int64 trade_ids = 5;
int32 traded_quantity = 6;
}
message CancelOrderRequest {
int64 request_id = 1;
int64 order_book_id = 2;
int64 order_id = 3;
}
message CancelOrderResponse {
int64 request_id = 1;
string error_message = 2;
int64 cancellation_timestamp = 3;
int32 remaining_quantity = 4;
}

66
proto/risk_limits.proto Normal file
View File

@ -0,0 +1,66 @@
syntax = "proto3";
package optiver.exchange.risk_limits;
message RollingWindowLimit {
int32 limit = 1;
int32 window_in_seconds = 2;
}
message UserRiskLimits {
int64 max_outstanding_quantity = 1;
RollingWindowLimit message_rate_rolling_limit = 2;
}
message InstrumentRiskLimits {
int64 max_outstanding_quantity = 1;
double max_outstanding_amount = 2;
RollingWindowLimit order_quantity_rolling_limit = 3;
RollingWindowLimit order_amount_rolling_limit = 4;
}
// ------------------------------------------------------------
// Requests accepted by this service
// ------------------------------------------------------------
message GetUserRiskLimitsRequest {
int64 request_id = 1;
}
message GetUserRiskLimitsResponse {
int64 request_id = 1;
string error_message = 2;
UserRiskLimits user_risk_limits = 3;
}
message SetUserRiskLimitsRequest {
int64 request_id = 1;
UserRiskLimits user_risk_limits = 2;
}
message SetUserRiskLimitsResponse {
int64 request_id = 1;
string error_message = 2;
}
message GetInstrumentRiskLimitsRequest {
int64 request_id = 1;
}
message GetInstrumentRiskLimitsResponse {
int64 request_id = 1;
string error_message = 2;
map<string, InstrumentRiskLimits> risk_limits_by_instrument = 3;
}
message SetInstrumentRiskLimitsRequest {
int64 request_id = 1;
string instrument_symbol = 2;
InstrumentRiskLimits instrument_risk_limits = 3;
}
message SetInstrumentRiskLimitsResponse {
int64 request_id = 1;
string error_message = 2;
}

36
pyproject.toml Normal file
View File

@ -0,0 +1,36 @@
[project]
name = "Optivex"
version = "0.1.0"
description = "Career Kickstarter - Optiver's Exchange"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"flake8>=7.3.0",
"flake8-mypy>=17.8.0",
"jsonschema>=4.26.0",
"mypy>=1.19.1",
"mypy-protobuf>=5.0.0",
"protobuf>=7.34.0",
"pytest>=9.0.2",
"types-jsonschema>=4.23.0.20241208",
"types-protobuf>=6.32.1.20260221",
]
[project.scripts]
sample-app = "sample_app.main:main"
[build-system]
requires = ["setuptools", "wheel", "uv"]
build-backend = "setuptools.build_meta"
[tool.setuptools.package-data]
"application" = ["*.json"]
[tool.uv]
package = true
[tool.pytest.ini_options]
pythonpath = ["src"]
[tool.mypy]
exclude = "src/proto"

View File

View File

@ -0,0 +1,100 @@
from abc import ABC, abstractmethod
import argparse
from datetime import datetime
import logging
from pathlib import Path
import signal
import sys
from types import FrameType
from typing import Any, Union
from importlib.resources.abc import Traversable
import jsonschema
import json
logger = logging.getLogger(__name__)
class BaseApplication(ABC):
def __init__(self, config_schema: Union[Path, Traversable], app_name: str | None = None) -> None:
self._executable_name = Path(sys.argv[0]).absolute().name
self._app_name = app_name or self._executable_name
self._config_schema = _load_json(config_schema)
self._init_args_parser()
self._args = self._parser.parse_args()
self._config_file = self._find_config_file_path()
self._config = _load_json(self._config_file)
self._validate_config()
self._init_logging()
logger.info(f"Config loaded: {json.dumps(self._config, indent=4)}")
def run(self) -> None:
"""Call this method to run the application."""
self._register_signal_handlers()
try:
logger.info("Starting application...")
self._start()
logger.info("Normal application exit, shutting down")
except Exception:
logger.exception("Oops, something went wrong.")
logger.error("Shutting down")
raise SystemExit(1)
@abstractmethod
def _start(self) -> None:
"""This method should be implemented by the subclass to start the application."""
pass
def _init_args_parser(self) -> None:
self._parser = argparse.ArgumentParser()
self._parser.add_argument("-c", "--config", help="(String) Path to config file to load", default=None)
def _find_config_file_path(self) -> Path:
if self._args.config is None:
default_config_file = Path(sys.argv[0]).parent / f"{self._app_name}_config.json"
logger.info(f"No config file specified, using default: {default_config_file}")
return default_config_file
return Path(self._args.config)
def _validate_config(self) -> None:
try:
jsonschema.validate(instance=self._config, schema=self._config_schema)
except jsonschema.ValidationError as e:
raise SystemExit(f"Failed to validate config: {e}")
def _init_logging(self) -> None:
log_level_str = self._config["logLevel"]
log_directory = Path(self._config["logDirectory"])
log_file = log_directory / f"{self._app_name}_{datetime.now():%Y%m%d_%H%M%S}.log"
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
level=_get_log_level(log_level_str),
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler()
]
)
logger.info(f"Logging initialized. Log file: {log_file}")
def _register_signal_handlers(self) -> None:
signal.signal(signal.SIGINT, handler=self._shutdown)
signal.signal(signal.SIGTERM, handler=self._shutdown)
def _shutdown(self, signum: int, frame: FrameType | None) -> None:
logger.info(f"Received signal {signal.Signals(signum).name} ({signum}) at frame {frame}, shutting down")
raise SystemExit(0)
def _get_log_level(log_level_str: str) -> Any:
logging_level = logging.getLevelNamesMapping().get(log_level_str)
if logging_level is None:
raise ValueError(f"Invalid log level: {log_level_str}")
return logging_level
def _load_json(file: Union[Path, Traversable]) -> dict:
logger.debug(f"Loading JSON file: {file}")
with file.open("r") as f:
return json.load(f)

View File

@ -0,0 +1,55 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "/optiver/application_config.json",
"title": "ApplicationConfig",
"type": "object",
"properties": {
"logLevel": {
"type": "string",
"enum": ["DEBUG", "INFO", "WARN", "ERROR"],
"description": "The level of logging for the application."
},
"logDirectory": {
"type": "string",
"format": "uri",
"description": "The directory where log files will be stored."
},
"listenOn": {
"$ref": "#/$defs/ConnectionConfig"
},
"connectTo": {
"type": "object",
"patternProperties": {
"^.*$": {
"$ref": "#/$defs/ConnectionConfig"
}
}
},
"dataFilePath": {
"type": "string",
"description": "Path to the JSON data file with user data. Schema of this file is defined in data_file_schema.json."
}
},
"required": ["logLevel", "logDirectory", "listenOn", "dataFilePath"],
"additionalProperties": false,
"$defs": {
"ConnectionConfig": {
"type": "object",
"properties": {
"host": {
"type": "string",
"format": "hostname",
"description": "The hostname or IP address of the server."
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"description": "The port number the server will listen on."
}
},
"required": ["host", "port"],
"additionalProperties": false
}
}
}

View File

@ -0,0 +1,42 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "/optiver/data_file.json",
"title": "DataFile",
"description": "Static data file containing pre-registered users for the exchange.",
"type": "object",
"properties": {
"users": {
"type": "array",
"description": "List of users who can authenticate with the exchange.",
"items": {
"$ref": "#/$defs/User"
}
}
},
"required": ["users"],
"additionalProperties": true,
"$defs": {
"User": {
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": 1,
"description": "Unique username for authentication."
},
"password": {
"type": "string",
"minLength": 1,
"description": "Password for authentication."
},
"full_name": {
"type": "string",
"minLength": 1,
"description": "Full display name of the user."
}
},
"required": ["username", "password", "full_name"],
"additionalProperties": false
}
}
}

0
src/application/py.typed Normal file
View File

View File

View File

@ -0,0 +1,82 @@
import logging
import socket
from abc import ABC, abstractmethod
from typing import Callable, Generic, Type, TypeVar
from connection import message_codec
from connection.ip_address import IpAddress
from google.protobuf.message import Message
logger = logging.getLogger(__name__)
ProtoMessage = TypeVar('ProtoMessage', bound=Message)
class ConnectionHandler(ABC):
def __init__(self, socket_fd: socket.socket, ip_address: IpAddress,
close_callback: Callable[[], None]) -> None:
self.socket_fd = socket_fd
self.ip_address = ip_address
self.close_callback = close_callback
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback) -> None:
logger.info(f"Closing connection to {self.ip_address}...")
self.close_callback() # TODO graceful flag to avoid reentrance?
@abstractmethod
def handle_message(self, message_type: int, message: bytes) -> None:
pass
@abstractmethod
def on_disconnect(self) -> None:
pass
def send_message(self, message_type: int, message: ProtoMessage) -> None:
logger.info(f"Preparing to send message of type {message_type} to {self.ip_address}...")
logger.debug(f"Message: {message}")
try:
serialized_message = message.SerializeToString()
encoded_message = message_codec.encode_message(message_type, serialized_message)
self._send_message(encoded_message)
logger.info(f"Sent message successfully")
except socket.error as e:
logger.exception(f"Failed to send message to {self.ip_address}")
logger.info(f"Closing connection to {self.ip_address}...")
self.close_callback()
def _send_message(self, encoded_message: bytes) -> None:
logger.debug(f"Sending message of {len(encoded_message)} bytes")
self.socket_fd.sendall(encoded_message)
@staticmethod
def _deserialize_message(proto_message_type: type[ProtoMessage], message: bytes) -> ProtoMessage:
proto_message = proto_message_type()
proto_message.ParseFromString(message)
logger.debug(f"Deserialized message of type {proto_message_type.__name__}: {proto_message}")
return proto_message
ConnectionHandlerType = TypeVar('ConnectionHandlerType', bound=ConnectionHandler)
class ConnectionHandlerFactory(ABC, Generic[ConnectionHandlerType]):
@abstractmethod
def on_new_connection(self, socket_fd: socket.socket, ip_address: IpAddress,
close_callback: Callable[[], None]) -> ConnectionHandlerType:
pass
@abstractmethod
def on_connection_closed(self, connection_handler: ConnectionHandlerType) -> None:
pass
class LambdaConnectionHandlerFactory(ConnectionHandlerFactory[ConnectionHandlerType]):
def __init__(self, on_new_connection_lambda: Callable[[socket.socket, IpAddress, Callable[[], None]], ConnectionHandlerType]) -> None:
self._on_new_connection_lambda = on_new_connection_lambda
def on_new_connection(self, socket_fd: socket.socket, ip_address: IpAddress, close_callback: Callable[[], None]) -> ConnectionHandlerType:
return self._on_new_connection_lambda(socket_fd, ip_address, close_callback)
def on_connection_closed(self, connection_handler: ConnectionHandlerType) -> None:
# No-op by default; can be extended if needed
pass

View File

@ -0,0 +1,10 @@
from dataclasses import dataclass
@dataclass(frozen=True)
class IpAddress:
host: str
port: int
def __str__(self) -> str:
return f"{self.host}:{self.port}"

View File

@ -0,0 +1,33 @@
import logging
import socket
from typing import Literal
logger = logging.getLogger(__name__)
BYTE_ORDER: Literal['little', 'big'] = 'big'
MESSAGE_SIZE_BYTES = 4
MESSAGE_TYPE_BYTES = 4
def encode_message(message_type: int, message: bytes) -> bytes:
message_size: int = len(message) + MESSAGE_TYPE_BYTES
output_stream: bytes = message_size.to_bytes(MESSAGE_SIZE_BYTES, byteorder=BYTE_ORDER)
output_stream += message_type.to_bytes(MESSAGE_TYPE_BYTES, byteorder=BYTE_ORDER)
output_stream += message
return output_stream
def read_message(socket_fd: socket.socket) -> tuple[int, bytes]:
raw_msg_len = socket_fd.recv(MESSAGE_SIZE_BYTES)
if not raw_msg_len:
raise BrokenPipeError("No data on socket")
msg_len = int.from_bytes(raw_msg_len, byteorder=BYTE_ORDER)
logger.debug(f"Received expected message length: {msg_len}")
raw_msg = socket_fd.recv(msg_len)
logger.debug(f"Actual message length: {len(raw_msg)}")
message_type = int.from_bytes(raw_msg[:MESSAGE_TYPE_BYTES], byteorder=BYTE_ORDER)
message = raw_msg[MESSAGE_TYPE_BYTES:]
return message_type, message

0
src/connection/py.typed Normal file
View File

View File

@ -0,0 +1,187 @@
from dataclasses import dataclass
import socket
import selectors
import errno
import logging
from typing import Callable
from connection import message_codec
from connection.connection_handler import ConnectionHandler, ConnectionHandlerFactory, ConnectionHandlerType
from connection.ip_address import IpAddress
from enum import Enum
logger = logging.getLogger(__name__)
NO_TIMEOUT: int | None = None
class _TcpServerContextManager:
def __init__(self, close_callback: Callable[[], None]) -> None:
self.close_callback = close_callback
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback) -> None:
self.close_callback()
class _ConnectionType(Enum):
SERVER = 1
CLIENT = 2
@dataclass
class _ConnectionData:
connection_type: _ConnectionType
handler_factory: ConnectionHandlerFactory
handler: ConnectionHandler | None = None # Only used for client connections
_LambdaConnectionHandlerFactory = Callable[[socket.socket, IpAddress, Callable[[], None]], ConnectionHandlerType]
class _LambdaConnectionHandlerFactoryWrapper(ConnectionHandlerFactory[ConnectionHandlerType]):
def __init__(self, factory: _LambdaConnectionHandlerFactory) -> None:
self.factory = factory
def on_new_connection(self, socket_fd: socket.socket, ip_address: IpAddress,
close_callback: Callable[[], None]) -> ConnectionHandlerType:
return self.factory(socket_fd, ip_address, close_callback)
def on_connection_closed(self, connection_handler: ConnectionHandlerType) -> None:
pass
class TcpConnectionManager:
def __init__(self) -> None:
self.socket_selector = selectors.DefaultSelector()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback) -> None:
self.socket_selector.close()
# TODO is this the right way to close the connection?
# for connection_handler in self.connection_map.values():
# connection_handler.__exit__(exc_type, exc_value, traceback)
def listen(self, ip_address: IpAddress, handler_factory: ConnectionHandlerFactory | _LambdaConnectionHandlerFactory) -> _TcpServerContextManager:
"""
Starts a non-blocking TCP/IP server on the specified host and port.
"""
if callable(handler_factory):
handler_factory = _LambdaConnectionHandlerFactoryWrapper(handler_factory)
logger.info(f"Starting server on {ip_address}")
server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allow address reuse
server_socket.bind((ip_address.host, ip_address.port))
server_socket.listen()
server_socket.setblocking(False)
logger.info(f'Listening on {ip_address}')
connection_data = _ConnectionData(_ConnectionType.SERVER, handler_factory)
self.socket_selector.register(server_socket, selectors.EVENT_READ, data=connection_data)
logger.info(f"Server started")
close_callback = lambda: self._close_socket(server_socket, ip_address)
return _TcpServerContextManager(close_callback)
def connect(self, ip_address: IpAddress,
handler_factory: ConnectionHandlerFactory[ConnectionHandlerType] | _LambdaConnectionHandlerFactory) -> ConnectionHandlerType:
if callable(handler_factory):
handler_factory = _LambdaConnectionHandlerFactoryWrapper(handler_factory)
logger.info(f"Connecting to {ip_address}")
conn_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
result = conn_socket.connect_ex((ip_address.host, ip_address.port))
if result != 0:
raise ConnectionError(f"Failed to connect to {ip_address}: {errno.errorcode[result]}")
logger.info(f"Connected to {ip_address}")
connection_handler = self._on_new_connection(conn_socket, ip_address, handler_factory)
return connection_handler
def wait_for_events(self, timeout_in_seconds: float | None = NO_TIMEOUT) -> int:
"""
Check for events on the server socket and client sockets.
Call this method in a loop to keep the server running.
@param timeout_in_seconds: The time in seconds to wait for events before returning. Zero means non-blocking.
@return: The number of events that occurred.
"""
logger.debug(f"Checking for socket events with timeout {timeout_in_seconds}")
events = self.socket_selector.select(timeout=timeout_in_seconds)
logger.debug(f"Received {len(events)} events")
for key, mask in events:
assert isinstance(key.data, _ConnectionData)
connection_data = key.data
if connection_data.connection_type == _ConnectionType.SERVER:
assert isinstance(key.fileobj, socket.socket)
assert connection_data.handler_factory is not None
self._accept_client(key.fileobj, connection_data.handler_factory)
elif mask & selectors.EVENT_READ:
self._read_from_socket(key)
else:
raise ValueError(f"Unexpected event mask {mask}")
logger.debug(f"Done checking for socket events")
return len(events)
def _accept_client(self, socket_fd: socket.socket, handler_factory: ConnectionHandlerFactory) -> None:
client_socket, address_info = socket_fd.accept()
logger.info(f"Accepted connection from {address_info}")
assert isinstance(address_info, tuple) and len(address_info) == 2
ip_address = IpAddress(host=address_info[0], port=address_info[1])
self._on_new_connection(client_socket, ip_address, handler_factory)
def _on_new_connection(self, client_socket_fd: socket.socket, ip_address: IpAddress,
handler_factory: ConnectionHandlerFactory[ConnectionHandlerType]) -> ConnectionHandlerType:
logger.debug(f"Setting up client connection with {ip_address}")
client_socket_fd.setblocking(False)
close_callback = lambda: self._close_socket(client_socket_fd, ip_address)
client_connection = handler_factory.on_new_connection(client_socket_fd, ip_address, close_callback)
connection_data = _ConnectionData(_ConnectionType.CLIENT, handler_factory, handler=client_connection)
self.socket_selector.register(client_socket_fd, selectors.EVENT_READ, data=connection_data)
logger.debug(f"Done setting up client connection with {ip_address}")
return client_connection
def _read_from_socket(self, key: selectors.SelectorKey) -> None:
socket_fd: socket.socket = key.fileobj # type: ignore
connection_data: _ConnectionData = key.data
assert connection_data.connection_type == _ConnectionType.CLIENT
assert connection_data.handler is not None
client_connection = connection_data.handler
ip_address = client_connection.ip_address
logger.debug(f"Reading from socket of {ip_address}")
try:
message_type, message = message_codec.read_message(socket_fd)
logger.debug(f"Received message type {message_type} with length {len(message)}")
except (BrokenPipeError, ConnectionResetError) as e:
logger.warning(f"Error while reading from {ip_address}: {str(e)}. Client will be disconnected")
self._close_socket(socket_fd, ip_address)
return
try:
client_connection.handle_message(message_type, message)
except Exception:
logger.exception(f"Error while handling message from {ip_address}. Client will be disconnected")
self._close_socket(socket_fd, ip_address)
raise
logger.debug(f"Done handling message")
def _close_socket(self, socket_fd: socket.socket, ip_address: IpAddress) -> None:
logger.debug(f"Closing socket on {ip_address}...")
connection_data: _ConnectionData = self.socket_selector.get_key(socket_fd).data
self.socket_selector.unregister(socket_fd)
socket_fd.close()
logger.info(f"Socket on {ip_address} closed")
if connection_data.connection_type == _ConnectionType.CLIENT:
assert connection_data.handler is not None
client_connection = connection_data.handler
client_connection.on_disconnect()
connection_data.handler_factory.on_connection_closed(client_connection)

26
src/sample_app/README.md Normal file
View File

@ -0,0 +1,26 @@
# Sample App
This is a sample application for the Stock Exchange training program.
## Quick Tip: Connecting to the Server via Command Line (Linux)
1. **Connect to the Server**
Use `telnet` to connect to the server:
```bash
telnet <server_address> <port>
```
Replace `<server_address>` and `<port>` with the appropriate values.
2. **Send Messages**
Once connected, type your message and press `Enter` to send it to the server.
3. **View Responses**
The server will echo back the messages you send.
### Example
```bash
telnet localhost 8080
Hello, Server!
# Server response
```

View File

View File

@ -0,0 +1,31 @@
import logging
import socket
from typing import Callable
from connection import message_codec
from connection.connection_handler import ConnectionHandler, ConnectionHandlerFactory
from connection.ip_address import IpAddress
logger = logging.getLogger(__name__)
class PingPongClientHandler(ConnectionHandler):
def on_disconnect(self) -> None:
logger.info(f"Client {self.ip_address} disconnected")
def handle_message(self, message_type: int, message: bytes) -> None:
logger.info(f"Received message of type {message_type}")
logger.debug(f"Message: {str(message)}")
logger.info("Bouncing message back to client")
encoded_message = message_codec.encode_message(message_type, message)
self._send_message(encoded_message)
logger.info("Message bounced back")
class PingPongClientHandlerFactory(ConnectionHandlerFactory[PingPongClientHandler]):
def on_new_connection(self, socket_fd: socket.socket, ip_address: IpAddress,
close_callback: Callable[[], None]) -> PingPongClientHandler:
return PingPongClientHandler(socket_fd, ip_address, close_callback)
def on_connection_closed(self, connection_handler: PingPongClientHandler):
pass

35
src/sample_app/main.py Normal file
View File

@ -0,0 +1,35 @@
import logging
from pathlib import Path
from application.application import BaseApplication
from connection.ip_address import IpAddress
from connection.tcp_connection_manager import TcpConnectionManager
from sample_app.connection_handler import PingPongClientHandlerFactory
logger = logging.getLogger(__name__)
class SampleApplication(BaseApplication):
def _start(self) -> None:
logger.info("Starting the sample application...")
connection_handler_factory = PingPongClientHandlerFactory()
tcp_connection_manager = TcpConnectionManager()
server_ip_address = IpAddress(
host=self._config["listenOn"]["host"],
port=self._config["listenOn"]["port"])
logger.info(f"Starting server on {server_ip_address}")
with tcp_connection_manager.listen(server_ip_address, connection_handler_factory):
logger.info("Server started.")
logger.info("Running event loop until interrupted.")
while True:
tcp_connection_manager.wait_for_events()
def main() -> None:
config_schema_path = Path(__file__).parent.parent / "application" / "config_schema.json"
app = SampleApplication(config_schema=config_schema_path, app_name="sample_app")
app.run()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,18 @@
{
"logLevel": "DEBUG",
"logDirectory": "./logs",
"listenOn": {
"host": "localhost",
"port": 51301
},
"connectTo": {
"client1": {
"host": "localhost",
"port": 51302
},
"client2": {
"host": "localhost",
"port": 51303
}
}
}

0
tests/__init__.py Normal file
View File

View File

@ -0,0 +1,25 @@
from connection.message_codec import BYTE_ORDER, MESSAGE_SIZE_BYTES, MESSAGE_TYPE_BYTES, encode_message
class TestEncodeMessage:
def test_encodes_empty_payload(self):
result = encode_message(message_type=1, message=b"")
expected_size = MESSAGE_TYPE_BYTES.to_bytes(MESSAGE_SIZE_BYTES, byteorder=BYTE_ORDER)
expected_type = (1).to_bytes(MESSAGE_TYPE_BYTES, byteorder=BYTE_ORDER)
assert result == expected_size + expected_type
def test_encodes_payload(self):
payload = b"\x0a\x0b\x0c"
result = encode_message(message_type=42, message=payload)
expected_size = (MESSAGE_TYPE_BYTES + len(payload)).to_bytes(MESSAGE_SIZE_BYTES, byteorder=BYTE_ORDER)
expected_type = (42).to_bytes(MESSAGE_TYPE_BYTES, byteorder=BYTE_ORDER)
assert result == expected_size + expected_type + payload
def test_size_field_excludes_itself(self):
payload = b"\xff"
result = encode_message(message_type=0, message=payload)
size_field = int.from_bytes(result[:MESSAGE_SIZE_BYTES], byteorder=BYTE_ORDER)
assert size_field == MESSAGE_TYPE_BYTES + len(payload)

494
uv.lock Normal file
View File

@ -0,0 +1,494 @@
version = 1
revision = 2
requires-python = ">=3.11"
[[package]]
name = "attrs"
version = "25.3.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" },
]
[[package]]
name = "flake8"
version = "7.3.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "mccabe" },
{ name = "pycodestyle" },
{ name = "pyflakes" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9b/af/fbfe3c4b5a657d79e5c47a2827a362f9e1b763336a52f926126aa6dc7123/flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9f/56/13ab06b4f93ca7cac71078fbe37fcea175d3216f31f85c3168a6bbd0bb9a/flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e" },
]
[[package]]
name = "flake8-mypy"
version = "17.8.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "attrs" },
{ name = "flake8" },
{ name = "mypy" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/97/9a/cddd1363d7314bb4eb452089c6fb3092ed9fda9f3350683d1978522a30ec/flake8-mypy-17.8.0.tar.gz", hash = "sha256:47120db63aff631ee1f84bac6fe8e64731dc66da3efc1c51f85e15ade4a3ba18" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a8/5d/b172de53870fc6fa2336a6f55dca0d67a70e2bf31218ed160aea3239e644/flake8_mypy-17.8.0-py35.py36-none-any.whl", hash = "sha256:cff009f4250e8391bf48990093cff85802778c345c8449d6498b62efefeebcbc" },
]
[[package]]
name = "iniconfig"
version = "2.1.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" },
]
[[package]]
name = "jsonschema"
version = "4.26.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "attrs" },
{ name = "jsonschema-specifications" },
{ name = "referencing" },
{ name = "rpds-py" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce" },
]
[[package]]
name = "jsonschema-specifications"
version = "2024.10.1"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "referencing" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" },
]
[[package]]
name = "librt"
version = "0.8.1"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/1d/01/0e748af5e4fee180cf7cd12bd12b0513ad23b045dccb2a83191bde82d168/librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9d/4d/7184806efda571887c798d573ca4134c80ac8642dcdd32f12c31b939c595/librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ae/88/c3c52d2a5d5101f28d3dc89298444626e7874aa904eed498464c2af17627/librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d6/5d/6fb0a25b6a8906e85b2c3b87bee1d6ed31510be7605b06772f9374ca5cb3/librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b2/a6/8006ae81227105476a45691f5831499e4d936b1c049b0c1feb17c11b02d1/librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ee/19/60e07886ad16670aae57ef44dada41912c90906a6fe9f2b9abac21374748/librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9c/cf/f666c89d0e861d05600438213feeb818c7514d3315bae3648b1fc145d2b6/librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/8f/ef/f1bea01e40b4a879364c031476c82a0dc69ce068daad67ab96302fed2d45/librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9b/80/cdab544370cc6bc1b72ea369525f547a59e6938ef6863a11ab3cd24759af/librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9d/9c/48d6ed8dac595654f15eceab2035131c136d1ae9a1e3548e777bb6dbb95d/librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/16/01/35b68b1db517f27a01be4467593292eb5315def8900afad29fabf56304ba/librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/71/02/796fe8f02822235966693f257bf2c79f40e11337337a657a8cfebba5febc/librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/28/ad/232e13d61f879a42a4e7117d65e4984bb28371a34bb6fb9ca54ec2c8f54e/librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61" },
]
[[package]]
name = "mccabe"
version = "0.7.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" },
]
[[package]]
name = "mypy"
version = "1.19.1"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
{ name = "mypy-extensions" },
{ name = "pathspec" },
{ name = "typing-extensions" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247" },
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d" },
]
[[package]]
name = "mypy-protobuf"
version = "5.0.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "protobuf" },
{ name = "types-protobuf" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d5/48/658827446368bca30a94e545598065587ece9cd09b678d7d2895c37a59d2/mypy_protobuf-5.0.0.tar.gz", hash = "sha256:6fdd1cfdbb4419c713291d800a332d4bba6510dbd1341ed95e0bcc82fcadb6b5" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b4/b1/ab1e7a49930a8c1d1f7a570bbd4ec7d552ef035acc7aa4b97906e17a34a9/mypy_protobuf-5.0.0-py3-none-any.whl", hash = "sha256:3a7dd753ef3e3b8783a824eb51f07983f62812f9ec066e4fbb1b22d6c5dc36d0" },
]
[[package]]
name = "optivex"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "flake8" },
{ name = "flake8-mypy" },
{ name = "jsonschema" },
{ name = "mypy" },
{ name = "mypy-protobuf" },
{ name = "protobuf" },
{ name = "pytest" },
{ name = "types-jsonschema" },
{ name = "types-protobuf" },
]
[package.metadata]
requires-dist = [
{ name = "flake8", specifier = ">=7.3.0" },
{ name = "flake8-mypy", specifier = ">=17.8.0" },
{ name = "jsonschema", specifier = ">=4.26.0" },
{ name = "mypy", specifier = ">=1.19.1" },
{ name = "mypy-protobuf", specifier = ">=5.0.0" },
{ name = "protobuf", specifier = ">=7.34.0" },
{ name = "pytest", specifier = ">=9.0.2" },
{ name = "types-jsonschema", specifier = ">=4.23.0.20241208" },
{ name = "types-protobuf", specifier = ">=6.32.1.20260221" },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759" },
]
[[package]]
name = "pathspec"
version = "1.0.4"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723" },
]
[[package]]
name = "pluggy"
version = "1.5.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" },
]
[[package]]
name = "protobuf"
version = "7.34.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f2/00/04a2ab36b70a52d0356852979e08b44edde0435f2115dc66e25f2100f3ab/protobuf-7.34.0.tar.gz", hash = "sha256:3871a3df67c710aaf7bb8d214cc997342e63ceebd940c8c7fc65c9b3d697591a" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/13/c4/6322ab5c8f279c4c358bc14eb8aefc0550b97222a39f04eb3c1af7a830fa/protobuf-7.34.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e329966799f2c271d5e05e236459fe1cbfdb8755aaa3b0914fa60947ddea408" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/45/99/b029bbbc61e8937545da5b79aa405ab2d9cf307a728f8c9459ad60d7a481/protobuf-7.34.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:9d7a5005fb96f3c1e64f397f91500b0eb371b28da81296ae73a6b08a5b76cdd6" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/cc/79/09f02671eb75b251c5550a1c48e7b3d4b0623efd7c95a15a50f6f9fc1e2e/protobuf-7.34.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4a72a8ec94e7a9f7ef7fe818ed26d073305f347f8b3b5ba31e22f81fd85fca02" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b5/57/89727baef7578897af5ed166735ceb315819f1c184da8c3441271dbcfde7/protobuf-7.34.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:964cf977e07f479c0697964e83deda72bcbc75c3badab506fb061b352d991b01" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/1f/3e/38ff2ddee5cc946f575c9d8cc822e34bde205cf61acf8099ad88ef19d7d2/protobuf-7.34.0-cp310-abi3-win32.whl", hash = "sha256:f791ec509707a1d91bd02e07df157e75e4fb9fbdad12a81b7396201ec244e2e3" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/cb/71/7c32eaf34a61a1bae1b62a2ac4ffe09b8d1bb0cf93ad505f42040023db89/protobuf-7.34.0-cp310-abi3-win_amd64.whl", hash = "sha256:9f9079f1dde4e32342ecbd1c118d76367090d4aaa19da78230c38101c5b3dd40" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a4/e7/14dc9366696dcb53a413449881743426ed289d687bcf3d5aee4726c32ebb/protobuf-7.34.0-py3-none-any.whl", hash = "sha256:e3b914dd77fa33fa06ab2baa97937746ab25695f389869afdf03e81f34e45dc7" },
]
[[package]]
name = "pycodestyle"
version = "2.14.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d" },
]
[[package]]
name = "pyflakes"
version = "3.4.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" },
]
[[package]]
name = "pytest"
version = "9.0.2"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b" },
]
[[package]]
name = "referencing"
version = "0.36.2"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "attrs" },
{ name = "rpds-py" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0" },
]
[[package]]
name = "rpds-py"
version = "0.30.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4" },
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e" },
]
[[package]]
name = "types-jsonschema"
version = "4.23.0.20241208"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
dependencies = [
{ name = "referencing" },
]
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/ad/e6/9e5cd771687086844caa43dbb211ec0d1cfa899d17c110f3220efcd46e83/types_jsonschema-4.23.0.20241208.tar.gz", hash = "sha256:e8b15ad01f290ecf6aea53f93fbdf7d4730e4600313e89e8a7f95622f7e87b7c" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/91/64/4b2fba8b7cb0104ba013f2a1bf6f39a98e927e14befe1ef947d373b25218/types_jsonschema-4.23.0.20241208-py3-none-any.whl", hash = "sha256:87934bd9231c99d8eff94cacfc06ba668f7973577a9bd9e1f9de957c5737313e" },
]
[[package]]
name = "types-protobuf"
version = "6.32.1.20260221"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/5f/e2/9aa4a3b2469508bd7b4e2ae11cbedaf419222a09a1b94daffcd5efca4023/types_protobuf-6.32.1.20260221.tar.gz", hash = "sha256:6d5fb060a616bfb076cbb61b4b3c3969f5fc8bec5810f9a2f7e648ee5cbcbf6e" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/2e/e8/1fd38926f9cf031188fbc5a96694203ea6f24b0e34bd64a225ec6f6291ba/types_protobuf-6.32.1.20260221-py3-none-any.whl", hash = "sha256:da7cdd947975964a93c30bfbcc2c6841ee646b318d3816b033adc2c4eb6448e4" },
]
[[package]]
name = "typing-extensions"
version = "4.13.0"
source = { registry = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/simple" }
sdist = { url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/0e/3e/b00a62db91a83fff600de219b6ea9908e6918664899a2d85db222f4fbf19/typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b" }
wheels = [
{ url = "https://optiver.jfrog.io/artifactory/api/pypi/pypi/packages/packages/e0/86/39b65d676ec5732de17b7e3c476e45bb80ec64eb50737a8dce1a4178aba1/typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5" },
]