Skip to content

SDK Core Concepts

This page covers the foundational concepts shared across all Meridian SDKs. Currently available in Python and Go, with Java, C++, and Rust coming soon.

Development Modes

Mode Infrastructure Use
Local None — everything in-process Strategy development, backtesting, unit testing
Connected Sidecar + bus + kernel (coming soon)
# Local mode
from meridian import LocalClient
client = LocalClient(starting_cash=100_000)

# Strategy code works identically in local and deployed environments

The Sidecar Protocol

When deployed, every plugin communicates with a sidecar on localhost:9191 via gRPC. The sidecar is the plugin's only external dependency.

Three Operations

Operation Method Description
Publish publish(topic, payload) Send a message to a topic
Subscribe subscribe(pattern) Receive messages matching a topic pattern
Request request(topic, payload) Send a command to the kernel, wait for a response

Message Envelope

Every message on the bus is wrapped in an envelope:

Field Type Description
topic string Where the message is going
payload bytes The message content (JSON or protobuf)
message_id string Unique per message
source_id string Which plugin sent it
timestamp_ns int64 Nanosecond-precision timestamp
trace_id string End-to-end correlation (shared across a workflow)
correlation_id string Links request to response

Plugin Lifecycle

Every deployed plugin goes through these states:

STARTING → REGISTERING → RECOVERING → READY → RUNNING → SHUTTING_DOWN → STOPPED
State What Happens
STARTING Plugin process starts, SDK initializes
REGISTERING Sidecar registers the plugin with the bus (sends instance metadata)
RECOVERING Sidecar replays missed messages from the durable store
READY Recovery complete, subscriptions active
RUNNING Normal operation — publishing, subscribing, handling messages
SHUTTING_DOWN Graceful shutdown — drain in-flight messages, close connections
STOPPED Plugin process exits

Lifecycle Hooks

SDKs provide hooks for each transition:

plugin = SignalPlugin("my-signal")

@plugin.on_ready
async def on_ready():
    print("Ready to process data")

@plugin.on_shutdown
async def on_shutdown():
    print("Shutting down gracefully")

plugin.run()
plugin := meridian.NewSignalPlugin("my-signal")

plugin.OnReady(func() {
    log.Println("Ready to process data")
})

plugin.Run()

Plugin Taxonomy

Type Publishes Subscribes Kernel API
DGM Market data (platform.data.*) Config updates None (data source)
Signal Alpha signals (platform.signal.*) Market data None (read-only)
Portfolio Targets (platform.portfolio.*) Signals, risk None (read-only)
OMS Cleared orders (platform.oms.*) Targets, fills Order CRUD, compliance
EMS Child orders (platform.ems.*) Cleared orders, fills Execution, routing
CCM Fills (platform.ccm.*) Child orders None (venue adapter)
BOR Positions, NAV (platform.bor.*) Fills, corp actions Position, cash, settlement

Topic Naming Convention

platform.{domain}.{scope}.{entity}.{action}
Segment Values Example
domain data, signal, oms, ems, ccm, bor, portfolio data
scope Instance ID or * for wildcard dgm-bloomberg
entity What the message is about eod (end-of-day price)
action What happened (optional) AAPL (symbol)

Examples:

platform.data.eod.AAPL              # EOD price for AAPL
platform.signal.momentum-1.alpha    # Alpha signal from momentum engine
platform.oms.main.order.cleared     # Order cleared by compliance
platform.ems.main.fill.parent       # Aggregated fill from EMS
platform.ccm.broker-a.fill.AAPL     # Raw fill from broker
platform.bor.main.position.changed  # Position update

Kernel API (Request/Reply)

Plugins interact with the kernel via synchronous request/reply commands through the sidecar:

# Create an order via the kernel
result = await plugin.create_order({
    "instrument_id": "AAPL",
    "direction": "BUY",
    "ordered_quantity": "100",
    "order_type": "MARKET",
})
print(result)  # {"order_id": "ORD-000001", "status": "CREATED"}
result, err := plugin.CreateOrder(meridian.Order{
    InstrumentID:    "AAPL",
    Direction:       meridian.DirectionBuy,
    OrderedQuantity: "100",
    OrderType:       meridian.OrderTypeMarket,
})

84 Kernel Operations

Both SDKs implement the same 84 kernel API operations across three surfaces:

Surface Operations Examples
OMS (22 ops) Order CRUD, compliance rules, allocation create_order, request_compliance_check, allocate_block
EMS (19 ops) Execution, strategy selection, RFQ, fills submit_execution, request_quotes, aggregate_fills
BOR (37 ops) Positions, cash, NAV, recon, settlement publish_positions, publish_cash_movement, publish_nav_calculated
Adapters (6 ops) DGM, CCM, Signal convenience methods add_price, submit_fill, publish_signal

The Domain API (Local Mode)

For local development, the LocalClient provides a higher-level domain API that wraps the kernel operations into simple, synchronous methods:

Method What it does
load_bars(symbol, source) Load CSV/Parquet/DataFrame bar data
submit_order(symbol, direction, quantity) Submit an order (fills at close price)
positions(symbol=None) Query current positions
cash(currency=None) Query cash balance
fills(start=None, end=None) Query fill history
journal(start=None, end=None) Query double-entry accounting journal
orders(status=None) Query order history
history(symbol, start, end) Query bar history

Code written against the LocalClient domain API will work identically when deployed. Connected mode (real-time data and live execution) is coming in a future release.

Next Steps