Building a CCM Adapter¶
A CCM (Capital & Connectivity Manager) plugin connects Meridian to a broker or trading venue. This guide walks through building one from scratch.
When to Build a CCM¶
Build a CCM when you need to connect to a broker or venue that doesn't already have a Meridian adapter. Each CCM handles one venue — if you trade through three brokers, you deploy three CCM instances.
Project Setup¶
Or start from the SDK directly:
Required Methods¶
Handle Incoming Orders¶
@plugin.on_order
async def handle_order(**data):
"""Called when EMS routes a child order to this venue."""
symbol = data["security_id"]
side = data["side"]
quantity = data["quantity"]
price = data.get("price")
# Send to your broker's API
venue_order_id = await broker_api.submit(symbol, side, quantity, price)
# When the broker fills, publish back
await plugin.submit_fill(symbol, side, quantity, fill_price)
Report Session Status¶
@plugin.on_ready
async def on_ready():
await plugin.report_session_status("CONNECTED", "Session established")
@plugin.on_shutdown
async def on_shutdown():
await plugin.report_session_status("DISCONNECTED", "Shutting down")
Fill Requirements¶
Every fill you publish must include:
| Field | Required | Description |
|---|---|---|
security_id |
Yes | Instrument identifier |
side |
Yes | BUY or SELL |
quantity |
Yes | Filled quantity |
price |
Yes | Fill price |
The sidecar automatically adds:
- ts_init — when the sidecar received the fill
- Deduplication key — composite of (trade_id, side, price, quantity, venue)
Session Management¶
Report status changes so the EMS knows whether to route orders to you:
| Status | When |
|---|---|
CONNECTED |
Session established, ready for orders |
DISCONNECTED |
Session lost, cannot accept orders |
DEGRADED |
Partial connectivity (e.g., can receive but not send) |
RECONNECTING |
Attempting to reconnect (exponential backoff) |
Testing¶
Use FakeSidecar to test without a broker connection:
from meridian import CCMPlugin
from meridian.testing import FakeSidecar
sidecar = FakeSidecar()
plugin = CCMPlugin("test", instance_id="test-ccm", sidecar=sidecar)
await plugin.submit_fill("AAPL", "BUY", 100, 150.0)
assert sidecar.messages[0].topic == "platform.ccm.test-ccm.fill.AAPL"
Common Mistakes¶
- Not handling reconnection (broker connections drop — implement exponential backoff)
- Not publishing session status changes (EMS doesn't know to stop routing)
- Duplicate fills (ensure your broker API deduplicates, or track locally)
Next Steps¶
- Conformance Tests — tests your CCM must pass
- Container Contract — how to containerize for deployment
- Marketplace Submission — publish to the registry