Skip to content
Open RaamTrade

OHLCV Access

Every declared Symbol() exposes the current bar’s price data:

self.trade.close # current bar close
self.trade.open # current bar open
self.trade.high # current bar high
self.trade.low # current bar low
self.trade.volume # current bar volume

All return decimal (financial precision — never float).

OHLCV access works on any symbol slot — trading symbol or data feed.

class SPYFiltered(Strategy):
trade = Symbol(session=USEquityDaily, trade=True)
spy = Symbol(ticker="SPY")
sizing = FixedCapital()
def execute(self):
if self.spy.close > self.spy.open and self.trade.is_flat:
self.buy(self.trade)

Using OHLCV in indicators

Pass an OHLCV field as the on= argument to compute an indicator on something other than close:

high_sma = SMA(20, on=self.trade.high)
low_ema = EMA(10, on=self.trade.low)
vol_sma = SMA(30, on=self.trade.volume)

More on on= →

Bar time

Every symbol slot also exposes the current bar’s timestamp, expressed in that symbol’s session timezone:

self.trade.timestamp.hour # 0–23, in the session's timezone
self.trade.timestamp.minute # 0–59
self.trade.timestamp.weekday() # Monday = 0 … Sunday = 6 (Python's weekday())
self.trade.timestamp.month # January = 1 … December = 12 (Python's month)

A symbol bound to a New York session reads New York wall-clock time; a symbol on a Tel Aviv session reads Israel time. Daylight-saving transitions are handled per bar — “9:30 New York” means 9:30 in New York in January and July alike.

The month follows the same clock: a New York bar at 19:00 on 31 January reads 1 (January), even though that instant is already 1 February in UTC. Like .hour, .month is a field, written without parentheses. Each symbol reads its own bar, so a daily symbol keeps the month of its latest daily bar until the next one arrives.

class SkipFebruary(Strategy):
trade = Symbol()
skip_month = Param(2, min=0, max=12) # 0 skips nothing
sizing = FixedCapital()
def execute(self):
fast = SMA(10, on=self.trade)
slow = SMA(30, on=self.trade)
month = self.trade.timestamp.month
if month == self.skip_month and self.trade.is_long:
self.sell(self.trade, self.trade.position)
if month != self.skip_month and fast.crossed_above(slow) and self.trade.is_flat:
self.buy(self.trade)
class MorningOnly(Strategy):
trade = Symbol()
sizing = FixedCapital()
def execute(self):
t = self.trade.timestamp.hour * 60 + self.trade.timestamp.minute
in_ny_morning = t >= 570 and t < 660 # 09:30–11:00, session-local
if in_ny_morning and self.trade.is_flat:
self.buy(self.trade)

Reading another region’s clock

astimezone(...) re-expresses the same instant in another timezone — Python’s datetime.astimezone, taking an IANA timezone name:

self.trade.timestamp.astimezone("Asia/Jerusalem").hour
self.trade.timestamp.astimezone("America/Chicago").weekday()
self.trade.timestamp.astimezone("Asia/Tokyo").month

The timezone must be a string literal — a typo like "Asia/Jerusalm" fails the compile with RC2_TIMEZONE_UNKNOWN, never a running backtest. astimezone(...) is an access path, not a value: chain it directly to .hour, .minute, .month, or .weekday() rather than storing it in a variable. Python’s no-argument astimezone() (machine-local time) is not supported.

“Current bar” — what does it mean?

self.trade.close always returns the closed bar’s value. By the time execute() runs, the bar has finished forming — its OHLC are final.

For values from earlier bars, you need an indicator. There is no self.trade.close_at(1) for direct historical access — wrap with SMA(1), EMA(1), or a custom indicator using Series:

yesterday = SMA(1, on=self.trade.close).value_at(1)

This is intentional. Direct historical-price arrays would let strategies snapshot arbitrary windows, which complicates the determinism story (warmup, walk-forward, live replay). Indicators encapsulate “look back N bars” with explicit warmup semantics.