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: {...})
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
SampleMACrossOveron MES (see Backtesting and Optimization). - Default commission is zero: matches NT8 Strategy Analyzer's out-of-the-box behavior. Pass
fill.commission_templateto apply a specific template. - Optimization is cartesian-only: the
optimizerargument ("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:
fitnessis 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
parametersbut not inparameters_sweep. - Trade count off-by-one:
performance.all.TradesCount(live counter) may exceedtrades[].lengthby 1 when a position straddles the date boundary. Matches NT8 UI behavior. GetBarsvsRunStrategyBacktestperiod_type casing:GetBarsuses lowercase (minute,day,week,month,year) and supports a smaller subset;RunStrategyBacktest.bars_period.period_typeuses 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:
| Tool | Notes |
|---|---|
OpenChart | Returns already_open:true if the chart is visible. |
StopStrategy | Stopping an already-stopped deployment is a no-op. |
Operational Limits
| Limit | What to do |
|---|---|
| Add-on offline | Stop and ask the user to reconnect NT8. |
Add-on below v1.13.0 | Stop and ask the user to update the add-on. |
| Async job runs too long | Poll GetMcpJob, then use CancelMcpJob if the user wants to stop it. |
| Cached deployment state differs from live state | Trust the live block from GetDeployedStrategyState or ListDeployedStrategies. |
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
| Tool | Retry behavior |
|---|---|
OpenChart | Returns already_open:true if the chart is already visible. Safe to retry. |
StopStrategy | Stopping 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 / Cancel | Not idempotent. Each call mutates the working-order set. Read state with ListOrders before retrying. |
DeployStrategy | Not idempotent. A retry creates a second deployment. Call ListDeployedStrategies first. |
EmitMcpAlert | Honors dedup_seconds. Within the dedup window, identical alerts are suppressed. |