Connecting an AI agent to NinjaTrader 8 is a solved problem in the trivial sense: something has to translate the agent’s intent into an order the platform accepts. The question worth answering is where that translation runs and what gets to inspect the order on the way through. Get that wrong and you have handed a language model a direct line to a futures account. This walks the three real approaches, why an external agent is usually the one you want, the risk that comes with it, and the actual uvx pitbridge setup that puts a hard limit between the model and the platform.
Three ways to automate NinjaTrader 8
There are three architectures in common use, and they differ in where the decision logic lives.
In-platform NinjaScript is the native path. You write a strategy in NinjaScript, NinjaTrader’s C# environment, and it runs inside the platform against live data, with no external process in the order path. This is the right tool for a mechanical strategy you can express in code. It is not an AI agent: the logic is compiled C#, not a model reasoning in a loop, and teaching it to reason over natural-language context or call out to a model is a fight against the grain.
An external agent talks to NinjaTrader 8 from outside, over a protocol the platform or a bridge exposes. This is where an AI agent fits: the model runs in its own runtime, reads account state through tools, and proposes orders. It brings its own reasoning and its own data, and it does not need to be rewritten in C#. The catch is that something now reaches into the platform from outside, so the boundary between the two is where all the safety has to live.
A cloud bridge is a hosted variant of the external approach. The agent talks to a vendor’s cloud, which relays the order to a small add-on inside your NinjaTrader. It is the fastest to set up because the vendor operates the moving parts, at the cost of putting a third party’s servers in the path of every order.
| Property | in-platform ninjascript | external agent | cloud bridge |
|---|---|---|---|
| where the logic runs | compiled C# inside NT8 | a model in its own runtime | a model plus a vendor cloud |
| fits an AI agent | no, it is C# strategy code | yes, the native fit | yes, hosted |
| order path | inside the platform | your machine to the platform | through the vendor's servers |
| where risk checks can live | in your strategy code | in the local bridge | split across the vendor and the add-on |
| setup cost | learn NinjaScript | run a local daemon | sign up, grant access |
in-platform ninjascript
- where the logic runs
- compiled C# inside NT8
- fits an AI agent
- no, it is C# strategy code
- order path
- inside the platform
- where risk checks can live
- in your strategy code
- setup cost
- learn NinjaScript
external agent
- where the logic runs
- a model in its own runtime
- fits an AI agent
- yes, the native fit
- order path
- your machine to the platform
- where risk checks can live
- in the local bridge
- setup cost
- run a local daemon
cloud bridge
- where the logic runs
- a model plus a vendor cloud
- fits an AI agent
- yes, hosted
- order path
- through the vendor's servers
- where risk checks can live
- split across the vendor and the add-on
- setup cost
- sign up, grant access
Why reach for an external agent at all
If NinjaScript already runs strategies natively, why go outside the platform? Because an AI agent is a different kind of driver. It reasons over live state instead of executing a fixed rule, it reads the book before it acts, and it can take instruction in plain language. Expressing that inside a compiled NinjaScript strategy means bolting a model onto an environment that was not built to host one.
This is also where the common search for a NinjaTrader Python API runs into a wall. NinjaTrader 8 automates through NinjaScript in C#, and it ships no official Python API. If your agent, your tooling, or your own scripts live in Python, or speak MCP, the platform has no native door for them. You need something outside NinjaTrader that the platform can talk to and that your code can drive. That is exactly the shape of a bridge: it speaks the agent’s protocol on one side and NinjaTrader’s on the other.
The Model Context Protocol has become the standard way agents connect to tools, which makes an MCP server the natural front door for a trading account. The agent reads state with typed tools, proposes an order as a tool call, and reads the structured result. What a trading MCP server exposes, and why a proposal is not an execution, is covered in what a trading MCP server does.
The risk you are actually taking on
An agent with order access is a language model with a place_order tool, and a language model produces a distribution of outputs. Most of the time it does the sensible thing. The tail of that distribution includes an order for the wrong size, the wrong instrument, or the wrong side, reached through a plain mistake, a misread of the current position, an unusual market, or an input crafted to talk it past its instructions. Prompt injection is now routinely compared to SQL injection, because untrusted text and trusted instructions travel the same channel and the model cannot reliably tell them apart.
The tempting fix is to write the limits into the system prompt. Never exceed two contracts. Stop after a five hundred dollar loss. That configures behavior, it does not guarantee it. On the run where the model does not comply, a prompt limit does nothing, because the prompt was never the thing standing between the order and the account. Enforcement has to live below the model, in code the model cannot rewrite. Why a prompt is advice and an out-of-process check is a control is the whole subject of risk controls an LLM cannot override.
What a local-first guardrailed MCP bridge changes
A local-first bridge answers both problems at once: it keeps the order path on your machine, and it puts a deterministic checkpoint between the agent and the platform. The agent calls a tool, the call arrives at the daemon, a guardrail engine checks it against the limits you set, and only an allowed order continues to NinjaTrader 8. A blocked order comes back as a structured result with a reason code, not an exception that kills the session, so the agent can read why it stopped and move on.
Example decisions: ALLOW buy 2 MES, all rules pass. BLOCK buy 10 MES, size 10 exceeds max_contracts_per_order 2.
The order route: the agent proposes, the guardrail engine adjudicates, and only an allowed order reaches NinjaTrader 8. The block is shown as plainly as the allow.
Two properties make this a control rather than a suggestion. First, the checks are deterministic and run out of process: the same order and the same config always produce the same decision, and the model is not in the adjudication. Second, the agent’s surface is exactly nine tools, and none of them can weaken a limit. It has five reads and four order actions. There is no tool to release the kill switch, no tool to arm live trading, and no tool to change a threshold. Those live on an operator-only CLI the model never touches. The strongest safeguard here is an absence: the capability to raise its own limits is simply not in the agent’s world.
reads5
get_accountsget_positionsget_ordersget_account_stateget_guardrail_status
Return state. They never change anything.
writes4
place_ordercancel_orderclose_positionflatten_account
Request a change. Every place_order still passes the guardrail engine.
Local-first is the other half. The daemon, the guardrail engine, and your credentials run on your machine, and no order is relayed through a PitBridge server. On one Windows box the daemon and NinjaTrader run together on localhost. In paired mode the daemon runs on your Mac and the AddOn connects out to it from the Windows box over your own network. Either way, PitBridge’s own cloud is never in the order path. That property is the subject of the local-first trading bridge page.
The quickstart: uvx pitbridge
PitBridge is published on PyPI, so uvx resolves and runs it with no separate install step. The fastest look is the demo sandbox: with no config at all, the MCP server boots a safe paper account with every guardrail on and live execution locked, so an agent can read state before you configure anything.
$ uvx pitbridge mcp # no config found: booting a safe paper DEMO sandbox. # fake account, all 12 guardrails on, live locked. # your agent can now call get_accounts and get_guardrail_status.
The demo needs no NinjaTrader install and cannot place a real order. It exists so an agent can see the tools and the guardrail engine before setup.
When you are ready to point it at your own account, scaffold a config, edit it, and verify it. Every step stays in paper mode until you deliberately unlock live.
$ uvx pitbridge init # writes ~/.pitbridge/config.toml $ uvx pitbridge doctor # read-only PASS / WARN / FAIL self-check
init scaffolds a starter config; doctor checks config, accounts, guardrails, the deny-list, the kill switch, the live gate, and the version.
The config is one TOML file that names your account and its limits. The keys are real; the values are yours to set.
[daemon]
bind = "127.0.0.1" # localhost only
port = 8873
[accounts.sim]
nt_account = "Sim101" # the NinjaTrader account name
mode = "paper" # read_only | paper | live
[accounts.sim.guardrails]
instruments = ["MES 09-26"] # allowlist
max_contracts_per_order = 2 # reject any single order larger than this
daily_loss_halt = 500 # halt new entries after -$500
trading_window = { tz = "America/New_York", open = "09:30", close = "16:00" }A real config.toml. Leave a key out and that check does not run. A reason code in a block names the exact key that refused an order.
Wire the runtime you already use
The bridge does not care which model is on the other end, because the guardrail engine checks every order regardless of the client. Claude Code registers the stdio server with one command.
$ claude mcp add pitbridge -- uvx pitbridge mcp # custom config path? append: --config /path/to/config.toml
Claude Code one-liner. Claude Desktop and OpenClaw attach the same stdio server; see the quickstart for each.
For an agent runtime that speaks MCP over HTTP, such as Hermes or your own client, run the daemon and point the client at the local HTTP endpoint. It is the same nine tools, the same guardrail pipeline, and the same audit log as stdio.
$ uvx pitbridge run # daemon up on 127.0.0.1:8873 # REST at /v1/*, WebSocket at /v1/ws, MCP over HTTP at /mcp # point any MCP-over-HTTP client at: # http://127.0.0.1:8873/mcp
Streamable HTTP MCP has been served next to stdio since 0.2.0. On a localhost bind the agent token is optional; a non-localhost bind requires paired mode plus a token.
If you are the one reaching for a NinjaTrader Python API, this is the seam: uvx pitbridge run exposes a localhost REST route, so a Python script can POST an order to http://127.0.0.1:8873/v1/place_order and it travels the same guardrail pipeline as the agent’s tool call. The full per-runtime setup, including Claude Desktop, OpenClaw, and plain curl, is in the quickstart.
See a block, on purpose
The moment that proves the setup is a refused order. Ask the agent to check the account, buy two contracts, then buy ten. The first fills in paper; the second breaks the per-order cap you configured, and the engine refuses it before NinjaTrader sees anything.
get_account_state -> link_up: true, day_pnl: 0.0 place_order account=sim instrument="MES 09-26" side=BUY qty=2 -> SUBMITTED, then FILLED filled_qty=2 place_order account=sim instrument="MES 09-26" side=BUY qty=10 -> BLOCKED reason_code=MAX_CONTRACTS_PER_ORDER
Example agent tool calls against the local daemon. Real tool names and reason code. Fills are synthetic simulation fills, not live results. The block returns as a structured result the agent can read and explain.
A refused order is the system doing its job, not an error to hide. The whole decision, allow or block, is written to an append-only, hash-chained audit log, so pitbridge audit why <order_id> can explain any single order after the fact and pitbridge audit verify proves the log has not been edited.
Honest status: paper by default, live gated
Everything above runs against a paper or simulation account. In the open core mode = "live" is intentionally impossible: arm-live refuses because the live-execution provider ships separately as a paid, closed component, and there is no agent tool to change that. Live execution has been in production on a funded futures account since 30 July 2026, behind an operator-run arm step. The upside is that you can exercise the entire safety kernel, every reason code, on a Mac before any real money is in scope. When you do want live execution, the pricing page explains exactly what the paid unlock is.
Before a funded or prop account
Start on a NinjaTrader simulation account such as Sim101, in read-only or paper mode, and watch how the agent behaves across a full session before you consider anything funded. If that account is with a prop firm, one more rule applies: PitBridge enforces the limits you configure, but it does not know or bypass your firm’s automation policy. Some firms prohibit agent or automated order entry outright, some allow it with conditions, and the rules change. Confirm your firm’s written policy yourself and map it into your config. This is the subject of prop firm rules in software.
Connecting an AI agent to NinjaTrader 8 the safe way is not about trusting a well-behaved model. It is about making it impossible for the model to leave the limits you set. The NinjaTrader 8 MCP bridge is the front door, the guardrails page lists exactly what the engine refuses, and the security model covers where each part runs. If you want checks like these between your agent and your account, tell us your platform on the waitlist. PitBridge is trading infrastructure, not financial advice: it enforces the limits you configure and does not promise any trading outcome. Futures trading carries a substantial risk of loss.