Skip to content

Repository files navigation

Order Splitter

Small REST API for a robo-advisor style order splitter: you send a BUY or SELL with a total dollar amount and a weighted portfolio; the service resolves prices, figures advisory scheduling against market hours, splits the order into per-ticker lines, and stores everything in memory (no database).


How to run

You’ll need Node.js (this repo is on TypeScript 6 / modern Node — 20+ is a safe bet).

npm install
cp .env.example .env   # optional; defaults match .env.example anyway
npm run build
npm start

The server listens on PORT from env (default 3000). You should see something like Server listening on port 3000.

Dev (auto-reload on src changes):

npm run dev

Tests:

npm test

Architecture (short)

Requests hit Express middleware first (json body, clock, response-time logging), then order routesOrderController. The controller parses query params for list, validates the POST body with OrderRequestValidator, and delegates to OrderService.

OrderService is the orchestration layer: it uses PriceResolver (fixed default vs per-line market price), MarketScheduler (Luxon + market window) for scheduling, OrderSplitter for per-ticker quantities and rounding, and InMemoryOrderRepository for persistence. Domain types live under src/domain/; ValidationError / NotFoundError are mapped to HTTP status in the global errorHandler.

Rough flow for creating an order:

sequenceDiagram
  participant C as Client
  participant X as Express
  participant Ctrl as OrderController
  participant V as OrderRequestValidator
  participant S as OrderService
  participant P as PriceResolver
  participant M as MarketScheduler
  participant Sp as OrderSplitter
  participant R as InMemoryOrderRepository

  C->>X: POST /orders JSON
  X->>Ctrl: createOrder
  Ctrl->>V: validate body
  V-->>Ctrl: ok
  Ctrl->>S: createOrder
  S->>P: resolve prices
  P-->>S: per-line prices
  S->>M: advisory scheduledAt
  M-->>S: instant
  S->>Sp: split order
  Sp-->>S: splits
  S->>R: save
  R-->>S: stored order
  S-->>Ctrl: result
  Ctrl-->>C: 201 + body
Loading

Layered view (same idea, flatter):

flowchart LR
  HTTP[HTTP / Express] --> OC[OrderController]
  OC --> OV[OrderRequestValidator]
  OC --> OSvc[OrderService]
  OSvc --> PR[PriceResolver]
  OSvc --> MS[MarketScheduler]
  OSvc --> OSp[OrderSplitter]
  OSvc --> Repo[InMemoryOrderRepository]
Loading

Request examples

Replace the URL host/port if you changed PORT.

Health

curl -s http://localhost:3000/health

Create an order (POST)

One JSON object = one order. There is no bulk endpoint; fire multiple POSTs if you need several orders.

curl -s -X POST http://localhost:3000/orders \
  -H "Content-Type: application/json" \
  -d '{
    "type": "BUY",
    "totalAmount": 100,
    "portfolio": [
      { "ticker": "AAPL", "weight": 60, "marketPrice": 189.5 },
      { "ticker": "TSLA", "weight": 40 }
    ]
  }'

Notes that tend to matter in practice:

  • marketPrice on a line is optional; if omitted, the server uses a configured default (see .env.example: DEFAULT_STOCK_PRICE).
  • type is BUY or SELL only.

List orders (GET) with optional filters

# everything stored so far
curl -s "http://localhost:3000/orders"

# BUY only
curl -s "http://localhost:3000/orders?type=BUY"

# date range: from / to are UTC calendar dates (YYYY-MM-DD), inclusive on createdAt
curl -s "http://localhost:3000/orders?from=2026-01-01&to=2026-12-31"

# combined
curl -s "http://localhost:3000/orders?type=SELL&from=2026-04-01&to=2026-04-30"

Get one order by id

Use the id from the POST response:

curl -s "http://localhost:3000/orders/<paste-uuid-here>"

Optional: simulated “now” (non-production)

If NODE_ENV is not production, you can send an ISO-8601 instant so scheduling logic uses that time instead of the real clock:

curl -s -X POST http://localhost:3000/orders \
  -H "Content-Type: application/json" \
  -H "X-Simulated-Time: 2026-04-06T17:00:00.000Z" \
  -d '{"type":"BUY","totalAmount":10,"portfolio":[{"ticker":"X","weight":100}]}'

Postman

There’s an importable collection under postman/Order_Splitter.postman_collection.json if you prefer clicking instead of curl.

Written answers to the take-home prompts (approach, assumptions, challenges, production considerations, LLM use) are in ANSWERS.md.


Configuration

See .env.example for all variables. The important ones:

Variable Role
PORT HTTP port
DEFAULT_STOCK_PRICE Fallback $/share when request omits marketPrice
SHARE_QUANTITY_PRECISION / SHARE_QUANTITY_ROUNDING_MODE How split share quantities are rounded
MARKET_TIMEZONE, MARKET_OPEN_TIME, MARKET_CLOSE_TIME Session used for advisory scheduledAt

Dependencies

Runtime

Package What it’s for
express HTTP server and routing
luxon Dates/times and market session math (timezone-aware)
uuid Generating order ids
dotenv Loading .env into process.env at startup

Development / testing

Package What it’s for
typescript Typed JS, tsc build
ts-node Run TS directly in npm run dev
nodemon Restart dev server on file changes
jest + ts-jest Unit/integration tests
supertest HTTP assertions against the Express app
@types/* TypeScript typings for the libraries above

That’s the stack the assignment asked to document; versions are pinned in package.json.


Repo layout (quick pointer)

  • src/server.ts — listen + config load
  • src/app.ts — app factory, wires services and routes
  • src/routes/ — route registration
  • src/controllers/, src/services/, src/repositories/, src/validation/ — usual split
  • tests/ — Jest tests (unit + integration)

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages