R2/DOCS threepio.xyz →

Bringing your own strategy

The ten rules a NinjaScript strategy must meet to be taken in.

What a NinjaTrader 8 strategy must satisfy before R2 will accept it through the dashboard's Intake tab, size it, backtest it and let it trade.

R2 checks every rule and names all the ones you break, worst first — you do not fix one, resubmit, and discover the next. One .cs file at a time.

The ten rules

1. One strategy per file

A single C# file containing exactly one class deriving from Strategy. Everything it uses must be in that file or in NinjaTrader's own assemblies. No partner files, no shared helpers.

2. A name nobody else has

The class name must not already exist in NinjaTrader — not as a shipped R2 book, not in a compiled assembly, not as a source file. Rename the class and its Name in SetDefaults.

3. One place to set size

Exactly one of:

  • the slot inputs Slot1Contracts … SlotNContracts (what R2's own books use), or
  • exactly one integer input named Contracts, Quantity, Qty or Size, used by every entry.

No entry may carry a literal quantity — EnterLong(2, "x") is refused.

The one thing R2 repairs for you. If every entry shares the same literal quantity, R2 lifts it into its own contracts input and accepts the strategy. If entries carry different literals, it is refused: a ratio between entries is a sizing decision and R2 will not guess it.

4. Every entry protected

Each entry has a stop — SetStopLoss, SetTrailStop, an ExitLongStopMarket / ExitShortStopLimit-style order, or a stop in the unmanaged bracket. The distance is yours; R2 will never choose it.

5. Flat before the close

IsExitOnSessionCloseStrategy = true, or a time-based flatten of your own (a ToTime(Time[0]) check plus an exit call). In the Strategy Analyzer no round trip may last over 10 hours — that is the signature of a backtest that did not close, and nothing may be sized on it.

6. Minute bars

The primary series must be minute-based — any instrument, any period. Further series come from AddDataSeries with instrument inputs, never a hard-coded contract month.

7. Its own orders only

  • No Account.Submit / Cancel / Flatten / Create / Change
  • No touching another strategy's orders
  • Never sets IsEnabled itself — R2 owns enabling and disabling

8. No side channels

No file, network, process, registry or reflection calls. Specifically refused: System.IO.*, File.Read/Write/Append/Delete/Open/Exists/Copy, StreamReader, StreamWriter, Directory.*, WebClient, HttpClient, HttpWebRequest, System.Net.*, sockets, TcpClient, Process.*, Registry, Microsoft.Win32.*, System.Reflection.*, .GetMethod(, .GetProperty(, Assembly.*.

A strategy talks to nothing. R2 does the talking.

9. Deterministic enough to backtest

DateTime.Now, Random and tick-driven calculation are warned, not refused. They make a backtest irreproducible, so treat a warning as a reason to check the result twice.

10. It compiles

Against NinjaTrader 8.1's assemblies. The compiler's first error is the refusal reason, so compile it in NinjaTrader yourself before uploading and you will skip a round trip.

What R2 adds after you pass

R2 inserts its plumbing between // --- R2 plumbing markers and changes nothing else about what your strategy trades:

  • A read of policy.json — the contract counts R2 worked out for that account are applied as the strategy configures.
  • A self-enable, and a self-disable. If the policy names a different strategy for that account, yours switches itself off and prints why. If policy.json is missing or unreadable, your dialog values stand.

Sizing is only ever applied for slots 1..N and only for values 0..100.

What happens next

Stage What R2 does
Received your file is kept exactly as it came
Checked the ten rules; a refusal names the line and the fix
Formatted R2's plumbing added between markers, nothing else
Compiled or staged into R2User.dll, or kept as source outside NinjaTrader until R2 can put it in and compile it safely (NinjaTrader closed, or the New York close with every account flat) - a running NinjaTrader compiles any source put into bin\Custom at once
In NinjaTrader after its next start
Backtested seven years in the Strategy Analyzer, on a simulation account
Candidate if it clears the same gate as R2's own books — then into the nightly loop

Things that are easy to get wrong

  • A hard-coded contract month. Use instrument inputs. Contracts roll four times a year and a strategy pinned to an expired one sees no bars and cannot trade.
  • A literal quantity in one entry only. R2 can lift a shared literal; it cannot lift one.
  • Setting IsEnabled in OnStateChange. Common, and always refused — R2 owns that.
  • Reading a settings file of your own. Rule 8 refuses it. Use a NinjaScript input.
  • A backtest with an overnight hold. Usually a missing session-close flatten (rule 5).
Nothing on this page matches. The list under the search box shows matches on other pages.