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

Strategy Anatomy

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

Every RaamCode strategy follows the same shape. The transpiler enforces it.

class StrategyName(Strategy):
"""Description shown in the catalog."""
# ── Class body: Param(), Symbol(), and sizing ──
fast_period = Param(7, min=2, max=50)
slow_period = Param(21, min=10, max=200)
trade = Symbol()
sizing = FixedCapital()
def execute(self):
# ── 1. Indicators at the top (the transpiler hoists them) ──
fast = SMA(self.fast_period, on=self.trade)
slow = SMA(self.slow_period, on=self.trade)
# ── 2. Exit rules (optional, can be dynamic) ──
self.stop_loss(2.0)
# ── 3. Order logic ──
if fast.crossed_above(slow) and self.trade.is_flat:
self.buy(self.trade, reason="Bullish crossover")
if fast.crossed_below(slow) and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Bearish crossover")

Required pieces

Piece Required? Notes
Inherit from Strategy Yes The only valid base class.
Class name Yes Becomes the strategy name in the catalog. One class per file.
Docstring Recommended Shown as the catalog description.
At least one Symbol() Yes Otherwise: RC2_NO_SYMBOL.
One sizing = … declaration Yes Otherwise: RC2_NO_SIZING.
def execute(self) Yes Otherwise: RC2_NO_EXECUTE.
Helper methods Optional def name(self, args), callable from execute().

Class body rules

The class body may contain only:

  • Param(...) assignments
  • Symbol(...) assignments
  • A single sizing = ... assignment
  • Plain literal assignments (e.g. position_size = 100)

Anything else — function calls, control flow, arithmetic, expressions — emits RC2_CLASS_BODY.

class Bad(Strategy):
trade = Symbol()
sizing = FixedCapital()
threshold = 30 + 5 # ❌ RC2_CLASS_BODY — no expressions
def execute(self):
if self.trade.is_flat:
self.buy(self.trade)
class Good(Strategy):
trade = Symbol()
sizing = FixedCapital()
threshold = 35 # ✅ plain literal — persistent state
overbought = Param(70) # ✅ Param call
def execute(self):
rsi = RSI(14, on=self.trade)
if rsi.value > self.overbought and self.trade.is_long:
self.sell(self.trade, self.trade.position)

Reading a class-body literal alongside an indicator does not compile today

A plain-literal class-body assignment declares persistent state, and reading it back (self.threshold) from an execute() that also declares an indicator currently produces a raw C# error (CS0103) rather than a RaamCode diagnostic. Until that is fixed, express a constant you want to read as a Param() — which is what you usually want anyway, since a Param is tunable and shows up in the optimizer.

Execution order inside execute()

The transpiler hoists every indicator declaration to the top of execute() so each indicator receives every bar — even when your if branch is False. The rest of the body runs in declaration order.

If execute() finishes without firing a signal, the strategy holds its current position. There is no implicit “do nothing else” — holding is the default.

Mode-specific entry points

Method When it runs Common use
execute(self) Every bar after warmup The trading logic.
Helper methods When called by execute() Risk-sizing math, signal helpers.

Multiple symbols

When a strategy declares more than one Symbol(), exactly one must be marked trade=True — that symbol is the only one orders can be placed against. The others are data feeds (read-only OHLCV).

class SPYFiltered(Strategy):
trade = Symbol(session=USEquityDaily, trade=True) # the trading slot
spy = Symbol(ticker="SPY") # data feed
sizing = FixedCapital()
def execute(self):
spy_sma = SMA(50, on=self.spy)
if self.spy.close > spy_sma.value and self.trade.is_flat:
self.buy(self.trade)

More on Symbol() →

Common diagnostics

Code What it means
RC2_NO_SYMBOL No Symbol() declared in the class body.
RC2_NO_TRADE Multiple symbols but none marked trade=True.
RC2_NO_SIZING Missing sizing = ... assignment.
RC2_NO_EXECUTE Strategy has no def execute(self).
RC2_CLASS_BODY Disallowed construct in the class body.