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

7.1 KiB

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 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 for details.

  3. Read 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:

[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
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).
    • protocols — which protocols this component implements (admin, order_book, info, execution, risk_limits).
    • config — the component's runtime config (see Component Configuration).
  • systemTests — which test suites to run (see 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:

"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