Skip to content
Open RaamTrade

Helper Methods

You can define your own methods on the strategy class and call them from execute(). Helpers keep execute() readable when the logic gets dense.

class RiskManaged(Strategy):
risk_pct = Param(1.0, min=0.5, max=5.0)
stop_distance = Param(2.0, min=0.5, max=10.0)
trade = Symbol()
sizing = FixedQuantity()
def execute(self):
sma = SMA(20, on=self.trade)
self.stop_loss(self.stop_distance)
if self.trade.close > sma.value and self.trade.is_flat:
shares = self.position_size_for_risk(self.risk_pct, self.stop_distance)
self.buy(self.trade, qty=shares, reason="Risk-managed entry")
def position_size_for_risk(self, risk_pct, stop_pct):
risk_amount = self.capital * risk_pct / 100
stop_amount = self.trade.close * stop_pct / 100
if stop_amount > 0:
return int(risk_amount / stop_amount)
return 0

Signature rules

  • First argument must be self (RC2_HELPER_SIGNATURE).
  • All other arguments are positional and untyped.
  • Every call must pass every declared parameter.
def my_helper(self, period, multiplier): # ✅
...

A default value parses, but does not make the argument optional

def my_helper(self, period, multiplier=1.5) is accepted by the parser, and then the arity check counts multiplier anyway — calling self.my_helper(20) is RC2_HELPER_ARITY. Write the parameters you intend callers to pass, and pass all of them.

What helpers can access

Available Notes
Params self.fast, self.risk_pct, etc.
Symbols and OHLCV self.trade.close, self.spy.high.
Position helpers self.trade.is_long, etc.
Persistent state Read and write self.my_counter.
Strategy properties self.capital, self.bar_count.
Other helpers Call them like any method.
Order verbs self.buy(...), self.sell(...) work from helpers too.
Exit rules self.stop_loss(...) works from helpers.

What helpers cannot access

Indicator locals. Indicators live in execute()’s local scope. Helpers don’t see them.

def execute(self):
rsi = RSI(14, on=self.trade)
self.check_signal(rsi) # ✅ pass it in explicitly
def check_signal(self, rsi):
if rsi.value < 30: # ✅ — passed-in indicator works fine
return True
return False
def execute(self):
rsi = RSI(14, on=self.trade)
self.check_signal() # tries to read rsi from helper scope
def check_signal(self):
if rsi.value < 30: # ❌ — rsi is not in scope here
return True

If a helper needs an indicator value, pass it as an argument. Same for any other local variable from execute().

Helpers and persistent state

Helpers read persistent state freely. What they cannot do is rebind a scalar slot: self.counter = … inside a helper does not propagate back to the caller, so rather than let the write vanish, the transpiler refuses it.

class Bad(Strategy):
trade = Symbol()
sizing = FixedQuantity()
counter = 0
def execute(self):
self.bump()
if self.trade.is_flat:
self.buy(self.trade)
def bump(self):
self.counter = self.counter + 1 # ❌ scalar rebind inside a helper
return 0

Index and key writes into a class-body list or dict are allowed from a helper — those mutate through a reference both sides share, so the change survives the return:

class Good(Strategy):
trade = Symbol()
sizing = FixedQuantity()
session_highs = [0.0]*5
counts = {"up": 0, "down": 0}
def execute(self):
self.update_extremes()
if self.trade.close > self.session_highs[0] and self.trade.is_flat:
self.buy(self.trade)
def update_extremes(self):
if self.trade.high > self.session_highs[0]:
self.session_highs[0] = self.trade.high # ✅ index write — propagates
self.counts["up"] += 1 # ✅ key write — propagates
return 0

If a helper really has to change a scalar, have it return the new value and let execute() do the assignment.

Common diagnostics

Code What it means
RC2_HELPER_SIGNATURE First arg isn’t self.
RC2_HELPER_ARITY Helper called with the wrong number of args.
RC2_HELPER_STATE_WRITE Helper rebound a scalar state slot. Index and key writes into collections are fine.