Skip to main content

Build an AI NinjaTrader Strategy: Compile, Backtest and Simulate

An AI-generated strategy should leave you with more than a code block and a confident explanation. You need the rules, the compiled source, the exact test settings, and the trades that came back. This walkthrough uses CrossTrade MCP to keep that evidence together.

There are two separate execution stages. Historical backtests run on NinjaTrader's isolated Backtest account. Forward simulation is an optional later deployment to an available simulation account such as Sim101. A historical result does not mean a strategy has been deployed or placed an order.

This is a repeatable recipe, not a recorded performance result. The example requests below do not promise a profit figure, trade count, or execution time. Your data, platform version and settings determine the output.

Before you start​

You need CrossTrade Elite, a supported CrossTrade NT8 Add-On, NinjaTrader running, and an AI client connected to https://app.crosstrade.io/v1/api/mcp. Follow the MCP setup guide for your client.

Start by checking the connection and available tools. Read-only inspection uses mcp:read. Compilation, source writes, new backtest jobs and deployment require mcp:trade. That scope grants tools; it does not impose a confirmation dialog or a simulation-account restriction. Set approvals in your AI client and make the intended account explicit.

Use read-only tools first. Confirm CrossTrade MCP and the NT8 Add-On are
connected. Show the add-on version, accounts and available NinjaScript
tools. Inspect existing strategy files before choosing a new class name.
Do not compile, write, deploy, place, change, cancel or flatten anything.

The useful reads include GetMcpCapabilities, GetConnections, ListAccounts, ListNinjaScriptFiles, and the relevant account position/order reads. If a tool is unavailable, report that before proceeding.

1. Fix the strategy rules before generating code​

Use a small example you can inspect. For this walkthrough:

SettingRule
InstrumentMES, with the full dated contract confirmed for the test period
BarsFive-minute bars, evaluated on completed bars
EntryEnter long when the 9-period EMA crosses above the 21-period EMA
ExitExit on the reverse cross; declare session-close behavior explicitly
SizeOne contract; no additional entry while already long
DirectionLong only; no short entries
Initial classA new, unused name such as CrossTradeEmaDemo_v1

This is an instructional strategy, not a recommendation. Keeping it small makes it easier to spot a wrong-side entry, an off-by-one-bar signal or an unexpected session exit. Stops, targets and other filters can be added as separately reviewed versions.

Restate these rules and identify anything ambiguous before writing code.
Show how you will handle warm-up bars and session close. Use a new class
name and expose FastPeriod and SlowPeriod as public strategy inputs.
Do not add optimization, scaling or extra filters to the first version.

2. Inspect the installed NinjaScript API​

Ask the agent to read GetNinjaScriptHelp(topic="recipe", kind="strategy") and the relevant feature guidance. It can then use SearchNinjaScriptSymbols and LookupNinjaScriptSymbol to inspect EMA factories, entry/exit methods and the base strategy API.

GetNinjaScriptHelp is authoring guidance. LookupNinjaScriptSymbol is the runtime type/member lookup. They are different tools; asking the help tool for an arbitrary indicator name does not replace a symbol lookup.

Read the strategy recipe and managed-order guidance. Look up the NT8
types and signatures needed for this draft. Use the returned language
constraints. Show the proposed source and explain which bar triggers
each entry and exit. Wait for approval before compilation.

3. Compile the draft and retain the diagnostics​

After approval, the agent submits the source to CompileNinjaScript with in_memory: true. Compilation is asynchronous: keep the returned job_id and poll GetMcpJob until the job finishes. A submitted job is not a successful compile.

Read the completed job's result, including compiled, errors and warnings. If compilation fails, look up the unresolved type or method and repair that problem. Use a bounded number of attempts so the agent does not spend the session cycling through guesses.

Compile the approved source in memory and poll the job. If it fails,
show the diagnostics, look up unresolved symbols and repair the draft.
Stop after three failed attempts. Do not save or deploy anything yet.

Keep the source that corresponds to each result. A green compile only establishes that the compiler accepted it. Review the rules and trade behavior separately.

4. Save the source and verify the NT8 rebuild​

After reviewing the source, authorize WriteNinjaScriptFile with the class name, kind: "strategy", the approved source and overwrite: false. An existing filename is a reason to choose another version or review the overwrite, not to replace a script silently.

The sandbox compiler and NinjaTrader's Custom assembly are separate. Inspect the file writer's compile_engine:

  • reflection.<...> means the add-on triggered a rebuild. Check for compilation errors.
  • file_only means the source was saved but the strategy is not yet rebuilt into NT8. Press F5 in the NinjaScript Editor, resolve errors, then continue.

See the file-write reference. A successful sandbox compile does not remove this step.

5. Run a fixed historical backtest​

Confirm the full contract, available historical data, dates, bars, trading hours, source version and input values. Select a real commission template for a performance assessment and record slippage. If you use no commission and zero slippage for a smoke check, label it that way.

Here is a smoke-check request using the built-in SampleMACrossOver, not the custom strategy drafted above. It helps separate a backtest-interface problem from a new strategy problem. Confirm that the class, contract, trading-hours template and data are available in your install before running it.

{
"strategy_class": "SampleMACrossOver",
"instrument": "MES 06-26",
"bars_period": { "period_type": "Minute", "value": 5 },
"from": "2026-04-01T00:00:00Z",
"to": "2026-04-30T23:59:59Z",
"trading_hours": "CME US Index Futures ETH",
"parameters": { "Fast": 10, "Slow": 25 },
"fill": {
"type": "Standard",
"slippage_ticks": 0,
"commission_template": ""
},
"account": { "initial_cash": 100000, "denomination": "UsDollar" },
"timeout_ms": 120000
}

Submit this to RunStrategyBacktest. The account object configures the isolated Backtest account; it is not an account-name selector. Do not pass "account": "Sim101". strategy_class and bars_period.period_type are the current argument names.

Poll GetMcpJob using the returned job ID. Inspect the terminal job status and the result. Report a failure, missing data or an unsupported build explicitly rather than replacing it with an empty or invented result.

After the smoke check, propose a second request for your compiled custom class. Its input names are FastPeriod and SlowPeriod if that is what you declared, not automatically the built-in sample's Fast and Slow. Use the costs and dates you approved for the custom strategy. Keep both job results labelled with the class that actually ran.

6. Check behavior and compare with the desktop UI​

Look at trades as well as summary metrics. For the long-only example, inspect entries around the crossover, exits on the reverse cross, quantity, session boundaries and any unexpected short trades. A profitable total does not excuse a strategy that implements the wrong rules.

For an engine comparison, use the same compiled class and supported settings in NT8 Strategy Analyzer: data, bars, dates, trading hours, inputs, fill resolution, commissions and slippage. Retain both result sets. Investigate mismatches before drawing conclusions.

Current MCP limitations matter here. The interface does not enable Strategy Analyzer Tick Replay. Choosing tick bars changes the primary bar series; choosing High order-fill resolution changes the fill setting. Neither is a Tick Replay switch.

7. Add a small sweep only after the fixed run makes sense​

If you approve a parameter search for the custom strategy, add this block to its complete backtest request:

{
"optimization": {
"fitness": "MaxNetProfit",
"parameters_sweep": [
{ "name": "FastPeriod", "min": 8, "max": 10, "step": 1 },
{ "name": "SlowPeriod", "min": 20, "max": 22, "step": 1 }
]
}
}

That grid contains nine combinations. The current implementation uses exhaustive Cartesian enumeration, even if a genetic optimizer name is supplied. Read the returned ranking metadata for the actual fitness and any fallback. On add-on versions before 1.13.8, fixed parameters were not reapplied in sweep iterations; use a current supported version when relying on fixed inputs.

Re-run the selected settings as a single backtest for full trade detail, then evaluate a separate period that did not select the parameters. Record weak and negative results as faithfully as strong ones. Optimization is a way to inspect a hypothesis, not evidence that a strategy will work in the future.

8. Optional: deploy to a verified simulation account​

Stop for review before deployment. If you choose to forward-test, re-read accounts, positions, working orders and strategies. Confirm an available simulation account such as Sim101, the current tradable contract, quantity, bar period and final inputs. The April historical contract in the smoke check is not a default choice for a later forward test.

Show the proposed simulation deployment: account, current contract,
strategy class, inputs and bar period. Read existing positions, orders
and strategies first. Wait for my confirmation before DeployStrategy.
After deployment, retain deployment_id and call GetDeployedStrategyState.
Show live.state, live.is_trading, current bar, positions and active orders.
Do not describe a processing strategy as a filled trade.

Use the live block returned by GetDeployedStrategyState, not just the deploy-time response. A strategy may still be processing historical bars, warming up, or waiting for its first signal. Zero trades can be normal. Verify forward behavior over time before considering another account.

Stopping needs its own plan. Disabling an account-hosted strategy can terminate it while leaving its account position open. Closing a strategy and flattening positions are different actions. Review the relevant tool's behavior and verify positions/orders afterward rather than treating an unchecked “disabled” label as a flat account.

Capture a demo that someone else can reproduce​

Show the AI conversation and the NT8 window side by side. Capture these checkpoints:

CheckpointEvidence to keep
SpecificationEntry, exit, sizing, session rules and versioned class
EnvironmentNT8/add-on versions, instrument, data availability and client
CompilationSubmitted job ID and actual completed diagnostics
Source writeSaved filename and compile_engine; F5 result if required
Historical testComplete request, job ID, returned trades and metrics
Optional sweepGrid, fixed inputs, ranking metadata and separate evaluation period
Forward simulationApproved simulation account, deployment ID and fresh live state

Use a demonstration account and keep credentials and tokens out of the recording. Label illustrative prompts as prompts, and show results only after they have actually returned. This checklist is a recording recipe; it is not a claim that a video or new performance benchmark has already been produced.

Next steps​

The AI strategy-building page explains the product workflow. The RunStrategyBacktest reference documents the request surface and limitations. If you are choosing a route, compare CrossTrade with NinjaTrader's native MCP, Nina and AI Strategy Builder.