# Strategy SDK Specification ## 1. Purpose This document defines the core strategy contract for `trader-mcp`. It separates: - strategy logic - engine lifecycle control - capability access - configuration - runtime UI output The goal is simple, single-file strategies that stay easy to reason about, safe to run, and consistent across the app. ## 2. Design Goals ### 2.1 Keep the surface small Strategies should have a minimal API and minimal boilerplate. ### 2.2 Separate responsibilities - strategy, decides what to do - engine, decides when to load, unload, and tick - context, enforces permissions and binds instance identity (`account_id`, `client_id`) to exec calls - database, owns persistent config and identity ### 2.3 Prefer deterministic behavior A given strategy instance should behave predictably for the same config and input stream. ### 2.4 Restrict capabilities explicitly Strategies must not touch engine internals or persistence directly. All external actions go through the injected context. ## 3. Strategy Contract Each strategy is a Python class extending `Strategy`. ```python from strategy_sdk import Strategy class MyStrategy(Strategy): CONFIG_SCHEMA = { "risk": {"type": "float", "default": 0.01}, "window": {"type": "int", "default": 20} } def init(self): return { "prices": [], "position": 0 } def on_tick(self, tick): price = tick["price"] self.state["prices"].append(price) def render(self): return { "widgets": [ {"type": "line_chart", "data": self.state["prices"]} ] } ``` ## 4. Base Class Shape ```python class Strategy: CONFIG_SCHEMA = {} def __init__(self, context, config): self.context = context self.config = config self.state = self.init() def init(self): return {} def on_tick(self, tick): pass def render(self): return {"widgets": []} ``` ## 5. Configuration ### 5.1 Purpose `CONFIG_SCHEMA` drives validation and UI generation. ### 5.2 Rules - config is read-only from the strategy’s perspective - config is supplied by the engine - config changes are handled by reload, not mutation ### 5.3 Recommended schema style ```python CONFIG_SCHEMA = { "risk": { "type": "float", "default": 0.01, "min": 0.0, "max": 1.0 }, "window": { "type": "int", "default": 20 } } ``` ## 6. State ### 6.1 Ownership `self.state` belongs to the strategy instance. ### 6.2 Lifecycle ```text init() -> state created on_tick() -> state updated reload -> state reset ``` ### 6.3 Guidance - keep state serializable where practical - do not let state depend on hidden external globals ## 7. Context The context is a capability boundary, not a config loader. ### Allowed responsibilities - market/data access - order placement - logging - engine-mediated actions - strategy-scoped metadata, including attaching the instance `account_id` and `client_id` to execution calls ### Not the responsibility of context - config storage - persistence - lifecycle control - strategy identity ownership - deciding strategy behavior Example: ```python class StrategyContext: def get_price(self): raise NotImplementedError def place_order(self, side, amount): raise NotImplementedError def get_orders(self): raise NotImplementedError def log(self, message): raise NotImplementedError ``` ## 8. UI Output Strategies should return structured UI data, not HTML. ```python def render(self): return { "widgets": [ {"type": "line_chart", "data": [...]}, {"type": "metric", "label": "PnL", "value": 123.45} ] } ``` ### Principles - no frontend code inside the strategy - no DOM or template output - dashboard owns rendering ## 9. Summary The strategy SDK should make it obvious that: - strategy defines behavior - engine defines lifecycle - context defines permissions - config defines initial conditions - state belongs to the instance