דלגו לתוכן
פתיחת RaamTrade

Python Subset

תוכן זה אינו זמין עדיין בשפה שלך.

RaamCode is a strict subset of Python. Most everyday constructs work. A handful of categories don’t — and they don’t for specific reasons.

What works

Control flow

if condition:
pass
elif other:
pass
else:
pass
for i in range(n):
...
for x in self.some_list: # a class-body-declared list
pass
for k in self.some_dict: # yields KEYS
pass

pass is the empty-body placeholder. Python’s ... (Ellipsis) is not a statement RaamCode accepts — it raises RC2_EXEC_STMT.

No while, no break / continue, no for … else. range() takes one or two arguments — the three-argument stepped form is refused. Iteration over a list or dict works only for a persistent-state collection declared in the class body; there are no ad-hoc lists. See For Loops.

Arithmetic and comparison

x = a + b * c / d
y = (a + b) % c
z = a ** 2
result = max(a, min(b, c))
# Comparisons
a == b, a != b, a < b, a > b, a <= b, a >= b

All arithmetic on indicator values and OHLCV happens in decimal precision — never float. Mixing types (e.g., decimal * float) is allowed; the result stays in decimal.

Logical operators

a and b
a or b
not a

Short-circuit semantics are preserved (same as Python).

Built-in functions

int(x), float(x), bool(x)
abs(x), min(a, b), max(a, b), round(x), round(x, ndigits)
len(self.my_list), len(self.my_dict)
print(...)

min and max need at least two arguments — min(x) is RC2_BUILTIN_ARG.

len() in a strategy works only on a persistent-state list or dict declared in the class body. Inside a custom indicator’s compute(), len(self.buffer) and sum(self.buffer) work on Series objects — sum is an indicator-side builtin and is not available in strategy code.

Series scans — custom indicators only. These take a declared self.<series> and exist nowhere else; a strategy has no Series-shaped value to hand them, so calling one there is a compile error, not a surprise at run time:

sum(self.buffer), min(self.buffer), max(self.buffer)
stddev(self.buffer)
median(self.buffer)
percentile(self.buffer, p) # p in 0-100
rank(self.buffer, x) # the fraction strictly below x, 0-1

See Custom indicators → Series API for what each answers, including the empty-window and even-window rules.

math.* needs no import: math.sqrt, math.log, and the rest of the standard library are ambient. Writing import math is RC2_IMPORT_BLOCKED, with a message saying exactly that.

F-strings

reason = f"RSI is {rsi.value:.1f}, position={self.trade.position}"
self.buy(self.trade, reason=reason)

Supported format specs: .Nf, .Ne, .Ng, .N%, d, x, X. Anything else — alignment and padding like :>10, thousands separators like :,.2f, explicit signs like :+.2f — raises the warning RC2_FSTRING_SPEC: the strategy still compiles, and the value is interpolated with no formatting applied.

Ternary expressions

size = 100 if self.trade.close > 50 else 50

What’s blocked

Construct Why
import anything No external code. The transpiler can’t reason about what’s inside someone else’s module.
try, except, finally, raise Error handling is the platform’s responsibility. Letting strategies catch and suppress errors hides bugs.
async, await, yield Strategies are pure functions of bar data. Concurrency doesn’t belong here.
with (context managers) All resources are platform-managed. There’s nothing user-level to enter / exit.
lambda Eliminates entire classes of escape mechanism (callable values capturing scope).
global, nonlocal, del State management is bounded. These three break that bound.
eval(), exec(), open() Dynamic-code execution and file I/O are out of scope.
Tuples as state Lists and dicts are supported as persistent state, but only when declared in the class body with a literal (see Persistent State). Tuples are not, and no collection may be built ad hoc inside execute().

Each blocked construct emits a specific diagnostic. See the Error Reference.

Use print(...) in your strategy code exactly like in plain Python — print(*objects, sep=' ', end='\n') (the file= and flush= kwargs are accepted for paste compatibility and ignored). Captured output appears in the Console tab on the run board (during a backtest) or the Log tab (on a live runner). One print(...) call = one row.

class Instrumented(Strategy):
trade = Symbol()
sizing = FixedCapital()
def execute(self):
rsi = RSI(14, on=self.trade)
if rsi.crossed_above(70) and self.trade.is_long:
print("RSI overbought at", self.trade.close)
self.sell(self.trade, self.trade.position)

print is silent on the optimizer’s fast path

print(...) does not slow a strategy down — it keeps the analyzed fast path. But the graph body that path emits contains no print call at all, so an optimization or walk-forward run produces no output. Instrument with print while backtesting; do not expect it during a sweep.

print(...) works in execute() and on_update_indicators(). It is suppressed when used inside a custom indicator’s compute() method (the value is cached across runs, so the call would not re-fire). Each captured row carries the bar timestamp it fired on.

Why a subset?

Three reasons:

  1. Determinism. A strategy must produce identical results in backtest, paper, and live. Anything that can vary based on the environment (network, files, randomness) breaks that.
  2. Isolation. Thousands of strategies run on shared infrastructure. Nothing escapes its own sandbox — and the easiest way to guarantee that is to refuse imports and I/O at compile time, not at runtime.
  3. Reviewability. A strategy that’s a few classes, a few methods, and arithmetic is easy to read. A strategy that pulls in arbitrary dependencies is not.

The subset is large enough that almost any trading idea fits. If yours doesn’t, open an issue — most “I need X” requests turn out to fit through an existing primitive once we look together.

Common diagnostics

Code What it means
RC2_IMPORT_BLOCKED import statement found.
SDK012 Blocked built-in called (eval, open, etc.).
RC2_EXEC_STMT Disallowed statement type in execute() — try, with, while, raise, or a bare ....
RC2_FSTRING_SPEC An f-string format spec the transpiler can’t translate. Warning only.
RC2_BUILTIN_ARG A builtin called with the wrong arguments.