mts1b-oms — public API surface
OMS state machine, broker routing, position store. REST + gRPC + NATS interfaces.
Python client
from mts1b_oms import OmsClient
oms = OmsClient(base_url="http://localhost:8001")
# Submit an order
order = Order(...)
result = await oms.submit(order)
# Cancel
ok = await oms.cancel(order.order_id)
# Inspect
order = await oms.get_order(order.order_id)
positions = await oms.get_positions(fund_id="paper-momentum")
open_orders = await oms.list_open_orders(fund_id="paper-momentum")
# Stream updates (NATS-backed websocket)
async for update in oms.stream_updates(fund_id="paper-momentum"):
print(update)
REST endpoints
POST /v1/orders — submit
POST /v1/orders
{
"order_id": "ord-abc",
"idempotency_key": "strategy-v7-2026-05-23-AAPL-long",
"symbol": "AAPL",
"side": "buy",
"quantity": "100",
"order_type": "limit",
"limit_price": "180.50",
"tif": "day",
"fund_id": "paper-momentum",
"strategy_id": "momentum_v1",
"broker": "paper",
"actor": "research_signal_executor",
"created_at": "2026-05-23T16:00:00Z"
}
→ 202 Accepted
{
"order_id": "ord-abc",
"state": "PENDING_RISK",
"submitted_at": null
}
DELETE /v1/orders/{order_id} — cancel
DELETE /v1/orders/ord-abc
→ 200 OK
{
"order_id": "ord-abc",
"state": "CANCELED",
"canceled_at": "2026-05-23T16:01:00Z"
}
GET /v1/orders/{order_id} — fetch
GET /v1/orders/ord-abc
→ 200 OK
{
"order_id": "ord-abc",
"state": "FILLED",
"submitted_at": "2026-05-23T16:00:01Z",
"accepted_at": "2026-05-23T16:00:01Z",
...
}
GET /v1/positions?fund_id=X — list positions
GET /v1/positions?fund_id=paper-momentum
→ 200 OK
[
{"symbol": "AAPL", "quantity": "100", "avg_price": "180.50", ...},
...
]
GET /v1/funds/{fund_id}/nav — current NAV
GET /v1/funds/paper-momentum/nav
→ 200 OK
{
"fund_id": "paper-momentum",
"asof": "2026-05-23",
"nav_usd": "102400.00",
...
}
gRPC
service Oms {
rpc Submit(Order) returns (Order);
rpc Cancel(CancelRequest) returns (Order);
rpc GetOrder(OrderId) returns (Order);
rpc GetPositions(FundId) returns (PositionList);
rpc StreamUpdates(FundId) returns (stream OrderUpdate);
}
from grpc import aio
import oms_pb2, oms_pb2_grpc
async with aio.insecure_channel("localhost:50051") as ch:
stub = oms_pb2_grpc.OmsStub(ch)
request = oms_pb2.Order(order_id="ord-abc", ...)
response = await stub.Submit(request)
NATS subjects
Subscribed (consumed)
| Subject | Payload |
|---|---|
mts.v1.brokers.<broker>.fills.raw | broker-native fill dict |
mts.v1.risk.envelope.updated | RiskEnvelope (reloads on change) |
mts.v1.risk.halt.requested | HaltRequest (stops accepting orders) |
Published
| Subject | Payload |
|---|---|
mts.v1.oms.orders.created | Order (initial state) |
mts.v1.oms.orders.accepted | Order (passed risk) |
mts.v1.oms.orders.submitted | Order (sent to broker) |
mts.v1.oms.orders.filled | Order (terminal) |
mts.v1.oms.orders.partial | Order (partial fill) |
mts.v1.oms.orders.canceled | Order |
mts.v1.oms.orders.rejected | Order (with rejected_reason) |
mts.v1.oms.orders.closed | Order (terminal cleanup) |
mts.v1.oms.fills.created | Fill |
State machine
CREATED ──▶ PENDING_RISK ──┬─▶ REJECTED (terminal)
│
▼
ACCEPTED ──┬─▶ CANCELED (before submit)
│ │
▼ │
SUBMITTED ──┴─▶ FILLED → CLOSED (terminal)
└─▶ PARTIAL → FILLED → CLOSED
└─▶ EXPIRED (terminal)
Transitions are owned by OMS exclusively. Other services see them via NATS events.
Routing
# Configuration (mts1b.config or per-fund)
routing:
default_broker: paper
large_order_threshold_usd: 100000
use_execalgos:
enabled: true
default_algo: vwap
horizon_minutes: 30
Large orders auto-route through mts1b-oms-algos (VWAP/TWAP/Almgren-Chriss/Iceberg/IS). Each child order goes through the full pipeline including risk gates.
Position store
# Internal (used by OMS workers)
from mts1b_oms.positions.store import PositionStore
store = PositionStore(dsn=...)
pos = await store.get(fund_id="paper-momentum", symbol=Symbol("AAPL"))
await store.apply_fill(fill)