For Loops
for loops in execute() and in helper methods support three shapes: range(...) iteration, list iteration, and dict-key iteration.
class Demo(Strategy): trade = Symbol() sizing = FixedCapital() recent_highs = [0.0]*5 counts = {"up": 0, "down": 0} total = 0.0
def execute(self): # Integer ranges for i in range(5): # 0..4 self.total = self.total + self.recent_highs[i]
for i in range(2, 5): # 2..4 self.total = self.total + 1
# List iteration — yields each element for h in self.recent_highs: self.total = self.total + h
# Dict iteration — yields each KEY (Python default) for k in self.counts: self.total = self.total + self.counts[k]range(n) / range(start, stop)
Two argument forms. start defaults to 0; both are exclusive on the upper bound.
class Shift(Strategy): trade = Symbol() sizing = FixedCapital() recent_highs = [0.0]*5
def execute(self): for i in range(4): # i = 0, 1, 2, 3 self.recent_highs[i] = self.recent_highs[i+1] self.recent_highs[4] = self.trade.highThe three-argument range(start, stop, step) is not supported — it raises RC2_FOR_RANGE. For a non-unit step, index arithmetic inside a unit-step loop does the same job.
range(...) is legal only as a for-loop’s iterable. r = range(3) on its own is refused.
Iterating a list
for x in self.<list>: yields each element. The loop variable is a fresh local; its type matches the list’s element type.
class Sum(Strategy): trade = Symbol() sizing = FixedCapital() samples = [0.0, 1.5, 2.25, 3.125, 4.0] total = 0.0
def execute(self): self.total = 0.0 for x in self.samples: self.total = self.total + x
if self.total > 10 and self.trade.is_flat: self.buy(self.trade)Iterating a dict
for k in self.<dict>: yields each key (a string). Pair it with self.<dict>[k] to read the value.
class CountAggregate(Strategy): trade = Symbol() sizing = FixedCapital() counts = {"up": 0, "down": 0, "doji": 0} total = 0
def execute(self): self.total = 0 for k in self.counts: self.total = self.total + self.counts[k]
if self.total > 100 and self.trade.is_flat: self.buy(self.trade).items(), .keys(), and .values() are not supported. Use the key-then-lookup pattern above.
len()
len(self.<list>) and len(self.<dict>) return the element count as an int:
class Sizes(Strategy): trade = Symbol() sizing = FixedCapital() recent_highs = [0.0]*5 counts = {"up": 0, "down": 0, "doji": 0}
def execute(self): n = len(self.recent_highs) # 5 seen = len(self.counts) # 3
if n + seen > 0 and self.trade.is_flat: self.buy(self.trade)len() is supported only on persistent-state lists and dicts — not on strings, locals, or other expressions. Anywhere else it raises RC2_BUILTIN_ARG.
Rolling-window idiom
The canonical use: shift the buffer left, write the newest value at the right edge.
class RollingHigh(Strategy): trade = Symbol() sizing = FixedCapital() recent_highs = [0.0]*5 # 5-bar rolling high buffer
def execute(self): for i in range(4): self.recent_highs[i] = self.recent_highs[i+1] self.recent_highs[4] = self.trade.high
# Compare against the oldest value still in the window if self.trade.close > self.recent_highs[0] and self.trade.is_flat: self.buy(self.trade, reason="Break above the 5-bar high")Loop-variable scope
The loop variable is a local declared by the for statement, and it is not persistent state — it resets on every bar. Reading it after the loop gives its last value, but write a separate named local if you actually need the final index; relying on loop-variable leakage is the kind of thing that reads as a bug on the next pass.
What’s not supported
range(start, stop, step)(the three-argument form)whileloopsbreak/continuefor … elseclausesenumerate(),zip(),reversed(),sorted()— all blocked builtins (SDK012).items()/.keys()/.values()on dictslen()on anything other than a persistent-state list or dict
Common diagnostics
| Code | What it means |
|---|---|
RC2_FOR_TARGET |
The loop variable isn’t a simple name. |
RC2_FOR_ITER |
The iterable isn’t range(...) or a persistent-state list / dict. |
RC2_FOR_RANGE |
range() called with the wrong number of arguments. |
RC2_BUILTIN_ARG |
len(...) called with an unsupported argument. |
SDK012 |
A blocked builtin (map, filter, enumerate, …). |
Related
- Persistent State — where the lists and dicts these loops walk are declared.
- Helper Methods — loops work the same inside a helper.
- Python Subset — the full list of what the language does and doesn’t take.