Skip to content
Open RaamTrade

RC2_IND_CYCLE

Indicator <name> references itself (on=<name>). Indicators cannot depend on themselves.

What it means

An indicator’s on= names its input series. Naming itself would mean “my value this bar is computed from my value this bar” — a definition with no base case. There is no ordering of the computation that satisfies it, so the transpiler refuses the declaration outright rather than emitting a graph that cannot be evaluated.

Why it happens

Nearly always a copy-paste: a chain was duplicated and the local name on the left was updated while the on= on the right was not (or the other way round).

class Bad(Strategy):
trade = Symbol()
sizing = FixedCapital()
def execute(self):
smooth = EMA(5, on=smooth) # ❌ smooth is its own input
if smooth.value > 0 and self.trade.is_flat:
self.buy(self.trade)

Note that a longer cycle cannot be written at all. Indicators resolve strictly in source order, so a = EMA(5, on=b) followed by b = SMA(20, on=a) fails on the first line with RC2_IND_ON_UNKNOWN — b is not declared yet. Self-reference is the only cycle the language can express, which is why this diagnostic is about exactly that.

How to fix it

Point on= at the thing the indicator is actually measuring — a symbol, a bar field, or a different indicator declared above it.

class Good(Strategy):
trade = Symbol()
sizing = FixedCapital()
def execute(self):
rsi = RSI(14, on=self.trade)
smooth = EMA(5, on=rsi) # ✅ a different, earlier indicator
if smooth.value > 50 and self.trade.is_flat:
self.buy(self.trade)
if smooth.value < 50 and self.trade.is_long:
self.sell(self.trade, self.trade.position)

If what you wanted was feedback — this bar’s value depending on the previous bar’s — that is not chaining. Keep the previous value in persistent state, or write a custom indicator, where self.<state> and a Series buffer make the recurrence explicit.