Strategy_Contract.md 2.1 KB

Strategy Contract

This is the canonical contract for trader-mcp strategies.

Purpose

A strategy defines behavior. It does not own persistence, lifecycle, or UI rendering.

Strategy class

class Strategy:
    LABEL = "Human readable name"
    CONFIG_SCHEMA = {}
    STATE_SCHEMA = {}
    TICK_MINUTES = 1.0

    def __init__(self, context, config):
        self.context = context
        self.config = config
        self.state = self.init()

Responsibilities

Strategy

  • decides what to do
  • reads config
  • mutates self.state
  • returns structured render data

Not strategy responsibilities

  • persistence
  • lifecycle control
  • config storage
  • identity ownership
  • direct access to engine internals

Configuration

  • CONFIG_SCHEMA is declarative metadata
  • config is supplied by the engine
  • config changes are handled by reload
  • config should stay read-only from the strategy perspective

State

  • self.state belongs to the instance
  • STATE_SCHEMA declares what state is durable / persisted
  • keep state serializable where practical
  • do not depend on hidden global state
  • the engine snapshots and restores state, not the strategy

Lifecycle

init() -> state created
on_tick() -> state updated
reload -> state restored from engine snapshot if available

Context

The context is capability-only.

Allowed:

  • market/data access
  • order placement
  • logging
  • engine-mediated actions
  • binding account_id and client_id into exec calls

Not allowed:

  • persistence
  • config storage
  • lifecycle decisions
  • strategy identity ownership

UI output

Strategies return structured widgets, not HTML.

{
  "widgets": [
    {"type": "metric", "label": "PnL", "value": 123.45},
    {"type": "line_chart", "data": [...]}
  ]
}

Example

from src.trader_mcp.strategy_sdk import Strategy

class Strategy(Strategy):
    LABEL = "Hello World"
    CONFIG_SCHEMA = {"label": {"type": "string", "default": "hello world"}}
    STATE_SCHEMA = {"counter": {"type": "int", "default": 0}}

    def init(self):
        return {"counter": 0}