Skip to main content

Workflows and Safety for Trading Agents

MCP is powerful because an AI trading agent can sequence tools. That also means safety needs to be part of the sequence, not an afterthought.

For trader-facing prompt templates built around these safety rules, see MCP Trading with AI Agents.

Safe Session Start

Begin with environment and risk checks:

GetMcpCapabilities
McpSelfTest
ListAccounts
GetConnections
GetAllPositions
GetAllOrders
GetWatermarks

Only continue if the add-on is connected, the account is correct, and the open risk matches what the user expects.

Strategy Authoring → Backtest → Deploy

The full loop: confirm environment, write the strategy, verify syntax, backtest against the user's NT8, optionally sweep parameters, then deploy with the winning configuration.

GetMcpCapabilities // confirm backtest_engine.available
GetAccountSummary
GetWatermarks
ListPositions
ListOrders
GetQuote
MarketInfo
GetNinjaScriptHelp(topic: "strategy_template")
SearchNinjaScriptSymbols(query: "EnterLong")
LookupNinjaScriptSymbol(name: "Strategy")
CompileNinjaScript(source: <C# source>, in_memory: true)
WriteNinjaScriptFile(name: "MyStrategy", kind: "strategy", source: ..., overwrite: true)
RunStrategyBacktest(strategy_class: "MyStrategy", instrument: ..., from: ..., to: ..., parameters: ...)
GetMcpJob(job_id: ...) // poll until completed
// Optional: sweep
RunStrategyBacktest(strategy_class: "MyStrategy", optimization: {fitness: "MaxNetProfit", parameters_sweep: [...]})
GetMcpJob(job_id: ...) // poll
// Full detail re-run with winning params
RunStrategyBacktest(strategy_class: "MyStrategy", parameters: best.parameter_values)
DeployStrategy(strategy_class: "MyStrategy", account: ..., instrument: ..., parameters: best.parameter_values)
GetDeployedStrategyState(deployment_id: ...)
EmitMcpAlert(alert_subtype: "deployment_started", details: {...})
Deployment Gate

Before deployment, confirm the strategy compiles cleanly in in_memory mode, a backtest produced metrics that meet the user's thresholds, the account and instrument are correct, and the user has approved the size and risk parameters. Return any compile diagnostics, surface failed gates explicitly, and never deploy based on the optimizer's best result without re-running a single backtest to inspect full trade detail.

Backtest Engine Limitations

The backtest engine inherits both NT8's behavior and the constraints of our MCP implementation. Plan for these:

  • Parity scope: single backtests are bit-identical to NT8 Strategy Analyzer UI for the same configuration. Verified against SampleMACrossOver on MES (see Backtesting and Optimization).
  • Default commission is zero: matches NT8 Strategy Analyzer's out-of-the-box behavior. Pass fill.commission_template to apply a specific template.
  • Optimization is cartesian-only: the optimizer argument ("DefaultOptimizer" / "GeneticOptimizer" / "StrategyGenerator") is recorded in the response but the iteration loop is always exhaustive cartesian. Genetic and StrategyGenerator semantics are NOT honored.
  • Sweep ranking is by NetProfit: fitness is resolved but ranking ignores it. Re-sort the result client-side if you need a different fitness key.
  • Sweep parameter types: only int / long / double / float / decimal are sweepable. Booleans and enums can be fixed inputs via parameters but not in parameters_sweep.
  • Trade count off-by-one: performance.all.TradesCount (live counter) may exceed trades[].length by 1 when a position straddles the date boundary. Matches NT8 UI behavior.
  • GetBars vs RunStrategyBacktest period_type casing: GetBars uses lowercase (minute, day, week, month, year) and supports a smaller subset; RunStrategyBacktest.bars_period.period_type uses NT8's PascalCase enum and supports the full set. These are independent surfaces, and neither accepts the other's casing.

NinjaScript Round Trip

Confirm NinjaScript signatures before relying on examples from memory:

GetNinjaScriptHelp(topic="strategy_template")
SearchNinjaScriptSymbols(query="EnterLong")
LookupNinjaScriptSymbol(name="Strategy", include_inherited=true)
CompileNinjaScript(source=..., in_memory=true)
GetMcpJob(job_id="...")
WriteNinjaScriptFile(name="...", kind="strategy", source=..., overwrite=true)
DeployStrategy(strategy_class="...", account="...", instrument="...", mode="account")
GetDeployedStrategyState(deployment_id="...")

Idempotent Tools

These tools are designed to be safe to call more than once with the same arguments:

ToolNotes
OpenChartReturns already_open:true if the chart is visible.
StopStrategyStopping an already-stopped deployment is a no-op.

Operational Limits

LimitWhat to do
Add-on offlineStop and ask the user to reconnect NT8.
Add-on below v1.13.0Stop and ask the user to update the add-on.
Async job runs too longPoll GetMcpJob, then use CancelMcpJob if the user wants to stop it.
Cached deployment state differs from live stateTrust the live block from GetDeployedStrategyState or ListDeployedStrategies.
Trading Commands Are Final

Order placement, flattening, strategy deployment, and strategy shutdown affect live NinjaTrader state. Design AI trading agents to confirm the account, instrument, quantity, current position, and working orders immediately before sending write commands.

Agent Handoff and Session State

MCP itself is stateless: the server does not remember which client started which deployment. State lives in NT8 and in the MCP deployment registry. When a new agent session takes over (different client, restart, scope change), it should rebuild context before acting:

GetMcpCapabilities // version and feature flags
GetConnections // is NT8 actually up?
ListAccounts // which accounts is this token allowed to see?
ListDeployedStrategies // what is registered as running?
GetDeployedStrategyState(...) // for each registered deployment, is_trading?
GetAllPositions / GetAllOrders // current exposure
ListAlerts // armed alerts from prior sessions
ListMcpJobs(status: "running") // in-flight backtests or compiles

A handoff agent that skips this and starts issuing trade commands is a bug, not a feature.

Idempotency and Retry

ToolRetry behavior
OpenChartReturns already_open:true if the chart is already visible. Safe to retry.
StopStrategyStopping an already-stopped deployment is a no-op. Safe to retry.
CompileNinjaScript(in_memory: true)Pure function of source. Re-running with the same source is safe and cheap.
RunStrategyBacktest (single)Idempotent on a fixed window: the same inputs produce the same trades and metrics.
RunStrategyBacktest (sweep)Idempotent given the same parameters_sweep. Re-running re-enumerates from scratch; the agent should reuse the prior job_id if it is still queryable.
PlaceOrder / Change / CancelNot idempotent. Each call mutates the working-order set. Read state with ListOrders before retrying.
DeployStrategyNot idempotent. A retry creates a second deployment. Call ListDeployedStrategies first.
EmitMcpAlertHonors dedup_seconds. Within the dedup window, identical alerts are suppressed.