Skip to content
Open RaamTrade

Sandbox

RaamCode restricts member programs to a checked language subset. The compiler rejects unsupported constructs, and RaamVM interprets the accepted program through the platform’s supported operations.

What “sandbox” means here

The compiler validates syntax, types, and permitted operations before a strategy or indicator can run. Its output is a RaamVM program, not generated C#. Member code is never compiled into or loaded as a .NET assembly. Runtime execution remains subject to the interpreter’s supported operations and execution limits.

What’s not allowed

Category Examples
External code import anything (stdlib or third-party).
I/O open(), file reads, file writes. (print() is allowed — see below.)
Network No HTTP, no sockets, no DNS.
Dynamic code eval(), exec(), compile().
Error handling try, except, finally, raise.
Concurrency async, await, yield, threading, multiprocessing.
Context managers with statements.
Closures and lambdas lambda expressions, nested function definitions.
Scope escape global, nonlocal, del.
Unsupported persistent state set, tuple, and undeclared or rebound collection slots. Class-declared typed list and dict slots are supported; see Persistent State.
.NET reflection Not reachable at all: a strategy or indicator is data the RaamVM runtime interprets — no C# is generated from it and nothing it writes is ever compiled or loaded (#1355).

For each blocked construct the transpiler emits a specific diagnostic with a line number. See the Error Reference.

Why these restrictions

Three reasons, in priority order:

1. Determinism

A strategy must produce identical results across backtest, paper, and live. Anything that can vary with the environment violates this.

  • Network calls → return different data depending on when you run.
  • File I/O → depends on what’s on disk, which differs per host.
  • Randomness → varies per invocation.
  • Threading → introduces ordering nondeterminism.

By refusing all of these, the platform can guarantee: same inputs, same outputs, every time. Without this, walk-forward and live replay break down.

2. Isolation

Thousands of strategies run on shared infrastructure. The platform’s reputation hinges on one strategy not affecting another.

  • No file I/O → can’t poison another strategy’s data.
  • No network → can’t make outbound calls that violate rate limits or expose credentials.
  • No reflection → can’t reach into other assemblies.
  • No import → can’t pull in code with its own escape hatches.

Compile-time checks and the interpreter work together: rejected operations have no accepted program representation, and supported operations stay within the RaamVM execution model.

3. Reviewability

A strategy that’s a few classes, a few methods, and arithmetic is auditable in five minutes. A strategy that pulls in third-party packages is auditable in five days. The platform’s published-strategy market depends on auditability.

What you can still do

The subset is large enough that almost every trading idea fits:

  • Arithmetic, comparison, logical operators.
  • Control flow: if, elif, else, for ... in range(n).
  • Built-in functions: int, float, bool, abs, min, max, round, len, sum.
  • F-strings with format specs.
  • Ternary expressions.
  • Helper methods, custom indicators, sessions.
  • Persistent scalar state (int, float, bool, str) and class-declared typed lists and dictionaries.
  • All built-in indicators and order verbs.

See Python Subset → for the full list.

“I really need X”

If a blocked construct seems necessary, there’s almost always a platform primitive that replaces it:

You want RaamCode answer
import numpy for math Built-in min, max, abs, sum, and indicators cover the cases that matter.
print() for debugging Allowed — output appears in the Console / Log tab on the board.
Read a file Sessions and Symbols handle data delivery. Files are not a thing here.
Persistent list of recent values A class-declared typed list, or Series inside a custom indicator.
Long-lived storage Persistent state — within a run. For cross-run memory, use the optimizer (parameters are the persistent thing).

If you find a case that genuinely doesn’t fit, open an issue. Most of them turn out to fit through an existing primitive once we look together.

Code Cause
RC2_IMPORT_BLOCKED import statement.
SDK012 Blocked builtin (e.g. open, eval).
RC2_EXEC_STMT Disallowed statement type (try, with, raise, etc.).