Scripting
Write scanner logic as code in fastur/1, the engine's language. Script structure, fields, windows, studies, LogicBlocks and worked examples.
Overview
Every scanner can be written as code. Open a scanner, click Code, and the editor shows the script the scanner runs. Edit it, press Run, and the engine runs the script exactly as written: what you see is what runs.
Scripts are written in fastur/1, the engine's own language. It reads like
Python: lowercase keywords, x if condition else y, name(args) calls and
indentation for blocks.
The editor checks your script against the engine as you type. Problems are underlined on the line they occur, with the engine's own explanation.
Format (Shift+Alt+F) lays the code out in one house style: four-space
indents, key = value fields, one blank line before each block. Run formats
the code too, so every script reads the same way. Formatting never changes
what a script does.
A Script
A script defines one rule (which symbols qualify) and one scan (how the results are shown).
# Stocks moving more than 2% today
prev_close = d1.close[1]
logicblock MOVE_PCT = (trade.last - prev_close) / prev_close * 100
rule movers:
on = cadence.trade
when = MOVE_PCT > 2 and trade.last > 5
scan "Movers":
rule = movers
universe = "ALL"
columns = [trade.last, MOVE_PCT, trade.last - prev_close, trade.volume]
sort = MOVE_PCT
extract = descending| Part | Meaning |
|---|---|
name = expr | A helper, used only inside this script |
logicblock NAME = expr | A named signal; save it to reuse it in other scans |
rule NAME: | on is when it is evaluated, when the condition |
scan "Label": | rule, universe, columns, sort, optional extract |
ontakes a cadence:cadence.trade,cadence.quote,cadence.s30,cadence.m1,cadence.m5orcadence.daily.columnslists up to 10 expressions, the sort included. The results table shows them in this order, headed as written.extractisdescendingorascending.- A scanner runs exactly one scan. Triggers and watchlists are not written in the scanner's Code tab.
Values
Tick Values
| Spelling | Meaning |
|---|---|
trade.last | Last trade price |
trade.size | Last trade size |
trade.volume | Tick volume |
quote.bid, quote.ask | Current quote |
quote.bid_size, quote.ask_size | Current quote sizes |
clock.time | Eastern seconds since midnight |
clock.time is a number: 09:30 is 34200, 10:30 is 37800, 16:00 is
57600.
Bars
Bars are written <timeframe>.<field>. Timeframes are d1 (daily), w1
(weekly), m1 m3 m5 m10 m15 m30 m60 (minutes) and s15 s30 (seconds).
| Field | Meaning |
|---|---|
open, high, low, close | OHLC |
volume | Share volume |
vwap | Volume-weighted average |
range | High minus low |
money | Dollar volume |
count | Number of trades |
[0] is the current, still-forming bar and [1] the last completed one.
d1.close[1] is yesterday's close; d1.volume is today's volume so far.
Windows
A window covers several bars and is always reduced to one value:
d1.close[1:20].mean
d1.high[1:20].max
m1.high[t"04:00":t"09:29"].max
m1.volume[0:14].sum- Windows are inclusive:
[1:20]is 20 bars. - Reducers:
.mean,.max,.min,.sum,.range,.std,.slope,.bar_count, and more. - Exclude the current bar when you compare against it. A new 20-day high
is
trade.last > d1.high[1:20].max. A window starting at[0]contains today, so the comparison can never be true. - Time windows (
t"HH:MM") cover today only, on minute and second bars.
Studies
Eight studies are available under ta.:
| Call | Outputs |
|---|---|
ta.sma(series, n) | value |
ta.ema(series, n) | value |
ta.rma(series, n) | value |
ta.rsi(series, n) | value |
ta.atr(timeframe, n) | value |
ta.bb(series, n, mult) | .upper .middle .lower |
ta.macd(series, fast, slow, signal) | .line .signal .hist |
ta.stoch(timeframe, n, k, d) | .k .d |
series is a bar field such as m5.close or d1.volume; ta.atr and
ta.stoch take the timeframe itself: ta.atr(d1, 14). A study reads the
last completed bar.
Logic and Operators
- Combine conditions with
and,or,not. Equality is==, inequality!=. - Comparisons do not chain: write
a < b and b < c, nota < b < c. &and|are bitwise operators, not logic.x if condition else ypicks a value.- Booleans are
TrueandFalse.
LogicBlocks
LogicBlocks are named signals you reuse across scans, such as CHGPCT (change
from yesterday's close, as a ratio: 0.05 is 5%), AVGVOL21 or your own.
- Write a LogicBlock from your library by its name; the scanner imports it.
- A
logicblockline in a script is private to that scan until you save it: click Save as LogicBlock above the editor. A saved LogicBlock must stand on its own, so it cannot use the script's helpers. - LogicBlocks whose description says "(%)" are already multiplied by 100:
a 3% gap is
GUP >= 3.
Examples
Breakout Above the Last Five Minutes
rule hot:
on = cadence.trade
when = trade.last > m1.high[1:6].max
scan "Breakout":
rule = hot
universe = "ALL"
columns = [trade.last, m1.high[1:6].max, ta.rsi(m1.close, 14)]
sort = ta.rsi(m1.close, 14)Liquid Names Up on the Day
rule up:
on = cadence.trade
when = trade.last > 5 and trade.volume > 100000
scan "Price Up":
rule = up
universe = "ALL"
columns = [trade.last, trade.volume]
sort = trade.volumeThe Assistant
The assistant beside the editor writes and changes scripts with you. Ask for a new scan or a change to the current one ("only stocks above $5", "add relative volume as a column"). Every script it proposes is checked on the engine first and shown as a change you can Apply or Discard.