Provides liquidity by quoting bid and ask around the best bid-offer (BBO) from Coinbase Advanced Trade Level 2 data — with inventory skew and a circuit breaker when risk limits are hit.
L2 WebSocket (or synthetic BBO)
│
▼
┌───────────────┐
│ Quote engine │ spread + inventory skew
└───────┬───────┘
│
▼
┌───────────────┐
│ Risk layer │ inventory cap · daily loss breaker
└───────┬───────┘
│
▼
Limit order refresh (sim logs today)
- Stream BBO from Coinbase L2 WebSocket (or synthetic prices in simulation).
- Build quotes at
SPREAD_BPSaround mid, skewed whenbaseInventorydrifts fromTARGET_BASE_INVENTORY. - Refresh quotes every
QUOTE_REFRESH_MS— throttle prevents quote spam. - Protect with inventory cap (
MAX_BASE_INVENTORY) and daily loss circuit breaker. - Persist risk state and last quote to Redis for restart continuity.
flowchart TB
START([bootstrap]) --> REDIS{Redis enabled?}
REDIS -->|yes| PING[pingRedis]
REDIS -->|no| MEM[In-memory cache]
PING --> LOAD[load mm-state]
MEM --> LOAD
LOAD --> STREAM{SIMULATION_MODE?}
STREAM -->|true| SYN[syntheticBboStream]
STREAM -->|false| L2[streamBbo L2 WS]
SYN --> ONBBo[onBbo handler]
L2 --> ONBBo
ONBBo --> HALT{circuit breaker?}
HALT -->|yes| PAUSE[Pause quoting]
HALT -->|no| INV{inventory cap?}
INV -->|exceeded| TRIP[tripBreaker + cooldown]
INV -->|ok| QUOTE[buildQuotes]
QUOTE --> CACHE[cache last-quote]
QUOTE --> SIM{simulation?}
SIM -->|yes| LOG["[sim] quote refresh"]
SIM -->|no| LIVE[Wire live orders]
When you accumulate too much base asset, the bot shifts quotes down (wider bid, tighter ask) to encourage sells and rebalance toward TARGET_BASE_INVENTORY. Skew is expressed in basis points (skewBps) in logs.
Neutral inventory Long inventory (too much BTC)
───────────────── ─────────────────────────────
bid ████████ ask bid ██████ ask ████████
↑ mid ↑ quotes shifted down
market-making-bot/
├── .env.example
├── package.json
├── tsconfig.json
├── README.md
│
├── scripts/
│ └── smoke-test.ts # Quote engine math smoke test
│
└── src/
├── index.ts # BBO stream hook, quote loop, shutdown
│
├── config/
│ ├── env.ts # Spread, inventory, breaker settings
│ └── logger.ts
│
├── cache/
│ ├── redis.ts # ioredis-xyz
│ └── store.ts # mm-state, last-quote
│
├── book/
│ ├── types.ts # BBO shape
│ └── level2.ts # L2 WebSocket + synthetic stream
│
├── engine/
│ └── quoter.ts # buildQuotes — spread + skew
│
└── risk/
└── circuit.ts # shouldHalt, tripBreaker, day roll
| Path | Responsibility |
|---|---|
src/book/level2.ts |
Real L2 subscription or deterministic synthetic BBO for sim |
src/engine/quoter.ts |
Computes bid/ask prices and size from BBO + inventory |
src/risk/circuit.ts |
Daily P&L tracking, halt until haltedUntil timestamp |
src/index.ts |
Wires stream → quoter; saves mm-state on breaker trip |
src/cache/* |
Keys: mm-state, last-quote (prefix cb-mm:) |
cp .env.example .env
npm install
npm run check
SIMULATION_MODE=true npm startnpm run dev # watch mode| Variable | Default | Description |
|---|---|---|
SIMULATION_MODE |
true |
Synthetic BBO stream |
PRODUCT_ID |
BTC-USD |
Coinbase product |
SPREAD_BPS |
10 |
Half-spread around mid (basis points) |
ORDER_SIZE |
0.001 |
Base size per quote side |
TARGET_BASE_INVENTORY |
0 |
Neutral inventory target |
MAX_BASE_INVENTORY |
0.01 |
Trips breaker if exceeded |
QUOTE_REFRESH_MS |
2000 |
Min ms between quote updates |
MAX_DAILY_LOSS_USD |
100 |
Circuit breaker loss threshold |
CIRCUIT_BREAKER_COOLDOWN_MS |
300000 |
Pause duration after trip |
Redis:
REDIS_URL=redis://localhost:6379
REDIS_ENABLED=true
REDIS_KEY_PREFIX=cb-mm:sequenceDiagram
participant WS as L2 / synthetic stream
participant IDX as index.ts
participant Q as quoter.ts
participant R as circuit.ts
participant C as cache
WS->>IDX: BBO { bid, ask }
IDX->>R: shouldHalt(state)?
R-->>IDX: ok / paused
IDX->>Q: buildQuotes(bbo, inventory)
Q-->>IDX: { bidPrice, askPrice, size, skewBps }
IDX->>C: cacheSet(last-quote)
IDX->>IDX: log [sim] or place orders
Live order placement is not wired in index.ts — extend with coinbase-api limit order calls when SIMULATION_MODE=false. Test skew and breaker behavior in simulation first.