{"prefix": "trading", "name": "Trading", "kind": "harness", "description": "A shared forex chart you and the agent both see: live candles from your MetaTrader 5 terminal, pair/timeframe switching, and a read of the chart the agent draws on. The agent reads the chart with read_chart before opining; drawing and analyzing are free; it never places a trade unprompted.", "operations": [{"name": "read_chart", "description": "Look at the chart before opining on it \u2014 never opine on a chart you haven't looked at. One structured snapshot of the WHOLE workspace: every pane ({pair, timeframe} + summary + last candle), the active pane's full candles and anchor times, the active drawings for every pair on screen, the clock (mode, cursor), the open trades and the running PnL. This is your main eye. Call it whenever the conversation turns to the chart.\n\nIN REPLAY the snapshot is cut at the cursor \u2014 you see exactly what the person sees and nothing more. A pane may report lastBarIsPartial: that bar is IN PROGRESS, built from base-timeframe data up to the cursor; treating its close as final is the same mistake as peeking ahead. You cannot know what happens next \u2014 if asked, say so.", "params": {"properties": {}}}, {"name": "get_candles", "description": "Fetch bounded raw OHLC candles for a pair/timeframe (defaults to the active pane). count defaults to 120, capped at the configured max. In replay the series stops at the cursor and the last bar may be a synthesized partial (flagged with partial: true) \u2014 asking for more bars than have happened returns fewer, never bars from the future.", "params": {"properties": {"pair": {"type": "string", "description": "e.g. 'EUR/USD' or 'EURUSD'. Defaults to the view's pair."}, "timeframe": {"type": "string", "description": "e.g. 'm15','h1','h4','d1'. Defaults to the view's timeframe."}, "count": {"type": "number", "description": "Number of bars. Default 120, capped at the configured max."}}}}, {"name": "get_price", "description": "Get the current bid/ask for a pair (defaults to the active pane's pair). Use for a live read of where price is now. In replay this is the price AT THE CURSOR \u2014 the last close the person can see, plus the simulated spread.", "params": {"properties": {"pair": {"type": "string", "description": "e.g. 'GBP/USD'. Defaults to the view's pair."}}}}, {"name": "set_view", "description": "Move ONE pane of the shared chart to a different pair and/or timeframe. Targets the ACTIVE pane unless you name one. The panel the user sees follows automatically (~1.4s via the spine). Use when the user asks to switch instruments or timeframes, or when analysis warrants a different lens. To change the layout or several panes at once, use set_workspace.", "params": {"properties": {"pair": {"type": "string", "description": "e.g. 'USD/JPY'. Omit to keep the pane's pair."}, "timeframe": {"type": "string", "description": "e.g. 'm5','h1','h4'. Omit to keep the pane's timeframe."}, "pane": {"type": "number", "description": "Which pane to move (0-based). Omit to move the active pane."}, "activate": {"type": "boolean", "description": "Also focus this pane."}}}}, {"name": "draw", "description": "Mark the chart with a drawing that carries your reasoning. This is the shared-analysis scene \u2014 you and the person both see each other's marks WITH the why. The mark appears on the chart in your color (~1.4s via the spine). Every drawing REQUIRES a rationale (one line on why it's there). Drawing and analyzing are free. Types: 'level' (1 anchor: horizontal support/resistance line), 'zone' (2 anchors: supply/demand price band), 'trendline' (2 anchors: sloped line), 'fib' (2 anchors: Fibonacci retracement between two swing points), 'note' (1 anchor: text annotation). IMPORTANT: anchor times MUST be actual candle timestamps from read_chart's anchorTimes list (or close to them). Do NOT pick arbitrary timestamps \u2014 the chart can only place marks at times that exist in the candle data, and arbitrary times will render incorrectly. Pick swing highs/lows from the anchor times and use their prices. For trendlines, pick two points that span a meaningful portion of the visible chart (not just the last few hours).", "params": {"properties": {"type": {"type": "string", "description": "One of: level, zone, trendline, fib, note."}, "anchors": {"type": "array", "description": "[{time, price}] \u2014 1 for level/note, 2 for zone/trendline/fib."}, "rationale": {"type": "string", "description": "REQUIRED. Why this mark is there \u2014 one line. This is what makes it analysis, not graphics."}, "label": {"type": "string", "description": "Optional short label (e.g. 'supply', 'demand', 'R1')."}, "pair": {"type": "string", "description": "Defaults to the view's pair."}, "timeframe": {"type": "string", "description": "Timeframe of authorship. Defaults to the view's timeframe."}}}}, {"name": "update_drawing", "description": "Edit a drawing's anchors, rationale, label, or type. Either side may edit either's marks \u2014 it's one shared document. Bumps updated_at.", "params": {"properties": {"id": {"type": "string", "description": "The drawing id (from read_chart or list_drawings)."}, "patch": {"type": "object", "description": "Fields to change: type, anchors, rationale, label, params, watch, pair."}}}}, {"name": "archive_drawing", "description": "Soft-archive a drawing so it vanishes from the chart (and from read_chart). It stays in the locker \u2014 listable with list_drawings {includeArchived: true}. Use when a mark is no longer relevant.", "params": {"properties": {"id": {"type": "string", "description": "The drawing id to archive."}}}}, {"name": "list_drawings", "description": "List drawings for a pair (defaults to the view's pair). Set includeArchived: true to see archived marks too. Use when you want the full analysis history for a pair, including marks no longer on the chart.", "params": {"properties": {"pair": {"type": "string", "description": "Defaults to the view's pair."}, "includeArchived": {"type": "boolean", "description": "Include archived drawings. Default false."}}}}, {"name": "replay_start", "description": "Rewind the whole workspace to a moment in history and hand the clock to the person. Every pane freezes at that instant; from then on YOU SEE EXACTLY WHAT THEY SEE AND NOTHING MORE \u2014 every candle op is cut at the cursor, and a higher-timeframe bar in progress is built from base data up to the cursor, never shown finished.\n\nThis is practice, not hindsight. You cannot know what happens next; if asked, say so plainly. Read the structure in front of you, propose, let them step, then grade the outcome against the thesis.\n\nReturns: sessionId, cursor (unix seconds) + cursorTime (ISO), the workspace, and a snapshot of every pane.", "params": {"properties": {"startTime": {"type": "string", "description": "REQUIRED. The moment to rewind to \u2014 ISO ('2026-03-14T08:00:00Z' or '2026-03-14') or unix seconds. Snapped BACK to the last completed bar, never forward."}, "panes": {"type": "array", "description": "Optional [{pair, timeframe}] to set up the panes at once, e.g. [{pair:'EUR/USD',timeframe:'h1'},{pair:'EUR/USD',timeframe:'h4'}]. Omit to keep the current workspace."}, "layout": {"type": "string", "description": "'1', '2' (side by side) or '2x2'. Inferred from panes when omitted."}, "referenceTimeframe": {"type": "string", "description": "The timeframe a 'step' counts in. Defaults to the active pane's timeframe."}, "title": {"type": "string", "description": "Optional name for the sitting (shows in replay_sessions)."}}}}, {"name": "replay_step", "description": "Advance the clock by N bars of the reference timeframe. EVERY pane moves together, across pairs \u2014 the whole workspace shares one clock.\n\nFills are evaluated on base-timeframe bars during the step, so a stop touched intrabar registers even when you are stepping h4. Returns the newly revealed candles per pane, any fill/SL/TP events, the open trades and the session's running PnL. When an event comes back, say what it means against the thesis \u2014 do not let a stop pass in silence.", "params": {"properties": {"bars": {"type": "number", "description": "How many bars to advance. Defaults to config trading.replay.defaultStep."}, "pane": {"type": "number", "description": "Step in this pane's timeframe instead of the session's reference timeframe (0-based index)."}}}}, {"name": "replay_seek", "description": "Jump the clock to any instant \u2014 forwards to skip ahead, or BACKWARDS to re-examine a moment. Stepping back is exact: the ledger is derived from the orders, so a trade that filled after the target is simply un-filled again. Same return shape as replay_step.", "params": {"properties": {"time": {"type": "string", "description": "REQUIRED. ISO time or unix seconds. Snapped back to the last completed bar."}}}}, {"name": "replay_end", "description": "Return the workspace to the live clock. The sitting is KEPT \u2014 its trades, notes and final cursor stay listable.\n\nAfter this, write the trade journal: a desk item kind:'trade-journal' with, for each trade, the thesis, what actually happened, and the lesson \u2014 then one line on the sitting as a whole. A trade without a graded outcome teaches nothing.", "params": {"properties": {}}}, {"name": "replay_sessions", "description": "List past replay sittings, newest first: id, title, start time, final cursor, pairs, order count. Use it to find a sitting to review or journal.", "params": {"properties": {}}}, {"name": "replay_session", "description": "Open one past sitting in full: its panes, every order given, the ledger as it stood at the session's final cursor, the notes, and the PnL. This is the raw material for a trade-journal entry.", "params": {"properties": {"id": {"type": "string", "description": "The session id (from replay_sessions)."}}}}, {"name": "replay_note", "description": "Record an observation against the current sitting, stamped with the cursor it was made at. Notes survive the session and can be quoted in the journal \u2014 use them for the read you had BEFORE the outcome was known, which is the only kind worth grading.", "params": {"properties": {"text": {"type": "string", "description": "REQUIRED. The observation, one or two sentences."}}}}, {"name": "place_trade", "description": "Place a simulated trade on the replay clock.\n\nNEVER place a trade unprompted \u2014 the act belongs to the person; you propose, they decide. When they do ask, the trade must carry its reasoning: `thesis` is REQUIRED (one line on why), and `basis` should list the drawing ids the entry and stop rest on, so the analysis and the act stay bound together.\n\nFill model (candle-grain, documented): the order fills at the OPEN of the next base-timeframe bar \u2014 you do not get the price you were looking at. SL/TP fill when a bar's range crosses the level; if one bar crosses BOTH, the stop wins. The configured spread is charged once, at entry. PnL is in the pair's quote currency.\n\nReplay only in this build; on the live clock it refuses.", "params": {"properties": {"direction": {"type": "string", "description": "REQUIRED. 'long' or 'short'."}, "thesis": {"type": "string", "description": "REQUIRED. Why this trade, in one line. Refused without it."}, "pair": {"type": "string", "description": "Defaults to the active pane's pair."}, "units": {"type": "number", "description": "Position size in base-currency units (10000 = 0.1 lot). Defaults to config trading.sim.defaultUnits."}, "sl": {"type": "number", "description": "Stop-loss price. Must be the losing side of the current price."}, "tp": {"type": "number", "description": "Take-profit price. Must be the winning side of the current price."}, "basis": {"type": "array", "description": "Drawing ids this trade rests on (from read_chart)."}}}}, {"name": "close_trade", "description": "Close an open replay trade at the cursor price (the last close the person could actually see), or cancel it if it has not filled yet. Returns the closed trade with its PnL. Say the outcome against the thesis \u2014 that is what the journal is made of.", "params": {"properties": {"id": {"type": "string", "description": "The trade id (from list_trades or read_chart)."}}}}, {"name": "update_trade", "description": "Move a trade's stop or target. SL and TP ONLY \u2014 entry, thesis, basis, direction and size are fixed the moment the order is given, so the record of what was actually decided stays honest. If the plan genuinely changed, close the trade and place a new one with a new thesis.\n\nThe change is stamped with the current cursor and only applies from there on: moving a stop now cannot rescue a bar that already happened.", "params": {"properties": {"id": {"type": "string", "description": "The trade id."}, "sl": {"type": "number", "description": "New stop-loss price (null to remove)."}, "tp": {"type": "number", "description": "New take-profit price (null to remove)."}}}}, {"name": "list_trades", "description": "The ledger for the current (or a named) replay sitting: every order with its fill state, exit reason, PnL in pips and quote currency, plus the running session PnL. Open trades are marked to the cursor price.", "params": {"properties": {"openOnly": {"type": "boolean", "description": "Only pending + open trades. Default false."}, "sessionId": {"type": "string", "description": "A past session's id. Defaults to the current sitting."}}}}, {"name": "trade_preview", "description": "R:R, risk and reward for a trade NOT yet placed \u2014 the same numbers the on-chart position tool shows while the person drags it. Use it to talk about sizing and risk before anyone commits, so you and the panel quote identical figures.", "params": {"properties": {"direction": {"type": "string", "description": "'long' or 'short'."}, "units": {"type": "number", "description": "Position size in base-currency units."}, "entry": {"type": "number", "description": "Entry price. Defaults to the current/cursor price."}, "sl": {"type": "number", "description": "Proposed stop."}, "tp": {"type": "number", "description": "Proposed target."}, "pair": {"type": "string", "description": "Defaults to the active pane's pair."}}}}, {"name": "get_workspace", "description": "Read the workspace: layout ('1'|'2'|'2x2'), the panes ({pair, timeframe} each), which pane is active, and the clock (mode 'live'|'replay', cursor, sessionId). Cheaper than read_chart when you only need to know what is on screen and what time it is.", "params": {"properties": {}}}, {"name": "set_workspace", "description": "Change the layout, the focused pane, or several panes at once \u2014 e.g. lay out 2x2 with EURUSD h1 + h4, GBPUSD h1 and EURUSD m15 for a multi-timeframe read. To move a single pair/timeframe, prefer set_view.", "params": {"properties": {"layout": {"type": "string", "description": "'1', '2' (side by side) or '2x2'. Growing clones the last pane; shrinking drops the tail."}, "panes": {"type": "array", "description": "[{pair, timeframe}] applied by index."}, "activePane": {"type": "number", "description": "Which pane the toolbar and pane-less ops act on (0-based)."}}}}, {"name": "research.strategies", "description": "List all registered strategies available for backtesting. Each entry: name, description, parameter specs, authorship door (coded = Python module, composed = declarative config via research.compose). This is the registry \u2014 pick from here, then call research.backtest.", "params": {"properties": {}}}, {"name": "research.compose", "description": "T24 door (b): compose a declarative strategy from rules over REGISTERED indicators. No code, no eval \u2014 a safe config validated against the indicator registry, then registered like any other strategy. Available indicators: rsi, sma, ema, atr. Expression language: indicator(params) OP value, joined by AND/OR. Example rules: {entry: 'rsi(14) < 30', exit: 'rsi(14) > 50', session: 'london', direction: 'long'}. After composing, the strategy is immediately available for research.backtest.\n\nRULES FORMAT:\n  entry: required \u2014 a comparison expression (e.g. 'rsi < 30')\n  exit: optional \u2014 exit condition (e.g. 'rsi > 50')\n  session: optional \u2014 'all' (default), 'london', 'new_york', 'overlap', 'tokyo'\n  direction: optional \u2014 'long', 'short', 'both' (default)\n\nEXAMPLE: 'What if RSI under 30 but only during London?' ->\n  {name: 'rsi_london', description: 'RSI oversold during London session',\n   rules: {entry: 'rsi < 30', exit: 'rsi > 50', session: 'london', direction: 'long'}}", "params": {"properties": {"name": {"type": "string", "description": "Unique strategy name (lowercase, underscores)."}, "description": {"type": "string", "description": "Human-readable description."}, "rules": {"type": "object", "description": "{entry: 'expr', exit: 'expr', session: 'london|...', direction: 'long|short|both'}", "properties": {"entry": {"type": "string", "description": "Entry rule expression, e.g. 'rsi < 30'"}, "exit": {"type": "string", "description": "Optional exit rule, e.g. 'rsi > 50'"}, "session": {"type": "string", "description": "Session filter: all|london|new_york|overlap|tokyo"}, "direction": {"type": "string", "description": "long|short|both"}}}}}}, {"name": "research.backtest", "description": "Run a backtest through the sealed pipeline (T25). The agent NEVER computes \u2014 it proposes, the engine computes. Returns an ARTIFACT: id, fingerprint (engine + data hash), metrics, equity summary, trade list summary, and the RED-FLAG BATTERY as structured fields (leakage test, costs-included flag, Sharpe sanity, multi-symbol status, param count).\n\nThe artifact is the source of truth. Your competence OBLIGES you to:\n  1. Surface every triggered red flag UNPROMPTED in your verdict\n  2. NEVER state a figure absent from the artifact\n  3. If asked for a metric the artifact lacks, say the pipeline doesn't produce it\n  4. After the verdict, write a research-journal desk item (hypothesis, artifact id, engine numbers, triggered flags, honest verdict, next step)\n\nDeterminism: same data + params produce same signals, always.\n\nArgs:\n  strategy: strategy name from research.strategies\n  params: strategy parameters (defaults applied for omitted)\n  pair: e.g. 'EUR/USD' (defaults to the view's pair)\n  timeframe: e.g. 'h1' (defaults to the view's timeframe)\n  range: {start, end} optional ISO date range; defaults to all available history\n  hypothesis: one-line description of what you're testing (recorded in the artifact)", "params": {"properties": {"strategy": {"type": "string", "description": "Strategy name from research.strategies."}, "params": {"type": "object", "description": "Strategy parameters (defaults applied for omitted)."}, "pair": {"type": "string", "description": "e.g. 'EUR/USD'. Defaults to the view's pair."}, "timeframe": {"type": "string", "description": "e.g. 'h1'. Defaults to the view's timeframe."}, "range": {"type": "object", "description": "{start: ISO, end: ISO} optional date range."}, "hypothesis": {"type": "string", "description": "One-line description for the artifact."}}}}, {"name": "research.result", "description": "Retrieve a full artifact by ID. Returns the complete result: metrics, red flags, trade list, equity summary, data/config hashes, environment fingerprint. Use this when you want to inspect a specific past result.", "params": {"properties": {"id": {"type": "string", "description": "The artifact ID (from research.backtest or research.results)."}}}}, {"name": "research.results", "description": "List all artifacts (compact summaries). Optionally filter by strategy name. Returns id, strategy, instrument, timeframe, metrics, red-flag summary, status, verdict.", "params": {"properties": {"strategy": {"type": "string", "description": "Filter by strategy name (optional)."}}}}, {"name": "research.scan", "description": "Run registered strategies on CURRENT data to get live signals. Uses the SAME signal generation code path as backtest \u2014 one pipeline, no second implementation. Returns per strategy: signal count, last 20 signals, latest signal, current position. Use for a live read of what strategies are saying right now.", "params": {"properties": {"pair": {"type": "string", "description": "Defaults to the view's pair."}, "timeframe": {"type": "string", "description": "Defaults to the view's timeframe."}, "strategies": {"type": "array", "description": "Optional list of strategy names. Default: all."}}}}], "injection": {"enabled": true, "maxUrgency": "nudge"}, "ui": {"enabled": true, "bundle": "ui.html", "hash": "sha256-bNLUapdbGmj5um6fIY6IlEPpZuf6y/hLyE+m2CiyW18=", "preferredWidth": 720}, "desk": {"items": [{"seedKey": "trading.legend", "kind": "note", "surface": "session-start", "title": "Trading room legend", "body": "This room has the trading harness \u2014 a shared chart you and the agent both see.\n\n- `read_chart` is how you (and the agent) look before opining. Never opine on a chart you haven't looked at.\n- Drawing and analyzing are free \u2014 anyone can mark the chart and explain why.\n- The agent never places a trade unprompted. Trades happen only at the person's explicit word.\n- Switch pairs and timeframes anytime; the chart follows for both of you."}, {"seedKey": "trading.journal", "kind": "doc", "surface": "on-demand", "title": "Trade journal", "body": "## Trade journal\n\nRecord each trade against the thinking that produced it, then grade the outcome honestly.\n\n### Entry format\n- **Thesis:** why this trade \u2014 the levels, the structure, the read. (one or two sentences)\n- **Outcome:** what happened \u2014 where it filled, where it closed, the result. (the facts)\n- **Lesson:** what the trade teaches \u2014 about the market, the read, or yourself. (the takeaway)\n\n_A trade without a thesis can't be graded; a thesis without an outcome can't teach._"}, {"seedKey": "trading.vocab", "kind": "note", "surface": "on-demand", "title": "Trading vocabulary", "body": "The drawing envelope \u2014 every mark on the chart is one of these, and every mark carries a **rationale** (why it's there):\n\n- **level** \u2014 a single horizontal price line (support/resistance).\n- **zone** \u2014 a price band (supply/demand area).\n- **trendline** \u2014 a sloped line or channel.\n- **fib** \u2014 Fibonacci levels.\n- **note** \u2014 a free-text annotation anchored to a price.\n\n**rationale** \u2014 the why; what makes a mark *analysis* rather than graphics. Both sides see why the other drew it.\n**watch** \u2014 an optional trigger on a drawing (touch / break / approach \u2192 nudge).\n**basis** \u2014 the drawings a trade rests on.\n**thesis** \u2014 the shared reasoning behind a trade, in a sentence."}, {"seedKey": "trading.research", "kind": "note", "surface": "on-demand", "title": "Research faculty", "body": "## The research faculty\n\nThis room has a backtesting engine \u2014 the **sealed pipeline** (T25). The agent proposes, the engine computes, and every result is an **artifact** (deterministic, fingerprinted, with the red-flag battery attached).\n\n### The loop\n1. **Idea** \u2014 you describe a hypothesis in plain words.\n2. **Compose** \u2014 the agent translates it into a strategy (coded or declarative via `research.compose`).\n3. **Backtest** \u2014 `research.backtest` runs the engine. The artifact is the source of truth.\n4. **Verdict** \u2014 the agent reads the artifact, surfaces red flags, and gives an honest assessment.\n5. **Journal** \u2014 the result is recorded on the Desk as a research-journal entry.\n6. **Repeat** \u2014 tweak, retest, compare.\n\n### The agent's obligations\n- Every number it states exists in the referenced artifact.\n- Every triggered red flag is surfaced unprompted.\n- It never computes \u2014 only proposes, relays, and interprets.\n\n### Available ops\n- `research.strategies` \u2014 list registered strategies\n- `research.compose` \u2014 compose a new strategy from rules\n- `research.backtest` \u2014 run a backtest, produce an artifact\n- `research.result` \u2014 retrieve a full artifact by ID\n- `research.results` \u2014 list all artifacts\n- `research.scan` \u2014 run strategies on current data for live signals\n\n_The research journal lives on the Desk alongside the trade journal._"}, {"seedKey": "trading.replay", "kind": "note", "surface": "on-demand", "title": "Replay sittings", "body": "## Replay \u2014 practice, not hindsight\n\nThe workspace can be rewound to any moment in history. Several charts (up to 2x2, each its own pair + timeframe) freeze at that instant and move together on **one clock**.\n\n### The honesty rule\nNothing past the cursor is sent \u2014 to the agent any more than to you. A higher-timeframe bar still in progress is **built from base-timeframe data up to the cursor**, marked `partial`, and completes only when the clock passes its period end. There is no way to peek, by either hand.\n\n### The loop\n1. `replay_start` at a date \u2014 read the structure that was actually there.\n2. Talk it through; mark the chart; write a `replay_note` with the read **before** the outcome.\n3. `place_trade` with a thesis and the drawings it rests on.\n4. `replay_step` \u2014 watch it play out. Fills, stops and targets arrive as events.\n5. `replay_end` \u2014 write the trade-journal entry: thesis, outcome, lesson.\n\n### The fill model (dumb on purpose)\nMarket orders fill at the **next base-bar open** \u2014 not the price you were looking at. SL/TP fill when a bar's range crosses the level; **if one bar crosses both, the stop wins**. The spread is charged once, at entry. Candle-grain: this is a thesis-practice tool, not a tick-accurate backtester, and it says so rather than pretending otherwise."}]}, "competence": "## Chart-reading craft\n- **Structure first, opinion second.** Before any view, name what the market is doing: trending or ranging, and where price sits relative to the recent structure (at a high, mid-range, testing a level). The structure is the frame; every opinion hangs on it.\n- Read left to right: the older bars set the context the current bar lives in. The last close is a fact, not a verdict.\n- Timeframe matters. A level on H4 carries more weight than a level on M5. Say which timeframe you're reading.\n\n## Drawing conventions\n- Every mark carries a **rationale** \u2014 one line on *why* it's there. A mark without a reason is noise.\n- Author colors distinguish hands: the user's marks and the agent's marks read differently on the same chart. You can draw on either side's analysis; say when you do.\n- Levels and zones are tools, not predictions. They name where attention is, not what will happen.\n\n## Thesis discipline\n- No trade without basis drawings **and** a thesis sentence. A trade is the conclusion of an analysis, never its start.\n- When a trade closes, grade the outcome against the thesis \u2014 did the read hold, did the market do something else, or was the thesis never really there? Outcomes are journaled, not buried.\n\n## The JIT habit\n- `read_chart` before opining \u2014 always. Never describe a chart you haven't looked at in this conversation; the view may have moved.\n- When the user asks what you think, look first, then answer about *that* chart with reference to its actual structure.\n\n## Config awareness\n- Cadences and caps are tunable in conversation via `config.set` (poll intervals, candle fetch sizes, the active feed provider). If a default feels wrong for how this person trades, offer to change it rather than working around it.\n\n## Research craft (the sealed pipeline \u2014 T25)\n- **The five-step loop:** idea, compose, backtest, verdict, journal. You propose, the engine computes, the artifact is truth.\n- **Never state a number that is not in the artifact.** If asked for a metric the pipeline doesn't produce, say so \u2014 do not improvise.\n- **Surface every triggered red flag unprompted.** The battery runs on every backtest; your job is to name what it flags, not to hide it.\n- **Be suspicious of beauty.** Sharpe > 3 = investigate (likely leakage or overfit). Win rate > 70% = check for look-ahead. Profit factor > 3 = too good to be true. Fewer than 30 trades = not statistically significant.\n- **One-symbol wonders are luck, not strategies.** A strategy that works on EURUSD but not GBPUSD is probably overfit to one instrument.\n- **Start simple.** 2-3 parameters is enough for a first test. More parameters = more overfitting risk. The battery flags >5 params.\n- **The red flags list:** Sharpe > 3 (danger), Sharpe > 2 (warn), win rate > 70% (warn), profit factor > 3 (warn), < 30 trades (warn), > 5 params (warn), costs not included (warn), single symbol (info), leakage test failed (danger).\n- **After every backtest verdict, write a research-journal desk item:** hypothesis, artifact id, the engine's numbers, triggered flags, honest verdict, next step. The room remembers what was tested.\n- **Anchor times for drawings must come from candle times returned by ops (read_chart's anchorTimes), never computed or guessed.** The pipeline snaps times to bar buckets \u2014 off-candle times are rejected at the door.\n\n## Replay craft (the sitting \u2014 T27/T28)\n- **In replay you know nothing past the cursor, and neither does the person.** Every candle op is cut server-side. If asked what happens next, say you cannot see it \u2014 do not reach for what you may have read about that period elsewhere. The whole value of the sitting is that the read is made blind.\n- **A `partial` bar has not closed.** When a pane reports `lastBarIsPartial`, that higher-timeframe bar was built from base data up to the cursor: its high, low and close can still change. Read it as 'so far', never as 'it closed here'.\n- **Multi-timeframe means one clock.** All panes are frozen at the same instant; an h4 pane and an m15 pane are the same moment seen at two grains, not two moments.\n- **The read comes before the outcome.** Say what you see and what would change your mind BEFORE the step, and put it in a `replay_note`. A read written after the bars printed is worthless for learning, however good it sounds.\n- **Never place a trade unprompted** \u2014 in replay as in live. Propose, size, name the risk; the person decides. When they do ask, the thesis is required and the basis drawings go with it.\n- **Report every outcome event against its thesis.** A stop that passes in silence teaches nothing. Say which it was (SL/TP/manual), what it cost or made, and whether the thesis held, was wrong, or was never really there.\n- **Be honest about the fill model when it matters.** Candle-grain: entry at the next base-bar open, SL wins a bar that crosses both levels, spread charged at entry. If a result hinges on intra-bar path, say the simulation cannot know it.\n- **Close the sitting with a journal.** After `replay_end`, write a `kind:'trade-journal'` desk item \u2014 per trade: thesis, outcome, lesson; then one line on the sitting. That is how practice becomes memory.", "streams": {"ticks": {"path": "/stream"}}}