Features
Scripts can declare their own timeframe
indicator()'s script-level timeframe and timeframe_gaps parameters are now
implemented. A script declared with timeframe='W' executes once per completed
weekly bar, aggregated from the chart feed, on the chart bar whose close reaches
the period close resolved from the trading schedule. The other chart bars carry
na with timeframe_gaps=True, or the last value with timeframe_gaps=False.
Output rows stay keyed by chart bars, and the period close never precedes the bar
that closes it, so no higher-timeframe value becomes visible early.
The new ScriptTimeframe driver reuses HTFAggregator with a caller-supplied
period completion and the session-aware close of security.actual_bar_close. It
validates that chart bars nest inside the script period, counts last_bar_index
from the periods actually present in the feed, and raises for live mode, the bar
magnifier and a timeframe lower than the chart's.
timeframe.period, main_period, multiplier, the is* properties and every
"own bar period" site in lib (time(""), time_close, bars_back, the
trading-day and scheduled-grid trackers) now read the run timeframe;
syminfo.period keeps describing the data grid. request.security compares
seconds and modifier when detecting a same-context request, so timeframe='1W'
with a "W" request no longer spawns a child process.
Fixes
Strategy engine
- Simultaneous triggers run in activation order. Orders resting at one price
level trigger at the same instant, and TradingView runs them in the order they
reached the book, not entries first. Each order now carries its activation slot:
a re-issue that keeps its price levels and quantity keeps the resting order's
slot, one that changes them is re-slotted last, and an exit leg issued against a
still-pending entry is slotted when that entry fills. - An exit leg activated behind a reversing entry is no longer cancelled by that
entry's fill. It is locked into the batch with the quantity its binding had
when the batch started, closes what is left and opens the remainder under its own
exit id. A leg bound to a pending entry is grown to the whole fill, including the
part of the entry's frozen flip that found nothing to close. Only the opening part
of a reversal entry is margined; the frozen flip component closes the opposite
position and needs none. - Exit legs are priced off the fill that activated them, not off the first open
trade under the id, so a pyramid add or a position opened earlier by a committed
leg cannot fire a fresh bracket at a stale price. - An intrabar-activated exit leg gets the rest of the bar path. A leg whose
level already stands on the wrong side of the fill price (a long's protective stop
above it, a profit limit already in the money) is triggered the moment the entry
opens the trade and fills at the entry's own price; a leg activated mid-bar can
also cross the stretch between the bar open and the extreme the first leg reached
on the way back. Previously such a leg could only fill from the next bar on. - Pyramid adds are covered by live
strategy.exitcalls. An entry that fills
after astrategy.exitwas issued gets its own leg, sized to that fill and priced
from it, and the call stops covering further adds once one of its legs has fired.
Trailing legs seed their water mark from the entry they are bound to instead of the
oldest FIFO row under thefrom_entry_id, and the trailing pre-walk runs the whole
book segment by segment, so several legs filling on one bar are booked in the order
the assumed path reaches them.
Builtins
array.join()'sseparatoris now optional. TradingView defaults it to the empty
string, but it was declared required, so a Pine script callingarray.join(id)
died with aTypeError.ta.valuewhen()'s occurrence ring is no longer rolled back with the bar.
TradingView fills the ring once per execution of the body and a discarded
re-execution never gives its push back, so on acalc_on_order_fillsbar whose
condition is true the same value is recorded again for every order that fills
there.varassignments andta.cumstay un-doubled on those bars.lib.timeused inside a nested helper declares its history buffer inmainwith
an int element type, so a conditionally called helper readstimehistory on the
chart-bar grid instead of its own call grid.time_closeon D/W/M bars returns the end of the last scheduled session of the
period, including effective-dated session corrections, instead of the open plus a
nominal span.
request.security
- A context's own read now falls back to the caller's default. A security child
replays the whole script, so a context's own read can run before its own write;
that read returned the raw last published value,Noneuntil the first
publication, while every other empty answer in the same function returns the
caller's default. An array context asking for[]received aNonethat no
array.*builtin accepts, and the child process died taking the parent run
with it. - Shared memory is released for contexts that allocate it without spawning a child
process (same-context and ignored contexts).
Performance
- Security ring lookups are binary searches. Every lookup built a Python list of
all live closes just to bisect it, one struct unpack per entry — on the hot path,
sincehas_closeis thewait_for_closepredicate and a consumer of a 1-minute
lower-timeframe context waits thousands of times against a ring holding thousands
of intrabars. The search now reads only the log2(count) entries a binary search
needs; the column is sorted, so the result is identical. Measured on a daily plus
1-minute context over BINANCE:BTCUSDT 30m: 110 million entry reads in 60 seconds of
one consumer and a run past 2100 seconds, now 161 seconds with every plot matching.
Internal
- Plot typing on
fill()arguments, simplified strategy script availability checks,
removed redundant_price_roundoverloads, clarified order variables and hit
typing in the strategy engine.