言語
Script kinds
これらのページは英語です。仕様は一つの言語で保つことで、一つのことだけを語ります。
A T0 file has the extension of what it is. The extension is not decoration: the compiler holds the file to it (E-KIND-EXT), the terminal's script browser filters and badges by it, and the
host refuses to bind or start a file as something it is not.
| Kind | Extension | What it is | Entry point | Who runs it |
|---|---|---|---|---|
| Indicator | .tzi |
component indicator "name" version "x"; -- reads candles and the price, gives a line and signals |
fn compute(cfg, input) -> indicator.Output |
the terminal, about once a second, on the market in hand |
| Control | .tzc |
a program for the terminal: charts, panels, alerts, other scripts | fn main() |
a hook, a trigger line, the editor, another control script |
| Risk | .tzr |
component risk -- authorizes and sizes orders |
authorize, optionally size |
the host, for every order that opens exposure |
| Portfolio | .tzp |
component portfolio -- shares capital among strategies |
allocate |
the host, when it rebalances |
| Strategy | .tzs |
a strategy block or component strategy -- decides entries and exits |
handlers and rules | the backtest engine and the live engine |
| Manager | .tzm |
component manager -- from the candles, says which strategies, risk script and portfolio controller of a playbook are on |
fn decide(cfg, input) -> manager.Decision |
the terminal (live) and the backtest engine, when a candle of the playbook's timeframe closes |
| Program | .tz |
any program; the extension from before kinds existed | fn main() |
as a control program |
| (module) | .tz |
a library file for import; it declares no role |
none | the importing program |
The rule#
A file named for a kind must declare that kind:
guard.tzrmust containcomponent risk …;-- acomponent portfolioin it isE-KIND-EXT, and the message says which extension it belongs to.layout.tzcmust havefn main()and nocomponentheader and nostrategy.trend.tzsmust declare a strategy.filter.tzmmust containcomponent manager …;.- A plain
.tzfile, or a buffer with no name yet, is held to nothing: everything written before kinds keeps compiling and running.
A component is pure: the host calls its entry point with what it needs and it answers; it cannot read the market, touch a file or start anything. A control program is the opposite on purpose -- it exists to do things (with the permissions it was given), so it is a program, not a component.
Who can start whom#
A script is never started by a script on its own: something in the terminal sees the event and starts it, under limits.
| From | Starts | How |
|---|---|---|
| Indicator | a control script or a webhook | each Emit it gives becomes the event indicator_signal; the hooks attached to it run |
| Strategy running live | a control script or a webhook | the strategy's signals become strategy_signal |
Control script (or .tz) |
another control script | script.run(path, args), permission script.run |
| Manager | nothing -- it switches | the host suspends or resumes the playbook's strategies and swaps its risk script and portfolio controller by what the manager decided; every change is the event playbook_changed |
| Risk, portfolio | nothing | they stay pure; what they decide already reaches the hooks (risk_declined, portfolio_rebalanced, …) |
script.run is bounded: the target is a file of the scripts folder and a .tzc or .tz; it holds at most the permissions of the script that started it (and never more than a hook may
hold); the chain is at most three deep; at most 16 arguments of 200 characters; and the terminal serves at most 30 starts a minute, 16 waiting. The started script reads the words it was
given with event.current() (kind script_run, with caller and depth). Nothing waits for it and its end is not reported back.
Playbooks and managers#
A playbook is a named group of scripts -- strategies, risk scripts, a portfolio controller -- and one manager (.tzm) that says which of them are on. The manager is a pure calculation of the closed
candles of the playbook's timeframe (a trend filter, a session window, a volatility switch, anything computable from candles and the price). It is called when a candle closes, never on a tick, in a backtest
and live alike, with the decision it gave last time as its only memory, and it answers with the complete set that should be active: a strategy it does not name is suspended (no new entries; what it
holds keeps its stops and targets), and risk / portfolio name the member in force (None leaves the account's own). The same script gives the same decisions in both places. It reads the market only --
not the account and not a member's profit -- which is what makes that true; the risk firewall already limits losses. The playbook (members, pins, timeframe, fault policy) is a JSON file the Playbooks window keeps.
Telling the world#
A hook on indicator_signal or strategy_signal can be a script or a built-in webhook (Discord, Telegram or JSON, with a message template and an optional filter by the source
indicator or strategy). The webhook goes through the same guards as alert.webhook: https only, no private addresses, one message a second and 20 a minute per address, 1900 characters.
No script needs to be written to be told when a strategy running live signals.
Parameters in a dialog: // @param#
An indicator's Config is what a person adjusts, and in the terminal they adjust it with controls, not by writing JSON: putting an indicator on opens its settings dialog (and each
indicator keeps a Settings button), with one control per field of struct Config, the chart redrawing as it moves; Apply and Done keep it, Cancel and the window's [x] put back what
it was. The control follows the field's type -- a switch for Bool, a box for Int and Float that takes only that, 5000 USDT for Money, 30s for Duration -- and the language has no
attributes to say more, so a comment on the line above the field does:
struct Config {
// @param "Window" min 1 max 200 step 1 default 9 hint "Bars averaged."
window: Int,
// @param "Source" options "close|hl2|ohlc4" default "close" group "Data"
source: String,
}| Word | Takes | Does |
|---|---|---|
| the first quoted text | a label | the name beside the control (otherwise the field's name, max_gross as Max gross) |
min, max |
a number | with both, a slider; with one, the value is checked against it. Only on Int, Float, Decimal, Quantity, Ratio |
step |
a number above 0 | how far a slider moves per notch; it snaps to it |
default |
a value in the form of the box (9, 0.85, true, 5000 USDT) |
what the dialog opens on for a new indicator, and what Defaults restores |
options |
"a|b|c" |
a dropdown of those values; only on a String |
group |
a name | the card the field is in (fields with none share the first, Inputs) |
hint |
a text | a line of help under the control |
The comment is read from the script's own text by the terminal; the compiler ignores it, so it changes nothing about what the script means and a script with none still has a form. A comment that
cannot be honoured -- a range on a Bool, a default outside its range or not in options, a word it does not know, an unclosed quote, a comment with no field after it -- is shown in the dialog
as a note and the rest of it still applies. When a script has // Default config: {..} in its header, that fills in the fields the comments give no default for. A Config can hold only
scalars (and List or Option of them), so a dropdown is always a String with options; a List is typed as JSON.
The settings of any program: marked constants#
A program that is run (a strategy, a monitor) is asked for its settings the same way. A top-level const of type Int, Float, Decimal, Bool, String or Duration with a // @param comment on the line above is a setting; the terminal shows a
dialog with a control for each before the program runs, and runs it with the values given in the place of the written ones -- the file is not changed, and the terminal remembers the values last used for it. The comment takes the same words
as above (a label, min, max, step, default, options, group, hint); a Bool is a switch.
// @param "Deviations of the VWAP" min 0.5 max 5 step 0.5 default 2 group "Setup"
const SD_K: Decimal = 2.0;
// @param "Target at the nearest node" default true group "Exit"
const TP_AT_NODE: Bool = true;A constant with no mark is not a setting. The values reach the program as text in the place of the literal on that line, so every diagnostic still points at the line the person sees. strategies/ideas/absorption_vwap.tzs is written this way.
Decorators on strategy parameters#
A strategy's parameters are what a sweep or an optimizer varies, and the source says which ones and how far, with a decorator written before the parameter (language 0.2):
strategy ema_cross(
@param(label: "Fast EMA", min: 3, max: 30, step: 1, group: "Signal") fast: Int,
@param(min: 20, max: 200, step: 5, above: fast) slow: Int,
@param(min: 0.5, max: 4.0, step: 0.5) stop_atr: Decimal,
size: Quantity, // no decorator: a fixed value, never optimized
) version "1.0.0" { … }| Key | Takes | Does |
|---|---|---|
min, max |
a number written out (a whole number for an Int) |
required (unless the parameter is a switch); the range the optimizer searches (and the bounds the terminal's form checks a typed value against) |
step |
a number above 0 | the grid the value moves on; none means continuous (a Decimal) |
log |
true / false |
spread the range evenly in its logarithm (needs min above 0) |
label, group, hint |
a string | the name, the card and the line of help in the terminal's form |
above, below |
the name of another parameter of the same strategy | an order the search keeps: slow with above: fast is always searched with fast < slow |
switch |
true |
an Int that is 1 (on) or 0 (off), shown as a checkbox; the same as min: 0, max: 1, step: 1, and it takes no min, max, step, log or off |
off |
a number (a whole number for an Int) |
the value that means "this filter is off", for a parameter that has a range when it is on (a ceiling that 0 removes): the terminal shows a checkbox beside the value and sends this one when it is unticked; it is not part of the range searched |
The value the program passes (ema_cross(9, 21, …) in main) is where the form starts; the decorator only says how far it may move. The compiler checks the keys, the values, the order of the range and the
references (E-DECO-UNKNOWN, E-DECO-VALUE, E-DECO-REF; a decorator on a function's parameter is E-DECO-PLACE; a program that says lang ">=0.1.0, <0.2.0" cannot use one: E-VER-FEATURE). It then
puts the range on the signal graph (StrategyParam.tune, IR 0.1.6), which the terminal reads for the Backtests window: a Parameters panel per strategy and an Optimize tab. A decorator never changes
what a strategy does; a strategy with decorators and the same strategy without them are the same strategy.
Examples#
71_level_cross.tzi-- an indicator that givescross-up/cross-downat a level.76_alma.tzi-- the Arnaud Legoux Moving Average as an indicator: its line on the chart, andcross-up/cross-downwhen the price goes through it (its strategy is77_alma_cross.tzs).72_on_level_cross.tzc-- the control script for its signal: chimes and starts another withscript.run.74_trend_filter.tzm-- a manager: trend or range from two averages, with hysteresis, each state naming its strategies and risk script.75_session_hours.tzm-- a manager that is not about candles: strategies and risk script by the hour of the UTC day.61–70-- risk policies (.tzr) and portfolio controllers (.tzp).