gtat-tech-career-kickstarte.../README.md

123 lines
7.1 KiB
Markdown

# 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` |